1 분 소요

좀비 프로세스와 고아 프로세스를 이해하기 위해서 먼저 프로세스를 생성하는 과정을 좀 더 자세하게 알아보자.

프로세스 생성

부모 프로세스는 시스템 콜 fork()를 통해서 자식 프로세스를 생성한다.

fork()는 부모 프로세스의 주소 공간을 그대로 복사한다. 이 상태에서 시스템 콜 exec()을 호출하게 되면 생성된 자식 프로세스의 메모리 공간을 새로운 프로그램으로 대체할 수 있다. 이러한 과정을 통해 부모와 다른 실행 흐름을 가진 자식 프로세스를 생성할 수 있다.

자식 프로세스 트리

img1

리눅스에서는 커널이 자체적으로 생성한 init 프로세스가 있고 이후에 생성된 모든 프로세스는 init의 자손이 되며 트리구조를 이룬다.

일단 모든 프로세스의 조상 프로세스는 커널이 생성한 init이라는 것만 기억하고 넘어가자. (아래에 설명하겠다.)

실행 흐름

위에서 자식 프로세스를 생성하는 방법을 설명했다. 그렇다면 이렇게 생성된 자식 프로세스와 부모 프로세스는 어떤 방식으로 실행될 수 있을까?

결론부터 말하자면 2가지 흐름이 있을 수 있다.

  1. 부모 프로세스가 자식 프로세스의 종료까지 기다리고 이후에 실행한다.
  2. 부모 프로세스와 자식 프로세스를 동시에 실행한다.

1번 케이스

img1

이 경우는 부모 프로세스가 wait()을 호출하여 자식 프로세스의 종료를 기다린다. 그리고 자식 프로세스가 exit()을 호출하면 wait()의 파라미터로 자식 프로세스의 상태가 들어오게 되는데 이를 통해서 자식 프로세스가 사용하던 자원을 모두 반납한다.

이 경우는 별다른 문제가 없는 것을 확인할 수 있다.

2번 케이스

이 케이스는 부모 프로세스에서 wait()을 호출하지 않는다. 부모 프로세스가 wait()을 호출하지 않는다면 어떤 일이 생길까?

이제 좀비 프로세스와 고아 프로세스가 등장한다.

좀비 프로세스

정의

좀비 프로세스는 자식 프로세스가 부모 프로세스보다 먼저 종료된 프로세스를 말한다.

정의만 보면 무슨말인지 잘 이해가 안될 것이다. 자식 프로세스가 종료되면 종료된 것 아닌가?

여기서 주목해야 하는 점은 부모 프로세스에서 wait() 을 호출하지 않았다는 것이다. wait()을 호출하지 않았기 때문에 자식 프로세스가 종료되더라도 종료에 대한 정보가 부모 프로세스에 전달되지 못하고 사용하던 리소스를 반납할 방법이 없어진다.

종료되도 리소스가 계속 남아있기 때문에 좀비 프로세스라고 부른다.

커널은 이런 상황을 대비하여 프로세스에 대한 최소한의 정보를 저장하고 있다. 이후에 부모 프로세스에서 wait()을 호출하여 남아있는 리소스를 제거할 수 있다.

고아 프로세스

정의

고아 프로세스는 부모 프로세스가 자식 프로세스보다 먼저 종료된 프로세스를 말한다.

이 경우도 좀비 프로세스 경우와 마찬가지로 부모 프로세스에서 wait()을 호출하지 않은 경우에 발생한다.

부모 프로세스가 wait()을 호출하지 않고 자신의 로직을 수행하다 자식 프로세스보다 먼저 종료되면 자식 프로세스는 종료됐을 때 리소스를 반납해줄 주체가 없어진다. 이렇게 부모가 없이 남겨진 프로세스를 고아 프로세스라고 한다.

이렇게 부모가 사라진 경우 고아 프로세스는 위에서 설명한 init 프로세스의 자식 프로세스가 된다. init 프로세스는 이 프로세스가 종료되면 리소스를 회수하고 정상적으로 프로세스를 제거한다.

정리

좀비 프로세스 : 부모가 wait()을 호출하지 않은 상태에서 자식 프로세스가 종료되어 리소스를 반납하지 못하는 프로세스

고아 프로세스 : 부모가 먼저 종료되서 남겨진 프로세스

참고