Thread
Review of Process
- Process - abstraction for
- Execution unit - 스케줄링 단위
- 프로그램은 스케줄링의 단위가 될 수 없다. 하나의 프로그램 안에서 여러 개의 프로세스가 발생할 수 있기 때문이다.
- Protection domain - 소유하고 있는 자원에 대한 보호
- Execution unit - 스케줄링 단위
- Program과 Process 관계
- Executing program with a single thread of control
- 지금까지의 프로세스 개념
- 하나의 실행 흐름을 가지고 실행 중인 프로그램
- 지금까지의 프로세스 개념
- 1 개의 실행 흐름을 여러 개로 만든다면? -> thread 개념의 시작
- if 문과 같은 분기점도 실행 흐름을 여러개 갖는 것 같아 보일 수 있지만 X
- Executing program with a single thread of control
Thread란 무엇인가?
- 정의
- Execution Unit
- 프로세스 내의 실행 흐름
- 프로세스보다 작은 단위(finer grain)
- 프로세스와 같은 protection domain은 없음
- 스레드가 서로 간의 데이터에 접근 가능
- 동기
- 하나의 프로세스에는 하나의 control만이 존재하기 때문에 한 번에 하나의 일만을 처리
- Single Thread : 하나의 프로세스 안에는 하나의 core만 사용이 가능하므로 core가 여러개여도 성능 개선이 전혀 안되며, CPU자체의 clock 성능을 높이는 수 밖에 없다.
- 프로세스에서 할 작업을 여러 개로 나눈 후 (Execution Flow가 여러개로 나눠짐)에 각각을 thread화 한다면 병렬적으로 작업을 완수할 수 있음
- 이렇게 되면 한 개의 프로세스가 여러 개의 core를 동시에 사용할 수 있게 된다.
- Cooperative process와의 차이점
- Cooperative process는 서로 다른 Protection Domain이므로
- 프로세스간 통신(IPC) 비용
- 프로세스 간의 문맥 교환 비용
- Process 내에서 cooperation하는 thread로 만든다면
- Process보다 적은 비용으로 cooperative process가 하는 일을 동일하게 수행 가능
- Cooperative process는 서로 다른 Protection Domain이므로
- 하나의 프로세스에는 하나의 control만이 존재하기 때문에 한 번에 하나의 일만을 처리
Thread와 CPU utilization
-
Thread의 수와 Throughput(처리량)
-
Thread의 수가 증가할수록 CPU의 utilization이 증가
- 임계값을 넘어가면 다시 감소 -> thread switching 비용이 증가함
- CPU(core를 의미)의 수가 많은 시스템일수록 thread를 이용하는 게 유리
- Multi-processor, 즉 CPU의 수가 많을수록 한 process의 여러 thread를 parallel하게 실행
Process와 Thread
- Process
- 하나의 thread(실행 흐름)을 가짐
- Process간의 memory는 독립적이므로, 서로의 영역을 접근 못함
- Process간의 switching 비용이 큼 (상대적으로 heavyweight)
- Thread
- 하나의 프로세스 안에 여러 개의 thread가 존재
- 프로세스의 code 영역과 data 영역은 thread간에 공유
- thread들은 같은 memory 영역을 공유하여 thread간 switching 비용이 적음 (process switching보다 lightweight)
- Called “lightweight process” (lwp)
Thread의 구성 요소
- Thread도 실행에 관련된 자료 구조가 필요 (process는 PCB가 필요했던 것처럼)
- Thread ID - thread 식별자
- Program counter - 현재 실행중인 instruction의 주소
- Register set - CPU의 레지스터 값들
- Stack per thread (execution하는 동안의 임시 메모리)
- 동일한 프로세스 내에 있는 thread가 공유하는 것
- Code - 프로그램의 code section
- Data - 프로세스의 data section
- File - 프로세스에서 open한 file (이것은 디스크에 존재하는 file 그자체가 아니라 해당 file에 대해 임시적으로 생성된 커널 내 자료구조를 말한다.)
- 위의 이유로 thread switching 비용이 적음
단일 thread와 multithreaded process
- code, data와 files는 여러 개의 thread가 공유하되,
- stack과 registers는 thread마다 각각 존재한다.
Myltithread Program의 장점
- Responsiveness
- 프로그램의 어느 부분을 담당하는 thread가 block되거나 시간이 걸리는 작업을 하더라도, 다른 thread들은 실행되고 있기 때문에 user의 입장에서는 그 프로그램이 interactive하다고 볼 수 있음
- 다만, 여러 개의 thread가 read가 아닌 write를 하고 있을 때에는 consistance를 맞춰 덮어쓰기가 발생하지 않도록 해야한다. (동기화)
- 이것은 read only인 code보다, read&write가 가능한 data에서 조심해야 한다.
- Single Thread 프로세스는 block되면, 해당 프로세스는 아예 멈추고 실행이 다른 프로세스로 넘어가게 된다.
- 프로그램의 어느 부분을 담당하는 thread가 block되거나 시간이 걸리는 작업을 하더라도, 다른 thread들은 실행되고 있기 때문에 user의 입장에서는 그 프로그램이 interactive하다고 볼 수 있음
- Resource sharing
- Thread들간에는 프로세스의 memory와 다른 자원들을 공유
- Economy
- Thread들은 단일 프로세스 memory영역에서 실행되기 때문에 새로운 프로세스를 생성하는 것보다 thread를 생성하는 것이 비용이 적게 들어감
- 같은 개수의 process를 만드는 것보다 같은 개수의 thread를 만드는 것이 더 비용이 적게 들어간다.
- Thread들은 단일 프로세스 memory영역에서 실행되기 때문에 새로운 프로세스를 생성하는 것보다 thread를 생성하는 것이 비용이 적게 들어감
- Scalability (core가 늘어날수록 성능이 늘어나는 능력)
- 여러 개의 thread가 각각 다른 processor에서 동시에 실행 가능(parallelism)
Multicore Programming
- 최근 프로세스 설계 동향은 하나의 chip에 여러 개의 computing core을 탑재 -> multicore processor
- multithread programming에 아주 적합한 하드웨어
- GPU 또한 요즘에는 core의 수가 4000개를 넘어서는 것도 있다.
- Multithread programming은 multicore 시스템에서도 효율적
- Muticore processor는 운영체제에서 각각의 core를 하나의 프로세서로 인식하고 스케줄링
- 각각의 thread를 core에 할당하여 실행 가능
- multicore는 cache를 공유하기 때문에, data, code 등 프로세스의 자원을 공유하는 multithreaded programming에 보다 효과적임
- multi processor는 cache를 공유하지 않기 때문에 Cache coherency protocol이 별도로 필요하다. 즉, 여러개의 프로세서들이 같은 메모리를 동시에 쓰게 되면, 그 값을 invalidation을 해주는 과정이 계속 필요하다.
User and Kernel threads - User thread
- Thread를 지원하는 주체에 따라 나뉨
- User thread - User Space에 존재
- Kernel thread - Kernel에 존재
- User threads
- 커널 영역 위에서 지원되며 일반적으로 user level의 라이브러리를 통해 구현됨
- 라이브러리에서 thread를 생성, 스케줄링과 관련된 관리를 해줌
- 장점
- 동일한 메모리 영역에서 thread가 생성, 관리되므로 이에 대한 속도가 빠르다.
- 단점
- 여러 개의 user thread 중에서 하나의 thread가 system call의 요청으로 block이 된다면 나머지 모든 thread 역시 block된다.
- Kernel은 여러 개의 user thread들을 하나의 process로 간주하기 때문
- user thread에 대해 할당된 메모리는 kernel 공간에서 할당된 것이 아니다.
- Kernel은 여러 개의 user thread들을 하나의 process로 간주하기 때문
- 여러 개의 user thread 중에서 하나의 thread가 system call의 요청으로 block이 된다면 나머지 모든 thread 역시 block된다.
User and Kernel threads - Kernel thread
- Kernel Thread
- 운영체제에서 thread를 지원
- 커널 영역에서 thread의 생성, 스케줄링 등을 관리
- user level의 multithread가 가능하려면, kernel 안에서의 multithread가 먼저 가능해야 한다.
- user level의 thread는 kernel 입장에서 하나의 프로세스일 뿐이므로 정말로 여러개의 하드웨어(core)를 동시에 사용하는 효과를 내려면, kernel 공간에서 그만큼의 thread가 필요한 것이다.
- 실제로 user program이 여러 개의 core를 사용하려면 여러 개의 kernel thread가 이 프로그램에 할당되어있어야 한다.
- user level의 thread는 kernel 입장에서 하나의 프로세스일 뿐이므로 정말로 여러개의 하드웨어(core)를 동시에 사용하는 효과를 내려면, kernel 공간에서 그만큼의 thread가 필요한 것이다.
- 장점
- Thread가 system call을 호출하여 block이 되면, kernel은 다른 thread를 실행함으로써 전체적인 thread blocking이 없음
- Multi(core) processor 환경에서 커널이 여러 개의 thread를 서로 다른 core에 할당할 수 있음
- 단점
- User thread보다 생성 및 관리가 느림
Q : Single Core에서 여러 개의 thread가 존재한다면 한 순간 t에서 보았을 때, 여러 개가 동시에 실행되고 있는건가요? 프로세스처럼 하나만 실행되고 있는건가요? A : core는 하나이므로 하나의 thread만 동작하고 있는 것이다.
Q : 그러면 프로세스와 다른 점은 메모리 공간이 공유가 가능하다는 점에서 장점을 가지는건가요? A : 그렇다. 추가적으로 switching 비용도 적다. context switching을 하려면 status를 저장/로딩해야 하는데 code, data, file은 기본적으로 동일하기 때문에, switching할 때 저장해야할 것들은 registers와 stack 뿐이다.
Q : kernel이 user thread를 구별 못한다고 하셨는데 시스템콜을 요청한게 프로세스인지 스레드인지는 구별하는건가요? 구별한다면 어떤 프로세스의 스레드인지는 구별하나요? A : 좀 더 구체적으로 설명하자면, 프로세스가 하나 존재한다면, 이미 그 프로세스에 대해서 Kernel thread는 하나가 할당이 되어있다는 뜻이다. 즉 하나의 context에 대하여 User space 관점에서는 여러 개의 thread가 돌아갈지 언정, Kernel 관점에서는 프로세스에 대하여 한 개의 Kernel thread만 할당되었다고 볼 수 있다. 이러한 상황에서는 Kernel은 User space에서 움직이는 것이 thread인지 process인지 구분을 못한다.
Mapping of User & Kernel Thread : Many to One
- Many-to-One
- Thread 관리는 user level에서 이루어짐
- 여러 개의 user level thread들이 하나의 kernel thread로 매핑됨
- kernel thread를 지원하지 못하는 시스템에서 사용됨
- 한계점
- 한번에 하나의 thread만 커널에 접근 가능
- 하나의 thread가 커널에 접근(calling system call)하면 나머지 thread들은 대기해야함
- 진정한 concurrency는 지원하지 못함
- 동시에 여러 개의 thread는 하나의 process이기 때문에 multiprocessor(core)이더라도 여러 개의 precessor(core)에서 동시에 수행 불가능
커널 스레드를 지원하는 커널에서는 커널이 multithread화 될 때 프레임 쉘이 발생한다. 프레임 쉘이 발생하면 모든 status를 저장해야 한다.
Mapping of User & Kernel Thread : One to One
-
One-to-One
- 각각의 user thread를 kernel thread로 1:1 매핑
- User thread가 생성되면, 그에 따른 kernel thread가 생성
- Many-to-One 방법에서 문제가 되었던, system call 호출 시 다른 thread들이 block되는 문제를 해결
- 여러 개의 thread를 multiprocessor에서 동시적으로 수행 할 수 있음
- 한계점
- Kernel thread도 한정된 자원이기 때문에 무한정으로 생성 할 수 없음
- Thread를 생성, 사용하려 할 때 그 개수에 대한 고려가 필요
Mapping of User & Kernel Thread : Many to Many
- Many-to-Many
- 여러 개의 user-thread를 여러 개의 kernel thread로 매핑
- 프로세스에 얼마 만큼의 하드웨어 자원(프로세서/코어)을 할당하는지 여부에 따라 달라짐
- Many-to-One과 One-to-One model의 문제점을 해결
- Kernel thread는 생성된 user thread와 같거나 적은 수 만큼만 생성이 되어 적절히 스케줄링함
- One-to-One처럼 사용할 thread의 수에 대해 고민할 필요 없음
- kernel thread가 user thread보다 적더라도 스케줄링을 통해 수행 가능하므로
- Many-to-One처럼 thread가 system call을 사용할 경우, 다른 thread들이 block되는 현상에 대해 걱정할 필요 없음
- One-to-One처럼 사용할 thread의 수에 대해 고민할 필요 없음
- 커널은 적절히 user thread와 kernel thread 매핑을 조절하여 위와 같은 장점을 보장할 수 있음
- 여러 개의 user-thread를 여러 개의 kernel thread로 매핑
- user thread에 대한 라이브러리가 적절히 user thread들을 multiplexing 해줌
Q : 그러면 many-to-many 방식을 채택하면 커널이 쓰레드 수를 조절하기 때문에 4페이지에 나오는 그래프처럼 쓰레드 수 증가에 따라 throughput이 감소하는 현상이 발생하지 않게 되나요? A : Thread와 Throughput 상관 그래프에서 thread는 정확히는 kernel thread의 수를 말한다. user thread는 개수가 늘어나도 성능증가가 core 개수만큼 나타나지 않는다. user thread는 어차피 하나의 core밖에 사용할 수 없기 때문이다.
Thread로 인한 운영체제의 변화
- 프로세스 기반의 운영체제 시스템 콜
- 프로세스 관련 시스템 콜 semantics는 프로세스 기준으로 작성됨
- Thread의 개념에 대한 고려가 필요
- fork(), exec()
- Thread 지원을 위해서는 변화가 필요
- Thread 종료 문제
- Multi-thread일 경우 함께 일하는 thread의 종료는 프로세스보다 복잡해짐
- Thread cancellation issue
- Multi-thread일 경우 함께 일하는 thread의 종료는 프로세스보다 복잡해짐
- 프로세스 관련 시스템 콜 semantics는 프로세스 기준으로 작성됨
- Multi-threaded programming에 대한 지원
- Thread 스케줄링
- Thread 간 통신 방법
- Thread가 사용할 메모리 공간의 할당
- Stack, thread specific data
Threading Issues: Creation
- The fork and exec system call
- Multithreaded program에서의 fork와 exec의 의미는 달라져야 함
- fork
- 하나의 프로그램 내의 thread가 fork를 호출하면 모든 thread를 가지고 있는 프로세스를 만들 것인지 아니면 fork를 요청한 thread만을 복사한 프로세스를 만들 것인가의 문제
- Linux에서는 2가지 버전의 fork를 만들어 각각의 경우를 처리하도록 함 (상황에 따라 필요한 fork 버전이 다르다.)
- fork1 - fork를 요청한 thread만 복사
- fork - 전체 thread 복사
- Linux에서는 2가지 버전의 fork를 만들어 각각의 경우를 처리하도록 함 (상황에 따라 필요한 fork 버전이 다르다.)
- 하나의 프로그램 내의 thread가 fork를 호출하면 모든 thread를 가지고 있는 프로세스를 만들 것인지 아니면 fork를 요청한 thread만을 복사한 프로세스를 만들 것인가의 문제
- exec
- fork를 하여 모든 thread를 복사 했을 경우 exec을 수행하면 모든 thread들은 새로운 program으로 교체가 됨
- 교체될 thread들의 복사는 불필요한 작업
- fork를 하고 exec을 수행할 경우 fork를 요청한 thread만이 복사되는 것이 더 바람직함
- 그러나 fork를 하고 exec을 수행하지 않는 경우에는 모든 thread의 복사가 필요하기도 함
- fork를 하여 모든 thread를 복사 했을 경우 exec을 수행하면 모든 thread들은 새로운 program으로 교체가 됨
Threading Issues: Cancellation
- Cancellation Thread cancellation : thread의 작업이 끝나기 전에 외부에서 작업을 중 지시키는 것
- 하나의 thread에서 중지 명령이 결국은 다른 thread의 모든 작업을 중지 시켜야 하는 경우
- Ex) 웹 브라우저에서 이미지를 읽어 오는 과정에 사용자가 stop 버튼을 눌러 취소할 경우 이미지를 읽어오는 thread 역시 중지가 되어야 함
- 자원이 thread에게 할당된 경우 cancellation의 문제
- System의 자원을 사용하고 있는 thread가 중지 될 경우 할당된 자원을 함부로 해제 할 수 없다.
- 다른 thread가 그 자원을 사용하고 있을지도 모르기 때문
- System의 자원을 사용하고 있는 thread가 중지 될 경우 할당된 자원을 함부로 해제 할 수 없다.
Threading Issues: Thread pools
- Thread pools
- Thread가 자주 생성되고 제거되는 상황에서 새로이 thread를 만드는 시간이 실제 thread가 동작하는 시간보다 긴 경우가 있음
- Thread는 system의 자원이기 때문에, system의 동작을 보장하는 최대 한의 thread 수에 대한 제한이 필요
- 해결 방안
- Thread pool을 만들어 프로세스가 새로이 실행 할 때, 정해진 수만 큼의 thread를 만든 후 pool에 할당
- 새로운 thread가 필요하면 pool에서 가져오고, 작업이 끝나면 그 thread를 제거하지 않고 다시 pool에 넣어둠
- Pool을 통해서 제한된 수의 thread를 관리하면, 그러지 않은 경우 보다 thread의 생성에 걸리는 시간이 적음
- 전체적인 system에서 생성할 수 있는 thread의 수가 제한되어 있기 때 문에, 너무 많은 thread의 생성에 따른 system의 부하를 줄일 수 있음
- Pool에서 관리할 thread의 수는 system의 memory나 예상되는 작업의 수를 통해서 휴리스틱하게 결정
- provisioning : 프로세스에 하드웨어를 할당하는 것을 말하며, 보통은 리퀘스트가 가장 많이 들어올 때를 대비해서 하드웨어를 맥시멈으로 할당한다.
Threading Issues: 스레드간 IPC
- Multithreaded 프로그래밍에서는 스레드 간 통신이 필요함
- 스레드 간 IPC의 구현은?
- 동일한 프로세스 내의 스레드 간 통신은, 공유 메모리가 효율적임
- 이유 – thread들은 같은 프로세스의 data 영역을 공유하므로, 자연스러운 공유 메모리가 가능해짐
- 동일한 프로세스 내의 스레드 간 통신은, 공유 메모리가 효율적임
- 결국 IPC가 최소화됨
- Thread들 간의 자원 공유로 인하여 가능해짐
- 그렇지만 특정한 경우에는 공유 메모리로 안되는 IPC가 있을 수 있으며 이 때에는 Socket과 같은 다른 방식의 IPC가 필요함
- 만일 다른 프로세스 내에 존재한 스레드와의 통신은?
- 프로세스의 경우와 비슷한 성능을 보임
- 이러한 통신이 빈번하다면, 프로그램 설계의 잘못
Implementation
- Thread library
- POSIX Pthread: interoperability(내가 만든 프로그램이 다른 곳에서도 작동됨)를 위한 표준
- IEEE 1003.1c: pthread_create()
- Windows threads API
- Via system call: CreateThread()
- Linux threads
- Introduced in version 2.2
- Via system call: clone()
- Java threads
- Thread class
리눅스에서 thread는
- Thread is hard to support !
- Also thread programming is difficult
- 스레드 간의 동기화와 디버깅의 까다로움
- Thread implemented as process
- simply share resources(code, data, file) like threads
- different from Windows and other Unix-descendants
- discourage the use of threads…
- thread API 대신 clone()을 사용
- But kernel thread exist
- process solely in kernel-space
- schedulable and preemptable
- interrupt가 가능하도록 하기 위함
- 원래는 interrupt를 처리할 때 커널에서는 그것만 수행했지만 이제는 interrupt 처리 과정을 비롯한 다양한 수행들을 스케줄링하기 위해 커널 스레드가 사용된다.
Q : 요즘 컴퓨터들을 보면 cpu 성능에 8코어 16쓰레드 처럼 하나의 코어에 여러개의 쓰레드가 달려있는 경우가 있는데 여기서 설명한 쓰레드와 같은 의미인가요? A : 그것은 hyperthread를 말하는 것으로, 우리가 배우는 thread랑은 다른 개념이다.
Q : 그렇다면 linux에서는 kernel thread와 user thread간의 mapping이 필요가 없는건가요? A : 그렇다.
Q : 그럼 쓰레드가 독립적으로 동작하는 것이 아니라 프로세스 내에서 그룹으로 동작하는 것이라고 생각하면 될까요? A : 쓰레드는 독립적으로 동작하지만 하드웨어와의 매핑 과정에서는 동기화에 의해 서로에게 영향을 줄 수는 있다.
Q : 교수님 프로그램이 multi core를 지원한다는 것은 multi threading이 가능하다는 것으로 이해하면 될까요? A : 그렇다.
Q : library에서 user thread를 만들면 library에서 user thread를 kernel thread에 매핑하는게 맞나요? A : user library는 thread를 user space 내에서 스케줄링까지만 해줄 뿐이다. 즉, kernel이 스케줄링 해준 타임 퀀텀 내에서의 스케줄링만 해준다.
Q : multithread를 활용하는 process이더라도 underlying kernel이 mapping을 어떻게 하는지에 따라 다른 성능을 보여주는게 맞나요? A : 그렇다. 리눅스가 서버용 하드웨어로 많이 사용되는 이유 중 하나가, 리눅스가 multi core가 되면서 커널 thread 매핑에 따른 성능 개선이 좋았기 때문이다.
Comments