[운영체제] 운영체제 2주차 스터디 (1) - 프로세스

진승범·2024년 8월 22일
0

운영체제

목록 보기
2/4
post-custom-banner

프로세스


프로그램 (Program)

영단어 Program무언가의 진행 목록이나 순서 를 의미하는 영단어이다. 이처럼 컴퓨터에서의 프로그램 (Program)은 어떤 문제를 해결하기 위한 처리 방법과 순서를 기술한 명령문 집합체의 의미를 갖는다. 프로그램 이야기하는 것은 주로 명령에 의한 진행 절차와 그 짜임을 더 중요시 하는 의미의 말이다.

그렇기 때문에 소프트웨어프로그램 은 유사한 의미를 갖지만, 서로 강조하는 바가 다르다고 볼 수 있다. 정리하자면 프로그램은 컴퓨터가 어떠한 동작이나 문제를 해결하기 위해 작성된 실행 가능한 코드 라고 할 수 있다.

프로세스 (Process)

프로세스란 컴퓨터에서 실행중인 프로그램의 인스턴스를 의미합니다. 일반적으로 컴퓨터에서 프로그램을 실행하면 해당 프로그램을 실행하는데 필요한 자원을 할당받아 프로세스가 생성됩니다.
프로세스는 다음과 같이 이루어져 있습니다.

  • 코드(Text) 영역 : 실행할 프로그램의 코드가 저장된 영역
  • 데이터(Data) 영역 : 전역 변수와 정적 변수가 저장된 영역
  • 힙(Heap) 영역 : 동적으로 할당된 메모리가 저장되는 영역 (프로그램 실행 중에 크기가 변할 수 있다)
  • 스택(Stack) 영역 : 함수 호출과 관련된 영역 (매개변수, 반환 주소, 지역 변수 등이 저장되는 영역)
  • 프로세스 제어 블록(PCB) 커널 : 운영체제가 프로세스를 관리하기 위해서 사용하는 자료구조로써 프로세스의 상태, 프로그램 카운터, CPU 레지스터 값, 메모리 관련 정보, I/O 상태 정보, 계정 정보 등이 포함됩니다

프로그램(Program)과 프로세스(Process)의 차이점
프로그램은 앞서 말한 대로 바이트 수준의 의미를 갖습니다. 어떠한 작업에 대한 명령문들이 대한 집합체를 의미합니다. 하지만 프로세스는 해당 프로그램이 메모리에 적재되어 실행되고 있는 상태를 말합니다. 프로그램은 상태를 가지지 않지만 프로세스는 다양한 상태를 가질 수 있고, 프로그램은 코드와 데이터만 취급하는 반면 프로세스 이를 포함한 Stack, Heap, 레지스터, PCB 등을 다룹니다.

프로세스의 상태

  • New
    프로세스가 생성 중인 일시적인 상태입니다. PCB와 같은 프로세스 자료구조와 같은 것들은 생성되었지만, 아직 메모리 획득을 하지 못한 상태입니다.

  • Ready
    프로세스가 실행되기 위해 준비되었으나, CPU 자원이 할당되지 않은 상태입니다. 준비는 되어있지만, 운영체제의 스케줄러가 프로세스를 선택하여 실행할 때까지 대기합니다. 이 때 프로세스는 Ready Queue에 적재됩니다.

  • Running
    프로세스가 현재 CPU 자원을 할당 받아 실행 중인 상태입니다. 프로세스는 CPU를 사용하여 명령어를 실행하고 있고, 프로그램 카운터(PC)가 현재 실행 중인 명령어를 가리키고 있는 상태입니다.

  • Blocked
    프로세스가 I/O 작업이나 다른 자원에 대해 대기 중인 상태입니다. 자원이 준비될 때까지 프로세스는 실행되지 않으며, 해당 자원이 준비되면 다시 대기 상태로 돌아오게 됩니다. I/O 작업 대기 중인 프로세스나 네트워크 응답을 대기 중인 프로세스의 상태를 예로 들 수 있습니다.

  • Waiting
    프로세스가 특정 이벤트나 조건이 발생할 때까지 대기하는 상태입니다. Blocked과 상태는 비슷하지만, 특정 이벤트가 발생하기를 기다리는 상태입니다.

  • Terminated
    프로세스가 실행을 완료하고 종료된 상태입니다. 프로세스의 실행이 끝나거나, 에러로 인해 종료되며 운영체제는 프로세스의 자원을 해제하고, PCB를 정리합니다.

  • Suspended State
    프로세스가 물리적인 메모리에서 내보내져 메모리 밖에 있는 상태를 의미합니다.

프로세스 상태 흐름
사용자가 프로그램을 실행하게 되면 커널에 해당 프로세스의 PCB가 할당되고 상태가 New 상태로 설정됩니다. 그 후 메모리에 적재되고 CPU를 할당받기를 기다리는 상태인 Ready 상태로 전이됩니다. 이 때 CPU 스케줄링에 의해 CPU를 할당받게 되면 프로세스는 Running 상태가 되어 작업을 처리 합니다. 그리고 CPU에 할당된 시간이 종료되어 Timeout Interrupt가 발생하면 CPU를 다른 프로세스에 양도하고 Running 에서 다시 Ready 상태로 전이된다. 만약 프로세스가 CPU를 할당받아 작업을 처리하던 중, I/O 작업 요청이 들어오게 되면 해당 프로세스는 Blocked 상태가 되고, CPU는 다른 프로세스에게 양도된다. 해당 작업을 완료하게 되면 다시 Ready 상태로 접어들어, CPU 할당을 기다린다.

프로세스 문맥 (Context)

현대의 운영체제는 CPU가 시간을 분할하여 작업을 처리하는 시분할 시스템으로 구성되어 있다. 이 때 CPU가 각 프로세스를 번갈아 가며 작업을 처리하게 되는데, 한 개의 프로세스를 여러번 작업 할 때, 이전 작업이 끝난 시점부터 이어서 작업을 하여야 하기 때문에 정확한 수행 시점과 상태를 재현할 수 있는 정보가 필요하게 된다. 그 정보를 프로세스 문맥 (Process Context) 라고 한다.

또한 프로세스 문맥은 다음과 같이 3가지 정도로 나뉘게 된다.

  • 하드웨어 문맥
    하드웨어 수준에서 말하는 프로세스의 문맥은 바로 PC(Program Count) 등을 비롯한 레지스터 값들을 의미한다.
  • 프로세스 주소공간
    Code, Data, Heap, Stack에 저장되는 데이터들을 의미한다.
  • 커널 수준 문맥
    프로세스의 메타데이터가 저장되는 PCB(Process Control Block)을 관리하고 취급하는 것을 의미한다.

Context Switching

문맥 교환 즉, Context Switching은 운영체제에서 CPU가 하나의 프로세스에서 다른 프로세스로 전환할 때 발생하는 과정이다. 해당 과정에서 실행중인 프로세스의 상태를 저장하고, 다음에 실행될 프로세스의 상태를 불러오는 작업을 의미한다.

Context Switching이 중요한 이유는 우리가 여러 프로그램을 동시에 실행하는 것처럼 느끼게 하는 요소의 핵심 메커니즘이다. 또한 Context Switching을 통해 시스템 자원을 효율적으로 관리하고 우선 순위에 따라 CPU 시간을 프로세스에 더 할당 할 수 있다. 또한 각각의 프로세스마다 PCB에 해당하는 프로세스의 정보를 저장하기 때문에 프로세스 간의 간섭을 받지 않고 안정적인 시스템 운영이 가능하다.

Context Switching 발생 과정

Context Switching은 기본적으로 프로세스 A 에서 프로세스 B로 전환될 때 발생하는 작업이다. 이 작업이 발생할 수 있는 경우는 여러 프로세스가 운영체제의 스케줄러에 의해 번갈아 수행될 때나, Interrupt가 발생하여 프로세스가 변경될 때 발생할 수 있다.

먼저 현재 프로세스의 상태를 저장하게 된다. 프로세스의 레지스터 값 (PC, SP 등..)과 프로세스의 메모리 상태, 프로세스 식별 정보, 스케줄링 정보 등을 커널의 메모리 영역에 있는 PCB 등에 저장한다. 이 때 해당 프로세스의 상태는 Blocked 또는 Ready 상태로 업데이트 됩니다.

그런 후 다음으로 처리 할 프로세스의 PCB를 읽어들여 복원한 후 해당 프로세스의 상태를 Running 으로 설정한 후 실행을 재개합니다.

프로세스의 메모리

프로세스의 메모리 공간은 다음과 같이 이루어져 있습니다.

  • Code
    Code 영역에서는 실행할 프로그램의 코드가 저장됩니다. CPU에서 이 영역에 있는 명령어들을 가져와서, 실행합니다.
  • Stack
    함수 호출 시에 지역변수 및 매개변수, 리턴 주소를 저장하는 영역입니다. 해당 영역에서는 함수 호출 시에 메모리가 할당되고, 함수 종료 시 메모리가 해제됩니다.
  • Heap
    프로세스 실행 중에 동적으로 할당되는 메모리 공간입니다. 해당 영역은 런타임 시에 필요에 따라 메모리를 동적으로 할당하고 해제합니다. malloc , calloc , realloc 와 같은 함수를 통해 메모리를 할당하며, free 함수를 통해 메모리를 해제합니다.
  • Data
    Data 영역에서는 이미 초기화 된 전역 변수와 정적(static) 변수가 저장됩니다. 해당 영역의 데이터들은 프로그램이 시작될 때 초기화되며, 프로그램 종료까지 유지됩니다.

    BSS(Block Started by Symbol) 라고 하는 영역에는 초기화 되지 않은 전역 변수와 정적 변수가 저장되는데, 시작 시엔 0으로 초기화됩니다.

PCB (Process Control Block)

프로세스 제어블록(이하 PCB)는 운영체제가 프로세스를 관리하기 위해 사용하는 데이터 구조이다. 프로세스의 상태, 자원, 속성 등을 추적하는 데 필요한 정보를 포함하고, 프로세스 생성 및 프로세스 스케줄링에 필수적인 역할을 한다.

PCB에는 프로세스의 상태 (e.g. New,Running 등), 프로세스 식별자(PID), 프로그램 카운터(PC), 레지스터, 메모리 관리 정보나 CPU 스케줄링 정보 등이 포함된다. PCB는 Context Switching이 발생할 때, 현재 프로세스의 위와 같은 데이터들의 정보가 저장되고, 다음 프로세스의 PCB의 해당 정보들을 불러와 복원하여 실행을 이어 갈 수 있다.

또한 PCB는 중요한 정보를 가지고 있기 때문에 아무나 접근할 수 없도록 보호된 커널 메모리 영역에 존재하고 Linked List 형태로 Process Table을 통해 각 프로세스에 대한 PCB 데이터를 관리한다.

멀티 프로세스 (Multi Process)

멀티 프로세스는 각각 독립된 메모리 공간을 가진 여러 프로세스가 동시에 실행되는 방식입니다. 멀티 프로세스는 하나의 프로세스가 실패해도 다른 프로세스에 영향을 주지 않기 떄문에 안정성이 높다. 이는 각각의 프로세스가 서로 다른 메모리 공간을 가지기 때문이다. 하지만 이에 따라 메모리 사용량이 증가하게 되고 각각의 프로세스가 서로 통신을 하게 된다면 IPC 비용이 발생한다.

멀티 프로세스를 사용하는 예시

  • Nginx 웹 서버의 경우 웹 서버에 대한 멀티 프로세스 + 싱글 스레드를 통해 웹 서버에 대한 요청을 처리한다.
  • 크롬 브라우저나 웨일 브라우저의 경우 각각의 탭을 프로세스로 관리하기 때문에 어떤 탭에서 문제가 발생하더라도 다른 탭이나 브라우저 자체에 영향을 주지 않는다.

IPC (Inter Process Communication)

서로 다른 프로세스가 협력을 하려면 서로 데이터를 주고 받을 수 있어야 한다. 하지만
프로세스들은 각각 독립된 메모리 영역을 가지고 있기 때문에 서로에 메모리 영역에 접근할 수 없다. 이를 해소하기 위해 IPC란 기술이 필요하다.

IPC 모델에는 크게 다음 2가지로 구분된다.

  • 공유 메모리
    협력하고자 하는 프로세스 간의 공유하는 메모리 영역을 만들어 해당 영역에 데이터를 Read/Write하여 공유하는 방식입니다. 동일한 영역의 데이터를 읽고 쓰기 때문에 동기화 문제에 대해 주의하여야 합니다.
  • Message Passing
    공유 메모리 방식과 같이 동일한 메모리 공간을 공유하지 않고도 프로세스 간 통신을 할 수 있는 모델입니다. 운영체제에서 프로세스 간의 통신 방법을 제공하고, 대신 전달해줍니다. Message Passing에 해당하는 방법에는 소켓 (Socket), 파이프(Pipe), 메시지큐(Message Queue) 등등이 있다.

fork()

운영체제에서 자식 프로세스를 생성하는데, fork() 시스템 콜을 통해 자식 프로세스를 생성합니다. 해당 시스템 콜이 발생하면 커널모드로 변경되고 이후 커널에서는 자식 프로세스에 대한 PCB를 할당하고, 부모 프로세스의 PCB 데이터를 자식 프로세스의 PCB에 복사합니다. 그런 후 자식 프로세스는 Ready Queue 에 적재되고 다시 CPU는 부모 프로세스의 작업을 처리하게 됩니다.

다음 코드를 살펴보자.

#include <stdio.h>
#include <unistd.h>

int main(void) {
  int pid;
  printf("Start!\n");
  pid = fork();
  printf("Who are you?\n);
  
  if (pid == 0) {
    printf("Child Process\n");
  } else {
    printf("Parent Process\n");
  }
}

해당 코드의 실행 결과를 예상해보면

Start!
Who are you?
Child Process 또는 Parent Process

라고 짐작할 수 있을 것이다. 하지만 실제 결과는 다음과 같다.

Start!
Who are you?
Parent Process
who are you?
Child Process

어떤 근거로 위와 같은 결과가 나오는 것일까?

fork() 시스템 콜을 호출하면 자식 프로세스를 생성하기 위해 커널모드가 변경됩니다. 자식 프로세스의 PCB가 할당되고, fork()를 호출한 부모 프로세스의 PCB 데이터를 자식 프로세스 PCB에 복사하게 됩니다. 그런 후 다시 부모 프로세스로 돌아와서 남은 작업을 처리합니다.

Start!
Who are you?
Parent Process

여기까지 위와 같은 결과로 출력될 것 입니다. Parent Process까지 호출하게 되면 부모 프로세스는 종료가 됩니다. 시간이 지나 Ready Queue에 적재되어 있던 자식 프로세스에 CPU가 할당되면 자식 프로세스를 생성할 당시의 부모 프로세스의 PCB 데이터를 기반으로 작업을 처리합니다. 당시 부모 프로세스 PCB의 PC(Program Count) 주소가 다음 수행할 코드의 주소를 printf("who are you?\n"); 로 가지고 있기 때문에 printf("who are you?\n"); 코드부터 실행하게 됩니다. 그 후 코드의 진행은 부모 프로세스와 똑같습니다.

Start!
Who are you?
Parent Process
who are you?
Child Process

결과적으로 위와 같은 출력 결과가 나온다는 것을 이해할 수 있습니다. fork() 는 영단어의 의미처럼 부모 프로세스를 복제하여 자식 프로세스를 생성한다는 것을 알 수 있습니다.

post-custom-banner

0개의 댓글