티스토리 뷰
강의 url www.kocw.net/home/search/kemView.do?kemId=1226304
1) 프로세스의 상태 변화와 프로세스 중 CPU 수행 단위인 스레드에 대해 알아본다.
2) 프로세스의 생성에 있어 자식 프로세스가 부모 프로세스를 복제, 새로운 과정을 덮어씌우는 과정과 프로세스의 종료에 대해 알아본다.
3) 프로세스 생성과 종료에서 쓰이는 네 종류의 시스템 콜, 프로세스 간의 협력에 대해 알아본다.
1) 프로세스 상태도
Suspended 개념이 추가되면서 프로세스 상태도를 조금 더 자세히 그린 것이다.
1) active/inactive하다는 표현은 이전 강의(포스트 Process 1) 후반부에서 서술했듯이 프로세스가 일을 하느냐 안하느냐 에 따라 나뉜 것이다. 더 자세하게 말하면 메모리를 빼았긴 것이기 때문에 프로세스 본인이 무언가를 할 수가 없다. Blocked 또는 Ready 상태에서 Swap out 당한 경우 각각 Suspended Blocked, Suspended Ready 로 부를 수 있다. I/O 작업을 하던 도중 (Blocked 상태에서) Swap out 한 경우 Suspended Block으로 넘어가는데, Suspended Block에서 I/O 작업은 계속 수행할 수 있으므로 작업이 끝난경우 Suspended Ready로 넘어갈 순 있다. (또는 event occur) 하지만 inactive로 넘어갈 순 없다. 외부에서 Swap In을 해주어야한다.
2) running은 CPU를 잡고 instruction을 수행중인 상태인데, user program이 running 일땐 user mode running이다 그리고 user program이 운영체제로 system call을 하였을 때엔 user programming이 kernel mode로 running이라고 한다.
Too Long, 읽을 가치는 있으나 추상적인 얘기임, 사용자 프로그램이 본인이 실행할 수 없는 작업을 운영체제를 시키려 system call을 호출하면 kernel code(OS의 code)가 실행된다. 하지만 이를 운영체제가 CPU를 잡고 기계어를 실행하고 있으니 운영체제가 Running이다 라고는 표현하지 않는다. 운영체제도 프로세스이고, 함수를 호출하나, 운영체제가 Ready-Blocked-Running이다라는 식으로 State를 나누지 않는다. 사용자 프로그램 A가 OS에 특정 작업을 요청하였고 OS가 CPU 제어권을 가져갔을 때 프로그램 A가 CPU를 빼았겼다기 보다는, 여전히 프로세스 A가 CPU를 잡고있다고 표현하는게 맞다고 생각한다. (교수님 왈) 어떠한 프로세스도 일생은 user mode->kernel mode->user mode ... 등의 일련이므로, 프로세스는 kernel mode에서도 running이라고 볼 수 있다. 괜히 설명이 헷갈릴 수도 있다. 간단히 말하면 둘 다 프로세스의 running인데, user mode냐 kernel mode냐 나눈 것이다. Interrupt 경우에도 조금 더 말해보면, 프로세스 A가 running 중 또 다른 프로세스 B의 IO 작업이 끝난 경우, 해당 디바이스의 컨트롤러가 interrupt를 날려 CPU제어권을 운영체제로 넘겨 해당 프로세스 B를 ready queue로 할당해야한다. 이 때, 잠시 CPU에게 제어권을 빼앗긴 프로세스 A는 어떤 상태이냐, 라고 물어보면 여전히 running 이라고 표현한다. 이 경우는 context switch에도 포함되지 않다는 점과 유사한 듯 하다.(Process 1 포스트의 context switch 참고)음, 매우 자세한데??ㅋㅋㅋ.
쓰레드
- eg. 웹 브라우저를 생각해보자. 브라우저 창 A, B, C를 띄어놨을 때, 프로세스가 각 A, B, C라 해보자. 그렇다면 프로세스의 주소 공간에는 Stack/Data/Code로 나눌 수 있을 것이고 메모리 상에는 총 3개의 Stack, 총 3개의 Data, 총 3개의 Code가 있을 것이다. 그런데 웹 브라우저는 같은 실행파일로 수행하는데, 즉 실행하는 기계어(Code영역) 는 같은데 굳이 Code 영역 3개를 메모리에 올려놔야 할까? 브라우저 창마다 상태는 다를 수 있으나, 주소창에 주소를 치고 네트워크 I/O를 통해 HTML 데이터를 읽어오며, 사용자의 마우스 입력을 받는 등의 같은 동작을 할 것이다. 즉 기계어의 다른 부분을 실행중이지만, 실질적인 기계어 Code는 같은 것이다. 기계어의 실행 부분에 따라 지역변수의 값(Stack)은 다를 것이다. 즉, Code의 영역은 공유하되, 별도의 Code 실행 상태를 가지고 있으며 Stack 영역을 별도로 가진다. 그에 따라 어디를 실행중인지 Program Counter, Register를 별도로 가질 것이다.
- 쓰레드란 동일한 프로세스에서 Data/Code영역 및 PCB를 공유하되, 각각의 stack 영역 및 PCB에서 각각의 Program counter 및 Register를 사용하게끔 관리하여 별도의 동작을 수행하도록 하는 것이다.
- 프로세스 A에서 프로세스 B로 넘어가는 문맥전환Context Switch 오버헤드를 발생시키지 않을 수 있다.
- 소프트웨어 아키텍쳐를 설계함에 따라 멀티쓰레드로 지원할 것이냐, 멀티프로세스로 지원할 것이냐 선택할 수 있다.
- 다중 스레드로 구현한 경우, 병목 작업을 한 쓰레드가 수행하고 다른 쓰레드가 수행running 할 수 있어 빠른 처리를 할 수 있다. (IO 작업 및 사용자 UI Thread)
- 장점 네가지는 1) Responsivness (UI Thread, Logic or I/O Thread) 2) Resource Sharing ( n threads share code, data, resource of process) 3) Economy ( creating & CPU switching thread rather than a process. Solaris 운영체제에서는 쓰레드 생성이 프로세스 생성보다 오버헤드가 30배낮으며, 쓰레드간 CPU Switching이 경우 5배 낮다.) 4) Multi processor 아키텍쳐에서는 병렬성을 추구할 수 있다.
- 쓰레드의 구현에는 크게 Kernel Thread, User Thread가 있으며, Kernel이 CPU switching을 할 때 Thread를 식별할 수 있을 때를 뜻하며, User Thread란 CPU switching시 kernel은 Thread를 식별할 수 없으며 Process 단위로 스케쥴링을 하며, Process 내부에서 Thread의 CPU 제어권을 관리한다.
2) 프로세스의 관리 (프로세스의 생성과 종료에 대해 알아본다. )
- 프로세스는 부모 프로세스가 자식 프로세스를 생성한다. 사람이 자식을 낳듯, 프로세스도 자식 프로세스를 낳는다. 다른 점은 프로세스는 자식 프로세스를 만들 때 자기 자신을 복제하므로 조금은 다르다.
- 부모 프로세스가 자식 프로세스를 만든다고 표현하지만, 실질적으로 본인이 직접 만들지는 못하고 운영체제의 역할이 필요하다. 자원을 공유받거나 PCB를 만들거나 PID를 할당하거나 등은 프로세스 역할 밖이다. 이러한 요청은 System call을 통하여 kernel에 전달할 수 있다. 이 System call은 fork()이다.
- 프로세스의 Tree 구조를 형성한다. 자식 프로세스는 자원을 필요로한데 일반적으로 부모 프로세스와 자원을 경합한다. 일부 공유할 수 도 있다.
- 수행Execution에 따라 부모-자식 프로세스는 두 가지 모델을 가질 수 있는데 1) 공존하며 수행되는 모델, 2) 자식이 종료(terminate)될 때 까지 부모가 기다리는 모델(wait) 이 있다.
프로세스 생성
- 자식 프로세스는 부모의 주소영역 및 컨텍스트를 그대로 Copy한다. 주소영역을 복사하면서 Code/Stack/Data 모두 Copy하게 되는데, 부모가 수행하고 있는 instruction을 그대로 복사하게된다. 또한 문맥도 복사하면서 Program counter, Register를 모두 복사하므로, 복사시점 당시 부모가 실행하는 부분을, 자식 프로세스는 실행하게 된다.
- 다른 프로그램을 실행하기 위해선, 먼저 부모가 복제fork()를 한뒤, 해당 프로세스를 새로운 프로그램으로 덮어씌우는 system call인 exec()을 호출하여 다른 프로그램을 실행한다.
프로세스 종료
- 프로세스가 마지막 명령을 수행한 후 운영체제에게 이를 알려준다. exit()
- 인간은 일반적으로 자식이 부모의 사후를 처리하지만, 운영체제에서는 항상 부모 프로세스가 자식 프로세스의 사후를 처리한다.
- 자식 프로세스가 exit()을 한 경우 : 자식이 부모에게 output data를 보냄, 프로세스의 각종 자원들이 운영체제에게 반납됨
- 부모 프로세스가 자식 프로세스를 abort() 한 경우 : 자식이 할당 자원의 한계를 넘어섬, 자식에게 할당된 태스크가 더 이상 필요하지 않음, 부모가 exit()하는 경우 해당 부모의 자손 프로세스들을 단계적 종료할 때.
fork() 시스템 콜
- 다음 소스코드를 보자.
int main(){
int pid = fork();
if (pid == 0) printf("HELLO I am child\n");
else if (pid>0) printf("HELLO I am parent\n");
} //부모
위 소스코드에서 fork() 코드를 실행하였을 때, 자식 프로세스가 복사되는데 주소 영역 모두 복사하게 되므로 코드도 똑같이 복사되며, 코드 실행 시점(program count)도 똑같이 복사된다.
int main(){
int pid = fork();
if (pid == 0) printf("HELLO I am child\n");
else if (pid>0) printf("HELLO I am parent\n");
} // 자식도 부모와 소스코드가 같으며, 실행하는 instruction도 같다.
부모 프로세스인 경우에는 fork() return value가 자식 프로세스의 PID(양수)를 받게되고, 자식 프로세스인 경우 fork()의 결과값을 0을 받게된다. 정리하자면, 어떤 프로세스가 fork()를 호출하면 자신과 같은 프로세스를 복제하게 되고, 어떤 프로세스가 CPU를 먼저 제어권을 받게 되든간에, pid 변수에 값 할당을 받는다. 본인 프로세스가 자식 프로세스인 경우엔 0을 받게되고, 부모 프로세스인 경우 자식 프로세스의 PID를 받게된다.
exec() 시스템 콜
- 다음 소스코드를 보자
int main(){
printf("\n HELLO\n");
execlp("/bin/date", "/bin/date", (char *)0);
printf("\n BYE");
}
exec() 시스템콜이 호출되면 현재 프로세스가 해당 프로그램으로 바뀌게 된다. 위 코드에서는 BYE가 출력되지 않고, "/bin/date" 프로그램이 실행되어, 터미널에 날짜가 보이게된다. fork() + exec()을 수행하여 새로운 프로세스를 생성할 수 있다.
Wait() 시스템 콜
- 프로세스가 자식 프로세스를 만들고 wait 시스템 콜을 호출하면, 커널은 child가 종료될 때 까지 프로세스 A를 sleep 시킨다. 이 경우 부모 프로세스가 blocked가 된다. child가 종료되면 커널은 프로세스 A를 깨워 A는 ready 상태로 진입한다.
exit() 시스템 콜
- 프로세스가 종료될 때, 삽입되는 시스템 콜이다. 프로세스가 종료되는 경우는 두 가지인데, 자발적 종료 또는 비자발적 종료로 나뉜다. 자발적인 종료의 경우, 마지막 statement 수행 후 exit() 시스템 콜을 통해 호출될 수 있으며, 프로그램에 명시적으로 적어주지 않아도 main 함수가 리턴되는 위치에 컴파일러가 넣어준다. 비자발적인 종료의 경우, 부모 프로세스가 자식 프로세스를 강제 종료 시키는 경우가 있다. 자식 프로세스의 한계치를 넘어서는 자원이 요청되거나, 자식에게 할당된 태스크가 더 이상 필요하지 않을 때의 경우다. 또는 부모가 종료 준비를 하면서 자식 프로세스들이 먼저 종료되는 경우이다. 또는 키보드로 kill, break 등을 친 경우이다. ( ctrl+c, z 등 )
프로세스 간 협력
- 독립적 프로세스, 프로세스는 각자의 주소공간을 가지고 수행되므로, 원칙적으로 하나의 프로세스는 다른 프로세스의 수행에 영향을 미치지 못함.
- 협력 프로세스, 프로세스의 협력 메커니즘을 통해 하나의 프로세스가 다른 프로세스의 수행에 영향을 미칠 수 있음.
- 독립적 프로세스의 성격에 따라, 다른 프로세스의 메모리 공간을 침범하거나 지켜보는 방식으로 참조할 수 없으나, 프로세스를 협력적으로 만들 수 있는데 이 메커니즘을 Interprocess Communication, IPC라고 한다.
- 방법은 message passing, shared memory 두 가지로 나뉜다.
- message passing은 Process P가 Process Q에게 메시지를 전달하는 방식이다. message passing은 프로세스에서 제공되는 기능은 아니며, 운영체제(kernel)에게 시스템 콜을 요청하여 운영체제를 활용한 방식이다. Direct Communication은 통신하려는 프로세스의 이름을 명시적으로 표시하는 방식으로 Process P: Send (to Q) -> Process Q: Receive (from P)로 표시할 수 있으며, 프로세스 양자간의 협의를 통해 이루어지는 방식이다. Indirect Communication은 mailbox (또는 port)를 통해 메시지를 간접적으로 전달하는 방식이다. Process P: send (to M) -> Mailbox M -> Process Q : receive (from M)으로 표시할 수 있다. 이 둘 방식은 모두 kernel의 기능을 활용한 것으로 Sender, Receiver 모두 운영체제로 메시지를 전달하거나 전달받는 방식이다.
- Shared Memory는 메모리를 공유하는 방식이다. 서로 다른 Process는 원칙적으로 메모리 공간을 공유할 수 없다. Process가 시스템 콜을 통해 운영체제에게 메모리 공유 공간을 요청하여 공유된 메모리 공간을 할당받는 방식이다. 이렇게 되면 Process P,Q 가 접근할 수 있는 메모리 공간을 할당받아 사용할 수 있는데, 서로 메모리 공유를 할 수 있는 관계가 되므로 신뢰할 수 있는 프로세스 임이여 함이 전제조건이다.
- 추가적인 예로 multi thread 간에 통신이 있다. thred 간의 통신은 하나의 프로세스 이므로 메모리 공간을 공유하고 있다. 따라서 프로세스간 협력, IPC로 보기는 어렵고 협력이 가능하다.
'운영체제' 카테고리의 다른 글
인강 ) 프로세스 동기화(2) (0) | 2021.05.05 |
---|---|
인강 6) 프로세스 동기화(1) (0) | 2021.04.29 |
인강 3) Process 1 (0) | 2021.04.15 |
1 Overview (0) | 2021.04.07 |
- Total
- Today
- Yesterday
- 아레나
- Propositional and Predicate Logic
- grafana cloud
- 데이터 중심 애플리케이션 설계
- 그라파나
- 명제논리
- 백준
- arena simulation
- 이산수학
- beginning javascript
- 대규모 시스템 설계 기초
- 항해99
- Simulation
- 자바스크립트 예제
- 로젠
- Discrete Mathematics
- 시뮬레이션
- 최단경로 알고리즘
- Arena
- paul wilton
- flutter
- 자바스크립트
- rosen
- 아레나 시뮬레이션
- 아레나시뮬레이션
- 가상 면접 사례로 배우는 대규모 시스템 설계 기초
- 엄청난 인내심과 시뮬레이션을 위한 아레나 툴
- 이산 수학
- javascript
- Grafana
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |