Process

EBAB!·2023년 7월 9일
0

OS

목록 보기
8/16
post-thumbnail

Process

Process프로그램을 실행하는 작업 단위입니다. 하나의 프로그램을 여러 번 실행하면, 여러 개의 process가 실행됩니다.

개념

프로세스 context(문맥) : CPU 수행 상태를 나타내는 하드웨어 문맥

  • Program Counter
  • 각종 register

프로세스 주소 공간 : code,data,stack

프로세스 관련 커널 자료 구조 : PCB(Process Control Block), Kernel stack

Process : program code, execution state, process state, process ID 등의 정보 + 자신의 virtual memory

  • virtual memory : stack area, heap area, data segment, text segment로 구성
  • VM 구조

Process State

Process는 반드시 다음의 다섯 가지 중 하나의 상태를 가지게 됩니다.

  • New: fork() 함수 등에 의해 Process가 만들어졌으나 아직 메모리가 할당되지 않은 상태.
  • Ready: CPU가 할당되기를 기다리고 있는 상태. Ready Queue에서 순서를 기다리고, CPU만 할당되면 바로 실행이 가능.
  • Running: CPU를 잡고 instruction을 수행중인 상태. 하나의 processor (core)에서는 하나의 process만 실행이 가능.
  • Waiting: sleep()함수 등의 이유로 특정 event가 발생할 때까지 대기하고 있는 상태. Wait Queue에서 대기하고 있으며, event가 발생하고 나면 ready 상태로 돌아가게 됨.
  • Suspended : 외부적인 ****이유로(스케줄러에 의해 CPU를 빼앗김) 프로세스 수행이 정지된 상태.
    • 프로세는 통째로 디스크에 swap out 됩니다.
  • Terminated: 실행이 완료된 상태.

종합 상태도

Process Control Block (PCB)

PCB는 각 process마다 따로따로 존재하며, 해당 process에 대한 정보를 담고 있는 자료 구조입니다. PCB들은 Kernel 내부에 보관되며, 다음과 같은 정보들을 포함하고 있습니다.

  • Process State
  • Program Counter: Process가 다음에 실행해야 하는 instruction의 위치를 가리키는 주소 값을 저장하고 있습니다.
  • CPU registers
  • CPU scheduling info: Process들의 실행 우선 순위 등에 대한 내용인데 뒤에서 다 자세히 다룰 예정입니다.
  • Memory management info: Page table, segment table 등에 관한 정보를 저장하고 있습니다.
  • Accounting info: CPU 사용 시간이나 Process의 number (PID)에 관한 정보를 가지고 있습니다.
  • I/O status info: 현재 열려있는 파일들이나 process에 할당된 I/O 장치들의 리스트를 저장하고 있습니다.

이러한 PCB에 저장되어있는 process들의 상태를 Context라고 부릅니다. CPU에서는 process를 실행할 때 register set에 이 PCB들을 불러와서 사용하게 됩니다.

CPU에서 실행하는 process를 바꿀 때 (기존에 실행되던 process가 Running 상태에서 Ready 상태나 Terminated 상태로 옮겨가고 Ready 상태에 있던 새로운 process가 Running 상태로 들어갈 때) 원래의 process의 context를 저장하고 새로운 process의 context를 불러오게 됩니다.

이를 Context Switch라고 부릅니다.

Context Switch

System call이나 Interrupt가 발생할 때 context switch가 일어날 수 있습니다. 하지만 반드시 context switch가 일어나는건 아닙니다. 단순 system call만 호출해서 mode bit만 바뀌는 상황도 있고, timer interrupt나 I/O 요청같이 프로세스 자체가 바뀌는 요청에서 context switch가 일어납니다.

Context Switch가 자주 발생할수록 프로그램이 느려질 수 있습니다.

실제로 CPU가 add 같은 단순 연산을 실행하는 데 1 정도의 힘이 든다고 가정한다면, Context Switch에는 1만에서 10만 정도의 힘이 들게 됩니다.

그래서 최대한 Context Switch를 줄이는 것이 프로그램의 성능에 큰 역할을 하게 됩니다.

Process Creation

모든 process는 자신만의 정수 값으로 표현되는 number를 가집니다. 이를 Process Identifier, 줄여서 PID라고 부릅니다

  • 참고로 최초에 존재하는 process는 swapper라고 불리며 pid는 0입니다.
    • 하지만 swapper는 사용자 모드가 아닌 kernel의 일부입니다.
    • 페이징을 담당하기 때문에 보통 root parent process로 취급하지 않고 대신 시스템의 시작과 종료에 사용되는 process, 즉 init process를 root parent process라고 부르며 이 init process의 pid는 1입니다. (현재는 Systemd라고 한다고 합니다.)

process는 자기 자신을 복제하여 새로운 process를 만들 수 있습니다. 이 과정에 나타나는 프로세스를 부모 process, 자식 process라고 부릅니다.

이렇게 자기 복제를 통해 process들을 만들고 나면 아래와 같이 tree 모양의 자료 구조가 탄생하게 됩니다.

Process를 생성하는 데 있어서 가장 중요하게 사용되는 함수목록

  • fork() 새로운 process를 생성하고, 자신의 코드와 데이터들을 복제하여 넘겨줍니다. 물론 자식 process는 자신의 pid를 할당받게 됩니다. 이렇게 생성된 부모 process와 자식 process는 동시에 (concurrently) 실행되게 됩니다.
  • exec() process의 메모리를 새로운 프로그램으로 덮는 역할을 합니다. 새로운 프로그램을 불러와서 실행시키는 것입니다. 따라서 원래 부모 process로부터 복제되었던 프로그램 코드는 새로운 코드로 덮어져 사라지게 됩니다.
  • wait() 부모 process가 실행을 마친 뒤 자식 process가 종료될 때까지 기다릴 때 사용합니다. 이때  부모 process는 wait 상태가 되어 wait queue에서 대기하게 됩니다.

Process Termination

Process는 종료를 위해 exit() system call을 호출합니다. exit() system call이 호출되게 되면 process가 가지고 있던 (메모리와 같은) resource들을 풀어주게 되고 부모 process에게 자신의 상태를 전달합니다.

부모 process 자식 process를 종료시킬 수 있는 특수 상황

  1. 자식 process가 과도하게 많은 resource를 사용할 때.

  2. 자식 process의 작업이 더 이상 필요 없을 때.

  3. 부모 process가 작업을 종료했고, 해당 운영체재가 orphan process를 허용하지 않을 때.

Orphan Process

  • 부모 process가 이미 죽었는데 자식 process가 살아있는 경우. 부모가 사라졌기 때문에 고아 process라고 부르는 것인데 이 경우 Systemd (init process)라는 process가 대신 부모의 역할을 맡게 되어 wait() system call을 호출하게 됩니다.

Zombie Process

  • Orphan Process와 반대로 자식 process는 실행이 종료되었는데 아직 부모 process가 wait() system call을 호출하지 않은 경우를 말합니다.
  • 자식 process가 exit() system call을 실행하면 모든 resource들을 해제시키지만 부모 process가 자식 process의 상태를 알고 싶어 할 상황을 대비하여 최소한의 정보는 kernel에 저장하고 있게 됩니다.
  • 물론 부모 process가 wait() system call을 호출하면, zombie process는 제거됩니다.

IPC(InterProcess Communication)

개요

Process들은 운영체제 내에서 병렬적으로 수행되기 때문에 서로 독립적이면서도 상호 협력적인 관계에 있습니다. 이러한 Process들 간의 협력을 통해 우리는 몇 가지 이득을 취할 수 있습니다.

  1. 정보 공유 (Information Sharing): 여러 응용 프로그램들이 동일한 정보에 흥미를 가질 수 있기 때문에 병행적으로 접근이 가능한 것이 좋다.

  2. 계산 가속화 (Computation Speedup): 작업을 빨리 끝마치기 위해서는 작업을 서브 작업들로 분할하여 여러 process들에서 병렬적으로 수행하는 것이 좋다.

  3. 모듈성 (Modularity): 시스템의 기능들을 프로세스들로 나누어 모듈화하여 시스템을 구성할 수 있다.

하지만 어쨋든 IPC에 대해서 한 문장으로 말하자면 IPC는 'Process들 간에 데이터와 정보를 교환할 수 있게 해 준다.'라고 할 수 있습니다.

IPC는 대표적으로 두 가지 방법을 통해 실행됩니다.

1. Shared Memory

Shared Memory는 쉽게 말해서 메모리에 서로 공유하고 있는 영역이 있어서 그 부분에 자신이 원하는 데이터를 작성하는 방법입니다.

물론 그 공유하는 영역을 만드는 작업은 운영체제의 도움을 받아야 가능합니다. Process들은 단순히 메모리에 데이터를 읽고 쓸 수 있으며, 어떤 업데이트가 일어나더라도 곧바로 모든 process들이 그 내용을 확인할 수 있습니다.

그리고 가장 중요한 장점은 일단 공유 메모리 영역을 만들어 놓기만 하면, 그 뒤에는 Kernel의 어떤 도움도 필요로 하지 않는다는 것입니다.

  • Kernel을 자주 이용한다는 것은 System call이 자주 호출된다는 것을 뜻하고, 이는 성능적으로 큰 overhead를 야기합니다. 따라서 shared memory는 성능적으로는 다른 한 가지 방법인 Message Passing에 비해 굉장히 큰 장점이 있습니다.

Shared Memory의 기본 개념

Shared memory 방식을 사용하기 위해 우선 process들은 자신의 주소 공간에 공유 공간으로 사용할 부분을 준비합니다. 그리고 이 shared memory를 이용해 통신하고자 하는 다른 process들은 이 세그먼트를 자신의 주소 공간에 추가하여야 합니다. 일반적으로 운영체제는 한 process가 다른 process의 memory에 접근하는 것을 금지하기 때문에 Kernel의 도움을 받아서 이 제약 조건을 제거하는 과정이 필요합니다. 이런 과정을 거친 뒤에 process들은 공유 영역에 메세지를 읽고 쓸 수 있습니다.

2. Message Passing

Message Passing 방법은 데이터를 메세지 처럼 전달하는 방법입니다. Message passing은 메세지를 보내는 send와 메세지를 받는 receive 두 가지 연산으로 실행됩니다. 그리고 어떤 방식으로 메세지를 주고받는지에 따라 또 두 가지 방법으로 분류할 수 있습니다.

Message Passing의 기본 개념

1. Direct Communication

Direct Commnuication은 말 그대로 point-to-point로 직접 전달하는 방식입니다. send 연산은 send(수신자 PID, message)로, receive 연산은 receive(송신자 PID, message)의 형태로 구성됩니다. 통신을 위해 상대의 ID만 알고 있다면 두 process 간에 자동으로 연결이 구축됩니다. 연산 자체에 수신자 혹은 송신자의 ID를 적어줘야 하기 때문에 혹시 ID가 수정되게 되면 아예 코드 전체를 수정해줘야 하는 단점이 있습니다. 또한 1대 1 통신만 가능하고 Broadcast 기능은 사용할 수 없다는 것도 한 단점입니다.

2. Indirect Communication

Indirect Communication은 직접 메세지를 전달하지 않고 mailbox를 이용하는 형태입니다. 다시 말해서, 수신자는 mailbox에 원하는 메세지를 담고, 송신자는 mailbox에서 원하는 메세지를 가져가는 형식입니다. 어떤 process라도 mailbox에 정보를 넣을 수 있고 빼 갈 수 있습니다. 연산은 send(mailbox 이름, message), receive(mailbox 이름, message)의 형태를 가집니다.

Indirect Communication 구조

Message Passing 방법에서는 Blocking과 Nonblocking 방식을 통해 메세지를 주고받습니다.

  1. Blocking Send: 송신하는 process는 메세지가 수신자에 의해 수신될 때까지 아무것도 하지 못하고 blocking 된다.

  2. Nonblocking Send: 송신하는 process가 메세지를 보내고 그 수신의 여부와 관계없이 다음 작업을 수행한다.

  3. Blocking Receive: 수신하는 process는 메세지가 이용 가능할 때까지 아무것도 하지 못하고 blocking 된다.

  4. Nonblocking Receive: 수신하는 process는 유효한 메세지인지 Null인지 계속 검색한다.

3. Pipes

두 process 간의 교류가 꼭 쌍방향일 필요는 없을 때가 있고 이 때 pipe를 유용하게 사용할 수 있다.

상황에 따라서는 생산자와 소비자의 관계로 한 process는 보내기만 하고 한 process는 받기만 하는 경우가 생길 수도 있습니다.

pipe() system call을 통해 pipe를 생성하면

  • pipe의 한쪽 끝은 (pipe [0]) 읽기를 담당하고
  • 나머지 한쪽 끝은 (pipe [1]) 쓰기를 담당합니다.

그리고 pipe는 오로지 자신을 생성한 process로부터만 접근이 가능합니다.

하지만 부모 process가 자식 process를 생성할 때 pipe에 관한 정보도 상속하기 때문에 부모 process가 생성한 pipe로 자식 process와 소통하는 것은 가능합니다. (pipe()를 먼저 호출한 뒤 fork()를 호출하면 부모와 자식 간에 소통이 가능합니다.)

특별한 케이스로 Named pipe라는 것이 존재합니다(FIFO).

pipe임에도 불구하고 양방향 통신이 가능합니다. 또한 꼭 부모-자식 관계일 필요도 없으며 일단 생성되면 같은 하드웨어 안에 있는 여러 process가 사용할 수도 있습니다.

게다가 생성한 process가 죽는다고 해서 이 pipe가 사라지지도 않습니다. mkfifo() system call을 이용해서 named pipe를 만들 수 있으며, open(), read(), write(), close()와 같은 system call을 이용해 제어할 수 있습니다.

4. Client-Server System

두 process들이 네트워크를 이용해 통신을 하는 방법으로 소켓을 사용할 수 있습니다.

소켓들은 IP 주소와 Port Number를 통해 서로를 구별하며 일반적으로 서버-클라이언트의 구조를 가집니다.

5. Remote Procedure Calls (RPC)

한국말로는 '원격 프로시져 호출'이라고 불리는데 함수 호출을 네트워크를 이용해 할 수 있는 방법을 뜻합니다.

즉, RPC 서버에 함수가 실제로 구현이 되어있고 클라이언트의 코드에서 함수를 호출하면 서버에 있는 함수가 실행되는 형태를 가집니다.

서버와 클라이언트 간에는 Stub라는 것을 이용해 서로 네트워크를 연결합니다.

profile
공부!

0개의 댓글