프로세스는 실행 중인 프로그램(Program In Execution)을 의미합니다. 프로그램은 하드디스크(보조기억장치)에 저장되어 아무 일도 하지 않는 상태입니다.
프로그램을 어떠한 요청에 의해 메인 메모리에 할당하여 CPU를 사용하면서 실행하게 되면 이를 프로세스로 부릅니다. Job, Task라고도 부릅니다.
프로세스는 프로세스의 의해 만들어집니다. 컴퓨터가 부팅이 되면 운영체제가 메모리에 올라오고 운영체제가 처음으로 수행하는 일 중에 하나는 최초의 프로세스를 생성하는 것입니다. 이 프로세스가 다른 프로세스를 만들고 그 프로세스가 또 다른 프로세스를 만드는 과정을 반복합니다.
UNIX 운영체제 기준으로 최초의 프로세스의 이름은 Init입니다.
프로세스들이 다양하게 생성되면 프로세스를 생성한 쪽을 부모 프로세스, 생성된 프로세스를 자식 프로세스라고 합니다. 같은 부모 프로세스를 가진 자식 프로세스는 형제 프로세스라고 합니다.
새로운 프로세스를 만드는 시스템 콜은 fork()입니다.
생성된 프로세스에서 어떤 파일을 실행하려면 시스템 콜 exec()를 사용합니다.
프로세스를 종료하는 시스템 콜은 exit()입니다. 프로세스가 종료되면 사용한 모든 자원을 회수해서 운영체제로 돌아가야합니다.
프로세스에 대한 모든 정보는 PCB라고 부르는 자료구조에 저장됩니다. 이 자료구조는 다음과 같은 정보를 가지고 있습니다.
Context Switching은 프로세스가 실행되다가 인터럽트가 발생해 CPU를 한 프로세스에서 다른 프로세스로 넘겨주는 과정입니다.
운영체제는 CPU를 내어주는 프로세스의 상태를 그 프로세스의 PCB에 저장하고, 새롭게 CPU를 얻는 프로세스의 상태를 PCB에서 읽어옵니다. 즉, PCB 정보가 바뀌는 것이 Context Switching입니다.
다만, 시스템 콜이나 인터럽트가 발생한다고 반드시 Context Switching은 아닙니다. 다른 프로세스에 CPU가 넘어가야 Context Switching입니다.
CPU 스케줄링이라고도 합니다.
멀티프로그래밍(Multiprogramming)의 목적은 CPU를 최대한 사용하기 위해 여러 개의 프로세스를 항상 실행시키는 것입니다.
시간 공유(Time Sharing)의 목적은 프로세스 간에 CPU를 빠르게 전환함으로써 사용자가 각 프로그램이 실행되는 동안 서로 상호작용할 수 있도록 만드는 것입니다.
이러한 목적들을 당성하기 위해 프로세스 스케줄러는 CPU에서 프로그램 실행을 위해 사용 가능한 프로세스를 선택합니다. 이렇게 어떤 프로세스를 CPU에 할당할 것인가를 결정하는 일을 프로세스 스케줄링(Process Scheduling)이라고 합니다.
CPU가 하나인 시스템은 하나의 running 프로세스를 가질 수 있고, 여러 프로세스가 존재하는 경우 나머지 CPU가 free 상태가 될 때까지 대기해야 하기 때문에 적절한 스케줄링이 필요합니다.
프로세스를 스케줄링하기 위한 자료구조로 Queue를 사용합니다.
프로세스는 실행되면서 상태가 여러 번 변하게 됩니다. 변한 상태에 따라서 서비스를 받아야 하는 곳이 다릅니다. 그리고 일반적으로 프로세스는 여러 개가 한 번에 실행되므로 실행되는 순서가 필요합니다.
스케줄러는 3가지 종류가 있습니다.
현대 OS는 거의 단기 스케줄러를 사용합니다. 나중에 포스팅할 가상 메모리 덕분에 단기 스케줄러 만으로도 OS가 정상적으로 작동할 수 있습니다.
자식 프로세스가 부모 프로세스보다 먼저 종료되는 경우에 자식 프로세스를 좀비 프로세스라고 합니다. 자식 프로세스가 exit() 시스템 콜을 호출하면서 종료되면 이 프로세스에 관련된 모든 메모리와 자원이 해제되어 다른 프로세스에서 사용할 수 있게 됩니다.
자식 프로세스가 부모 프로세스보다 먼저 죽는 경우, 부모 프로세스가 종료 상태를 회수하기 위해 커널은 자식 프로세스의 최소한의 정보(PID, 종료 상태 등)를 가지고 있게 됩니다. 부모 프로세스가 좀비 프로세스의 종료 상태를 회수하게 되면(wait() 시스템 콜 호출) 좀비 프로세스는 제거됩니다.
커널 입장에서 좀비 프로세스는 최소한의 정보를 가지고 있기 때문에 큰 성능 저하를 야기하지는 않습니다. 하지만 프로세스 스케줄링에 있어서 queue에 대기하고 있는 프로세스의 양이 증가하게 되고, 커널 구조체를 유지하기 위한 비용도 무시할 수는 없습니다.
부모 프로세스가 자식 프로세스보다 먼저 종료되는 경우에 자식 프로세스를 고아 프로세스라고 합니다. 이 때, 초기 프로세스(init())가 이 고아 프로세스의 새로운 부모 프로세스가 됩니다.
종료되는 프로세스가 발생할 때, 커널은 이 프로세스가 누구의 부모 프로세스인지 확인하고 이 고아 프로세스의 부모 프로세스 ID를 1(init 프로세스)로 바꿔줍니다. 고아 프로세스가 작업을 종료하면 init 프로세스가 wait()을 호출하여 고아 프로세스가 좀비 프로세스가 되는 것을 방지합니다.
고아 프로세스는 init 프로세스가 관리를 해 주지만 부모 프로세스가 종료되기 전에 모든 자식 프로세스를 wait() 처리 해주는 것이 좋습니다.