OS - 프로세스 1

프로세스 개요

Program in Execution(실행중인 프로그램) 의 약자

os_3_1

프로그램이 실행되면 독자적인 주소공간이 생기는데 이를 Virtual Memory 라 한다.
자기만의 메모리 공간이 생기게 된다.

지금 당장 필요한 부분은 메모리(RAM) 에 올라가게 되고 당장 필요하지 않은 부분은 Swap area(디스크) 에 올라가게 된다.

메모리(RAM) 주소는 물리적 개념이고
프로세스의 가상메모리 주소는 논리적 개념으로 다른 공간이다.
같은 0번지라 해도 다른 위치의 메모리 공간이다.

따라서 이를 매칭 시키기 위해 Address translation(주소변환) 작업이 필요하다.

프로세스 각각의 메모리 공간은 크게 3개로 나뉜다

  1. code
  2. data
  3. stack

code 부분은 실행파일의 코드가 올라오는 부분으로 CPU가 실행하는 기계어 코드들이다.

data 부분은 말 그대로 data가 저장되는데 프로그램이 실행되면서 필요한 전역변수, 배열 같은 프로그램 시작부터 끝까지 사용하는 변수들이 위치한다.

stack 은 프로그램의 함수가 사용하는 생성되었다 사라지는 변수들이 저장된다.

커널 주소 공간의 내용

운영체제 커널 또한 하나의 프로그램으로 함수구조로 되어있고
구조 또한 프로세스와 다를 것 없이 code, data, stack 으로 되어있다.

os_3_2

OS는 그림처럼 커널 data 아래 부분의 하드웨어자원도 관리하지만 메모리에 올라와있는 프로세스들도 관리해야한다.

프로세스 별로 하드웨어 자원 분배를 관리해야하기 때문에 커널 data 안에는 모든 하드웨어들을 관리하기 위한 자료구조, 모든 프로세스들을 관리하기 위한 자료구조를 가지고 있다.

커널 stack 은 각 프로세스 커널 함수들의 stack 저장공간이다.
프로그램들은 실행되다 여러 이벤트가 발생하면 OS를 호출한다(IO같은 시스템콜이 대표적).
OS또한 프로그램이기 때문에 함수로 이루어진다.

프로세스A에게 호출당하면 OS는 커널함수를 실행할 것이고 여러 지역 변수들을 만들 것 인데 프로세스마다 별도로 stack을 만들어 그 커널 stack 안에 저장한다.

프로세스의 문맥(Context)

os_3_3

프로그램이 실행되다 사용이 끝나면 중단이 되는데
문맥이란 그 중간 어느 시점을 잘라놓고 봤을 때
이 프로그램이 무엇을 어디까지 실행 했는지 현재 어떤 상태에 있는지 나타내기 위해 사용되는 개념이다.

즉 시간에 따라 바뀌는 프로세스의 현 상태를 문맥이라한다.
문맥을 설명하기 전에 구성부터 알아보겠다.

위의 그림처럼 프로세스의 문맥은 3가지 정도로 구성된다.

  • 하드웨어 문맥
    CPU와 관련된 수행 상태를 나타낸다. Program Counter, 각종 레지스터(R1~Rn)

  • 프로세스의 주소공간
    프로세스의 가상메모리, code, data, stack

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

프로세스는 실행되면 독자적인 주소공간을 만드는데

프로세스가 CPU를 잡게 되면 아래와 같은 작업들을 수행한다.

  • PC가 주소공간 코드 어느 위치를 가리키고 있는지 확인
  • 레지스터에 실행을 위한 값을 복구
  • 각종 Instruction 을 ALU 를 사용해 연산
  • 결과를 또 레지스터에 저장하거나 메모리에 저장
  • 계속 반복

이렇게 프로세스는 실행되다 CPU를 뺏기거나 넘겨줄 것이고 이 프로세스가 어느 시점까지 실행됬지를 규명하는 것이 문맥이다.

문맥은 아래와 같은 정보를 내포하고 있다.

  • PC가 어디를 가리키고 있는가
  • CPU가 코드의 어느 부분까지 실행 했는가
  • 프로세스 stack, data영역에 어떤 내용을 담고 있는가
  • 만약 함수 호출이 있었다면 스택에 과거 무엇을 호출했는가
  • data부분에 있는 변수들
  • 현재 레지스터 값들

이런 실행에 필요한 모든 요소가 문맥이 되겠다.

Kernel Stack

프로세스는 원활한 주소 이동을 위해 stack을 사용한다.
프로세스가 사용자 함수나 라이브러리 함수를 호출하고 종료하면 stack에 있던 복귀 주소 값을 사용한다.

OS 의 커널 stack 도 똑같다.
프로세스의 System Call 을 통해 커널이 CPU제어권을 갖게 된다면 PC는 커널 code 의 어느 부분을 가리킬 것이다.
커널 code 는 여러 프로세스가 공유하며 사용된다.

원활하게 커널 code 를 실행하기 위해 각 프로세스마다 커널 code 어떤 위치를 실행했는지 별도로 스택을 만들어 커널 stack 에 저장해두고 있다.

즉 프로세스도 커널 code 를 사용할 일이 있고 어디까지 실행되었는지 OS가 저장해둔다.

프로세스 혼자 알아서 실행된다면 알 필요 없지만 현대에는 Time Sharing, Multitasking과 같은 서비스를 요구하기 때문에
CPU를 놓쳤다 다시 잡게 될 때 어디까지 실행했는지 문맥을 저장해 놓지 않으면 처음부터 다시? 실행해야 하는 불상사가 일어날 수도 있다.

프로세스 상태(Process state)

문맥설명에 앞서 프로세스 상태부터 알아보고 가겠다.
프로세스는 5가지 상태로 나뉜다.

os_3_5

Running
CPU를 잡고 Instruction을 수행중인 상태

Ready
CPU를 기다리는 상태(여러 조건들을 모두 만족하고) => CPU만 주면 다 가능한 상태

Blocked(wait, sleep)
CPU를 주어도 당장 진행 못하는 준비되지 못한 상태, 즉 프로세스 자신이 요청한 Event가 만족되지 않아 CPU를 줘도 진행할수 없어 기다리는 상태
(요청한 I/O작업이 아직 다 수행 안 되었다던가, 공유데이터를 이미 다른 프로세스가 쓰고 있다던가 등)

위 3가지가 주된 상태
경우에 따라서 밑에 이 2개의 상태가 더 있긴 한데 매우 짧은 순간에 이 상태에 머물러 있는다.

New
프로세스가 생성중인 상태

Terminated
프로세스가 수행(Execution)이 끝난 상태(약간 정리가 필요한)

running 에서 다른 상태로 가는 화살표가 3개이다.

running -> terminated: 프로세스가 종료될 때 running -> ready: Timer interrupt 로 인해 실행중 대기 running -> wating: 프로세스가 IO event로 인해 System Call 후 대기

running -> wating 순간 Disk I/O Queue 의 맨 뒤로가 기다리게 된다.
Device Controller 의 지휘 하에 차례대로 수행하고 완료되면 인터럽트를 건다.
커널이 CPU 제어권을 잡으면 I/O를 요청했던 프로세스 상태를 바꾸고 프로세스A 가 요청했던 데이터도 디스크 로컬버퍼에서 메모리로 넘겨주고 다시 CPU를 얻을 수 있도록 ready 상태로 변경해준다.

os_3_4

그림에는 각 장치마다 큐를 만들어 놨지만 사실 커널 Data안에 있는 자료구조일 뿐이다.

Process Control Block

커널 data 영역에 프로세스가 하나 수행될 때 마다 Process Control Block 을 저장하고
커널 stack 에 각 프로세스의 stack 을 저장한다(프로세스 관리용).

커널의 프로세스 관리용 자료구조로 이 친구한테 CPU를 얼마나 할당할지, 메모리를 얼마나 줄지, 나쁜 짓을 하는 건 아닌지 관리하는 역할을 가진다.

즉 운영체제가 이 프로세스를 어떻게 평가하고 있는지, 이것도 문맥에 포함된다.

os_3_6

사진과 같이 크게 4가지로 구분하였다.

1. OS가 관리상 사용하는 정보

  • Pointer(다음PCB를 가리키는 포인터, 밑의 프로세스 스케줄링에 나옴)
  • Process state [ready, running, waiting, new, terminated]
  • Process number, Priority(그림엔 없지만 우선순위 정보)
    그림에선 큐를 사용해 순서대로 처리한 것 같지만 사실 우선순위를 리소스를 차등분배한다.

2. CPU 수행관련 하드웨어 값

  • Program Counter(어디까지 실행했는지 알기 위해 문맥을 위해 저장함)
  • Register(문맥 저장용으로 실제 CPU의 레지스터가 가졌던 값을 커널의 data공간 PCB에도 저장함)

3, 4 메모리 관련, 파일관련

  • Code, Data, Stack 메모리 위치정보(물리메모리에 있는 실제 위치)
  • Open File Descripter(사용하고 있는 파일들이 어떤 건지)

문맥교환(Context Switch)

프로세스는 CPU를 짧은 시간으로 얻었다, 뺐기다를 반복한다.
CPU를 다시 얻어 시작할 때 처음부터가 아닌 저장해 놨던 문맥을 통해 실행했던 곳부터 시작하는 메커니즘이 있다.

CPU를 한 프로세스에서 다른 프로세스로 넘겨주는 과정이 문맥교환이다.

좀더 상세히 말하면 문맥교환은 사용자 프로세스 에서 사용자 프로세스로 넘어가는 과정이다.

os_3_7

CPU가 다른 사용자 프로세스에게 넘어갈 때 운영체제는 아래 내용을 수행 한다.

  1. CPU를 내어주는 프로세스A 의 상태를 프로세스A 의 PCB에 저장.
  2. CPU를 새롭게 얻는 프로세스B의 상태를 PCB에서 CPU로 읽어옴.

프로세스A가 인터럽트나 시스템콜이나 어떤 이유에서든지 CPU를 빼앗기게 된다면 CPU에 있는 내용을 그냥 지워버리는 게 아니라
다음 번의 실행을 위해 PC, 레지스터 값, Memory Map(메모리 위치정보)을 프로세스의 PCB에 저장해 놓는다.

그렇다면 System Call, 인터럽트 발생시 무조건 문맥교환이 발생할까?

os_3_8

(1)상황 은 문맥교환이 아니다.
1번째 그림은 하드웨어 인터럽트나 시스템콜이 발생한 경우이다.
사용자가 키보드를 쳤다던가 SW적으로 문제가 생겼다던가, 인터럽트 코드가 끝나면 다시 프로세스A 에게 문맥교환 없이 CPU제어권이 돌아간다.

(2)상황 는 문맥교환이 일어난다.
Timer, 또는 I/O 로 인한 System Call 발생하면 커널이 처리 이후 다른 프로세스에게 제어권을 넘겨주는 경우가 많은데 이건 문맥교환이라 할 수 있다.

사실 (1)상황도 문맥교환은 아니지만 사용자의 코드에서 커널 코드로 넘어가는 것이기 때문에 약간의 문맥 일부를 PCB에 저장해야 한다.
하지만 (2)상황 에 비해 오버헤드(소모)가 훨씬 적다.

(2)상황 에 경우 Cash Memory까지 싹 비워줘야 하는(Cache Memory Flush)작업도 추가로 진행한다.

스케줄러(Scheduler)

스케줄러는 시간정하고 순서정하는 것이다. OS에서 스케줄링을 하는 코드이다.

프로세스를 스케줄링하기 위한 큐

운영체제는 프로세스들을 다 큐에 넣고 관리한다. 큐의 종류를 알아보겠다.

os_3_9

Job Queue – 현재 시스템 내에 있는 모든 프로세스의 집합

Ready Queue – 현재 메모리 내에 있으면서 CPU를 잡아 실행되길 기다리는 프로세스의 집합

Device Queue – I/O Device의 처리를 기다리는 프로세스의 집합(각종 디스크, 테이프).

Ready Queue - Ready Queue에는 당장 CPU를 쓸 수 있는 PCB 7, PCB 2가 있다.
disk, terminal 작업을 기다리는 큐도 PCB들이 linked 되어있다.

그림을 보면 알겠지만 실제 프로세스를 줄 세우는게 아니라 PCB 를 줄 세워 순서를 정한다.

전에 PCB그림에 Pointer변수가 있었는데 자기 뒤에 오는 PCB주소이다.

스케줄링 큐를 다른 방식으로 표현한 그림

os_3_10

4가지 이벤트(I/O, timer, fork, interrupt)가 들어오면
cpu제어권을 다른 프로세스에게 넘겨주고 ready큐에 들어가서 순환되는 그림이다.

Short-Term Scheduler

단기 스케줄러 = CPU Schedule

Timer Inturrupt 가 일어나면 어느 프로세스에게 CPU를 줄지.

  • 어떤 프로세스를 다음번에 Running 시킬지 결정
  • 프로세스에게 CPU를 주는 문제
  • 충분히 빨라야 함(Millisecond단위)

Long-Term Scheduler

장기 스케줄러 = 메모리 스케줄러 = Job Scheduler

  • 시작 프로세스 중(New상태) 어떤 것들을 Ready Queue(메모리)로 보낼지 결정
  • 프로세스에 Memory를 주는 문제
  • Degree Of Multiprogramming을 제어 (메모리에 올라가있는 프로세스 수, 메모리에 올라와있는 프로세스의 수를 제어)

현대 대부분 OS는 Time Sharing System 인데 보통 장기 스케줄러가 없다.
바로 메모리로 진입해서 바로 Ready상태로 시작, new상태는 존재하지 않음.
장기 스케줄러는 구시대 기술이다.

그래도 Degree Of Multiprogramming 는 요즘 컴퓨터에도 필요하다.

이를 중기스케줄러를 통해 프로세스 수를 제어한다. 메모리에 올라와있는 프로세스 수는 너무 많아도 적어도 안 된다.
메모리에 프로그램이 적으면 CPU가 노는 상황이, 많으면 메모리 부족으로 당장 쓸 것 도 메모리에 못 올라오는 상황이 발생한다.

Medium-Term Scheduler

중기 스케줄러 = Swapper

  • 만약 메모리에 너무 많은 프로세스가 올라가 있으면 일부를 골라 메모리에서 디스크로 쫓아냄
  • Degree Of Multiprogramming을 제어
  • 프로세스에게서 Memory를 뺏는 문제

어떻게 보면 중기스케줄러는 뺏기만 하니 악역으로 보이지만 높은 효율은 가진다.

그런데 메모리를 통째로 빼앗긴 프로세스는 무슨 상태일까?
Running? Ready? Blocked?
바로 Suspended 상태가 된다

Suspended 상태 는 외부적인 이유로 프로세스 수행이 정지된 상태 (중기스케줄러가 메모리를 뺏거나 사용자가 강제 정지)
Suspended 상태 프로세스는 통째로 디스크에 Swap Out된다.

Suspended 상태 프로세스는 중기 스케줄러 혹은 사용자가 Resume해줘야 다시 Ready상태로 바뀐다.

Blocked상태은 자진 납부상태로 Event를 스스로 기다리는 상태이다. 정지상태(Inactive)가 아니다
자신이 요청한 Event가 만족되면 Ready상태로 바뀌며 일반적으로 I/O 작업으로 System Call 을 호출한 프로세스.

Suspended 가 추가된 프로세스 상태도

os_3_11

다시 말하지만 Blocked 상태는 정지상태(Inactive)가 아니다.
CPU를 사용 못할 뿐이지 I/O작업은 계속 수행중이다.

아래를 보면 Suspended 상태inavtive 라 하고 2가지로 나눈다.

  • Blocked 상태에서 Suspended
  • Ready 상태에서 Suspended

inavtive 에서 active 가 되려면 외부에서 다시 메모리를 주어야 한다.
(중기스케줄러나 사용자가 메모리를 뺏어서 Inactive가 되었듯이)

Suspended Blocked 에서 Suspended Ready 로 가는 화살표(WakeUp)가 있는데
이는 프로세스 I/O 작업이 완료될 경우

Blocked -> Suspended Blocked 된 후 I/O작업을 끝마쳤을 때
Suspended Blocked -> Suspended Ready 로 변경된다.

inavtive 에서도 I/O작업은 Device Controller 가 수행한다.

만약 프로세스에서 I/O 작업을 해야해서 System Call 로 인해 유저모드 -> 커널모드 로 이동시
프로세스의 상태는 Running 일까 Blocked 일까?

헷갈릴 수 있은데 Running 이다.

위 그림에도 이러한 점 때문에 Running 상태를 2가지로 나뉘어 표현하였다.

  • monitor mode running(커널모드 Running)
  • user mode running

monitor mode running 에 제자리 화살표가 있는데 이는 인터럽트 도중에 또 인터럽트가 들어왔을 때를 의미한다.

만약 프로세스A 가 CPU를 잡고 실행중인데 Disk Controller 에서 프로세스B 의 I/O 작업이 끝나 Interrupt 가 들어왔다.
그럼 CPU제어권은 OS에게 넘어갈 것인데 프로세스A 의 상태는 무슨 상태 일까?

이것도 Running 이다.

프로세스A 와 상관없는 일로 CPU제어권이 넘어갔고 인터럽트 수행 후 제어권은 다시 프로세스A 에게 넘어올 것이기 때문에 Running 으로 간주한다.