프로세스

Hee Suh·2025년 2월 8일
0

[CS] OS

목록 보기
3/5
post-thumbnail

🦖 Operating System Concepts 10th

PART TWO PROCESS MANAGEMENT
Chapter 3 Processes

프로세스 개념 (Process Concept)

프로세스

Background

초기의 컴퓨터 시스템은 한 번에 하나의 프로그램만을 실행하도록 허용했다. 반면 오늘날의 컴퓨터 시스템들은 메모리에 다수의 프로그램이 적재되어 동시에 실행되는 것을 허용한다. 다양한 프로그램을 보다 견고하게 제어하고 보다 구획화해야 했고, 이러한 필요성에 따라 프로세스의 개념이 등장했다.

💡 프로세스

프로세스란 실행중인 프로그램을 말하며, 운영체제의 기본 작업 단위이다.
프로세스의 현재 활동 상태는 프로그램 카운터(PC) 값과 프로세서 레지스터의 내용으로 나타낸다.

**Figure 3.1** 프로세스 메모리 배치
Figure 3.1 프로세스 메모리 배치
  • 텍스트 섹션 — 실행 코드
  • 데이터 섹션 — 전역 변수
  • 힙 섹션 — 프로그램 실행 중에 동적으로 할당되는 메모리
  • 스택 섹션 — 함수를 호출할 때 임시 데이터 저장소 (e.g. 함수 매개변수, 리턴 주소, 지역 변수)

Cf. 스택에 활성화 레코드(activation record)가 push 되며 메모리가 동적으로 할당됨에 따라 힙이 커지고, 메모리가 시스템에 반환되면 축소된다. 스택 및 힙 섹션이 서로의 방향으로 터지더라도 운영체제는 서로 겹치지 않도록 해야 한다.

프로세스 상태 (Process State)

프로세스는 실행되면서 그 상태(state)가 변한다. 프로세스의 상태는 현재의 활동에 따라서 정의되며, 다음 상태 중 하나에 있게 된다.

**Figure 3.2** Diagram of process state.
Figure 3.2 Diagram of process state.
  • New — 프로세스가 생성 중이다.
  • Running — 명령어들이 실행되고 있다.
  • Waiting — 프로세스가 어떤 이벤트(e.g. 입출력(I/O) 완료 또는 신호 수신)가 일어나기를 기다린다.
  • Ready — 프로세스가 처리기(processor)에 할당되기를 기다린다.
  • Terminated — 프로세스의 실행이 종료되었다.

한 프로세서 코어에서는 오직 하나의 프로세스만이 실행(running)된다. 따라서 많은 프로세스가 준비완료(ready) 및 대기(waiting) 상태에 있을 수 있다.

프로세스 제어 블록 (Process Control Block)

프로세스 제어 블록(process control block, PCB)(a.k.a task control block)은 운영체제의 프로세스를 나타내는 커널 데이터 구조이다.

프로세스 제어 블록은 특정 프로세스와 연관된 여러 정보를 수록하며, 다음과 같은 것들을 포함한다.

**Figure 3.3** Process control block (PCB).
Figure 3.3 Process control block (PCB).
  • 프로세스 상태 — 상태는 new, ready, running, waiting, halted 등이다.
  • 프로그램 카운터 — 프로그램 카운터는 이 프로세스가 다음에 실행할 명령어의 주소를 가리킨다.
  • CPU 레지스터들 — CPU 레지스터는 컴퓨터 구조에 따라 다양한 개수와 유형을 가진다. 레지스터에는 누산기(accumulator), 인덱스 레지스터, 스택 레지스터, 범용(general-purpose) 레지스터들과 상태 코드(condition-code) 정보가 포함된다. 프로그램 카운터와 함께 이 상태 정보는, 나중에 프로세스가 다시 스케줄될 때 계속 올바르게 실행되도록 하기 위해서 인터럽트 발생 시 저장되어야 한다.
  • CPU-스케줄링 정보 — 이 정보는 프로세스 우선순위, 스케줄 큐에 대한 포인터와 다른 스케줄 매개변수를 포함한다.
  • 메모리 관리 정보 — 이 정보는 운영체제에 의해 사용되는 메모리 시스템에 따라 기준(base) 레지스터와 한계(limit) 레지스터의 값, 운영체제가 사용하는 메모리 시스템에 따라 페이지 테이블 또는 세그먼트 테이블 등과 같은 정보를 포함한다.
  • 회계(accounting) 정보 — 이 정보는 CPU 사용 시간과 경과된 실시간, 시간 제한, 계정 번호, job 또는 프로세스 번호 등을 포함한다.
  • 입출력(I/O) 상태 정보 — 이 정보는 이 프로세스에 할당된 입출력 장치들과 열린 파일들의 목록 등을 포함한다.

프로세스 제어 블록은 프로세스를 시작시키거나 다시 시작하는 데 필요한 모든 데이터와 함께 약간의 회계 데이터를 위한 저장소 역할을 한다.

스레드 (Threads)

단일 제어 스레드는 프로세스가 한 번에 단지 한 가지 일만 실행하도록 허용한다.

대부분의 현대 운영체제는 한 프로세스가 다수의 실행 스레드를 가질 수 있도록 허용한다. 즉, 프로세스가 한 번에 하나 이상의 일을 수행할 수 있도록 허용한다. 이 특성은 특히 여러 스레드를 병렬로 실행할 수 있는 멀티코어 시스템에 유용하다. 다수의 스레드를 지원하는 시스템에서는 PCB가 각 스레드에 관한 정보를 포함하도록 확장된다. 스레드를 지원하기 위해서는 시스템 전반에 걸친 다른 수정도 필요한데, 이는 다음 글인 스레드와 동시성에서 상세하게 살펴보자.

프로세스 스케줄링 (Process Scheduling)

멀티 프로그래밍의 목적은 CPU 이용을 최대화하기 위하여 항상 프로세스가 실행되도록 하는 데 있다.

시분할(time sharing)의 목적은 각 프로그램이 실행되는 동안 사용자가 상호 작용할 수 있도록 프로세스들 사이에서 CPU 코어를 빈번하게 교체(switch)하는 것이다.

멀티 프로그래밍과 시분할의 목적을 달성하기 위해 프로세스 스케줄러(process scheduler)는 코어에서 실행 가능한 여러 프로세스 중에서 하나의 프로세스를 선택한다. 각 CPU 코어는 한 번에 하나의 프로세스를 실행할 수 있다. 멀티 코어 시스템은 한 번에 여러 프로세스를 실행할 수 있다. 코어보다 많은 프로세스가 있는 경우, 초과 프로세스는 코어가 사용 가능해지고 다시 스케줄 될 때까지 기다려야 한다. 현재 메모리에 있는 프로세스 수를 다중 프로그래밍 정도(degree of multiprogramming)라고 한다.

프로세스 스케줄러의 세 가지 유형

  1. Long Term Scheduler
  2. Medium Term Scheduler
  3. Short Term Scheduler
프로세스 스케줄러
출처: https://www.geeksforgeeks.org/process-schedulers-in-operating-system/

Long Term Scheduler

장기 스케줄러는 job 스케줄러라고도 한다. 이 스케줄러는 프로그램을 제어하고 큐에서 프로세스를 선택하여 실행을 위해 메모리에 로드한다. 또한 멀티프로그래밍의 정도(degree)를 제어한다.

Cf. 멀티프로그래밍의 정도란 현재 메모리에 있는 프로세스 수다.

Medium Term Scheduler

중기 스케줄링은 swapping에서 중요한 부분으로, 실행 중인 프로세스가 I/O 요청을 하면 일시 중단될 수 있다. 프로세스를 메모리에서 디스크로 “swap out”하여 다른 프로세스를 위한 공간을 확보한다.

핵심 아이디어는 때로는 메모리에서 (and from active contention for the CPU) 프로세스를 제거하여 멀티 프로그래밍의 정도(degree)를 감소시키는 것이 유리할 수 있다는 것이다. 프로세스를 메모리에서 디스크로 “swap out”하고 현재 상태를 저장하고, 이후 디스크에서 메모리로 “swap in”하여 상태를 복원한다. swapping은 일반적으로 메모리가 초과 사용되어 가용공간을 확보해야 할 때만 필요하다.

Short Term Scheduler

단기 스케줄러는 CPU 스케줄러라고도 한다. 이 스케줄러의 주요 목표는 설정된 기준(criteria)에 따라 시스템 기능을 향상시키는 것이다. 준비(ready) 큐에 있는 프로세스 중에서 하나의 프로세스를 선택해서 CPU를 할당하며, 디스패처는 단기 스케줄러가 선택한 프로세스에 CPU 제어권을 부여한다.

Cf. https://www.guru99.com/process-scheduling.html

스케줄링 큐 (Scheduling Queue)

프로세스가 시스템에 들어가면 준비 큐(ready queue)에 들어가서 준비(ready) 상태가 되어 CPU 코어에서 실행되기를 기다린다. 이 큐는 일반적으로 PCB의 연결 리스트로 저장된다. 준비 큐 헤더에는 리스트의 첫 번째 PCB에 대한 포인터가 저장되고 각 PCB에는 준비 큐의 다음 PCB를 가리키는 포인터 필드가 포함된다.

프로세스에 CPU 코어가 할당되면 프로세스는 잠시 동안 실행되어 결국 종료되거나 인터럽트 되거나 I/O 요청의 완료와 같은 특정 이벤트가 발생될 때까지 기다린다. 이러한 프로세스는 대기 큐(waiting queue)에 삽입된다.

**Figure 3.4** The ready queue and wait queues.
Figure 3.4 The ready queue and wait queues.

프로세스 스케줄링의 일반적인 표현은 큐잉 다이어그램(queueing diagram)이다. 준비(ready) 큐와 대기(waiting) 큐 집합의 두 가지 유형의 큐가 제시되어 있다. 원은 큐에 서비스를 제공하는 자원을 나타내고 화살표는 시스템 프로세스의 흐름을 나타낸다.

**Figure 3.5** Queueing-diagram representation of process scheduling.
Figure 3.5 Queueing-diagram representation of process scheduling.

새 프로세스는 처음에 준비(ready) 큐에 놓인다. 프로세스는 실행을 위해 선택되거나 dispatch될 때까지 기다린다. 프로세스에 CPU 코어가 할당되고 실행 상태가 되면, 여러 이벤트 중 하나가 발생할 수 있다.

  • 프로세스가 I/O 요청을 발행(issue)한 다음, I/O 대기(wait) 큐에 놓일 수 있다.
  • 프로세스는 새 자식 프로세스를 만든 다음 자식의 종료를 기다리는 동안 대기(wait) 큐에 놓일 수 있다.
  • 인터럽트 또는 time slice 만료로 프로세스가 코어에서 강제로 제거되어 준비(ready) 큐로 돌아갈 수 있다.

처음 두 경우에는 프로세스가 결국 대기(waiting) 상태에서 준비(ready) 상태로 전환된 다음 준비(ready) 큐로 다시 들어간다. 프로세스는 종료될 때까지 이 사이클을 계속한다. 종료(terminate) 시점에 모든 큐에서 제거되고 PCB 및 자원이 반환된다.

💡 문맥 교환 (context swtich)

문맥 교환(context swtich)은 CPU 코어를 다른 프로세스로 교환(switch)하는 작업이다.

**Figure 3.6** Diagram showing context switch from process to process.
Figure 3.6 Diagram showing context switch from process to process.
  • Context Switch는 interrupt 또는 system call에 의해 실행된다.
  • Context Switch가 일어나면, 커널은 과거 프로세스의 문맥을 PCB에 저장하고, 실행 예정인 새로운 프로세스의 저장된 문맥을 복구한다.
  • Context Switch가 일어날 동안 시스템이 유용한 일을 못하기 때문에 문맥 교환 시간은 순수 오버헤드다.

프로세스에 대한 연산 (Operations on Processes)

대부분의 시스템 내의 프로세스들은 동시에 실행될 수 있으며, 동적으로 생성 및 삭제될 수 있다. 그러므로 운영체제는 프로세스 생성종료를 위한 기법을 반드시 제공해야 한다.

프로세스 생성 (Process Creation)

프로세스는 실행되는 동안 여러 개의 새로운 프로세스들을 생성할 수 있다.

  • 부모 프로세스 — 생성하는 프로세스
  • 자식 프로세스 — 새로운 프로세스

생성된 새로운 프로세스들도 각각 새로운 프로세스들을 생성할 수 있으며, 그 결과 프로세스의 트리를 형성한다.

프로세스는 고유한 프로세스 식별자(process identifier or pid)를 사용하여 구분되고, 이 식별자는 보통 정수다. pid은 커널이 유지하고 있는 프로세스의 다양한 속성에 접근하기 위한 index로 사용된다.

**Figure 3.7** A tree of processes on a typical Linux system.
Figure 3.7 A tree of processes on a typical Linux system.

일반적으로 프로세스가 자식 프로세스를 생성할 때, 그 자식 프로세스는 태스크 완수를 위해 특정 자원(CPU 시간, 메모리, 파일, 입출력 장치)이 필요하다. 자식 프로세스는 이 자원을 운영체제로부터 직접 얻거나, 부모 프로세스가 가진 자원의 부분 집합만을 사용하도록 제한될 수 있다.

프로세스가 새로운 프로세스를 생성할 때, 두 가지 실행 가능성이 존재한다.

  1. 부모는 자식과 동시에 실행(execute concurrently)을 계속한다.
  2. 부모는 일부 또는 모든 자식이 실행을 종료할 때까지 기다린다(wait).

새로운 프로세스들의 주소 공간(address-space) 측면에서도 두 가지 가능성이 았다.

  1. 자식 프로세스는 부모 프로세스의 복사본(duplicate)이다(자식 프로세스는 부모와 똑같은 프로그램과 데이터를 가진다).
  2. 자식 프로세스가 자신에게 적재(load)될 새로운 프로그램(new program)을 가지고 있다.

💻 UNIX의 fork() 시스템 콜을 사용한 프로세스 생성

**Figure 3.9** Process creation using the fork() system call.
Figure 3.9 Process creation using the fork() system call.
#include <sys/types.h>
#include <stdio.h>
#include <unistd.h>

int main() {
	pid_t pid;
	
	/* fork a child process */
	pid = fork();
	
	if (pid < 0) { /* error occurred */ 
		fprintf(stderr, "Fork Failed");
		return 1;
	}
	else if (pid == 0) { /* child process */
		execlp("/bin/ls","ls",NULL); 
	}
	else { /* parent process */
		/* parent will wait for the child to complete */
		wait(NULL);
		printf("Child Complete");
	}
	
	return 0;
}

Cf. fork() 시스템 콜의 return 값

  • original process에서는 child process의 PID
  • child process에서는 0

프로세스 종료 (Process Termination)

프로세스가 마지막 문장의 실행을 끝내고, exit() 시스템 콜을 사용하여 운영체제에 자신의 삭제를 요청하면 종료된다. 이 시점에서, 프로세스는 자신을 기다리고 있는 부모 프로세스에(wait() 시스템 콜을 통해) 상태 값(통상 정수)을 반환할 수 있다.

물리 메모리와 가상 메모리, 열린 파일, 입출력 버퍼를 포함한 프로세스의 모든 자원이 할당 해제되고 운영체제로 반납된다. 그러나 프로세스의 종료 상태가 저장되는 프로세스 테이블의 해당 항목은 부모 프로세스가 wait()를 호출할 때까지 남아 있게 된다.

  • 좀비(zombie) 프로세스 — 종료되었지만 부모 프로세스가 아직 wait() 호출을 하지 않은 프로세스
    • 프로세스 종료 시 모든 프로세스는 좀비 상태가 되지만 아주 짧은 시간 동안만 머무른다.
    • 부모가 wait()를 호출하면 좀비 프로세스의 프로세스 식별자(pid)와 프로세스 테이블의 해당 항목이 운영체제에 반환된다.
  • 고아(orphan) 프로세스 — 부모 프로세스가 wait()를 호출하는 대신 종료하는 경우의 자식 프로세스
    • 주기적으로 고아 프로세스의 종료 상태를 수집하고 새로운 부모 프로세스를 지정하여 문제를 해결한다.

프로세스 간 통신 (Interprocess Communication)

운영체제에서 동시에 실행되는 프로세스들은 독립적(independent)이거나, 협력적(cooperating)인 프로세스일 수 있다.

  • independent process — 시스템에서 실행 중인 다른 프로세스들과 데이터를 공유하지 않는 프로세스
  • cooperating process — 시스템에서 실행 중인 다른 프로세스들에 영향을 주거나 받는 프로세스 프로세스 협력(cooperation)을 허용하는 환경을 제공하는 데는 몇 가지 이유가 있다.
    • 정보 공유 (information sharing)
    • 계산 가속화 (computation speedup)
    • 모듈성 (modularity)

협력적(cooperating) 프로세스들은 데이터를 교환할 수 있는, 즉 서로 데이터를 주고 받을 수 있는 프로세스 간 통신(interprocess communication, IPC) 기법이 필요하다.

프로세스 간 통신에는 기본적으로 두 가지 모델인 공유 메모리(shared memory)와 메시지 전달(message passing)이 있다.

**Figure 3.11** Communications models. (a) Shared memory. (b) Message passing.
Figure 3.11 Communications models. (a) Shared memory. (b) Message passing.

IPC in Shared-Memory Systems

  • 공유 메모리를 사용하는 프로세스 간 통신에서는 통신하는 프로세스들이 공유 메모리 영역을 구축해야 한다. 그런 후에 프로세스들은 공유 영역에 읽고 씀으로써 정보를 교환할 수 있다.
  • 공유 메모리 영역을 구축할 때만 시스템 콜이 필요하고, 그 이후의 모든 접근은 일반적인 메모리 접근으로 취급되어 커널의 도움이 필요 없으므로, 메시지 전달보다 더 빠르다.
  • 예시로는 POSIX Shared Memory가 있다.
    Cf. POSIX: Portable Operating System Inteface (for uniX)

IPC in Message-Passing Systems

  • 통신이 협력 프로세스들 사이에 교환되는 메시지를 통하여 이루어진다.
  • 충돌을 회피할 필요가 없기 때문에 적은 양의 데이터를 교환하는 데 유용하다.
  • 분산 시스템에서 공유 메모리보다 구현하기 쉽다.
  • 통상 시스템 콜을 사용하여 구현되므로 커널 간섭 등의 부가적인 시간 소비 작업이 필요하기 때문에 공유 메모리 모델보다 더 느리다.
  • 예시로는 Pipes, Socket, RPC, Message Queue, Signal 등이 있다.
    Cf. Pipes: UNIX 시스템의 가장 오래된 IPC 기법 중 하나

소켓 (Socket)

소켓(socket)은 클라이언트 서버 통신의 endpoint를 뜻하며, 두 프로세스가 네트워크상에서 통신을 하려면 양 프로세스마다 하나씩, 총 두 개의 소켓이 필요해진다.

각 소켓은 IP 주소(IP address)와 연결된 포트(port) 번호를 통해 구별한다.

**Figure 3.26** Communication using sockets.
Figure 3.26 Communication using sockets.

Java는 세 가지 종류의 소켓을 제공한다.

  • Socket class: connection-oriented (TCP)
  • DatagramSocket class: connectionless (UDP)
  • MulticastSocket class: 데이터를 여러 수신자에게 보낼 수 있다.

소켓은 스레드 간에 구조화되지 않은 바이트 스트림만을 통신하기 때문에, 원시적인 바이트 스트림 데이터를 구조화하여 해석하는 것은 클라이언트와 서버의 책임이 된다. 이에 대한 대안으로 더욱 높은 수준의 통신 기법인 원격 프로시저 호출(remote procedure call, RPC)이 있다.

원격 프로시저 호출 (Remote Procedure Calls, RPC)

RPC는 원격 서비스와 관련한 가장 보편적인 형태 중 하나다. 네트워크에 연결된 두 시스템 사이의 통신에 사용하기 위하여 프로시저(함수) 호출 기법을 추상화하는 방법으로 설계되었다. 프로세스들이 서로 다른 시스템 위에서 돌아가기 때문에 원격 서비스를 제공하기 위해서는 메시지 기반 통신을 해야 한다.

RPC는 클라이언트가 원격 호스트의 프로시저 호출을 마치 자기의(local) 프로시저 호출처럼 해준다.

  • RPC 시스템은 클라이언트 쪽에 stub을 제공하여 통신을 하는 데 필요한 자세한 사항들을 숨겨준다.
  • 클라이언트가 원격 프로시저를 호출하면 RPC는 그에 대응하는 stub을 호출하고 원격 프로시저가 필요로 하는 매개변수를 건네준다. 그러면 stub이 원격 서버의 포트를 찾고 매개변수를 정돈(marshall)한다.
  • 그 후 stub은 메시지 전달(message-passing) 기법을 사용하여 서버에게 메시지를 전송한다. 이에 대응되는 stub이 서버에도 존재하여 서브 측 stub이 메시지를 수신한 후 적절한 서버의 프로시저를 호출한다.

Cf. 매개변수 정돈(parameter marshalling)은 클라이언트와 서버 기기의 데이터 표현 방식의 차이 문제를 해결한다. 어떤 기계는 big-endian 방식을, 어떤 기계는 little-endian 방식을 사용하더라도 XDR (external data representation)과 같은 중립적인 데이터 표현 방식으로 데이터를 바꾸어서 보낸다.

RPC는 분산 파일 시스템(distributed file system, DFS)을 구현하는 데 유용하다.

References

profile
원리를 파헤치는 것을 좋아하는 프론트엔드 개발자입니다 🏃🏻‍♀️

0개의 댓글

관련 채용 정보