오늘날의 운영체제들은 프로세스를 효율적으로 사용하고, 작업의 유연성을 얻기 위해 멀티스레드를 지원한다.
프로세스가 생성되면 CPU 스케줄러는 프로세스가 해야 할 일을 CPU에 전달하고, 실제 작업은 CPU가 수행한다. 이때 CPU 스케줄러가 CPU에 전달하는 일 하나가 스레드이다.
그러므로 CPU가 처리하는 작업의 단위는 프로세스로부터 전달받은 스레드인 것이다.
운영체제의 입장에서 작업 단위는 프로세스이고, CPU입장에서의 작업 단위는 스레드인 것이다. 따라서 프로세스 입장에서는 스레드를 다음과 같이 정의할 수 있다.
스레드 : 프로세스의 코드에 정의된 절차에 따라 CPU에 작업 요청을 하는 실행 단위이다.
크기 순서로 나열하면 다음과 같다.
Thread(operation) < Process(task) < Job
여러 개의 스레드가 모여 프로세스를 만들고, 여러 개의 프로세스가 모여 일괄처리하는 방법을 일괄 작업(job)이라고 한다.
가령, 집짓기는 job이고, 인테리어, 바닥 시공, 수도관 설치 등은 프로세스이다. 이 각각 속에서 하나하나의 작은 일들(수도관 설치에서 배수관 설치, 수로 연결 등)은 쓰레드라고 한다.
- 멀티스레드 : 멀티스레드는 프로세스 내의 작업을 여러 개의 스레드로 분할함으로서 작업의 부담을 줄인다.
- 멀티태스킹(시간 공유) : 운영체제가 cpu에 작업을 줄 때 시간을 잘게 나누어 배분하는 기법이다. 이렇게 여러개의 스레드에 시간을 잘게 나누어주는 시스템을 시분할 시스템이라고 한다. 따라서, 시분할 시스템에서 처리하는 작업의 단위는 프로세스가 아니라 스레드이다.
- 멀티프로세싱 : CPU를 여러 개 사용하여 여러 개의 스레드를 동시에 처리하는 작업 환경을 말한다. 여러 개의 CPU 혹은 하나의 CPU 내의 여러 개의 코어에 스레드를 배정하여 동시에 작동하도록 하는 것이다.
- CPU 멀티스레드 : 한 번에 하나씩 처리해야 하는 스레드를 파이프라인 기법을 이용하여 동시에 여러 스레드를 처리하도록 만든 병렬 처리 기법이다. 이는 위의 소프트웨어적 기법인
멀티스레드
와는 달리 하드웨어적 기법이다.
즉, 멀티프로세싱
은 processor(CPU)와 관련있는 것으로 CPU의 수와 코어에 관련된 것이고, 멀티태스크
는 시분할 운영체제의 운영방식을 이야기하는 것이다.
스레드가 없던 이전에는 프로세스에서 여러 개의 작업을 동시에 처리하기 위해서는 fork(), exec()
을 사용할 수 밖에 없었다. 그러나 이는 프로세스를 만드는 것이므로 부하가 컸고, 서로 중복되는 영역도 많았다.
가령 fork()
로 자식 프로세스를 만드는 경우 사실상 PID,PPID,CID 말고는 다를게 없지만, code 일부 영역이나 , data영역 등은 서로 완전 똑같았지만 따로 만들어 관리해야했다.
이러한 메모리의 중복은 비효율을 초래했고, 멀티태스킹에서의 낭비 요소로 자리잡았다.
스레드는 이러한 멀티태스킹의 단점을 해결하려고 했다. 비슷한 일을 하는 2개의 프로세스를 만드는 대신, 코드 , 데이터 등은 공유하면서 일을 하나의 프로세스 내에서 하는 것이다.
프로세스 내부의 영역은 크게 두 가지로 정적 영역, 동적 영역으로 나눌 수 있다.
- 정적영역 : 정적 영역은 프로세스가 실행되는 동안 바뀌지 않는 영역이다.
- 동적영역 : 스레드가 작업을 하면서 값이 바뀌거나 새로 만들어지거나 사리지는 영역이다.
동적 영역의 대표적인 예는 레지스터 값, 스택, 힙 등이있다.
이제 스레드가 정적 영역은 프로세스에서 공유하고, 동적 영역은 따로 갖게되면 이전의 fork(), exec()
으로 프로세스를 하나 더 만들어 비효율을 초래하는 일을 막을 수 있게 되게된다.
다음 그림은 멀티태스킹과 멀티스레드의 차이를 보여준다.
멀티태스킹으로 fork()를 통해 하나의 프로세스를 또 만든다. 이들은 굳이 만들 필요없는 정적 영역을 여러개 만들게 됨으로 부하가 있다. 이 문제를 해결하기 위해서 하나의 프로세스 내에 여러 개의 스레드를 생성하는 멀티스레드는 코드, 파일, 데이터 영역 등
의 자원을 공유함으로써 자원의 낭비를 막고 효율성을 향상시킨다.
이런 의미에서 스레드는 가벼운 프로세스(Light Weight Process, LWP)
라고 하고, 일반 프로세스는 무거운 프로세스(Heavy Weight Process, HWP)
라고 부른다.
https://slideplayer.com/slide/12698810/
thread 역시도 process와 마찬가지로 생성될 때 TCB(Thread Control Block)을 만든다. thread도 TCB로 OS에 의해 관리되고 제어된다. 다만 위의 그림처럼 TCB는 PCB보다 훨씬 더 가벼우며, 적은 내용을 가지고 있고, PCB의 thread list
에 링크드 리스트로 연결되어 관리된다.
- 프로세스끼리는 서로 독립적이기 때문에 코드 실행의 순서가 영향을 미치지 않는다. 반면 스레드는 프로세스 내부에 영역 일부분을 공유하기 때문에 순서가 영향을 미친다.
- 프로세스끼리 통신하기위해서는 주로 IPC(Inter Process Communication)를 사용한다. 반면 스레드는 변수(데이터 영역), 파일 등을 공유하고 있기 때문에 함수 호출이나, 전역 변수로 스레드간 통신이 가능하다.
- 프로세스의 생성으로
fork()
는 정적 영역(data, code, files)를 공유한다. 반면, 스레드의 경우에는 정적 영역은 공유하고, 동적 영역(stack, heap, register)는 따로 갖고 있어 더 효율적이다. (코드로는 clone()을 사용한다.)
- 장점
- 응답성 향상 : 하나의 스레드만 사용하면 하나의 연산에 block되지만 여러 스레드를 사용하면 하나가 오래걸려도 blcok되지 않아 응답성이 향상된다.
- 자원 공유 : 프로세스가 가진 자원을 모든 스레드가 공유함으로서 원할하게 진행가능하다.
- 효율성 향상 : 여러 개의 프로세스를 만드는 것보다 여러 개의 스레드를 만드는 것이 처리 시간이 단축된다.
- 다중 CPU 지원 : 2개 이상의 CPU를 가진 컴퓨터에서 멀티스레드를 이용하면 다중 CPU가 멀티스레드를 동시에 처리하여 CPU 사용량이 증가하고 프로세스 처리시간이 단축된다.
- 단점
- 스레드들이 프로세스의 자원을 공유하기 때문에 한 스레드의 영향이 다른 모든 스레드에 영향을 준다.
- 하나의 스레드로 구현하는 프로그램보다 훨씬 더 난이도가 높아진다. 동기화 문제가 발생한다.
두 개의 스레드 모델이 있다.
- 커널 스레드 : 커널이 직접 생성하고 관리하는 스레드
- 사용자 스레드 : 라이브러리에 의해 구현된 일반적인 스레드
사용자가 커널 스레드를 사용하려면 시스템 호출로 커널 기능을 이용해야 한다. 이때 커널 스레드와 사용자 스레드의 대응 방식에 따라 다음과 같이 분류 된다.
라이브러리를 통해 스레드를 구현하며, 라이브러리는 커널이 지원하는 스케줄링이나 동기화 같은 기능을 대신 구현해준다.
이는 라이브러리 차원에서 지원하는 스레드이기 때문에, 커널 입장에서 마치 하나의 프로세스처럼 다워진다. 따라서, 사용자 프로세스 내에 여러 개의 스레드가 존재하지만, 커널의 스레드 하나와 연결되어 있기 때문에 1 to N 모델이다.
사용자 스레드는 라이브러리가 직접 스케줄링을 하고 작업에 필요한 정보를 처리하기 때문에 문맥 교환이 필요 없다. 즉, 커널에서 작업하는 경우 스레드라도 stack, heap, register 영역에 대한 context switing이 발생하게 되지만, 라이브러리로 제공되는 사용자 스레드는 커널에서 스레드를 관리하지 않으므로, 응용 계층에서의 조작이 가능하다.
즉, 그림판에서 여러개의 캔버스를 다루는데 있어, 스레드에 필요한 일을 처리해주는 것은 변수 값 하나만 바꾸어 캔버스 focus를 바꾸어주는 것 밖에 없다. 굳이 다른 커널 단에 가서 thread에 대한 context switching까지 필요없다.
단, 사용자 스레드의 단점은 여러 개의 스레드가 하나의 커널 스레드와 연결되기 때문에 커널 스레드가 입출력 작업을 위해 대기 상태에 들어가면 모든 사용자 스레드도 같이 대기한다는 것이다.
또한, 한 프로세스의 타임 슬라이스를 여러 스레드가 공유하기 때문에 여러 개의 CPU를 동시에 사용할 수 없다는 것이다. CPU를 여러 개 갖추고도 커널 스레드 하나에 여러개의 스레드들이 할당되는 것이기 때문이다.
커널 스레드는 커널이 멀티스레드를 지원하는 방식으로 하나의 사용자 스레드가 하나의 커널 스레드와 연결되기 때문에 1 to 1 모델이라고 부른다.
커널 스레드는 독집적으로 스케줄링되므로 특정 스레드가 대기 상태에 들어가도 다른 스레드는 작업을 계속할 수 있다. 또한 커널이 제공하는 보호 기능과 같은 모든 기능을 사용할 수도 있다.
커널 스레드의 장단점은 사용자 스레드의 장단점과 반대라고 생각하면 된다. 커널 스레드는 커널 레벨에서모든 작업을 지원하기 때문에 멀티 CPU를 지원한다. 하나의 스레드가 대기 상태에 있어도 다른 스레드는 작업을 계속할 수 있다.
멀티레벨 스레드는 사용자 스레드와 커널 스레드를 혼합한 방식으로 M to N 모델이다. 사용자 스레드 >= 커널 스레드로 운용된다.
멀티 레벨 스레드는 혼용 방식이기 때문에 사용자 스레드와 커널 스레드의 장단점을 모두 가진다. 하나의 커널 스레드가 대기 상태에 들어가면 다른 커널 스레드가 대신 작업을 하고, 사용자 스레드보다 유연하게 작업을 처리할 수 있다.
그러나, 커널 스레드 N개로 같이 사용하기 때문에, context switching 시에 오버헤드가 있다.
따라서, 빠르게 구동하기 위해서는 사용자 스레드로, 안정적으로 구동하기위해서는 커널 스레드가로 작동한다.
추가적으로 헷갈릴까바 적어둔다.
context switching이 발생하는 것은 process나 thread나 마찬가지이다. 운영체제가 프로세스의 상태를 관리하여
running
상태로 만들면 프로세스 내부의 thread들이 CPU 작업으로 할당된다. 시분할로 운영되기 때문에 time quantum으로 잘게 쪼개져 thread들이 context switching 된다. process에 할당된 시간이 끝나면 os는 process를running
상태에서wait
상태로 바꾼다. 이때에도 context switching이 발생하는 것이다.
즉, CPU 스케줄링이란 결국 process에서도 발생하고, 그 내부의 thread에서도 발생하게 되는 것이다. 물론 명령어를 처리하는 CPU가 받는 것은 스레드 단위이다.