쓰레드와 프로세스
- 프로세스가 운영체제에 의한 스케줄링의 단위였다면 실제 처리 단위는 쓰레드
- 모든 프로세스는 메인쓰레드라는 적어도 하나의 쓰레드를 가지고 있음
- 프로세스는 쓰레드 단위로 처리
- 1개의 쓰레드가 곧 1개의 실행 흐름
- 쓰레드는 프로세스를 구성하는 프로세스보다 작은 단위의 실행 흐름
- 프로세스는 서로 다른 프로세스가 각각의 메모리 공간을 점유
- 운영체제로부터 Protection Domain을 지원받음
- 이를 통해 공간의 독립성을 유지
- 쓰레드는 각 쓰레드가 가진 메모리 공간이 Protection Domain을 지원하지 않음
- 각 쓰레드가 서로의 메모리 공간을 읽고 쓰는 등의 행위가 가능
- 하나의 쓰레드로 구성된 하나의 프로세스는 단일 흐름으로 작업을 처리
- 이 프로세스를 여러 흐름으로 나눠 다수의 쓰레드를 두게 되면 병렬적으로 작업을 완료할 수 있음
- 병렬성을 잘 보장하도록 쓰레드 처리를 하면 값비싼 Context switch가 자주 요구되지 않을 수 있음
- 여러 프로세스가 협업하는 방식을 여러 쓰레드가 작업하게 하면 훨씬 적은 비용으로 동일 작업 가능
- 단, 프로세스 내의 쓰레드가 동일한 메모리 공간을 공유하기 때문에 동기화에 신경 써야하함
프로세스 당 쓰레드의 수
- 적절한 병렬성을 보장 받기 위해 필요한 쓰레드 수는 머신이 처리할 수 있는 논리 코어에 의존적
- 예를 들어 8코어 16쓰레드의 CPU를 사용한다면 한 번에 16개의 쓰레드를 처리 가능 (커널에서 쓰레딩을 지원해야 이런 기능이 지원됨)
- CPU 스펙의 16쓰레드는 프로세스의 쓰레드와 다른 개념
- 여기서 16쓰레드는 논리 코어로 작동함

- 여기서 16쓰레드는 논리 코어로 작동함
- 논리 코어 개수에 맞게 쓰레드를 생성한다고 끝나지 않음
- 컴퓨터의 동작은 CPU에만 의존하는 것이 아니라 다양한 하드웨어에 의존함
- 서버 작업에서 DB를 참조하여 데이터를 찾아내는 식의 디바이스 타임을 많이 요구하는 작업이 있을 경우, 이 작업은 CPU 연산을 요구하지 않으므로 해당 CPU는 다른 쓰레드를 처리할 수 있음
- 즉, 디바이스 타임을 고려하여 쓰레드를 생성해야 함
- 적합한 쓰레드 개수
- 프로그램 내에 처리해야 하는 작업들이 디바이스 타임을 요구하지 않는다면 논리 코어의 수만큼 쓰레드를 두는 것이 적합
- 프로그램이 디바이스 타임을 요구한다면 그에 비례하게 쓰레드를 두는 것이 적합
- 1/4가 디바이스 타임을 요구하지 않는 작업, 3/4가 요구하는 작업이면 논리 코어의 3배만큼 쓰레드를 둬야 하는 것이 적합

- Tread Switch는 프로세스 간의 Context Switch보다 가벼운 작업
- 처리할 수 있는 논리 코어 수보다 쓰레드의 수가 더 많으면 쓰레드를 처리하는 시간보다 Thread Switch에 더 많은 시간을 소모하게 됨
- 이 때문에 CPU 처리량은 오히려 낮아짐
- 쓰레드 수가 많을수록 CPU의 활용량이 높아지긴 하지만 처리량이 높아지는 것은 아님을 명심해야 함
- Thread Switch가 가벼운 이유
- 동일한 프로세스 내의 쓰레드들은 Code Segment, Data Segment 및 프로세스 내에서 열어둔 파일 등에 대해서 공유하기 때문
- 각 쓰레드의 Stack에 대해서만 교체하면 됨
- 각 쓰레드는 서로 실행할 Instruction이 다르기 때문에 별도의 PC(Program Counter)를 가지고 있음
쓰레드 실행에 필요한 자료구조
https://junghyun100.github.io/%ED%9E%99-%EC%8A%A4%ED%83%9D%EC%B0%A8%EC%9D%B4%EC%A0%90/ https://devraphy.tistory.com/m/298
- 생성된 쓰레드들 중 어떤 것을 실행할지에 대해 구분해야 하므로 쓰레드 ID가 필요
- 쓰레드는 프로세스의 여러 메모리 영역을 공유하지만, 각 쓰레드의 Stack은 분리되어 있음
- 쓰레드마다 각기 다른 실행흐름을 가지므로 별도의 **PC(Program Counter)**를 가지고 있음
- 별도의 PC를 가지고 있으므로 별도의 Register Set이 필요함
User-Level 쓰레드 & Kernel-Level 쓰레드
https://www.crocus.co.kr/1255 https://velog.io/@grace0st/%EC%BB%A4%EB%84%90-%EB%A0%88%EB%B2%A8-thread-vs-%EC%9C%A0%EC%A0%80-%EB%A0%88%EB%B2%A8-thread https://youtu.be/sOt80Kw0Ols https://youtu.be/4y5BgddMY7o
특징
- 쓰레드는 User-Level 쓰레드와 Kernel-Level 쓰레드 2가지로 나뉨
- 사용자들이 주로 사용하는 쓰레드는 User-Level 쓰레드
- 별도로 속성에 대한 설정을 하지 않은 채 생성하면 User-Level 쓰레드를 이용하게 됨
- User-Level 쓰레드
- 커널 위에서 지원되는 쓰레드
- User-Level의 라이브러리를 통해 구현됨
- 라이브러리를 이용하여 쓰레드를 생성하고 동일한 메모리 영역을 공유한채 생성됨
- Kernel-Level 쓰레드
- 운영체제에서 쓰레딩을 지원하는 경우 이용 가능
- 커널 내에서 사용할 쓰레드를 의미
- 스케줄링 등을 관리하기 위해 사용됨
동작 방식

- User-Level 쓰레드는 Kernel-Level 쓰레드보다 상대적으로 속도가 빠름
- User-Level 쓰레드는 동일한 메모리 영역에서 생성되고 관리되기 때문
- 만약 운영체제에서 쓰레딩을 지원하지 않아 Kernel-Level 쓰레드를 두는 것이 불가능 한 경우
- 커널은 User-Level 쓰레드를 하나의 프로세스로 인식함
- User-Level 쓰레드 중 하나에서 시스템콜과 같은 Trap발생 할 경우 나머지 쓰레드도 Block됨
- 운영체제에서 쓰레딩을 지원하면 Kernel-Level 쓰레드를 이용할 수 있음
- Kernel-Level 쓰레드가 스케줄링에 관여하므로 User-Level 쓰레드의 시스템 콜로 인한 Block을 막을 수 있음
- User-Level 쓰레드가 시스템 콜을 호출해 Block이 발생해도 또 다른 Kernel-Level 쓰레드를 실행
- 또 다른 Kernel-Level 쓰레드가 다른 User-Level 쓰레드를 맡음으로써 전체적으로는 Block이 발생하지 않음
- Running 상태의 프로세스들이 보유하기 있는 각 User-Level 쓰레드는 Kernel-Level를 점유하고자 함
- 운영체제에서는 Kernel-Level 쓰레드가 스케줄링을 위한 하나의 프로세스이면서 하나의 처리단위임
- Kernel-Level 쓰레드를 어떻게 구성하느냐가 성능과 직결될 수 있음
- User-Level 쓰레드와 Kernel-Level 쓰레드의 구성을 Model이라고 함
- 두 종류의 쓰레드는 각각 존재하는 것이 아니라 서로 맞물리며 동작함
- 동일 프로세스 내에 존재하는 여러 User-Level 쓰레드들은 Kernel-Level 쓰레드들에 의해 Mapping 되어 동작함
- 여러 User-Level 쓰레드들이 하나의 Kernel-Level 쓰레드에 Mapping, 여러 Kernel-Level 쓰레드에 Mapping 될수도 있음
- 운영체제의 Model에 따라 Mapping 방식이 달라짐
참고
- 커널 내부에서 사용할 쓰레드를 두는 것은 기존에 Interrupt를 받지 않고 처리만 하던 커널이 Trap에 따른 Block을 만들지 않기 위해 Interrupt를 받는 것도 허용하겠다는 것
- 이에 따른 동기화가 요구되므로 User-Level보다 복잡한 구조를 가짐
Model
https://code-lab1.tistory.com/44
- User-Level 쓰레드와 Kernel-Level 쓰레드의 Mapping 관계를 의미
Many-to-One

- 여러 User-Level 쓰레드가 하나의 Kernel-Level 쓰레드로 Mapping 되는 Model
- 주로 운영체제가 쓰레딩을 지원하지 못할 경우 사용됨
- 한 번에 하나의 Kernel-Level 쓰레드만이 커널에 접근할 수 있음
- 시스템 콜을 호출하려는 User-Level 쓰레드가 Kernen-Level 쓰레드에 Mapping되면 나머지 User-Level 쓰레드는 Block됨
- 커널 입장에서는 Mapping된 여러 User-Level 쓰레드들을 하나의 프로세스로 취급
- 멀티 프로세싱 환경이더라도 여러 프로세스의 동시 처리는 불가능
One-to-One

- 사용하고 있는 User-Level 쓰레드들을 그대로 Kernel-Level 쓰레드에 Mapping하는 Model
- Many-to-One Model에서 문제가 되었던 User-Level 쓰레드들이 Block 되는 현상을 해결할 수 잇음
- 커널 입장에서는 각 User-Level 쓰레드를 별개의 프로세스로 취급
- 시스템 콜 호출에도 Block 되지 않고 동시에 처리할 수 있게 됨
- 멀티 프로세싱 환경의 이점을 챙길 수 있음
- User-Level 쓰레드의 수만큼 Kernel-Level 쓰레드를 두어야 하기 때문에 자원이 많이 들음
Many-to-Many

- 여러 User-Level 쓰레드들을 커널에 존재하는 여러 Kernel-Level 쓰레드로 Mapping하는 Model
- Many-to-Many의 Kernel-Level 쓰레드는 User-Level 쓰레드와 동일하거나 적은 수만큼 적절히 생성됨
- One-to-One과 같이 쓰레드 개수에 대해 고민할 필요가 없음
- User-Level 쓰레드에서 시스템 콜을 호출한다고 해도 나머지 쓰레드가 Block되지 않음
- 이와 같은 모델은 프로세스 당 사용할 수 있는 Kernel-Level 쓰레드에 대해 최대 개수를 따로 정해둠
