CS/OS

[OS] 프로세스란?(2) _ PCB, Context switch, Zombie process etc.

jhkimmm 2021. 12. 30. 10:08

지난 포스팅에 이어서 프로세스에 대해 알아보겠습니다.

PCB

PCB(Process Control Block)는 프로세스를 표현하기 위한 자료구조이며 커널안에 존재합니다. PCB는 프로세스에 대한 아래와 같은 다양한 정보들을 담고 있습니다.

  • Process state : New, Running, Wait등의 프로세스 상태
  • Program counter : 다음 인스트럭션의 주소
  • CPU register
  • CPU scheduling info : Priority, 스케줄링 큐의 포인터, 스케줄링 파라미터들과 같은 스케줄링에 관련된 정보
  • Memory-management info : base & limit registers, Page table, Segment Table
  • Accounting info : CPU사용량, PID(프로세스 번호)
  • I/O status info : I/O디바이스의 리스트, 열린 파일의 리스트

※accounting이란 누가 얼마만큼의 리소스를 사용하고 있는지를 추적하는 OS의 Task입니다.

 

리눅스에서 PCB는 task_struct라는 구조체로 표현되어 있으며 깃허브에서 소스코드를 확인할 수 있습니다.

https://github.com/torvalds/linux/blob/master/include/linux/sched.h

 

GitHub - torvalds/linux: Linux kernel source tree

Linux kernel source tree. Contribute to torvalds/linux development by creating an account on GitHub.

github.com

task_struct

위 코드는 실제 task_struct 구조체 코드의 일부입니다.

프로세스 상태를 나타내는 __state, cpu를 사용하고 있는지를 나타내는 on_cpu등의 구초체 멤버로 PCB를 구현한 것을 확인할 수 있습니다.

 

Context Switch

Context(문맥)이란 PCB에 저장되어있는 프로세스의 현재 상태이고, Context Switch는 현재 프로세스의 state를 저장하고 다른 프로세스의 state를 복원하는 것 입니다.

쉽게 말하자면 Context Switch는 현재 진행 중인 프로세스를 다른 프로세스로 전환하는 것을 의미합니다. 위 그림에서 회색상자는 Kernel mode로 전환됐다는 의미이며, 이 작은 순간이 OS가 실행되는 순간이고 이외의 시간에 OS는 Sleeping합니다. 

여기서 알 수 있는 것은 OS는 항상 백그라운드에서 실행되거나 하는 프로세스가 아니라는 점 입니다. OS는 코드들이고 메모리에 커널코드가 저장된 부분이 있어서 시스템콜을 호출하면 OS의 코드들을 실행하고 다시 돌아오는 것입니다.

또한 프로세스의 관리를 위해서 필요하긴 하지만 그동안 실질적인 작업을 하지 못하기 때문에 Context Switch는 가능한 빠르게 이루어져야합니다.

 

그리고 OS에서 시스템콜을 처리하고 반드시 이전에 실행되던 프로세스로 되돌아가는 것은 아니고 다른 프로세스를 실행시킬 수도 있습니다. 이처럼 Kernel mode에서 다시 어떤 프로세스로 가야할지 결정하는 것이 Scheduling입니다.

 

프로세스에 대한 Operation - fork(), exec(), wait(), exit()

리눅스에서의 프로세스

부모 프로세스와 자식 프로세스는 트리구조를 이룹니다.

PID : 프로세스를 구별하는 정수입니다.

init : 모든 유저 프로세스들의 root parent process입니다.

Daemon process : sshd, httpd, kthreadd 와 같이 이름이 d 끝나는 프로세스는 컴퓨터가 작동하는 동안은 exit하지 않고 계속해서 실행되는 daemon process입니다.

리눅스에서 제공하는 프로세스를 위한 함수는 아래와 같습니다.

함수 기능
fork() - 새로운 프로세스를 생성한다.

- 새로 생성된 프로세스는 parent process 복사본이다.

- PID를 제외하고 모든 메모리 데이터가 child process에게 복사된다.

- fork()이후에 부모와 자식 프로세스는 동시에 실행된다.

- return값이 부모와 자식 프로세스에서 다르다.
  pid = fork();
  위 코드를 실행할 때 pid에 담기는 값이 다르다는 의미
exec() - 프로세스의 메모리 공간을 지우고 새로운 프로그램(실행파일, binary file)으로 대체한 실행한다.

- exec() control 리턴 하지 않는다.
exec()의 실행전에는 control point가 유지되지만 exec()가 호출되면 control point가 초기화되면서 child process의 내용이 모두 지워지므로 return할 수 있는 방법이없다.
wait() 부모 프로세스가 wait() 호출하면 자식 프로세스가 종료할 까지 기다린다(block상태가 된다)
exit() - exit() 호출하면 프로세스가 종료된다.
이때 status value 부모에게 리턴하며(0 : success ,그 외 : failure) 리소스들도 해제된다.

 

사용 예시 및 흐름

부모는 자식 프로세스를 종료시킬 수 있으며 현재 리눅스에서는 부모가 먼저 종료된 고아프로세스를 허용합니다.

Zombie Process

종료되었지만 위 코드처럼 부모가 wait() 호출해주지 않아서 OS가 status값을 계속 가지고 있는 프로세스를 좀비프로세스라고 합니다.

작업 수행을 완료해서 exit되었지만 어딘가에서 status값을 받아주지 않았으므로 살아있지도, 죽지도 않은 프로세스라는 의미에서 "좀비 프로세스"라는 이름이 붙었습니다.

 

다행히 리눅스에서는 가장 최상위 Root 프로세스인 init 프로세스가 주기적으로 wait()를 호출해서 좀비 프로세스의 status값을 받아 처리해줍니다.