10.1 프로세스 개요

  • 실행 중인 프로그램, 이 프로그램을 프로세스 라고 한다

  • 프로그램은 실행되기 전까지 보조기억장치에 있는 데이터 덩어리지만

  • 메모리에 적재하고 실행하는 순간 프로그램은 프로세스가 된다

  • 이 과정을 ‘프로세스를 생성한다’ 라고 한다

  • (보조기억장치에서) 메모리에 적재되고, CPU의 자원을 받은 실행 중인 프로그램

10.1.1 프로세스 직접 확인하기

  • 포그라운드 프로세스: 사용자가 볼 수 있는 공간에서 실행되는 프로세스
  • 백그라운드 프로세스: 사용자가 볼 수 없는 공간에서 실행되는 프로세스
    • 데몬(daemon) or 서비스(service): 사용자와 상호작용하지 않고 그저 묵묵히 정해진 일만 수행하는 백그라운드 프로세스

image-20240430151249970.png

10.1.2 프로세스 제어 블록 (PCB)

  • CPU는 자원은 한정 되 어 있기에, 모든 프로세스가 동시에 CPU를 사용할 수 없다.

  • 프로세스들은 차례대로 돌아가며 한정된 시간 만큼 CPU를 이용하고,

  • 시간이 끝났음 알리는 인터럽트(타이머 인터럽트)가 발생하면 자신의 차례를 양보하고 기다린다.

    • 타이머 인터럽트는 클럭 신호를 발생시키는 장치에 의해 주기적으로 발생하는 하드웨어 인터럽트
    • 타임아웃 인터럽트라고도 부른다.
  • 프로세스에 CPU를 비롯한 자원을 배분하기 위해 운영체제는 Process Control Block(PCB) 를 이용한다.

  • PCB는 프로세스와 관련된 정보를 저장하는 자료 구조이다.

    • 상품에 달린 태그와 비슷
    • 운영체제 종류마다 담기는 정보에 차이가 있다
  • PCB는 커널 영역에 생성된다.

  • ‘새로운 프로세스가 생성되었다’는 말은 ‘운영체제가 PCB를 생성했다’는 말과 같고,

  • 프로세스가 종료되었다’는 말은 ‘운영체제가 해당 PCB를 폐기했다’는 말과 같다.

10.1.2.1 PCB에 담기는 대표적인 정보

image-20240430151902228.png

  1. 프로세스 ID
    • Process ID(PID): 프로세스를 식별하기 위해 부여하는 고유한 번호
    • 같은 일을 수행하는 프로그램이라 할지라도 두 번 실행하면 PID가 다른 두 개의 프로세스가 생성
  2. 레지스터 값
    • 프로세스는 자신의 실행 차례가 돌아오면 이전까지 사용했던 레지스터의 중간값들을 모두 복원
    • PCB 안에는 해당 프로세스가 실행하며 사용했던 프로그램 카운터(PC) 를 비롯한 레지스터 값들이 담깁니다.
  3. 프로세스 상태
    • 프로세스가 현재 어떤 상태인지 기록
  4. CPU 스케쥴링 정보
    • 프로세스가 언제, 어떤 순서로 CPU를 할당받을지에 대한 정보도 PCB에 기록
  5. 메모리 관리 정보
    • 프로세스마다 메모리에 저장된 위치가 다름
    • 그래서 PCB에는 프로세스가 어느 주소에 저장되어 있는지에 대한 정보가 있어야 함
    • PCB에는 베이스 레지스터, 한계 레지스터 값과 같은 정보들이 담김
    • 또한 프로세스의 주소를 알기 위한 중요 정보 중 하나인 페이지 테이블 정보도 PCB에 담김
  6. 사용할 파일
    • 프로세스가 실행 과정에서 특정 입출력 장치나 파일을 사용하면 PCB에 해당 내용이 명시된다.
    • 즉, 어떤 파일들을 열었는지에 대한 정보들이 PCB에 기록
  7. 사용한 입출력 장치 목록
    • 어떤 입출력장치가 이 프로세스에 할당되었는지 기록

10.1.3 문맥 교환 (Context Switching)

image-20240430152851715.png

  • 하나의 프로세스 수행을 재개하기 위해 기억해야 할 정보문맥(context) 이라 한다.
    • 하나의 프로세스 문맥은 해당 프로세스의 PCB에 표현되어 있다.
    • PCB에 기록되는 정보들을 문맥이라고 봐도 무방
  • 실행 문맥을 기억해 두면 언제든 해당 프로세스의 실행을 재개 가능하기 때문에, 인터럽트가 발생하면 운영체제는 해당 프로세스의 PCB에 문맥을 백업합니다.
  • 그리고 뒤이어 실행할 프로세스 B의 문맥을 PCB로 부터 복구 하여 새로운 프로새스를 실행, 프로세스 전환
  • 이를 문맥 교환(conext swiching) 이라 한다.

image-20240430152907279.png

  • 문맥 교환은 여러 프로세스가 끊임없이 빠르게 번갈아 가며 실행되는 원리이다.
  • 문맥 교환이 자주 일어나면 프로세스는 그만큼 빨리 번갈아 가며 수행되기 때문에 사용자는 프로세스들이 동시에 실행되는 것처럼 인식한다
  • 하지만, 문맥 교환이 많이 일어나면 오버헤드가 발생할 수 있기에 반드시 좋은 것만은 아니다.

10.1.4 프로세스의 메모리 영역

image-20240430153403680.png

  • Code Segment, 코드 영역

    • Text Segment, 텍스트 영역이라고도 부른다.

    • 말 그대로 실핼할 수 있는 코드, 즉 기계어로 이루어진 명령어가 저장

    • 읽기 전용 read-only 공간, 쓰기 불가

    • 정적 할당 영역 - 프로그램을 구성하는 명령어는 중간에 변경되지 않는다.

  • Data Segment, 데이터 영역

    • 프로그램이 실행되는 동안 유지할 데이터(전역 변수가 대표적)가 저장되는 공간
    • 정적 할당 영역 - 프로그램이 샐행되는 동안에만 유지된다.
  • Heap Segment, 힙 영역

    • 프로그래머가 직접 할당할 수 있는 저장 공간
    • 프로그래밍 과정에서 힙 영역에 메모리 공간을 할당했다면 언젠가는 해당 공간을 반환해야 한다.
    • 반환한다는 의미는 ‘더 이상 해당 메모리 공간을 사용하지 않겠다’라고 운영체제에 말해주는 것
    • 메모리 공간을 반환하지 않는다면 할당한 공간은 메모리 내에 계속 남아 메모리 낭비를 초래
    • 이런 문제를 메모리 누수 (memory leak) 라 한다.
    • 동적 할당 영역 - 크기가 실시간으로 변할 수 있음
    • 힙 영역은 메모리의 낮은 주소에서 높은 주소로 할당 (0 => 100)
  • Stack Segment, 스택 영역

    • 스택 영역은 데이터를 일시적으로 저장하는 공간
    • 데이터 영역에 담기는 값(전역 변수)과는 달리 잠깐 쓰다가 말 값들이 저장되는 공간
    • 이런 데이터로는 함수의 실행이 끝나면 사라지는 매개 변수, 지역 변수가 대표적입니다.
    • 동적 할당 영역
    • 저장할 데이터는 스택 영역에 PUSH, 필요하지 않는 데이터는 POP 됨으로써 스택 영역에서 사라짐
    • 스택 영역은 높은 주소에서 낮은 주소로 할당 (100 => 0)
      • 힙 영역과 스택 영역에 데이터가 쌓여도 새롭게 할당되는 주소가 겹칠 일이 없게됨.

10.2. 프로세스 상태와 계층 구조

10.2.1 프로세스 상태

image-20240430155950068.png

  • 프로세스는 생성, 준비, 실행, 대기, 종료 상태를 거치며 실행된다.

  • 운영체제는 프로세스의 상태를 PCB에 기록, 인식하며 프로세스들을 관리

  • 생성 상태, New

    • 프로세스를 생성 중인 상태

    • 이제 막 메모리에 적재되어 PCB를 할당 받은 상태

    • 생성 상태를 거쳐 실행할 준비가 완료된 프로세스는 곧바로 실행되지 않고 준비 상태가 되어 CPU의 할당을 기다림

  • 준비 상태, Ready

    • 준비 상태(ready)는 당장이라도 CPU를 할당받아 실행할 수 있지만, 아직 자신의 차례가 아니기에 기다리고 있는 상태

    • 준비 상태인 프로세스가 실행 상태로 전환되는 것을 디스패치(dispatch) 라고 한다.

  • 실행 상태, Running

    • 실행 상태(running)는 CPU를 할당받아 실행 중인 상태를 의미
    • 실행 상태인 프로세스는 할당된 일정 시간 동안만 CPU를 사용할 수 있습니다. 이때 프로세스가 할당된 시간을 모두 사용한다면 (타이머 인터럽트가 발생) 다시 준비 상태가 된다.
    • 실행 도중 입출력장치를 사용하여 I/O가 끝날 때까지 기다려야 한다면 대기 상태(blocked, waiting) 가 된다.
  • 대기 상태, Blocked, Waiting

    • I/O는 CPU에 비해 처리 속도가 느리기에, I/O를 요청한 프로세스는 입출력(I/O) 완료 인터럽트를 받을 때까지 기다려야 한다.
    • 이렇게 입출력장치의 작업(I/O)을 기다리는 상태를 대기 상태(blocked, waiting) 라고 한다.
    • 입출력 작업이 완료되면 해당 프로세스는 다시 준비 상태(ready) 로 CPU 할당을 기다립니다.
  • 종료 상태, Terminated

    • 종료 상태 (terminated)는 프로세스가 종료된 상태
    • 프로세스가 종료되면 운영체제는 PCB와 프로세스가 사용한 메모리를 정리

[대기 상태의 일반적인 정의]

  • 프로세스가 대기 상태가 되는 이유에 입출력 작업만 있는 것은 아니다
  • 조금 더 일반적으로 표현하자면 특정 이벤트가 일어나길 기다릴 때 프로세스는 대기 상태가 된다.
  • 다만, 대부분의 원인이 입출력 작업이기 때문에 ‘프로세스가 입출력 작업을 하면 대기 상태가 된다’고 생각해도 무방

10.2.2 프로세스 계층 구조

  • 프로세스는 실행 도중 시스템 호출을 통해 다른 프로세스를 생성할 수 있ek.
  • 부모(Parent) 프로세스: 새 프로세스를 생성한 프로세스
  • 자식(Chlid) 프로세스: 부모 프로세스에 의해 생성된 프로세스
  • 부모 프로세스와 자식 프로세스는 엄연히 다른 프로세스이기에 각기 다른 PID를 가진다.
  • 일부 운영체제에서는 자식 프로세스의 PCB에 부모 프로세스의 PID인 PPID (Parent PID) 가 기록된다.
  • 프로세스가 프로세스를 낳는 계층적인 구조로써 프로세스들을 관리하고 이는 트리 구조를 띄는다. 이를 프로세스 계층 구조이다.

image-20240430160332425.png

  • 최초의 프로세스는 운영체제 별로 유닉스 - init, 리눅스 - systemd, macOS - launchd 이다.
  • 최초의 프로세스(부팅 될 때 처음 실행되는 프로세스) PID는 항상 1번이며, 모든 프로세스 최상단에 있는 부모 프로세스이다.

10.2.3 프로세스 생성 기법

image-20240430161031280.png

  • 부모 프로세스는 fork를 통해 자신의 복사본을 자식 프로세스로 생성해내고,

  • 만들어진 복사본(자식 프로세스)은 exec를 통해 자신의 메모리 공간을 다른 프로그램으로 교체

  • fork: 자기 자신 프로세스의 복사본을 만드는 시스템 콜

    • 자식 프로세스는 부모 프로세스의 자원들 (메모리 내의 내용, 열린 파일의 목록 등)이 자식 프로세스에 상속된다.
    • 복사된 자식 프로세스라 할지라도 PID 값이나 저장된 메모리 위치는 다름
  • exec: 자신의 메모리 공간을 새로운 프로그램으로 덮어쓰는 시스템 콜

    • fork를 통해 복사본이 만들어진 뒤에 자식 프로세스는 exec을 통해 새로운 프로그램으로 전환
    • 새로운 프로그램 내용으로 전환하여 실행하는 시스템 콜입니다.
  • 메모리 공간에 새로운 프로그램 내용이 덮어 써진다는 점에서 자식 프로세스가 새로운 옷으로 갈아입었다고도 볼 수 있다.

  • exec를 호출하면 코드 영역과 데이터 영역의 내용이 실행할 프로그램의 내용으로 바뀌고, 나머지 영역은 초기화됩니다.

  • 예를 들어, 사용자가 bash 셸에서 Is라는 명령어를 쳤다고 가정

    1. 셸 프로세스는 fork를 통해 자신과 동일한 프로세스를 생성
    2. 그로부터 탄생한 자식 프로세스(셸의 복제)는 exec를 통해 Is 명령어를 실행하기 위한 프로세스로 전환되어 실행
    3. 그렇게 셸의 복사본으로 탄생한 자식 프로세스는 Is 명령어를 실행하기 위한 프로세스로 바뀌고,
    4. 메모리 공간에는 Is 명령어를 실행하기 위한 내용들이 채워진다.

image-20240430161736801.png

  • 정리하면, 부모가 자식 프로세스를 실행하며 프로세스 계층 구조를 이루는 과정은 fork과 exec가 반복되는 과정

    • 쉽게 말해, 부모 프로세스로부터 자식이 복사되고,
    • 자식은 새로운 프로그램으로 옷을 갈아입고, 또 그 자식 프로세스로부터 자식이 복사되고,
    • 옷을 갈아입는 방식으로 여러 프로세스가 계층적으로 실행된다.
  • 부모 프로세스가 자식 프로세스를 fork한 뒤에 부모, 자식 누구도 exec를 호출하지 않는 경우도 있다.

    • 이 경우 부모 프로세스와 자식 프로세스는 같은 코드를 병행하여 실행하는 프로세스가 된다.

10.3 스레드, Thread

  • (소프트웨어적) 스레드(Thread)란 프로세스를 구성하는 독자적인 실행의 흐름 단위
  • 하나의 프로세스는 여러 개의 스레드를 가질 수 있다.
  • 스레드를 이용하면 하나의 프로세스에서 여러 부분을 동시에 실행할 수 있음

10.3.1 프로세스와 스레드

  • ‘실행의 흐름 단위가 하나’인 프로세스들은 단일 스레드 프로세스 이다.

image-20240430162224070.png

  • 스레드라는 개념이 도입되면서, 하나의 프로세스가 한 번에 여러 일을 동시에 처리할 수 있게 됨.
  • 즉, 프로세스를 구성하는 여러 명령어를 동시에 실행할 수 있게 됨

image-20240430162341207.png

  • 스레드는 프로세스 내에서 각기 다른 스레드 ID, 프로그램 카운터 값을 비롯한 레지스터 값, 스택 으로 구성

    • 독자적인 스택이 있어 스레드마다 각기 다른 코드 또는 함수를 실행 가능
  • 스레드들은 실행에 필요한 최소한의 정보만을 유지한 채 프로세스 자원(힙, 데이터, 코드 영역)을 공유

  • 최근 많은 운영체제는 CPU에 처리할 작업을 전달할 때 프로세스가 아닌 스레드 단위로 전달합니다.

image-20240430162431340.png

  • 많은 운영체제가 프로세스와 스레드를 구분하지만, 리눅스프로세스와 스레드 간에 명확한 구분을 짓지 않음
  • 리눅스는 프로세스와 스레드 모두 실행의 문맥(context of execution) 이라는 점에서 동등하다고 간주
  • 프로세스와 스레드라는 말 대신 태스크(task) 라는 이름으로 통일하여 명명

10.3.2 멀티프로세스와 멀티스레드

  • 멀티프로세스 (multiprocess): 여러 프로세스를 동시에 실행하는 것
  • 멀티스레드 (multithread): 여러 스레드로 프로세스를 동시에 실행하는 것

image-20240430163224621.png

  • 동일한 작업을 수행하는 단일 스레드 프로세스 여러 개를 실행하는 것(멀티프로세스)하나의 프로세스를 여러 스레드로 실행하는 것(멀티스레드) 은 무엇이 다를까?

    • 프로세스끼리는 기본적으로 자원을 공유하지 않지만, 스레드끼리는같은 프로세스 내의 자원을 공유
    • 프로세스를 fork하여 같은 작업을 하는 동일한 프로세스 두 개를 동시에 실행하면 모든 자원이 복제되어 메모리에 적재
    • 한 마디로 PID, 저장된 메모리 주소를 제외하면 모든 것이 동일한 프로세스 두 개가 통째로 메모리에 적재 => 낭비, 중복 존재

    image-20240430163518174.png

    • 스레드는 프로세스가 가진 자원을 공유
      • 메모리를 더 효율적으로 사용 가능
      • 협력과 통신에 유리

image-20240430163611915.png

  • 멀티프로세스 환경에서는 하나의 프로세스에 문제가 생겨도 다른 프로세스에는 지장이 적거나 없다.
  • 하지만, 멀티스레드 환경에서는 하나의 스레드에 문제가 생기면 프로세스 전체에 문제가 생길 수 있다.
    • 모든 스레드는 프로세스의 자원을 공유, 하나의 스레드에 문제가 생기면 다른 스레드도 영향을 받기 때문

image-20240430164000426.png

10.4 IPC, 프로세스 간 통신

  • 프로세스끼리는 ‘기본적으로’ 자원을 공유하지 않지만, 프로세스끼리도 충분히 자원을 공유하고 데이터를 주고 받을 수 있다.
  • 프로세스 간의 자원을 공유하고 데이터를 주고받는 것을 IPC; Inter-Process Communication 라 한다.

image-20240430164406295.png

  1. 공유 메모리 (Shared Memory)

    • 커널 의존성이 낮아 속도가 빠르며, 유저 레벨에서 IPC가 가능해서 통신이 자유로움

    • 자원과 데이터를 공유하기 때문에 동기화 이슈가 발생

    • 문맥전환 관점 : 공유 메모리 모델에서의 IPC 는 해당 프로세스가 CPU를 사용하는 행위이며, IPC를 많이 한다고 문맥전환이 많이 일어나지 않음

    • 동기화 관점 : 메모리 영역에 대한 동시적인 접근을 제어하기 위한 방법이 필요, locking이나 세마포어 도입

  2. 메시지 교환(message passing)

    • 커널(OS) 내부에 buffer를 두고, 요청된 메시지를 이 buffer 에 복사해 뒀다가 memory protection을 통해 대리 전달해주는 기능.

    • 시스템 콜을 사용으로 인한 오버헤드가 크고, 비교적 느리다는 단점, 구현은 쉽다는 장점

    • 문맥 전환 관점 : 메시지 전달 모델에서의 IPC는 해당 프로세스 입장에서 일종의 I/O 로 볼 수 있으며 문맥전환이 많이 일어난다.

    • 동기화 관점 : send와 receive에 대해서 커널이 동기화를 제공

    • ex) 파이프, 시그널, 메시지 큐, 소켓

  3. 파이프(Pipe)

    • 1:1 통신이며, 보내진 순서대로만 받음

    • 프로세스에 속하는 자원이 아니라 fork를 통한 복사가 불가능

    • 부모 프로세스와 자식 프로세스 간에 통신함

  4. 시그널(Signal)

    • pid를 아는 특정 프로세스에게 커널을 통해 이벤트를 전달

    • 인터럽트라고 부르기도 함

    • 전달할 수 있는 메시지의 양이 적으며, 실제 데이터를 넘기지 못하기 때문에 실제 데이터를 보내려면 다른 전달 방법을 사용해야함.

    • 비동기적인 동작으로 스케쥴링이 이루어져야 신호에 대한 처리가 가능하므로 커널 서비스 루틴이 길어질 수 있음

  5. 메시지 큐

    • 고정된 크기를 갖는 메시지의 연결 리스트를 이용하여 통신

    • 동기화 필요

    • n 대 n 통신 가능

    • 다른 공유 방식에 비해 사용방법이 매우 직관적이고 간단함

    • 메시지 큐를 사용하기 위해서는 ID를 알아야하는데 일반적으로 메시지 큐 ID를 알기는 어려움.

  6. 소켓

    • 네트워크 통신 기법으로 많이 사용되며, 양쪽 PC에서 각각 임의의 포트를 정하고 해당 포트간의 대화를 통해 데이터를 주고 받음.
    • n 대 n 통신이 가능함
    • 운영체제가 제공하는 포트라는 추상화를 여러 프로세스를 사용 할 수 있도록 함
    • 소켓은 상대방의 프로세스의 포트번호만 알면 되어 사용하기가 쉽다.
    • 프로세스의 위치에 독립적이며 local 또는 remote 가능 ( 다른 IPC는 local만 가능 )
    • local 인 경우 포트 번호만으로 상대 프로세스를 식별 가능
    • remote인 경우 IP 주소 + 포트번호의 조합으로 상대 프로세스를 식별 가능

Reference