티스토리 뷰

이번 포스팅에서는 Process에 대한 전반적인 개념들에 대해 다룬다.

Process 란?

  1. Disk에서 올라와 메모리와 CPU에서 동작하고 있는 프로그램
  2. scheduling 객체

*프로그램(program) : Disk에 있는 ‘수행하기 전’의 상태, 수동적이며 ‘바이너리’라고 불린다.

*scheduling : 프로세스가 메모리에서 CPU로 번갈아 올라가는 것을 말한다. 한 개의 CPU에는 한 개의 프로세스밖에 올라가지 못하므로 순서를 결정해 CPU로 올리는 스케줄링이 필요하다. 이후에 자세히 다룰 예정


Process States

 

프로세스는 현재 어떤 상황인지에 따른 ‘상태’를 가진다. 프로세스의 **State(상태)**는 5가지이며, 상태에서 상태로 전이되는 Transition은 6가지 이다. 추가로 Memory에서 디스크의 swap공간으로 전이하는 suspendresume도 존재한다.

아래 그림에서 동그라미는 State, 화살표는 Transition이다.

State

1. new(created, embryo)

  • 프로세스가 새로 생성된 상태

2. ready

  • 프로세스가 run할 준비가 된 상태
  • new와의 가장 큰 차이점은 메모리에 자신의 address space가 모두 구성이 된 상태라는 것이다.

3. running

  • 수행 상태
  • ready와의 가장 큰 차이점은 running은 CPU를 가지고 있는 상태, ready는 CPU를 가지려고 하는 상태(아직 갖지 못한 상태)라는 것이다.

4. waiting(blocked)

  • 사건 발생 대기 상태
  • I/O가 끝나거나 이벤트가 발생하기를 기다리는 상태이다.

5. terminated(zombie)

  • 종료된 상태

Transition

1. admitted

  • new → ready
  • 프로그램이 메모리로 올라오는 것을 말한다.(우리가 PPT와 같은 프로그램을 클릭 했을 때 발생한다.)

2. dispatch(schedule)

  • ready → running
  • 메모리에 있던 프로세스가 CPU로 올라가는 것이다.
  • 스케줄링 떠올리면 용어 외우기 쉬울듯

3. timeout(preemptive, descheduled)

  • running → ready
  • 프로세스가 CPU에서 다시 메모리로 내려오는 것
  • 선점을 의미, 시분할 시스템의 기본 아이디어
    • 스케줄링 정책 중 선점형은 이러한 선점을 이용하여 수행이 끝나지 않은 프로세스를 CPU에서 메모리로 내리며 스케줄링한다.

4. wait(sleep, I/O initate)

  • running → wait
  • ex) 키보드에서 입력이 될 때까지 기다린다.

5. wakeup(I/O Done)

  • wait → ready
  • I/O 또는 이벤트가 완료될 경우 전이

6. exit

  • running → terminated
  • 프로세스의 수행이 끝나고 종료된 것

디스크-메모리 사이에서의 Transition

프로세스가 계속 메모리에 올라갈 경우 DRAM의 메모리가 부족해지는 현상이 나타날 수 있다. 이를 방지하기 위해 ready나 waiting상태의 프로세스 일부를 Disk의 Swap 공간으로 내리는 작업을 한다.

suspend

  • ready → swap OR waiting → swap
  • 프로세스를 swap 공간으로 내린다.

resume

  • swap → ready OR swap → waiting
  • 프로세스를 swap 공간에서 다시 올린다.

PCB(Process Control Block)

PCB란?

PCB 구조 (Source: A.Silberschatz, “Operating system Concept”)

OS가 프로세스를 관리하기 위해서는 각 프로세스에 대한 정보가 필요하다. 이때 프로세스를 관리하기 위한 정보 자료구조를 PCB라고 한다. PCB는 각 프로세스마다 하나씩 존재하며 kernel의 data segment에서 관리한다. PCB의 자원은 다음과 같다.

 

  1. Process State : 위에 설명한 프로세스의 상태들을 저장한다.
  2. Process ID(pid) : 새로 생성되면 각 프로세스는 자신의 고유한 pid를 가진다.
  3. Program Counter, CPU register : context switch 하는 동안 사용
  4. CPU scheduling information
    • ex) process의 CPU 사용량, 프로세스 우선순위
  5. Memory(address space)
    • Text : 명령어
    • Data : 전역 변수
    • Stack : 지역 변수, 함수 인자, 복귀 주소, …(메모리공간상 위에서 아래로 이동)
    • Heap : 동적할당(메모리 공간 상 아래에서 위로 이동)
  6. Open된 파일들
  7. I/O status information
    • ex) I/O를 얼마나 했는지(네트워크를 얼마나 사용했는지)
  8. Accounting information
    • ex) CPU, Memory를 얼마나 사용하는지

 

PCB의 예

그렇다면 실제 OS에서 PCB는 어떻게 구현되어 있을까?

리눅스에서의 PCB

리눅스는 오픈소스이기 때문에 어느 누구든 코드를 볼 수 있다. 위에서 볼 수 있듯이 리눅스에서의 PCB는 task structure로 구현되어 있으며, 그 안에 CPU, state, memory 등의 정보가 들어있다.

혹시 직접 보고싶다면 여기로 들어가면 된다.


프로그램이 수행 될 때 OS 내부 동작 과정

1. Load

  • 디스크 상에 있던 코드/데이터 부분을 메모리로 올리는 과정
  • 로딩하려면 디스크상에 존재하는 program의 정확한 형식을 알아야 하므로 수행 포맷을 갖는다.
    • ex) ELF(리눅스에서의 포맷), PE(윈도우 포맷) …

2. Dynamic allocation 동적할당

  • Heap, Stack 동적할당
  • 메인함수에서 사용하는 인자들(argc, argv)을 stack에 초기화한다.

3. Initialization

  • main함수 시작하기 전에 미리 초기화해야 할 부분들을 처리한다.(전처리)
    • 파일 디스크립터(0 - 표준 입력, 1 - 표준 출력, 2 - 표준 에러)
    • I/O, signal
    • 이 과정이 있기에 main함수가 명시적으로 open하지 않아도 바로 사용이 가능하다.

<——————>

*여기까지가 수행할 준비를 완료하는 과정이다.

4. Jump to the entry point

  • entry point로 점프한다.
    • 다수의 언어가 main이 entry point이다.

Process System Call

프로세스의 생성, 수행 등과 관련하여 여러 시스템콜이 존재한다. 대표적인 fork()부터 차례차례 알아보자~

1. fork()

  • 새로운 프로세스를 생성하는 시스템콜이다. 기존에 있던 프로세스를 parent, 새로 만들어진 프로세스를 child라고 부른다.
  • 리턴 값은 두 개이다. 하나는 parent를 위한 것, 하나는 child를 위한것
    • parent : child의 pid(항상 0보다 큰 값)
    • child : 0
  • parent와 child 중 어느 것이 먼저 수행 되는지는 우리가 알 수 없다. (내부 스케줄링 정책에 의해 결정되므로)
    따라서 non-determinism 하다. 하지만 wait()를 쓰면 순서를 보장할 수 있다! wait() 설명은 바로 아래에~

 

2. wait()

  • 프로세스의 상태를 wait으로 바꿔준다.(프로세스 상태는 위에서 설명했음~)
  • parent 프로세스를 child 프로세스 중 하나가 완전히 끝날 때까지 block시키는 것이다. (자식 중 누구일지는 또 모름)
  • wait()을 사용함으로써 부모 자식간의 순서를 개발자가 결정할 수 있게되었다! 따라서 deterministic 하다.

 

3. exec()

  • 새로운 프로그램을 수행하기 위한 시스템콜이다.
  • 로딩, 초기화, main 함수 수행 등을 작업하는 역할
  • never return! exec()를 수행하면 지정한 프로세스로 넘어간다. 즉, exec()를 호출한 프로세스는 새로운 프로세스에 의해 덮어 쓰여지게 된다.
  • fork()와 구분하는 이유는? 좀 더 유연하고 구조적인 프로그램 만들기 위해서, 즉 fork와 exec 사이에 새로운 기능을 쉽게 추가할 수 있게 하기 위해서이다.
    더 쉽게 설명하자면, fork를 할 경우 parent와 child 프로세스로 나뉜다. 이 때 wait()을 수행한 후에 child 프로세스에서 exec()를 호출할 경우 child 프로세스는 새로운 프로세스에 의해 덮여쓰여지게 되지만 부모 프로세스는 남아있게 된다.
    따라서 wait이 끝난 후에 부모 프로세스는 resume하게 된다.
    그림으로 설명하면 다음과 같다.

  • exec() 의 종류
    • execl(const char *path, const char *arg, ...) : 나열형 인자
    • execv(const char *path, char *const argv[]) : 벡터형(string array) 인자
    • execle(const char *path, const char *arg ,..., char * const envp[]) : +환경변수
    • execve(const char *path, char *const argv [], char *const envp[]) : +환경변수
    • execlp(const char *file, const char *arg, ...)
    • execvp(const char *file, char *const argv[])

4. 그 외

  • getpid() : get process id
  • kill() : 프로세스에 시그널을 보낸다.(인자로 signal number를 주면 해당 프로세스 종료)
  • signal() : signal을 받으면 그 signal을 처리할 수 있는 handler(catch function)를 등록할 수 있다.
  • nice() : 스케줄링때 자신의 우선순위를 높이거나 낮출 수 있다.

 

다음 포스팅에서는 이러한 프로세스들이 CPU에서 어떻게 동작하는지에 대해 다룰 예정이다~

'학교수업 > 운영체제' 카테고리의 다른 글

[운영체제] 4. Scheduling  (2) 2024.02.21
[운영체제] 3. Limited Direct Execution  (1) 2024.02.02
[운영체제] 1. OS Introduction  (0) 2024.01.18
[운영체제] 정리 전 회고  (2) 2024.01.18
가상화 (Virtualization)  (0) 2021.04.07
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/07   »
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31
글 보관함