Process Creation
프로세스 생성은 부모 프로세스가 연산을 통해 자식 프로세스를 만들어냅니다. 생성된 자식 프로세스 또한 새로운 자식 프로세스를 만들 수 있으며 이를 구별하기 위해 모든 프로세스는 각자 고유의 PID를 가지게 됩니다. 이렇게 생성된 프로세스 간의 관계는 하나의 큰 트리구조가 됩니다.
init 프로세스 : 리눅스, 유닉스 계열의 OS에서 최초로 실행되는 데몬 프로세스이며 PID는 항상 1입니다.
생성된 자식 프로세스는 각자 고유의 PID, 메모리, CPU 등 새 PCB가 할당되며 고유의 자원을 획득하게 됩니다. 이로 인하여 부모 프로세스의 자원 접근에 제한이 생기며 특수한 방법을 통해 공유할 수 있게 됩니다.
프로세스를 생성한 후 부모 프로세스는 다음과 같이 2가지 행동을 할 수 있습니다.
- 자식 프로세스가 끝날 때까지 기다립니다. ( -> waiting queue )
- 자식 프로세스와 함께 동작합니다. (멀티 프로세싱 환경)
자식 프로세스는 다음 중 하나의 프로세스가 됩니다.
- 부모 프로세스와 동일한 새로운 프로세스 : 이 경우 부모 프로세스의 프로그램, 데이터가 완전 복사됩니다.
- 새로운 프로그램 실행 : 새로운 프로그램을 메모리에 load 하고 이를 실행하게 됩니다.
fork()
Linux/UNIX 환경에서 새로운 프로세스를 만드는 시스템 콜 함수입니다.
- 생성된 자식 프로세스는 부모 프로세스의 데이터와 프로그램이 완전 복사가 되어 똑같은 프로그램을 수행하는 프로세스가 됩니다. 이를 통해 부모, 자식 프로세스가 서로 간편하게 의사소통을 할 수 있습니다.
- 멀티 프로세싱을 통해 부모, 자식 프로세스는 함께 동작합니다.
- fork() 함수는 부모 프로세스에서 자식의 PID를 반환하고, 자식 프로세스에서는 0을 반환하여 구분할 수 있도록 해 줍니다.
exec()
Linux/UNIX 환경에서 프로세스를 새로운 프로그램을 실행하는 프로세스로 대체하는 시스템 콜 함수입니다.
- fork()와 다르게 자식 자식 프로세스를 생성하는 것이 아닌 현재 프로세스의 프로그램 코드를 새로운 프로그램 코드로 바꿔줍니다. 이로 인하여 프로그램 코드, 메모리, 파일 등 프로세스 자원이 새로 바뀌게 됩니다.
- exec() 함수는 현재 프로세스가 완전히 새로운 프로그램을 실행하는 프로세스로 대체되므로 반환 값이 없습니다.
보통 동작하는 방식은 fork()를 통해 자식 프로세스를 생성하고 자식 프로세스에서 exec()를 통해 새로운 프로그램을 돌리게 됩니다. 이때 부로 프로세스가 자식 프로세스가 끝나기를 기다려야 한다면 wait() 시스템 콜 함수를 이용하여 기다릴 수 있습니다.
wait()
- pid_t wait(int *statloc) 형태이며 자식 프로세스가 종료될 때까지 현재 프로세스의 동작을 멈추는 시스템 콜 함수입니다.
- 자식 프로세스가 종료되면 자식 프로세스 종료 시그널(SIGSHLD)이 발생하여 waiting queue에 있는 부모 프로세스가 ready queue로 넘어가게 되어 다시 실행이 가능해집니다.
- 블럭 모드로 동작하기 대문에 비용이 비싸며 자식 프로세스 관리를 하지 않으면 무한정 대기, 혹은 함수가 OS에 의해 강제 종료당할 수 있습니다.
- 종료된 자식 프로세스의 PID를 반환하며 인자로 받은 statloc에 프로세스의 종료 상태를 알 수 있는 번호가 입력됩니다.
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() 함수를 통해 부모 프로세스는 자식 프로세스를 관리할 수 있으며 효율적인 관리를 위해 다음과 같은 경우 자식 프로세스를 중단시키는 것이 좋습니다.
- 자식 프로세스가 너무 많은 자원을 사용하는 경우
- 자식 프로세스의 동작 결과가 어떤 이유로 인하여 더 이상 필요 없을 경우
- 부모 프로세스가 동작을 마치고 종료하는 경우
부모 프로세스가 자식 프로세스를 관리하지 않거나 강제 종료당하는 등 여러 가지 이유에 의하여 자식 프로세스가 자원을 낭비할 수 있습니다. 대표적으로 좀비 프로세스와 고아 프로세스가 있습니다.
'스터디 > 운영체제' 카테고리의 다른 글
[운영체제] Inter Process Communication (0) | 2021.04.02 |
---|---|
[운영체제] 좀비 프로세스, 고아 프로세스 (0) | 2021.03.15 |
[운영체제] 비전섬 스케줄링 vs 선점 스케줄링 (0) | 2021.03.10 |
[운영체제] Scheduling (0) | 2021.01.22 |
[운영체제] Context Switch (0) | 2021.01.16 |