정글사관학교 개발일지/운영체제-PintOS

운영체제 (2) - 프로세스 (정글사관학교 54일차 TIL)

Woonys 2021. 12. 26. 02:38
반응형

프로세스

프로세스 개념

프로세스란 "실행 상태의 프로그램"을 의미한다. 여기서 프로그램이 실행 중이라는 의미는 디스크에 있던 프로그램을 메모리에 적재하여 운영체제가 다룰 수 있는 상태가 되었다는 것을 뜻한다. 즉, 메모리 내에 자신을 위한 주소 공간이 있음을 의미한다. 운영체제는 이를 가상 메모리로 관리해 각 프로세스마다 독립적으로 메모리를 할당해준다.

프로세스의 문맥(context)

프로세스에서 문맥이란 굉장히 중요한 개념이다. 앞으로 나올 운영체제의 작동 방식을 이해하는데 있어 필수적으로 이해해야 한다. 일반적으로 문맥이라는 단어의 뜻을 우리는 "글의 맥락, 글의 흐름"이라고 해석한다. 이를 컴퓨터에서는 "프로세스가 현재 시점을 기준으로 수행 완료한 상태"를 말한다. 즉, 이 프로세스가 1에서 10까지 범위를 갖고 있다고 할 때, 특정 시점에서 이 프로세스가 1에서 10 사이 중 어디까지 완료했니? 에 대한 상태를 뜻한다.

좀 더 자세하게 이해하기 위해, 프로세스의 진행과정을 살펴보자. 우리가 실행하려고 하는 프로세스를 A라고 하자.

  1. 프로그램 A가 실행되면 독자적인 주소 공간을 갖는 가상 메모리가 생성된다. 이 가상 메모리에는 code/data/stack 영역이 존재한다.
  2. 그러다 이 프로세스가 CPU를 할당받으면, program counter라는 레지스터가 프로세스 A의 가상 메모리 내 code 영역을 가리킨다. 그리고 매 순간마다 code 영역으로부터 컴파일해서 번역한 기계어를 읽어온 다음 레지스터에 값을 넣는다.
  3. 레지스터에서는 이를 ALU에 넣어 계산을 진행하고 계산을 완료한다.
  4. 연산이 끝난 값을 다시 레지스터에 넣거나 혹은 메모리로 옮긴다.
  5. 위의 1~4를 반복한다.

이 과정에서, 어떤 특정 시점에서 위의 작업이 멈췄다고 생각해보자. 나중에 다시 와서 이 프로세스를 봤을 때, 이 프로세스가 어디까지 진행되었는지를 알아야 그 시점부터 다시 이 프로그램을 돌릴 수 있을 것이다. 이때 "어디까지 진행됐는지"에 대한 정보가 바로 문맥이다.

이 문맥이란 놈을 알기 위해서는

  • 코드: 현재 시점에서 PC(program counter)가 코드의 어디를 가리키고 있는지를 파악해야 하며(=코드의 어느 부분까지 실행했는가)
  • 스택: 프로세스 메모리에는 어떤 내용을 담고 있는지 -> 코드가 계속해서 실행되면서 함수를 호출할 테고, 그 리턴값이 스택에 쌓여있을 텐데 현재 무슨 내용을 스택에 얼만큼 쌓아났는지에 대한 정보를 알아야 하며
  • 데이터: 데이터 영역의 변수 역시 진행되면서 변경됐을텐데, 어디까지 변경됐는지?
  • 레지스터: 레지스터 영역에서는 현재 어떤 값을 어디까지 넣어놓고 어디까지 연산했는가

이런 것을 다 기억하고 있어야 한다.

문맥은 크게 3가지로 구성되어 있다.

1. 하드웨어 문맥: CPU와 관련되어 있으며 CPU의 수행 상태를 나타낸다.

  • 프로세스는 CPU를 잡고서 매 순간마다 instruction을 실행한다. 현재 시점에서 이 프로세스가 어디까지 실행됐는지를 알려면 레지스터에 어떤 값을 넣고 있고 PC가 어디를 가리키고 있고 등의 요소(CPU 내의 구성 요소들에 한해서)가 필요하다.
  • PC 및 각종 레지스터의 정보가 여기에 해당한다.

2. 프로세스의 주소 공간(메모리와 관련)

  • 현재 시점에서 해당 프로세스의 가상 메모리 내 주소 공간을 의미하며 여기에는 code/data/stack이 해당한다.

3. 프로세스 관련 커널 구조

  • PCB(Process Control Block)
    • 커널 주소공간 내 데이터 영역에는 각 프로세스를 관리하기 위해 하나의 프로세스에 대응하는 자료구조인 PCB(Process control block)을 갖고 있다.
    • 프로세스 하나가 실행될 때마다 운영체제는 PCB를 하나씩 두고서 그 프로세스를 관리한다. 운영체제가 이 친구를 어떻게 관리하고 있는지의 측면에서 PCB가 필요하다.
  • 커널 스택
    • 커널 역시 함수로 구성되어 있다. 커널 코드에서 함수를 호출하면 관련 정보를 스택에 쌓는다.
    • 커널이 누구의 부탁을 받는지는 매번 다른데, 스택에 쌓을 때는 어떤 프로세스가 커널을 호출했는지에 따라 프로세스 별로 스택을 별도로 두고 있다.
    • 따라서 현재 프로세스를 규명하기 위해서는 해당 프로세스에 대한 커널 스택 역시 확인해야 한다. 그러면 현재 상태를 규명할 수 있다.

프로세스를 혼자 실행하면 당연히 뭘 기억하니 하는 과정이 필요없다. 하지만 앞으로 소개할 운영체제 작동 방식은 하나의 CPU가 여러 프로세스한테 자기를 빌려주는 식이다. 그러니 CPU가 프로세스 A 관련해서 연산을 수행하다가 갑자기 B에서도 와달라고 해서 B에 가서 연산 수행하고 다시 A로 온다고 했을 때, 이전에 어디까지 했더라? 이러고 있으면 개판 나는 거다. 이전 문맥을 백업해놓는 이유는 운영체제의 작동 방식 때문이다. 앞으로 찬찬히 소개할 것이다.

프로세스의 상태(Process State)

프로세스는 수행하는 과정에서 여러 가지 상태를 갖는다.

  1. Running
    • 위에서 잠깐 언급했듯, 운영체제 작동방식은 여러 프로세스한테 CPU를 짧게 짧게 빌려주는 방식이라고 했다. 이때, CPU는 한 개이다. 여러 프로세스를 번갈아가며 매우 빠르게 작업하지만 그 순간마다 CPU를 잡고 있는 프로세스는 딱 하나뿐이다. 따라서 이 때 instruction을 수행 중인 상태가 바로 running이다.
  2. Ready
    • Ready queue에서 CPU를 기다리고 있는 상태를 뜻한다. 이때 주의해야 할 점은, 가상 메모리 중에서 프로세스를 수행하는데 필요한 부분이 실제 물리 메모리에 다 올라와있어서 CPU만 오면 당장이라도 실행할 준비가 된 상태를 뜻한다. CPU만 얻으면 곧바로 instruction을 진행할 수 있는 상태이다.
    • Ready 상태인 프로세스들이 번갈아가며 CPU를 잡고 프로세스를 동시적으로 실행한다.
  3. Block(wait, sleep)
    • CPU를 줘도 당장 지시를 수행할 수 없는 상태이다. Process 자신이 요청한 이벤트가 즉시 만족되지 않아 기다리고 있는 상태이다.
    • 보통 I/O 관련 작업을 수행하고 리턴값을 기다리고 있을 때 block 상태로 전환한다. 예컨대 웹서버로부터 이미지를 불러오는 요청을 했다고 해보자. 요청을 보내고 나면 서버로부터 이미지가 오는 사이 동안은 CPU가 전혀 필요 없게 되니 프로세스를 재운다.
  4. New
    • 프로세스가 생성 중인 상태
  5. Terminated
    • 프로세스가 종료 중인 상태. 이때, 종료가 완료된 건 그 순간 이미 프로세스가 아니니, 종료된 것 말고 종료 중인 상태를 말한다.
    • 종료를 한다고 바로 종료!되는 게 아니라 수행 끝나고서 정리하는 동안 과정이 필요하다. 이때의 상태를 뜻한다.

이를 프로세스가 생성될 때부터 종료되기까지의 전체 흐름에 대한 도식으로 살펴보자.

1) 처음에 프로세스가 생성 중인 상태는 new에 해당한다.

2) 생성이 끝나면 ready 상태로 전환되며, 이때는 CPU만 얻으면 바로 running으로 전환한다. 실행에 필요한 최소한의 부분만 메모리에 올라와 있다.

3) 여기서 cpu를 할당받으면 running으로 바뀌고 작업을 수행한다. 이 과정에서 cpu를 내놓는 상황이 발생하는데,

4) waiting(blocked): I/O 처리같이 오래 걸리는 상황에서는 CPU 내어주고 결과가 완료될 때까지 프로세스를 재운다. 이는 ready와 다른 경우로, cpu를 할당받을 수 없는 상태이다.

5) Time interrupt: 사실상 2) ready로 상태를 전환하나, new에서 ready로 넘어오는 과정과는 다르다. 프로세스가 CPU를 빌릴 때는 정해진 시간만큼만 빌릴 수 있다. 그 시간 안에 작업을 완료하지 못하면 Timer가 끼어들어 작업을 일시정지시킨다. 그러면 CPU는 다른 프로세스로 넘어가고 해당 프로세스는 문맥을 백업한 채 ready queue에서 기다려야 한다.

6) 마지막으로 작업이 끝나면 종료 단계로 넘어가는데 이때 terminated 상태로 바뀐다.

 

Process Control Block(PCB)

 

운영체제가 각 프로세스를 관리하기 위해 프로세스당 유지하는 정보로, 아래 구성 요소를 구조체로 유지하며 가진다.

  1. OS가 관리상 사용하는 정보
    • Process state(ready, running, blocked), Process ID
    • 스케쥴링 정보, 우선순위
      • queue에 먼저 온 순서대로 처리하는 것처럼 보이지만 실제로는 그렇지 않음. round-robln. queue안에서 가장 우선순위가 높은 애를 먼저 실행!
  2. 프로세스의 문맥을 표시하기 위한 정보 - CPU 수행 관련 하드웨어 값
  3. 메모리 관련 문맥
    • code, data, stack 위치 정보
  4. 파일 관련 정보
    • Open file descriptors

문맥 교환(context switch)

  • CPU를 한 프로세스에서 다른 프로세스로 넘겨주는 과정
  • CPU가 다른 프로세스에게 넘어갈 때 OS는 아래를 수행
    • 뺏기던 시점의 문맥을 기억한다(PCB에 CPU를 내어줄때의 프로세스 상태를 저장)
    • CPU를 새롭게 얻는 프로세스 상태를 PCB에서 읽어온다.
  • CPU를 뺏겼을 때 ⇒ 레지스터에 저장되어 있던 값을 PCB에 저장! (레지스터 값, PC 안에 들어있는 값, 메모리 맵도 PCB에! ⇒ 여기서 말하는 것들은 전부 CPU 안에 들어있는 값들.)
  • 문맥교환될 때 save하는 건? 커널에서 프로세스의 PCB ⇒ CPU 문맥을 세이브하기 위한 위치는 메모리에서 커널이 관리하는 커널 주소 영역! 이게 PCB를 구성. PCB는 커널 내 각 프로세스마다 배정된 공간(PCB)에 저장.

context switch에서 헷갈리는 점 정리

  • 시스템 콜이나 하드웨어 interrupt 발생시 반드시 context switch가 일어나는 것은 아님.
    • 시스템 콜이나 인터럽트 발생하면 CPU제어권: 사용자 프로세스로부터 운영체제 커널로 넘어간다. 이때 사용자 프로세스로부터 CPU가 운체에 넘어간 걸 context switch라고 부르지 않음.
    • 사용자 프로세스 하나로부터 또다른 사용자 프로세스로 CPU가 넘어갈 때! 근데 시스템 콜은? ⇒ 운영체제 커널로 CPU 제어권이 넘어가. 이건 switch가 아님.
    • 운영체제가 CPU를 다른 프로세스한테 넘겨주면? (A→ 운 → B) 이건 switch!
      • timer interrupt: 다른 프로세스에게 넘기기 위한! 프로세스 A에 할당된 시간이 끝나서 다음에 기다리고 있던 B한테 넘겨주기 위해 timer interrput가 작동한 것이니!
    • 근데 A→ 운 → A는 switch가 아님! 문맥교환 없이 그냥 usr 모드로 복귀. 물론 이때도 프로세스 A에 대한 문맥을 세이브하긴 해야 하나, A→ 운 → B보다는 부담이 적음! 2)의 경우 캐시 메모리를 싹다 지워버려야 함.

Queue: 프로세스를 스케쥴링하기 위한 큐

  • Job 큐
    • 현재 시스템 내에 있는 모든 프로세스 집합
  • Ready 큐
    • 현재 메모리 내에 있으면서 CPU를 잡아 실행되기를 기다리는 프로세스 집합
  • Device 큐
    • I/O 디바이스 처리 기다리는 프로세스 집합
  • 프로세스들은 각 큐를 오가며 수행
  • 줄세우기: 포인터로 다음 애들 가리켜. 큐헤더가 다음 PCB를 가리키고 그 다음 PCB를 가리키고 ...

프로세스 스케쥴링 큐의 모습

스케쥴러 (Scheduler)

  • Long-term scheduler(장기 스케쥴러 or job scheduler)
    • 시작 프로세스 중 어떤 것들을 ready queue 로 보낼지 결정
    • 프로세스에 메모리 및 각종 자원을 주는 문제
      • new→ ready 상태로 넘어갈 때 메모리를 줄지 안 줄지를 결정!
    • degree of multiprogramming을 제어
      • 멀티 프로그래밍? 메모리에 여러 프로그램이 동시에 올라가는. degree: 메모리에 올라가는 프로그램 수. 이 개수를 제어하는게 롱텀!
    • 시분할 시스템에는 장기 스케쥴러가 없음 (무조건 ready) → 실제 우리가 다루는 시스템에서는 없다는 말..! 장기 스케쥴러 없이 프로그램 실행시키면 전부다 ready 상태로 넘어간다! 그럼 degree를 어떻게 조절? 아래 medium에서!
  • Short-term scheduler(단기 or CPU 스케쥴러)
    • 어떤 프로세스를 다음번에 러닝시킬지 결정
    • 프로세스에 CPU를 주는 문제
    • 충분히 빨라야 (밀리초 단위)
  • medium-term scheduler(중기 스케쥴러 or swapper)
    • 여유 공간 마련을 위해 프로세스를 통째로 메모리에서 디스크로 쫒아냄(스와핑)
    • 프로세스로부터 메모리를 뺏는 문제
    • degree of multiprogramming을 제어.

suspended(stopped): 프로세스 상태 중 위에서 소개하지 않은 나머지 상태

  • 메모리를 통째로 빼앗긴 상태를 뜻함. 이 상태는 running, ready, blocked 모두 아님.
    • running: cpu 잡고 있는 상태
    • ready: CPU 기다리는 상태
    • blocked: I/O 등 이벤트 기다리는 상태
    • suspended: 외부적인 이유로 프로세스 수행이 정지된 상태. 프로세스는 통째로 디스크에 swap out 된다. 메모리에서 통째로 쫓아내져.
    • *blocked와 suspended를 구분할 수 있어야!
      • blocked: 자신이 요청한 이벤트가 만족되면 ready.
      • suspended: 외부에서 resume해줘야 active.
  • 메모리에 너무 많은 프로세스가 올라와있을 때 등 시스템이 여러 이유로 프로세스를 잠시 중단시킴. (사용자가 프로그램을 일시 정지) ⇒ 이게 외부에서 시킨 것!
  • 여기서 running: 두 가지 케이스
    1. user mode: 프로세스가 CPU 갖고 있으면서 본인 코드 실행 중인 상태
    2. monitor mode: 시스템 콜, 인터럽트, 등 ⇒ 운영체제 코드를 실행 중일 것. 이때는 프로세스가 CPU를 잃고 운영체제가 CPU를 얻어서 운영체제가 running하고 있다고 말하지 않음.
      • 위에서 얘기하는 프로세스 state(running, blocked, ready) ⇒ 운영체제 입장에서는 이렇게 말하지 않는다.
      • 프로세스가 자기 코드 실행하다가 시스템 콜⇒ 커널로 넘겨주면 사용자 프로세스가 커널 모드에서 러닝하고 있다고 하지 운영체제가 running하고 있다고 말하지 않음. 이 차이를 주의!!
      • 인터럽트 등이 들어왔을 때 사용자 프로그램 실행되다가 운영체제 커널 코드가 실행 ⇒ 인터럽트는 프로그램 때문에 실행되는 것이 아니라 외부 다른 요인 때문에! 프로그램이 여전히 러닝하고 있다고 간주. 다만 커널 모드(모니터 모드)에서 돌아가고 있다고 말함.
      • 위 그림은 모두 사용자 프로그램의 상태도를 뜻함. (운영체제의 상태도가 아니라는 것!이 주의!!!)
      • blocked, ready
    • 최종은 아래 모습.
      • 외부에서 프로세스 정지시켰으면 외부에서 재개해줘야지만 inactive → active 상태로 바뀜.
      • 아랫 부분: 프로세스가 얼어붙어서 정지된 상태(inactive)
      • suspended blocked → 이벤트 발생 시 suspended ready로 변경.
      • suspended: 메모리를 통으로 뺏기니 CPU 관점에서는 아무 일도 할 수 X. but I/O 는 작업 진행할 수 있으니 여기서 blocked→ready로 넘어갈 수는 있음.
반응형