충남대학교 컴퓨터공학과 류재철 교수님의 "운영체제 및 실습" 강의를 필기한 내용입니다.

다소 잘못된 내용과 구어적 표현 이 포함되어 있을 수 있습니다.

프로그램 실행에서의 OS의 역할

  • 자원들(메모리 등)을 여러 프로세스들에게 적절하게 분배하고 관리함
  • 프로세스가 계속 변경되며 실행되어 동시에 실행되는것처럼 보이게 한다
  • 이렇게 프로세스와 IO디바이스들을 관리하는것이 OS가 하는 일이다

프로세스

  • 실행의 단위, OS의 관리의 단위, 실행됐지만 아직 죽지는 않은 것
  • 프로그램 코드 와 그 코드와 연결된 여러개의 데이터로 구성된다
  • 코드(text) : 내가 짠 프로그램 소스파일
  • 코드에 연동되는 데이터는 구체적으로 global 변수는 data에, local변수와 함수는 stack에, 동적할당을 위한 공간은 heap에(단 heap은 data와 합쳐 그냥 data로 하나로 퉁쳐서 부르기도 한다), 그리고 나머지 필요한 자료들은 PCB에 저장된다
  • OS는 프로세스 단위로 메모리를 할당하고 관리한다

PCB에 저장되는 정보들

  • PCB = Process Control Block : 프로세스의 정보들을 담은 구역(자료구조). 프로그램이 프로세스가 되기 위해서는 이 공간을 반드시 할당받아야 한다
  • identifier : 유닉스에서 PID같은놈. 프로세스들의 고유 번호이다
  • state : 프로세스의 현재 상태. 현재 실행중인건지, 기다리는 것인지 등등의 상태들이 저당된다
  • priority : 프로세스들 간의 우선순위. 시스템 프로세스 같은 중요한 것들이 먼저 구동될 있도록 우선순위가 매겨져있다. 하지만 하나의 프로세스가 cpu를 monopolize하는것을 막기 위해 우선순위는 계속 바뀌게 된다
  • 그리고 cpu로 들어갈때 레지스터에 쓸 값들 - program counter(다름 실행할 명령어의 주소), memory pointer(이 프로세스가 저장되어있는 메모리의 주소) 등등의 정보들이 저장되게 된다

System, Kernel, User Process

  • OS도 하나의 프로그램이므로 OS의 여러 기능들도 프로세스화되어 구동되게 된다
  • 이 OS의 프로세스를 system process라고 하며 그 중에서도 중요한 애들인 커널이 프로세스화 된 것이 kernel process(daemon) 이다. Kernel process같은 중요한 기능들은 항상 메모리에 상주한다 <-> 반대로 우리가 만든 프로그램들이 프로세스화되면 user process라고 하는 것

Dispatch, Context switch

  • Dispatch : ready상태인 프로세스들 중 가장 우선순위가 높은놈을 running상태로 바꿔 cpu를 할당하는 일을 말함
  • Context switch : 프로세스가 전환 후 새로운 프로세스가 실행되는것을 의미함
  • Dispatcher : 새로운 프로세스를 Dispatch하여 Context switch하는 일을 전담하는 kernel process
  • 여기서 중요한점은 새로운 프로세스가 Dispatch된 이후 새 프로세스가 실행되는것을 보고 Context switch가 일어났다라고 말한다 - 새로운 프로세스로 교체하는 “과정”을 Context switch라고 하는게 아니다 이말이야 - 따라서 Dispatch이후 Context Switch가 일어나는게 맞는거다

Process 실행과정 - 2 state process model

%E1%84%8B%E1%85%B5%E1%84%85%E1%85%A9%E1%86%AB02%20-%20%E1%84%91%E1%85%B3%E1%84%85%E1%85%A9%E1%84%89%E1%85%A6%E1%84%89%E1%85%B3%20abcc0abd8daa4524bf7af87f2e874e9c/image1.png

  • 프로세스가 생성되면(Enter / Creation) 먼저 **Not Running(Ready)**상태가 된다 - Dispatch를 기다리는 상태
  • 이제 이 프로세스가 Dispatch되면 Running상태가 된다 - 실행되는 상태
  • 그리고 또 이놈이 실행되다가 타임아웃 등의 인터럽트를 받으면 다시 Not Running의 상태로 간다 - pause된다
  • 또 Dispatch되면 Running상태로 가고 이 과정을 반복하가 종료 (Exit / Termination)된다
  • 따라서 프로그램이 fork()되어 프로세스로 creation이 됐다가 exit()되어 다시 프로그램의 상태로 termination될때까지 수많은 pause와 dispatch를 거친다
  • 하지만 system process들은 잘 terminate되지 않는다 - 중요하므로
  • 우리가 코딩할때도. system call을 이용해 creation, dispatch, pause, terminate를 직접적으로 명령할 수도 있다 - fork(), exec(), wait(), exit()
  • Not Running중인 프로세스들은 queue로 관리된다→ dispatch되면 queue에서 빠지고 pause되면 다시 queue로 들어간다

sys call : fork()함수

  • 프로세스가 실행되다가 fork()가 실행되면 새로 프로세스가 하나 더 만들어지는데 이때 fork()를 호출한놈이 parent 이고 만들어진 놈이 child 이다
  • fork()를 호출하면 parent와 동일한 놈이 하나더 child로 만들어지게 된다
  • 나머지는 전부 같으나 다른점이 몇가지 있다
    1. PID(identifier) 가 다르다 - 부모자식은 구별할 수 있어야 하므로
    2. fork()함수의 리턴값은 부모의 경우 자식의 PID, 자식의 경우 0을 리턴한다
  • child프로세스가 끝나기 전에 parent가 끝나면 좀 골치아파진다 - 원칙적으로 child가 끝나야 parent를 끝낸다 - cascade termination 이라고 한다
  • 하지만 부득이하게 parent가 끝나면 parent의 parent가 child의 parent로 바뀌게 된다

Termination condition

  1. Normal completion : 정상종료
  2. timeout과는 별개로 cpu를 차지하는 총 시간도 중요하다 - 무한루프에 빠졌을 가능성이 있으므로 - cpu를 차지하는 총 시간이 너무 긴 경우에도 강제로 termination하게 된다 - timeout과는 별개의 개념이다 - timeout의 경우에는 cpu를 연속적으로 사용하는 시간을 말하고 이때에는 이 cpu를 잡고있는 총 시간을 말하는거 -무한루프가 아닌 원래 시간을 많이 잡아먹는 일이면 작업관리자에 승인을 요청하는 작업을 해줘야 된다
  3. Memory unavailable : 더이상 가용 가능한 메모리가 없을 경우

Process 실행과정 - 5 state process model

%E1%84%8B%E1%85%B5%E1%84%85%E1%85%A9%E1%86%AB02%20-%20%E1%84%91%E1%85%B3%E1%84%85%E1%85%A9%E1%84%89%E1%85%A6%E1%84%89%E1%85%B3%20abcc0abd8daa4524bf7af87f2e874e9c/image2.png

  1. New : 새로 들어와서 프로세스로 바꾸는 과정 - 여러 resource들을 할당받는 상태 - 프로세스화가 끝나면 admit되어 다음 단계로 간다
  2. Ready : 프로세스화가 끝난 상태 - dispatch되어 running되기만 기다리는 상태이다
  3. Running : dispatch후 실행중인 상태 - 실행이 끝나면 release되어 다음 단계로 간다
    • 다만 timeout이 발생하면 다시 ready로 가게 된다 - timeout의 경우에는 어떤 이벤트가 일어나 지금 당장 실행할 수 없는 상태가 아니므로
    • ready 상태에 있는 놈들은 queue에서 기다리게 된다
  4. Blocked : 키보드 입력이라거나 그러한 이벤트로 인해 잠깐 멈춘 상태 - event wait
    • 얘네는 지금 바로 다시 실행할 수 있는 상태가 아니기 때문에 ready로는 가지 못하게 되는거다 - 따라서 이벤트가 처리되어(event occurs) 다시 running 가능해지면 running되는게 아니라 ready 단게로 가게 된다
    • event queue라는 것이 존재해서 event가 처리될때까지 queue에 머문다 - 그리고 event가 끝나면 ready queue로 옮겨져 또 기다리게 된다
  5. Exit : 프로세스가 종료되어 new에서의 역순으로 처리되는 과정 - resource를 전부 반납하게 된다

Process Swapping - 7 state process model

%E1%84%8B%E1%85%B5%E1%84%85%E1%85%A9%E1%86%AB02%20-%20%E1%84%91%E1%85%B3%E1%84%85%E1%85%A9%E1%84%89%E1%85%A6%E1%84%89%E1%85%B3%20abcc0abd8daa4524bf7af87f2e874e9c/image3.png

  • 중요한 이벤트가 발생해서 당장 실행해야 되는데 메모리에 공간이 없으면 덜 중요한 애들이 메모리를 양보하고 하드디스크로 내려간다 - swap-out
  • 이벤트가 종료되어 얘네들이 다시 메모리로 올라오는 것을 swap-in 이라고 한다
  • 이렇게 프로세스가 잠깐 하드로 내려가게 되는 것을 suspend라고 한다
  • 이런 suspend를 관리하기 위해 suspend state가 존재한다 - ready상태에서 swap-out를 먹으면 ready / suspend로 가고 blocked 상태에서 swap-out를 먹으면 blocked / suspend로 간다
  • 그리고 얘네들이 다시 swap-in을 먹으면. 원래의 상태로 돌아오게 된다 - 무조건 ready로 올라오는게 아니다!!
  • 당연하게도 일단 메모리에 있어야(ready 혹은 blocked여야) running 상태로 갈 수 있다 - suspend에서 바로 running으로 가지는 못한다
  • 하지만 running에서 suspend를 먹어서 내려갈 수는 있다
  • 또한 지금 메모리가 부족한 경우에는 프로세스가 만들어지자마자 ready / suspend로 갈 수도 있다

Figure 3.11

%E1%84%8B%E1%85%B5%E1%84%85%E1%85%A9%E1%86%AB02%20-%20%E1%84%91%E1%85%B3%E1%84%85%E1%85%A9%E1%84%89%E1%85%A6%E1%84%89%E1%85%B3%20abcc0abd8daa4524bf7af87f2e874e9c/image4.png

  • processes에 process table의 시작주소가 들어있고 그 테이블에 process들의 주소들이 들어있다
  • 여기서 process table이 PCB table이다 - 실제로는 프로세스를 구성하는 PCB, 데이터 등등이 어느 한곳에 같이 모여있는게 아니다 이말이야 - 그림에서의 process image에는 PCB는 안들어있고 그 나머지인 text, data, heap등이 저장되어 있는 구조이다
  • process table은 pointer를 이용해서 가변길이로 할 수도 있지만 중간에 포인터를 잃어버리면 나가리기 때문에 불변길이로 선언하는 경우가 많다 - 다만 n이 시스템에 존재할 수 있는 process들의 총 갯수이기 때문에 n을 적당한 크기로 정하는 것이 중요하다 - n이 너무 크면 메모리를 너무 많이 먹고 n이 너무 작으면 process가 생성되기 힘들다 - 이 table의 일부가 비어있어야 process가 생성될 수 있기 때문
  • 뭐 나머지 memory table, io table 등등도 다 비슷하다
  • 그리고 이 table들의 주소를 담고 있는 structure가 존재하는 형태이다
  • process table은 메모리에 상주하게 되는데 보통 이 n값이 굉장히 크기 때문에 메모리의 많은 부분을 차지하게 된다 - 그래서 PCB의 중요한 부분만 남기고 나머지 PCB중 덜 중요한 정보들은 하드디스크로 넘기게 되는데 이 부분이 u-area이다 - 다만 running 상태가 되면 이 u-area는 메모리로 다시 올라오게 된다 - state에 따라 어디 있을지 결정되는 것

Process creation 과정

  • creation : Id 만들기 → process를 위한 공간 할당 → PCB 생성 → 필요한 포인터들 연결 → 다른 자료구조들 생성
  • terminate : 이것의 역순이다

Ready, Blocked Queue의 구현

%E1%84%8B%E1%85%B5%E1%84%85%E1%85%A9%E1%86%AB02%20-%20%E1%84%91%E1%85%B3%E1%84%85%E1%85%A9%E1%84%89%E1%85%A6%E1%84%89%E1%85%B3%20abcc0abd8daa4524bf7af87f2e874e9c/image5.png

  • 보면 큐라고 해서 이 프로세스들이 물리적으로 막 움직이는게 아니다 - 이렇게 linked-list형태로 구성되게 된다
  • 루트 노드에는 첫번째 프로세스의 PCB.state의 주소가 담기고 이 PCB.state에는 다음 프로세스의 PCB.state의 주소가 담기는 식으로 다음 프로세스를 계속 가리키는 식으로 큐가 구현되어있더라 이말이야

Interrupt vs trap

  • Interrupt : 프로세스의 외부에서 이벤트가 발생해서 멈추는 것
    1. interrupt가 발생하면 실행 모드가 user → kernel mode로 바뀐다
    2. user의 process가 멈추고 kernel의 interrupt handler가 실행되게 된다 - 다음 실행할 instruction의 주소가 interrupt handler의 첫주소로 설정되는 것
    3. interrupt가 끝나면 다시 원래의 instruction 주소로 돌아오며 실행모드도 user mode 로 바뀌게 된다
  • Trap : 프로세스의 내부에서 이벤트가 일어나 멈추는 것

Context switching이 일어나기까지의 과정

  1. 중단된 시점의 레지스터 값을 전부 진행중인 프로세스의 메모리로 옮긴다(stop & save)
  2. state를 바꾼다(running → ready나 다른 상태들)
  3. queue에 추가한다
  4. 다음 process를 선택한다(select)
  5. 그 process의 state를 변경한다(ready → running)
  6. process의 메모리에서 레지스터 값들을 다 가져오는 등 새 프로세스의 중단지점으로 다 restore한다(restore)
  7. 바뀐 process를 진행한다