Process : 실행중인 프로그램
각각의 프로세스는 실행에 필요한 리소스를 차지(occupy)한다.
Thread : 프로그램이 동시에 실행되는 두 개 이상의 작업으로 분할하는 방법
스레드의 구성 : 스레드ID, 프로그램 카운터, 레지스터 집합, 스택 등등

single thread = 1프로세스 1스레드. / multi thread = 1프로세스 여러스레드
스레드 제어 블록(TCB)은 운영 체제 커널 내에서 관리하기 위해 필요한 스레드별 정보를 담고 있는 데이터 구조이다.
TCB에 포함될 수 있는 정보의 예시
이러한 정보는 각 스레드의 상태 및 실행에 필요한 모든 정보를 제공하여 스레드를 효과적으로 관리할 수 있도록 합니다. 예를 들어, 스레드의 상태를 추적하고 스레드 간의 전환을 수행하는 데 필요한 정보를 제공합니다. 또한 스레드가 실행되는 동안 프로그램 카운터 및 레지스터 값을 추적하여 스레드가 중단되고 다시 시작될 때 이전 상태로 복원할 수 있습니다.
프로세스 생성은 시간 및 리소스 측면에서 비용이 많이 든다.
ex) 웹 서버는 수천개의 요청을 받는다

프로그래머가 Multicore 또는 multiprocessor 시스템을 구현하기 위해서는 다음과 같은 도전과제를 극복해야 한다.
Parallelism(병렬화) : 시스템이 하나 이상의 작업을 동시에 수행할 수 있음을 의미한다
Concurrecncy(동시성) : 진행 중인 두 가지 이상의 작업을 지원한다
Concurrency vs. Parallelism

Concurrency - 하나 이상의 작업이 모두 다 조금씩 진행 (단일코어에서)
Parallelism - 완전히 동시에 실행 (멀티코어에서 가능)

직렬 및 병렬 구성 요소가 모두 있는 어플리케이션에 추가 코어를 추가하여 성능 향상을 식별한다.
S : 병렬 불가능한 부분, N : 코어 개수

ex) 어플리케이션이 75% 병렬, 25% 직렬인 경우 1코어에서 2코어로 이동하면 속도가 1.6배 빨라진다
2코어에서 4코어로 이동하면 속도가 1.6배에서 2.28배로 빨라진다
N이 무한대에 가까워지면 속도는 1/S에 가까워진다.
어플리케이션의 직렬부분은 추가 코어를 추가함으로써 얻을 수 있는 성능에 불균형적인 영향을 미친다.

대부분의 운영 체제 커널은 일반적으로 멀티 스레드이다. windows, Linux, macOS를 포함한 거의 모든 최신 운영 체제 → 커널 스레드 지원
각 스레드는 장치 관리, 메모리 관리, 인터럽트 핸들링과 같은 특정 작업을 수행한다
주요 이슈 : user thread-kernel thread 사이의 통신(correspondence)
세가지 멀티스레딩 모델들
단일 kernel 스레드에 많은 user-level 스레드가 매핑된다.
(-) 하나의 스레드가 차단되면 모두 차단된다.
(-) 한 번에 하나의 스레드만 커널에 있을 수 있으므로 다중 스레드는 multicore 시스템에서 병렬로 실행되지 않을 수 있다.

리눅스에서 많이 사용 - 심플!
각 user-level 스레드가 kernel 스레드에 매핑된다.
user-level 스레드를 생성하면 kernel 스레드가 생성된다.
(+) Many-to-One보다 더 많은 동시성
(-) 오버헤드로 인해 때때로 프로세스 당 스레드 수가 제한된다.

다수의 user-level 스레드를 다수(더 작거나 같은 수)의 kernel 스레드로 매핑한다.
(+) OS가 충분한 수의 kernel 스레드를 생성할 수 있도록 한다

num(user threads) >= num(kernel threads)
Many-to-Many와 비슷하지만, user 스레드를 kernel 스레드에 바인딩할 수도 있다.

num(user threads) >= num(kernel threads) && One-to-One model 가능
스레드 라이브러리(thread library)는 프로그래머에게 스레드 생성 및 관리를 위한 API를 제공한다.
구현하는 두 가지 주요 방법
user-level 또는 kernel-level로 제공될 수 있다.
스레드 생성 및 동기화를 위한 POSIX 표준 API
사양(specification), 구현 X(not implementation)
API는 스레드 라이브러리의 동작을 지정하며, 구현은 라이브러리의 개발에 달려있다.
UNIX OS에서 공통적으로 사용된다. (Solaris, Linux, Mac OS X) ????????????????
pthread_create() : 새 스레드를 생성한다
#include <pthread.h>
int pthread_create(pthread_t **thread*, const pthread_attr_t**attr*,
void*(**start_routine*)(void*), void**arg*);
프로세스를 호출해서 새 스레드를 시작한다
thread : 성공했을 때의 새 스레드 ID
attr : 스레드 특성 설정. 기본값은 NULL
start_routine : 스레드가 생성될 때 실행할 루틴
arg : 루틴을 시작하기위해 argument 전달
return 값 - 성공 : 0 / 실패 : error number
pthread_join() : 종료된 스레드에 연결한다.

지정한 스레드가 돌아올 때까지 호출 스레드를 차단한다
상위 스레드가 반환되면 모든 하위 스레드가 종료된다.
//ex) pthreads code_10개의 스레드를 연결하는
#define NUM_THREADS 10
//an array of threads to be joined upon
pthread_t workers[NUM_THREADS];
for(int i=0; i<NUM_THREADS; i++)
pthread_join(workers[i], NULL);
pthread_cancel() : 스레드에 취소 요청을 보낸다
자바 스레드는 JVM에서 관리한다.
일반적으로 기본 OS에서 제공하는 스레드 모델을 사용하여 구현된다
자바 스레드는 다음에 의해 생성될 수 있다.
//Runnable 인터페이스 구현
public interface Runnable {
public abstract void run();
}자바는 명시적으로 스레드를 생성하는 대신, 실행자 인터페이스를 중심으로 스레드를 생성할 수 있다.
암묵적 스레딩(Implicit threading) : 프로그래머가 아니라 컴파일러와 런타임 라이브러리에 의해 스레드가 생성 및 관리되는 것
스레딩의 생성과 관리 책임을 프로그래머→컴파일러와 실행시간 라이브러리에게 넘겨주는 Implicit threading을 사용하면 멀티 스레딩을 효율적으로 사용할 수 있다.
세가지 방법
기타 : Grand Central Dispatch, Intel Thread Building Blocks (TBB) 등등
작업 대기 중인 풀에 여러 개의 스레드 생성
(+) 일반적으로 새 스레드를 만드는 것보다 기존 스레드로 요청을 처리하는 속도가 약간 더 빠르다
(+) 어플리케이션의 스레드 수를 풀 크기로 바인딩할 수 있다
(+) 수행할 작업을 생성하는 메커니즘과 분리하면 작업을 실행하기 위한 다양한 전략을 사용할 수 있다
i.e. 작업이 시간 지연 후에 실행되거나 정기적으로 실행되도록 예약할 수 있다.
java.util.concurrent package 자바 실행 프레임워크에서 지원된다.
thread pool을 만드는 세가지 방법
+code
여러개의 스레드(작업)를 fork한 다음 결합(join)한다

ForkJoinTask는 추상적인 기본 클래스이다.
RecursiveTask, RecursiveAction 클래스는 ForkJoinTask를 확장한다.

+code
shared-memory(IPC) 환경에서 병렬 프로그래밍을 지원한다.
코어 수만큼 스레드를 생성한다.
#pragma amp parallel
루프를 병렬로 실행한다.
+code
멀티스레드 프로세스에서 fork()
멀티스레드 프로세스에서 exec()
스레드 취소의 문제점
두 가지 일반적인 접근 방식
thread cancellation을 호출하면 취소가 요청되지만, 실제 취소는 스레드의 상태에 따라 다르다.

스레드 취소가 비활성화(disabled)되어 있으면 스레드가 활성화(enabled)할 때까지 취소가 보류상태로 유지된다.
기본 타입은 deferred이다.
리눅스 시스템에서 스레드 취소는 신호(signal)를 통해 처리된다.
프로세스에서, 모든 스레드는 전역 변수를 공유한다.
Thread-local storage(TLS)를 사용하면 각 스레드가 자체 데이터 복사본을 가질 수 있다.
__thread int tls; //on pthread
각 스레드에는 고유한 ‘int tls’ 변수가 있다
지역 변수와 다르다
정적 데이터와 유사하다. 그러나 TLS는 각 스레드마다 고유하다.
스레드 생성 프로세스를 제어하지 못할 때 (스레드 pool을 사용할 때) 유용하다.
M:M 및 Two-level 모델 모두 어플리케이션에 할당된 적절한 수의 커널 스레드를 유지하기 위해 통신이 필요하다.
일반적으로 사용자 스레드와 커널 스레드 간에 중간 데이터 구조를 사용한다 - lightweight process (LWP)

이 통신을 통해 어플리케이션은 올바른 수의 커널 스레드를 유지할 수 있다.
신호 : 특정 이벤트가 발생했음을 프로세스에 알리기 위해 UNIX에서 제공하는 메커니즘
신호의 타입
신호 기질/성향 (signal disposition) (신호와 관련된 동작)
SIG_IGN으로 설정하여 신호를 무시할 수 있다.SIG_DFL로 설정하여 default disposition을 설정할 수 있다.
signal() 신호와 관련된 작업을 정의한다
sigaction() 신호 작업을 검사 및 변경한다
+code
신호를 어떤 스레드로 전달해야 하나?
가능한 옵션
다른 방식 : 신호를 전달할 스레드를 지정한다.
ex) pthread_kill(did, signal) in POSIX
리눅스는 thread보다 task라고 말한다.
스레드 생성은 clone() 시스템 콜을 통해 이루어진다
clone()을 사용하면 자식 태스크가 부모 태스크(프로세스)의 주소 공간을 공유할 수 있다.

struck task_struct는 프로세스 데이터 구조(공유 또는 고유)를 가리킨다.
(출처)
Operating System Concepts 도서
https://www.booksfree.org/operating-system-concepts-10th-edition-by-abraham-silberschatz-peter-b-galvin-greg-gagne-pdf/