스터디/운영체제

[운영체제] 프로세스 생성과 소멸

latter2005 2021. 3. 15. 00:14

Process Creation

프로세스 생성은 부모 프로세스가 연산을 통해 자식 프로세스를 만들어냅니다. 생성된 자식 프로세스 또한 새로운 자식 프로세스를 만들 수 있으며 이를 구별하기 위해 모든 프로세스는 각자 고유의 PID를 가지게 됩니다. 이렇게 생성된 프로세스 간의 관계는 하나의 큰 트리구조가 됩니다.

init 프로세스 : 리눅스, 유닉스 계열의 OS에서 최초로 실행되는 데몬 프로세스이며 PID는 항상 1입니다.

프로세스 트리 구조

 

생성된 자식 프로세스는 각자 고유의 PID, 메모리, CPU 등 새 PCB가 할당되며 고유의 자원을 획득하게 됩니다. 이로 인하여 부모 프로세스의 자원 접근에 제한이 생기며 특수한 방법을 통해 공유할 수 있게 됩니다.

 

프로세스를 생성한 후 부모 프로세스는 다음과 같이 2가지 행동을 할 수 있습니다.

  1. 자식 프로세스가 끝날 때까지 기다립니다. ( -> waiting queue )
  2. 자식 프로세스와 함께 동작합니다. (멀티 프로세싱 환경)

자식 프로세스는 다음 중 하나의 프로세스가 됩니다.

  1. 부모 프로세스와 동일한 새로운 프로세스 : 이 경우 부모 프로세스의 프로그램, 데이터가 완전 복사됩니다.
  2. 새로운 프로그램 실행 : 새로운 프로그램을 메모리에 load 하고 이를 실행하게 됩니다.

 

fork()

Linux/UNIX 환경에서 새로운 프로세스를 만드는 시스템 콜 함수입니다.

  • 생성된 자식 프로세스는 부모 프로세스의 데이터와 프로그램이 완전 복사가 되어 똑같은 프로그램을 수행하는 프로세스가 됩니다. 이를 통해 부모, 자식 프로세스가 서로 간편하게 의사소통을 할 수 있습니다.
  • 멀티 프로세싱을 통해 부모, 자식 프로세스는 함께 동작합니다.
  • fork() 함수는 부모 프로세스에서 자식의 PID를 반환하고, 자식 프로세스에서는 0을 반환하여 구분할 수 있도록 해 줍니다.

fork() 시스템 콜 함수

 

exec()

Linux/UNIX 환경에서 프로세스를 새로운 프로그램을 실행하는 프로세스로 대체하는 시스템 콜 함수입니다.

  • fork()와 다르게 자식 자식 프로세스를 생성하는 것이 아닌 현재 프로세스의 프로그램 코드를 새로운 프로그램 코드로 바꿔줍니다. 이로 인하여 프로그램 코드, 메모리, 파일 등 프로세스 자원이 새로 바뀌게 됩니다.
  • exec() 함수는 현재 프로세스가 완전히 새로운 프로그램을 실행하는 프로세스로 대체되므로 반환 값이 없습니다.

exec() 시스템 콜 함수

 

보통 동작하는 방식은 fork()를 통해 자식 프로세스를 생성하고 자식 프로세스에서 exec()를 통해 새로운 프로그램을 돌리게 됩니다. 이때 부로 프로세스가 자식 프로세스가 끝나기를 기다려야 한다면 wait() 시스템 콜 함수를 이용하여 기다릴 수 있습니다.

 

wait()

  • pid_t wait(int *statloc) 형태이며 자식 프로세스가 종료될 때까지 현재 프로세스의 동작을 멈추는 시스템 콜 함수입니다.
  • 자식 프로세스가 종료되면 자식 프로세스 종료 시그널(SIGSHLD)이 발생하여 waiting queue에 있는 부모 프로세스가 ready queue로 넘어가게 되어 다시 실행이 가능해집니다.
  • 블럭 모드로 동작하기 대문에 비용이 비싸며 자식 프로세스 관리를 하지 않으면 무한정 대기, 혹은 함수가 OS에 의해 강제 종료당할 수 있습니다.
  • 종료된 자식 프로세스의 PID를 반환하며 인자로 받은 statloc에 프로세스의 종료 상태를 알 수 있는 번호가 입력됩니다.

fork(), exec(), wait() 함수 사용 예시

 

Process Termination

exit()

현재 프로세스가 종료되어 SIGCHLD 시그널이 부모 프로세스로 전달되고, wait() 된 부모 프로세스는 자식 프로세스의 exit() 함수에 의해 정상 종료가 됨을 알 수 있습니다.

  • void exit(int status) 형태입니다.
  • status 값이 부모 프로세스 wait(int *statloc)의 statloc에 지정되어 반환합니다. 이를 통해 부모 프로세스는 자식 프로세스가 정상 종료 혹은 문제가 있었는지 확인할 수 있습니다.
  • 함수 호출 후 자식 프로세스에 할당된 모든 자원은 풀어집니다.
  • exit 함수가 없더라도 프로그램 코드의 끝에 도달하거나 return 키워드를 만나게 되면 자동으로 exit() 함수가 호출됩니다.

 

kill()

리눅스 명령어 kill 은 해당 프로세스를 종료시킵니다. 명령어와 달리 kill() 함수는 프로세스에게 특정 시그널을 전송하며 SIGKILL 시그널을 보내게 되면 명령어 kill과 같은 동작을 하게 됩니다.

  • int kill(pid_t pid, int sig) 형태이며 pid는 시그널을 보낼 프로세스의 PID, sig는 보낼 시그널입니다.
  • pid에 따라 단일 프로세스, 여러 프로세스에게 강제 종료 명령을 보낼 수 있습니다.

PID 인수에 따른 동작

  • 양의 정수 : 해당 PID를 가진 프로세스에 시그널을 보냅니다.
  • 0 : 같은 프로세스 그룹에 있는 모든 프로세스에 시그널을 보냅니다.
  • -1 : 프로세스가 전송 권한이 있는 모든 프로세스에 시그널을 보냅니다.
  • -1 외의 음수 : 절댓값을 취한 수의 프로세스가 속한 그룹에 포함된 모든 프로세스에 시그널을 보냅니다.

이 kill() 함수를 통해 부모 프로세스는 자식 프로세스를 관리할 수 있으며 효율적인 관리를 위해 다음과 같은 경우 자식 프로세스를 중단시키는 것이 좋습니다.

  1. 자식 프로세스가 너무 많은 자원을 사용하는 경우
  2. 자식 프로세스의 동작 결과가 어떤 이유로 인하여 더 이상 필요 없을 경우
  3. 부모 프로세스가 동작을 마치고 종료하는 경우

 

부모 프로세스가 자식 프로세스를 관리하지 않거나 강제 종료당하는 등 여러 가지 이유에 의하여 자식 프로세스가 자원을 낭비할 수 있습니다. 대표적으로 좀비 프로세스와 고아 프로세스가 있습니다.

반응형