* Task, Process, thread
; 멀티 태스킹(멀티 프로세스, 멀티 스레드)은 말 그대로 여러개의 프로그램이 동시에 실행되는 것 처럼 보이는 처리 방식을 말한다. 하지만 실제로 짧은 시간 동안 실행 가능한 상태(메모리에 적재되어있거나 페이지 파일로 존재하는 상태)에 있는 태스크들이 번갈아 가면서 프로세서(Processor, CPU)를 사용하는 것이다. 보통의 시스템에서는 프로세서가 한개가 있고 하이퍼 스레딩과 같은 기술로 논리적으로 2개의 프로세서를 사용하거나 물리적으로 CPU를 사용하여 2개 이상 사용가능하다. <말그대로 Process를 처리하는 것이 Processor(CPU)다...>
하지만 논리적이든 물리적이든 프로세서(CPU)의 수는 실제로 실행을 위해 메모리 혹은 페이지 파일로 존재하는 프로세스보다 적을 수 밖에 없다. 그렇기 때문에 실질적으로 동시에 실행되는 프로세스는 프로세스의 수로 한정된다. 그러므로 프로세스는 실행 가능 상태가 되더라도 무조건 실행 상태에 놓이는 것이 아니고 다른 프로세스들과 같이 공평(?)하게 순서를 기다리다가 수행되어야 한다.
일단 왜 프로세스를 태스크라고 말하는가...하면... 일단 프로세서에 의해 수행되기 때문에 프로세스라는 단어가 제일 적합 하겠지만, 보호 모드로 동작하는 독립적인 프로세스 내에서 병렬적으로 수행하는 스레드라는 개념이 생기면서 의미가 모호해졌다.
그래서 리눅스에서는 태스크라는 개념을 사용한다. 실제로 리눅스 커널을 보면 'task_struct'라는 구조체를 사용하여 main()함수만을 사용하여 구성한 단일 프로세스(메인 스레드라고도 한다.)도 처리하며, pthread_create()를 사용하여 스레드를 만들더라도 그 스레드 또한 이 구조체를 사용하여 처리한다. 물론 같은 프로세스의 스레드든 다른 프로세스의 스레드든 'task_struct'로 처리하기 때문에 실행 단위를 태스크라 한다.
반면 윈도우에서는 태스크라는 용어는 사용하지 않는다. CreateThread(), _beginthreadex()를 사용하여 만든 스레드는 물론이고 main()에 의해 생성되는 프로세스 역시 메인 스레드라고 부를 수 있기 때문에 윈도우에서는 처리 단위를 스레드로 정의하고 있다.
* 태스크(스레드) 상태 전이도
- 생성 상태 - 종료 상태 [출처] [OS] 태스크(프로세스, 스레드)의 상태 전이도 (윈도우, 리눅스)|작성자 깡
; 리눅스는 [fork(), pthreat_create()]를 통해 프로세스와 스레드를 생성하고, 윈도우는 [CreateProcess(), CreateThread(), _beginthreadex()]를 사용하여 프로세스와 스레드를 생성한다.
+ 대기 상태로 이동
: 프로세스가 생성되면 다른 태스크 사이에서 실행 권한을 얻기 위해 스케쥴링을 받기 위한 대기 상태로 이동해야된다.
- 대기 상태
; 태스크가 실행되기 위해 다른 프로세스들과 스케쥴링을 통해 실행 순서를 대기하는 상태를 말한다. 스케쥴링이란 태스크마다 우선 처리되야 하는 것을 가리고 먼저 실행시키고 우선 순위가 같은 것들 끼리는 공평하게 시간을 배분하여 실행시키는 것을 일정 관리에 비교하여 이렇게 부르는 것이다.
리눅스에서는 실행 큐와 대기(expired ,만료된) 큐라는 스케쥴링 관리 벡터(큐보다는 벡터 형식에 가까움;;;)에 태스크(task_struct)를 등록하여 실행을 처리한다. 실행 큐에서 실행 시간이 끝난 태스크를 대기 큐에 등록 시키고 실행 큐의 태스크를 다 소진하면 다시 대기 큐와 맞바꾸어 (대기 큐->실행 큐, 실행 큐->대기 큐)실행하는 형식으로 동작시킨다.
반면 윈도우는 우선 순위 별로 테이블을 두고 해당 우선 순위의 스레드가 대기 상태로 들어오면 큐 형식으로 등록한다음 우선 순위 높은 순서로 수행하고 같은 우선 순위는 라운드로빈(모든 프로세스가 돌아가면서 무조건 같은 시간 동안 프로세서 사용) 방식으로 동작하게 스케쥴링을 한다.
+ 실행 상태로 이동
: 스케쥴링을 통해 자신의 실행 차례를 부여 받으면 실제로 CPU를 사용하는 실행 상태로 이동된다.
- 실행 상태
; 스케쥴링을 통해 실행 권한을 받으면 실행 상태가 되어 할당된 시간만큼 CPU를 사용하게 된다. 리눅스에서는 이 할당 시간을 타임 슬라이스(Time Slice), 윈도우에서는 퀀텀(Quantum)이라 부른다.
실행 상태에에 들어간 태스크가 무조건 할당된 시간만큼 실행되는 것은 아니다. 우선 순위가 높은 태스크나 외부의 인터럽트 신호가 들어오면 스케쥴러에 의해 현재 실행되는 태스크가 강제적으로 실행이 중단되고 대기 상태로 전환될 수 있는 것이다.
실행 단계에서 여러 프로세스간에 번갈아 가면서 어떻게 실행 위치를 찾을 수 있는가?...에 대해서는 컨텍스트 스위칭(문맥전환, 태스크 스위칭)이라는 말을 들어본적이 있을 것이다. 여기서 컨텍스트란 범용 레지스터와 세그먼트 레지스터 등의 레지스터를 말한다. 이 레지스터 정보만 이용하면 해당 태스크간에 실행 위치를 저장하고 재개할 수 있다. (난 처음에 이 것만 가지고 어떻게 실행 위치를 찾는가?...에 대해 의아하기도 했었다...-_-;;;)
간단하게 설명하면 컨텍스트 중 다음 실행할 주소를 저장하는 EIP와 스택의 사용 위치를 나타내는 ESP는 이미 주소 정보를 가지고 있다. 그렇기 때문에 어디를 수행해야 할지 알고 있다. 이 주소 정보와 태스크의 정보만 있으면 태스크간에 실행이 전환되어도 문제 없을 것이다. 그렇다면 실행 중인 태스크는 어떻게 저장하는가...하지만 이것은 페이징이라는 개념만 알면 간단하다. 이미 사용하지 않는 태스크의 부분은 하드디스크에 페이지로 저장되어 커널에 관리 되기 때문에 신경 쓸 필요없다...실행 권한을 얻으면 커널이 알아서 실행되어야할 태스크의 페이지를 적재해준다. 그럼 컨텍스트는 또 어떻게 관리되는지 궁금하다. 이 컨택스트는 태스크 별로 커널 스택이라는 공간을 확보해서 실행 권한을 반환할때 여기에 컨텍스트를 저장하고, 다시 실행 권한을 얻으면 해당 태스크 내에 있는 이 커널 스택에서 컨텍스트를 읽어서 실행을 재개하는 것이다.
+ 대기 상태로 이동
: 바로 대기 상태로 이동하는 경우는 태스크의 할당 시간을 다 소진하거나 다른 태스크나 인터럽트에 의해 우선 순위에서 밀려 다시 스케쥴링을 통해 실행 권한을 얻기 위해 이동된다.
+ 휴면 상태로 이동
: 시스템 콜이나 동기화를 위해 블럭되는 경우 휴면 상태로 이동된다.
- 휴면 상태 / 블록 상태
; 블록되는 해당 프로세스가 외부 장치의 입출력을 하여야하는 경우와 같이 시스템 콜을 통해 커널의 개입을 필요로 하거나 외부 장치의 응답이나 태스크 동기화(임계영역 처리) 방식과 같은 동기화를 위해 블럭되는 경우이다.
블록 상태의 특징은 커널의 개입, 동기화 때 상대의 응답이 있을때 까지 태스크의 처리를 완전히 중단하는 것이다. 그러므로 블록되어 있는 동안 다른 태스크에게 실행 순서를 완전히 양도한다. 상대방의 응답이 있어야 실행 권한을 다시 얻게 되는데, 상대방의 응답이 있더라도 바로 실행 권한을 받는 것이 아니다...위의 전위도를 보면 알겠지만 응답을 받으면 그때가 되어야 다시 실행을 위한 스케쥴링을 하게된다. 블럭 상태는 외부의 조건에 의해서만 생기는 것은 아니다. 태스크 자체적으로 [리눅스는 sleep(), 윈도우는 Sleep()]을 통해 자진적으로 실행 시간을 지연시킴으로 블럭될 수가 있다.
+ 대기 상태로 이동
: 시스템 콜이면 커널, 동기화면 상대의 응답을 받게되면 실행을 위한 스케쥴링을 다시 하게된다. (바로 실행 상태가 되지 않는다!!!) 해당 태스크의 특징 및 운영체제의 특징에 따라 스케쥴링시 우선 순위에서 조금은 이점을 받을 수 있기도하다.
; 그냥 해당 태스크가 종료된 상태다...라고만 하면 섭섭하다;;; 종료 상태도 나름 중요하다. OS 별로 특징을 보자.
우선 리눅스는 태스크가 종료되면 자신을 생성한 부모 프로세스(태스크)에게 종료 상태를 알려줘야할 의무가 있다. (리눅스 계열에서 'int main()' 으로 int값으로 반환해줘야하는 이유가 바로 종료 상태를 알려주기 위해서다.) 부모 프로세스가 그 상태를 전달 받지 못하면 리눅스에서는 종료된 태스크는 '좀비 상태'로 프로세스의 정보를 가지고 완전히 죽지 못하여 커널에서 리소스 정보를 계속 가지고 있게된다. 그러므로 부모 프로세스에서는 wait() 계열의 시스템 콜을 통해 종료 정보를 받아서 완전히 소멸 시켜줘야한다.
윈도우에서는 프로세스든 스레드든 자원이면 핸들이라는 형태의 커널 오브젝트로 관리된다. 프로세스나 태스크 종료시 커널 오브젝트의 상태가 변환(Non-Signaled -> Signaled)이 된다. WaitForSingleObject() 계열의 함수를 사용하여 필요에 따라 처리가 가능해진다.
'Security > System' 카테고리의 다른 글
[06] 디스크 관리(디스크 스케줄링 기법) (0) | 2015.08.17 |
---|---|
[05] 주기억장치 / 가상기억장치 (0) | 2015.08.17 |
[04] 프로세스 교착상태 (0) | 2015.08.17 |
[03] Process Scheduling (0) | 2015.08.17 |
[01] The Process (0) | 2015.08.17 |