프로세스 관리

DEVJUN·2022년 7월 24일
0

운영체제(OS)

목록 보기
4/5
post-thumbnail

1. 프로세스의 개념

프로세스(process)란 실행 중인 프로그램을 뜻한다.

디스크에 실행파일 형태로 존재하던 프로그램이 메모리에 올라가서 실행 되기 시작하면 비로소 프로세스가 되며 프로세스는 CPU를 획득해 자신의 코드를 수행하기도 하고, 때로는 CPU를 반환하고 입출력 작업을 수행하기도 한다. 그러다가 자신의 임무를 다 수행하고 나면 종료되어 사라진다.

프로세스를 이해하기 위해서는 프로세스의 문맥(context)에 대해 알아야한다. 프로세스의 문맥이란 프로세스가 현재 어떤 상태에서 수행되고 있는지 정확히 규명하기 위해 필요한 정보를 의미한다.

시분할 시스템 환경에서는 타이머 인터럽트에 의해 CPU를 사용했다가 빼앗겼다가를 반복하기 때문에 CPU를 보유한 시기에 어느 부분까지 명령을 수행했는지 정확한 상태를 재현해야 한다. 이때 정확한 재현을 위해 필요한 정보가 프로세스의 문맥이다.

프로세스의 문맥은 그 프로세스의 주소 공간(코드, 데이터, 스택 상태)을 비롯해 레지스터에 어떤 값을 가지고 있었는지와, 시스템 콜 등을 통해 커널에서 수행한 일의 상태, 그 프로세스에 관해 커널이 관리 하고 있는 각종 정보 등을 포함한다.

2. 프로세스의 상태

프로세스의 상태는 실행(running), 준비(ready), 봉쇄(blocked, wait, sleep)의 세가지로 구분할 수 있다.

실행 상태는 프로세스가 CPU를 보유하고 기계어 명령을 실행하고 있는 상태를 가르킨다.
준비 상태는 프로세스가 CPU만 보유하면 당장 명령을 실행할 수 있지만 CPU를 할당받지 못한 상태를 가르킨다.
봉쇄 상태는 CPU를 할당받더라도 당장 명령을 실행할 수 없는 프로세스의 상태를 말한다.

이 밖에도 프로세스가 생성 중인 시작(new) 상태 종료 중인 완료(terminated) 상태가 있다. 시작 상태는 프로세스가 시작되어 그 프로세스를 위한 각종 자료구조가 생성되었지만 아직 메모리 획득을 승인받지 못한 상태이고, 완료 상태는 프로세스가 종료되었으나 운영체제가 그 프로세스와 관련된 자료구조를 완전히 정리하지 못한 상태를 말한다.

위에서처럼 프로세스의 상태를 구분하는 이유는 컴퓨터의 자원을 효율적으로 관리하기 위해서이다.

예를 들어 실행 상태에서 CPU의 제어권을 가지고 프로세스가 실행되는 중에 타이머 인터럽트가 발생하면 CPU의 제어권은 운영체제로 이양된다. 그러면 운영체제는 타이머 인터럽트 처리루틴으로 가서 수행 중이던 프로세스의 문맥을 저장하고 준비 상태에 있는 프로세스 중에서 새롭게 CPU의 제어권을 부여할 프로세스를 선택한다. 그러면 원래 수행 주이던 프로세스는 준비 상태로 변하고 새롭게 CPU를 할당받은 프로세스가 실행 상태가 된다.

위의 예시처럼 실행시킬 프로세스를 변경하기 위해 원래 수행 중이던 프로세스의 문맥을 저장하고 새로운 프로세스의 문맥을 세팅하는 과정을 문맥교환(context switch)라고 한다.

문맥 교환이 일어나는 경우는 타이머 인터럽트가 발생하는 경우 외에도 실행 상태에 있던 프로세스가 입출력 요청 등으로 봉쇄 상태로 바뀌는 경우도 있다. 이때 준비 상태에 있는 프로세스들 중에서 CPU를 할당받을 프로세스를 선택한 후 실제로 CPU의 제어권을 넘겨받는 과정CPU 디스패치(dispatch)?라고 한다.

3. 프로세스 제어블록

프로세스 제어블록(Process Control Block: PCB)이란 운영체제가 시스템 내의 프로세스들을 관리하기 위해 프로세스마다 유지하는 정보들을 담는 커널 내의 자료구조를 뜻한다.

PCB의 구성요소

  • 프로세스의 상태(process state)
  • 프로그램 카운터(program counter)의 값
  • CPU 레지스터(CPU register) 값
  • CPU 스케줄링 정보(CPU scheduling information)
  • 메모리 관리 정보(memory management information)
  • 자원 사용 정보(accounting information)
  • 입출력 상태 정보(I/O status information)

프로세스의 상태는 CPU를 할당해도 되는지 여부를 결정하기 위해 필요하다.

프로그램 카운터 값은 다음에 수행할 명령의 위치를 가리킨다.

CPU 레지스터 값은 CPU 연산을 위해 현 시점에 레지스터에 어떤 값을 저장하고 있는지를 나타낸다.

CPU 스케줄링 정보는 프로세스의 스케줄링을 위해서 필요한 정보이다.

메모리 관리 정보는 프로세스의 메모리 할당을 위해 필요한 정보이다.

자원 사용 정보는 사용자에게 자원 사용 요금을 계산해 청구하는 등의 용도로 사용된다.

입출력 상태 정보는 프로세스가 오픈한 파일 정보 등 프로세스의 입출력 관련 상태 정보를 나타낸다.


4. 문맥교환

문맥교환은 위에서 이야기 한 것처럼 하나의 사용자 프로세스로부터 다른 사용자 프로세스로 CPU의 제어권이 이양되는 과정을 뜻한다.

사용자 프로세스가 CPU를 할당받고 실행되던 중에 타이머 인터럽트가 발생하면 CPU의 제어권이 운영체제로 넘어가고, 운영체제는 타이머 인터럽트 처리루틴으로 가서 직전까지 수행 중이던 프로세스의 문맥을 저장하고 새롭게 실행시킬 프로세스에게 CPU를 이양한다. 이 과정에서 원래 수행 중이던 프로세스는 준비상태로 바뀌고 새롭게 CPU를 할당받은 프로세스는 실행 상태가 된다.

문맥교환 중에 원래 CPU를 보유하고 있던 프로세스는 프로그램 카운터값 등 프로세스의 문맥을 자신의 PCB에 저장하고, 새롭게 CPU를 할당받을 프로세스는 예전에 저장했던 자신의 문맥을 PCB로부터 실제 하드웨어로 복원시키는 과정을 거친다.

문맥교환은 타이머 인터럽트가 발생하는 경우 이외에도 프로세스가 입출력 요청이나 다른 조건을 충족하지 못해 CPU를 회수당하고 봉쇄 상태가 되는 경우에도 발생할 수 있다.

여기서 헷갈리지 말아야할 것은 프로세스가 실행 상태일 때 시스템 콜이나 인터럽트가 발생하면 CPU의 제어권이 운영체제로 넘어와 원래 실행 중이던 프로세스의 업무를 잠시 멈추고 운영체제 커널의 코드가 실행된다. 이 경우에도 CPU의 실행 위치 등 프로세스의 문맥 중 일부를 PCB에 저장하지만 이 과정을 문맥교환이라고 하진 않는다.

이는 하나의 프로세스의 실행모드만이 사용자모드에서 커널모드로 바뀌는 것일 뿐 CPU를 점유하는 프로세스가 다른 사용자 프로세스로 변경되는 과정이 아니기 때문이다.

이러한 모드 변경보다 문맥교환에서는 훨씬 많은 오버헤드가 뒤따른다.

문맥교환 중에 일어나는 작업이 실제 시스템에게 유용한 작업이 아니기 때문에 타이머에 CPU 할당시간을 아주 작게 세팅해 프로세스 간 문맥교환이 빈번하게 발생하도록 하면 이에 드는 오버헤드가 커지고 반대로 할당시간을 너무 크게 설정하면 시분할 시스템의 의미가 퇴색되므로 적절하게 CPU 할당시간을 정하는 것이 중요하다.

5. 프로세스를 스케줄링하기 위한 큐

운영체제는 준비 상태에 있는 프로세스들을 줄 세우기 위해 준비 큐(ready queue)를 두고 준비 큐의 제일 앞에 줄 서 있는 프로세스에 제일 먼저 CPU를 할당한다.

준비 큐 외에도 운영체제는 특정 자원을 기다리는 프로세스들을 줄 세우기 위해 자원별로 장치 큐(device queue)를 둔다.

위 이미지는 하드웨어 자원을 기다리는 프로세스들을 줄 세우기 위해 필요한 것들이다. 큐는 소프트웨어 자원을 기다리는 경우에도 필요하다. 예를 들어 공유 데이터에 대한 접근 권한은 소프트웨어 자원으로 분류될 수 있다.

프로세스가 공유 데이터를 사용하는 중에 다른 프로세스가 같은 데이터를 접근하면 데이터에 대한 일관성이 훼손될 수 있다. 따라서 공유 데이터는 매 시점 하나의 프로세스만이 접근할 수 있도록 해야 한다.

여기서 접근이란 CPU를 사용하는 것 뿐 아니라 준비 상태나 봉쇄 상태로 변경된 경우도 포함이다.

따라서 여러 프로세스가 공유 데이터에 동시에 접근하려고 할 경우 공유 데이터를 기다리는 큐에 줄 서게 하여 현재 그 데이터를 사용하고 있는 프로세스가 데이터를 반납하기 전까지는 접근하지 못하게 하고, 반납할 경우 큐에 줄 서 있는 순서대로 데이터의 접근 권한을 주는 방법을 사용하게 된다.

위 이미지와 같이 프로세스의 상태 관리는 커널의 주소 영역 중 데이터 영역에 다양한 큐를 두어 수행하게 된다. 각 프로세스가 CPU를 기다리는지, 입출력을 기다리는지 등의 정보를 커널이 총체적으로 관리한다는 뜻이다.

큐는 각 프로세스의 PCB를 연결리스트 형태로 관리하며 포인터를 사용해 순서를 정한다.

프로세스가 CPU를 할당받고 코드를 수행하다가 입출력 요청이 발생하면 해당 장치 큐에 가서 줄을 서게 된다. 장치 큐에 속한 프로세스들은 봉쇄 상태에 있다가 해당 장치의 서비스를 받고 나서 장치 컨트롤러가 인터럽트를 발생시키면 준비 상태로 바뀌어 준비 큐로 이동한다.

6. 프로세스의 생성

시스템이 부팅된 후 최초의 프로세스는 운영체제가 직접 생상하지만 그다음부터는 이미 존재하는 프로세스가 다른 프로세스를 복제 생성하게 된다. 이때 프로세스를 생성한 프로세스를 부모 프로세스라고 하고 새롭게 생성된 프로세스를 자식 프로세스라고 한다.

프로세스는 인간 세계와 달리 자식 프로세스가 먼저 죽고, 이에 대한 처리는 부모 프로세스가 담당하고 마지막에서야 부모 프로세스가 종료된다.

프로세스가 생성되면 자신만의 독자적인 주소 공간을 갖는다. 부모 프로세스가 자식 프로세스를 생성하면 자식 프로세스는 부모 프로세스와는 별도의 주소 공간을 갖게 되는데, 처음 주소 공간을 생성할 때에는 부모 프로세스의 주소 공간 내용을 그대로 복사해서 생성한다. 자식 프로세스가 다른 프로그램을 수행하기 위해서는 생성된 주소 공간 위에 새로운 프로그램의 주소 공간을 덮어씌워 실행하게 되는 것이다.

유닉스에서 fork() 시스템 콜은 자식 프로세스를 생성할 때 부모 프로세스의 내용을 그대로 복제 생성하게 된다. fork()를 통해 생성된 자식 프로세스는 exec() 시스템 콜을 통해 새로운 프로그램으로 주소 공간을 덮어씌울 수 있다.

프로세스의 종료는 두 가지로 나뉜다. 첫 번째는 프로세스가 마지막 명령을 수행한 후 운영체제에게 이를 알려 이루어지는 자발적 종료이다. 이는 프로세스는 명령을 모두 수행한 후 프로그램이 마쳐지는 코드 부분에 exit()라는 시스템 콜을 통해 운영체제에게 자신이 종료됨을 알릴 수 있다.

운영체제는 이 프로세스로부터 자원을 회수하고 시스템 내에서 이 프로세스를 정리하게 된다.

두 번째는 비자발적 종료로 부모 프로세스가 자식 프로세스의 수행을 강제 종료시키는 것이다.

강제종료가 발생하는 경우는...

1.  자식 프로세스가 할당 자원의 한계치를 넘어서는 많은 양의 자원을 요구할 때
2.  자식 프로세스에게 할당된 작업이 더 이상 필요하지 않을 때
3.  부모 프로세스가 종료되는 경우 등이다.

서버 컴퓨터에서 수행시킨 프로그램을 로그 아웃을 하고 계속 수행시켜야 하는 경우가 있을 수 있는데 이때 프로세스를 계속 실행시키려면 로그아웃 후에도 존재하는 시스템 프로세스의 자식으로 이양시키는 절차가 필요하다. 이러한 방식을 통해 부모가 죽기 전에 자식이 먼저 죽게 된다는 원칙이 여전히 지켜지는 것이다. 즉 다른 프로세스의 양자로 자식 프로세스를 보내어 기존 프로세스가 종료된 후에도 다른 프로세스 아래에서 계속 수행될 수 있도록 하는 것이다.

다음은 프로세스가 자식 프로세스를 생성하는 과정이다.

첫째로 프로세스가 fork()시스템 콜(운영체제를 통해서만)을 하게 되면 CPU의 제어권이 커널로 넘어가게 되고, 커널은 fork()를 호출한 프로세스를 복제해 자식 프로세스를 생성하게 된다.fork()를 통해 생성된 프로세스는 부모 프로세스의 주소 공간을 비롯해 프로그램 카운터, 레지스터 상태, PCB, 커널스택 등 모든 문맥을 그대로 복제해 자식 프로세스의 문맥을 형성하게 된다.

자식 프로세스는 부모 프로세스의 처음부터 수행을 시작하는 것이 아닌 부모 프로세스가 현재 수행한 시점(프로그램 카운터 지점)부터 수행한다.

자식 프로세스와 부모 프로세스의 유일한 차이점은 운영체제가 프로세스를 관리하기 위해 사용하는 프로세스 식별자의 차이 이다.

fork() 함수의 결과값은 원본에게는 양수, 복제본에게는 0을 준다.

자식 프로세스에게 부모와는 다른 독자적인 프로그램을 수행시킬 수 있도록 하기위해 exec() 시스템 콜을 지원한다.

exec() 시스템 콜은 프로세스가 지금까지 수행했던 상태를 잊어버리고 그 주 소 공간을 완전히 새로운 프로그램으로 덮어씌운 후 새로운 프로그램의 첫 부분부터 다시 실행을 시작하도록 하는 시스템 콜이다.

따라서 새로운 프로그램을 수행시키기 위해서는 fork()를 통해 기존 프로세스와 동일한 프로세스를 복제한 후 exec()를 통해 새롭게 수행시키려는 프로세스를 자식 프로세스의 주소 공간에 덮어씌우면 되는 것이다.

7. 프로세스 간의 협력

프로세스는 각자 자신만의 독립적인 주소 공간을 가지고 수행되며 프로세스가 다른 프로세스의 주소 공간을 참조하는 것을 허용하지 않는다. 즉 수행에 영향을 미칠수 없는 것이다.

하지만 운영체제는 프로세스 간의 협력 메커니즘을 제공해 독립적인 프로세스들의 업무의 효율성이 증진될 수 있도록 한다.

운영체제가 제공하는 대표적인 프로세스 간의 협력 메커니즘은 IPC(Inter-process Communication: 인터프로세스 커뮤니케이션)이 있다. IPC란 하나의 컴퓨터 안에서 실행 중인 서로 다른 프로세스 간에 발생하는 통신을 말한다.

이러한 통신에서는 공유 데이터를 서로 다른 두 프로세스가 사용할 수 있기 때문에 데이터의 불일치 문제가 발생할 수 있어서 의사소통 기능과 ⭐️동기화를 보장해 주어야 한다.

동기화를 보장하기 위해서는 하나의 프로세스가 공유 데이터의 값을 변경하는 동안 다른 프로세스는 그 데이터에 접근할 수 없게 해야 한다.

IPC의 대표적인 방법으로는 메시지 전달방식(message passing)공유메모리 방식(shared memory)이 있다. 두 방식의 차이는 프로세스 사이에 공유 데이터를 사용하는가, 그렇지 않는가 이다.

profile
🧑🏻‍💻iOS

0개의 댓글