# Process

:  OS위에서 프로그램을 실행시키는 기본 주체, 런타임 시스템의 수행 주체, 자원할당의 주체. 및 단위

: program in execution

:  An execution stream in the context of a particular process state


<process 구성>

- execution stream : 프로세스가 지금까지 수행한 모든 명령어들의 순서 

- process state : process의 수행에 필요한 정보. Context

1. Memory context : code, data, stack, heap

2. Hardware context : Register values(cpu registers, I/O registers, ...)

3. System context : Per-Process kernel information


- abstraction

- decomposition 복잡한 문제를  단순한 여러 개의 문제로 나누어 처리하는 방법론


- Multi-programming vs Multi-processing

Multi-programming : 메인메모리의 관점

Multi-processing : cpu의 관점


- swapping


<두가지 관점에서의 Process>

- Run-time entity vs Design-time entity

Run-time entity : 자원 할당의 주체

Design-time entity : 소프트웨어 개발에서 설계 단계에서 디자인 한 "set of tasks"들의 단위로 구현단계에서 구현하게 되면 프로세스단위의 프로그램이 만들어 짐



Process의 구현

* Program = Data structure + Algorithm


Data structure

1. System context

2. Hardware Context

3. Memory Context


- Process Control Block : 프로세스의 정보. process id, process scheduling, ...

- Process Table : 여러 프로세스를 관리하기 위해 array 형태로 PCB를 저장하는 단위

-> easy to many-play, but OS가 지원할 수 있는 프로세스의 수가 한정적임 => linked list로 구현

- Process State Transition : 어떤 프로세스의 어떤 상황을 설정하는 것

-> process의 life-cycle : new - ready - running - waiting - Terminated

* ready queue : ready 상태의 프로세스들을 관리하기 위해 queue 형태로 만든 자료구조. 일반적으로 PCB를 linked list로 구현

* waiting 상태의 프로세스는 waiting reason에 따라 각각 다른 queue로 관리되어 짐

 


# Process Scheduling

: 각 프로세스들이 공평하게 CPU를 공유할 수 있도록 다음에 수행해야 할 프로세스를 선택하는 작업(Fair, Share)


- Process Scheduler의 2단계 구성

1. policy : 다음에 수행될 프로세스를 선택하는 기준. scheduling policy

2. mechanism : CPU를 한 프로세스에서 다른 프로세스로 넘겨주는 방식. dispatcher mechanism


dispatcher : scheduler policy와는 무관하게 하드웨어 매커니즘으로 동작

program은 수동적, process는 능동적, OS는 능동적인 존재이지만 passive함,

OS 내부의 dispatcher는 굉장히 능동적인 존재임. 이렇게 생각하면 프로세서는 최소 2개가 필요. (dispacher가 도는 프로세서와 user program이 도는 프로세스)
하지만 싱글 프로세서 환경에서도 잘 동작함. 이유는????

=> CPU controller이 dispatcher(OS)와 user processor 사이에 컨르롤을 넘기면서 동작해야함. (인터럽트 매커니즘)

(Kernel mode <-> User mode,  mode changing, context switching)


# Context Switching

: 현재 프로세스의  state를 저장하고, 다음 수행 될 프로세스의  state를 불러오는 작업


* process의 state

1. process가 기억하고있는 모든 정보. Context를 표현할 때 (Memory Context, Hardware Context, System(kernel) Context)

2. Process의 State Transition 상태


- Context Saving. 저장해야 할 정보들

: 대피값들은 stack에 저장


1. CPU register들(다음 프로세스가 사용해야하니까), 

2. Memory 내용

- 전혀 저장하지 않는 경우 : 멀티프로그램드 배치 모니터 시절에는 여러잡을 한번에 배치로만든 후 메인메모리에 올렸으므로 CPU를 교체한다는 건 메모리자체를
 옮겨가는 것이므로 값들이 없어지지 않음

- 전부 저장해야하는 경우 : uni programming시에는 disk같은 장치에 메모리의 값들을 전부 저장. (rollin-rollout swapping)

- 부분적 저정하는 경우 : memory over-commit(degree of memory), swapping, memory hierarchy


* OS가 관리하는 Kernel data structure - 링크드리스트의 형태로 프로세스마다의 구조로 저장되므로 따로 저장하지 않아도 됨 


- Mechanism


1. 현재 인스트럭션의 수행 중간에 인터럽트 발생

2. 현재 인스트럭션의 수행을 끝낸 후 스택에 현재 PSW값과 주소(PC)를 저장 (->하드웨어의 support)

3. Process Status Word의 mode bit을 0으로 변경하여 kernel 모드로 변경

4. 마이크로 프로세서는 IRQ Number를 확인 한 후, 해당 number에 맞는 값을 인터럽트 벡터 테이블을 뒤진 후, 해당 인터럽트 서비스 루틴의 시작주소를 확인하여 점프

5. 인터럽트 서비스 루틴이 수행. 

: 인터럽트 서비스 루틴이 수행되면 초반에 CPU Register 값들이 우선적으로 stack에 저장된 후 인터럽트의 메인 로직이 수행됨

6. 인터럽트 서비스 루틴이 종료된 후 이전의 인스트럭션이 끝난 후의 주소로 돌아가야 함


- Stack Pointer register 

- OSPCBCur : OS 안에 존재하는 글로벌 변수 중 하나

OS : OS 변수

PCB : Process Control Block

Cur : Current, 현재 수행 중

=> 현재 수행중인 프로세스의 정보를 가지고 있음


- PCB : process identifier(id), Scheduling 관련 정보, Context Switching에 필요한 정보(Stack Pointer filed..)


- Interrupt

1. Stack의 top에 PSW, Return Address가 저장

2. 해당 인터러븥 서비스 루틴으로 점프

3. CPU register값들을 stack에 저장( intel : PUSHA, push all ) 후 인터럽트 루틴을 수행 (Context Saving 완료)

4. scheduler호출로 scheduler는 다음에 돌아야 할 프로세스의 PCB의 Pointer를 얻어와 OSPCBCur에 저장


- Stack Pointer Register value

: PCB의 SP 필드에 Stack Pointer값을 따로 저장해야 하는 이유는 stack에 값을 저장하면서 stack의 SP값은 계속 변하므로 돌아갈 SP의 값(저장해야 할 값)이 제대로 저장되지 않게 되므로 별도로 PCB의 SP 필드에 저장시킨다.


- Stack의 pop을 통해 저장한 값들을 cpu register로 들어옴

- 바뀐 stack의 값으로 새로 들어온 프로세스로 return


* fake stack

: 처음 프로세스가 수행될 때는 해당 매카니즘을 수행하기 위해 마치 인터럽트를 당한 것 처럼 PSW와 Return Address값에는 시작주소의 entry pointer값을 넣고 register의 값들은 0같은 값으로 셋팅하여 수행하게 됨



# Abstraction & Decomposition


OS의 Complexity 문제의 해결

- Abstraction : Layered Architecture

-----------------------------------

Software

Middleware

OS

(HAL, Hareware Abstration Layer)

H/W

-----------------------------------


*API, Application Programming Interface : 계층과 계층 사이의 통신을 위한 규격

- Decomposition


- Layer Principle : 위의 계층은 아레 계층이 제공하는 기능만을 사용할 수 있으며 반대의 경우는 발생하지 않아야 함(아래계층은 위계층에게 인터페이스 제공)

-> 레이어계층에서 윗계층에 종속적이라면 종속적인 것 외에는 받아들일 수 없어, 여러 상위 프로그램을 수행 할 수 없으므로


- Preemptive Scheduling : via HW interrupt. 어떤 프로세스가 하드웨어 인터럽트에 의해 CPU를 빼앗기게 되는 형태의 스케줄링. 

- Non-preemptive Scheduling : via SW interrupt.



# Process Creation&Termination


- creation : data structure allocation, (1970년대 ~ 현재)


<일반적인 프로세스 생성 개념>

1. file system에 대상 file의 path가 OS에게 전달

2. executable file의 code를 Memory Context의 code segment에 읽어들임

3. executable file의  global 변수의 값으로 data segment의 각 영역을 잡아 줌

(2, 3번 => 프로그램 "load"과정)

4. initially stack segment, initially heap segment

5. 실행될 프로세스의 PCB을 malloc으로 생성 후 값을 채움

6. 해당 PCB를 ready queue에 push


<unix에서의 프로세스 생성>

1. 첫번째 실행되는 프로세스만 일반적 과정으로 생성 (Process Zero만 이 과정을 수행)

2. 이후부터는 프로세스 생성을 복제의 과정을 통하여 생성


* Parent Process : Process Cloning을 초래하는 기존의 Process

- fork() 시스템 콜을 호출

- OS는 Parent Process를 일시정지 하여 Process ID를 제외한 모든 데이터를 복제하여 Child Process를 생성 

- Child Process의 PCB를 Ready Queue로 보내어 Run시킴


=> 클로닝을 통한 프로세스 생성에서의 문제점 : fork()만으로는 맨 처음 process외의 다른 Process는 수행할 수 없으므로 exec()이라는 another 시스템 콜을 호출


-  exec() : 생성되어 수행할 executable file의 path정보를 매개변수로 받은 후 실행을 수행


=> Parent Process : fork() -> wait(자신이 생성한 자식프로세스의 ID) 

Child Process : fork()로 copy가 된후 exec() -> exit() 


-> exit code를 검사하여 parent process에서 확인하여 wait()에서 깨어나게 됨. 

* Zombi Status : 모든 수행을 마친 후 Parent Process가 자신의 exit code를 읽어가를 기다리는, exit status만을 가지고 있는 프로세스의 상태


# Multi-threading


- Server Architecture : Server <-> Message Queue/Request Queue


- Server Architecture 구현의 2가지 방식

- iterative server : 서버가 message queue에서 request를 가져와 스스로 처리하는 수행과정을 반복

- concurrent server : message queue에서 request를 가져오면 서버가 직접처리하지 않고 worker process를 fork하여 생성된 그 자식 프로세스가 해당 request를 처리하도록 하고, 서버 프로세스는 다시 message queue에서 다른 request를 가져와 또 다른 worker process를 만들어 처리하도록 하는 병렬적인 방식


- Concurrent Server

단점 : request를 받아올 때마다 새로운 프로세스를 fork하는 작업을 수행하므로 오버헤드가 큼

=> Concurrency를 높여 이런 단점을 극복하고, Execution Unit을 생성하거나 수행시키는데 부담을 줄이는 방안으로 Multithreading이 나옴


- 새로운 모델의 필요로하는 시대적 배경

: 1980년대 중반인 인터넷이 대중화되기 직전(인터넷의 대중화 1990년대)에서 여러 서버들의 개념 등장

: 과학 연산의 필요. 병렬적 처리 수요의 증가, 병렬 프로세스 수행이 필요


=> Multithreading이 필요한 이유

1. 적은 비용으로 Concurrency를 얻기위해(response의 agility를 높이기 위해)

2. Massively Parallel Scientific Programming을 할 때 발생하는 오버헤드를 줄이기 위해

 

- 장점

: 프로세스보다 적은 오버헤드로 작업을 수행할 수 있음

: 적은 시간, 적은 메모리 사용, 스레드컨텍스트 스위칭 등은 프로세스보다 효율이 좋음

: 반응시간이 더 빠름


- Thread Architecture

: 서버를 구현하기 위한 하나의 프로세스 - dispatcher thread - worker threads


- Traditional Process Model

: Process = Thread of Control(Execution Stream) + Context(Process에게 부여된 Resources)

: 프로세스에서 Thread of Control을 여러개를 두어 Thread(혹은 light weight process)라고 부름

: 하나의 자원을 여러 Thread가 공용으로 사용


- 하나의 프로세스는 여러개의 Thread를 가짐

- Thread는 Stack으로 구현. 이유? Thread는 Execution stream 이므로 function들이 sequential 하므로 Thread는 Stack으로 구현

- Multi Threading를 수행하면, 하나의 프로세스가 여러개의 스택을 가지게 됨

- 해당 각 스레드도 id와 해당 정보의 저장이 필요하게 됨 => Thread Control Block (TCB)



# Task : decomposition을 통해 설계하여 나온 독립적으로 수행가능한 Entity (Design time Process)

* run time process : 자원을 할당하고 수행시키는 주체들


- Mach OS에서의 task의 의미 : Proc = Task(프로세스에 부여된 리소스들) + Thread 

- 리눅스에서는 User level에서는 Process와 Thread로 구분, Kernel에서는 내부적으로 process와 Thread를 혼용하여 task라고 부름


# Multithreading 구현

1. User-level thread implementation : Kernel이 모르게 구현

- stack 구현

- TCB을 user-level에 각 Thread 마다 구현

- thread간의 switching, scheduling을 하기 위해 user-level에 scheduler를 함수들을 library로 구현


<user-level thread의 문제> 

- preemtive scheduling을 수행할 수 없음(이유는, 운영체제가 스레드에게 직접 인터럽트를 전달할 수 없기때문에)
    * non-preemptive scheduling은 가능

- 한 thread가 수행하다가 blocking system call을 호출하여 blocking system interrupt가 발생하면  커널이 해당 thread 뿐 아니라 다른 모든 thread가 blocking됨. (Blocking Anomaly)


<장점>

멀티스레딩이 처음 나왔을때, OS를 수정하지 않고도 멀티 스레딩을 적용할 수 있었음

: 단순 연산을 수행하는 작업을 할 때는 외부로부터 인터럽트를 받을 필요가 없기 때문에 user-level thread의 적용이 무방함

=> 구현이 쉽고, OS 코드를 안거쳐도 되고, 병렬 연산에 잘 매핑되어 사용이 되었음


2. Kernel-level thread implementation : Kernel이 100% 알게 구현

: task가 생성되고 소멸되는 것을 kernel이 알아서 해줌

thread creation, termination이 kernel system call로 구현하므로 인터럽트 포워딩도 가능.


<장점>

: user-level thread의 단점을 해소


<단점>

: kernel 단의 수행 시간이 증가하면서 추가적인 오버헤드가 발생(시스템 콜 시 발생하는 시스템 콜 핸들러 호출, 커널함수가 dispatcher, 커널함수 수행...)


3. Combined User-level/Kernel-level thread implementation 

: 상당 부분은 user-level thread에서 처리하고, preemptive scheduling이 가능하도록 user-level에 기법을 추가


- 인터럽트가 발생하면 인터럽트를 user-level thread에게 forward해서 어떤 thread가 깨어나야 하는지 알려주는 기법 추가

- 어떤 프로세스가 blocking system call을 호출하면 현 수행중인 스레드의 수행은 중단시키고, 새로운 커널스택을 할당 후 user-level process에 붙여 다시 리턴.

user-level의 스레드 스케줄러가 다른 스레드를 골라서 수행. 



# PThread Programming Model


- Multithreading API : POSIX pthread API 


* POSIX(Portable Operating System Interface) : 다양한 유닉스 계열의 운영체제들의 API를 표준화 하기 위해 IEEE가 정의한 인터페이스


1. pthread_create() : 스레드의 코드. 즉 함수의 function pointer를 매개변수로 사용

2. pthread_exit() : calling thread를 terminate

3. pthread_join() : 매개변수로 온 스레드가 terminate할 때까지 wait. (main thread)

4. pthread_yield() : CPU를 포기하고 싶을 때. (thread의 status가 stop)


- main thread

- thread life cycle


- 해당 API가 user-level thread에서는 library로 구현되고 kernel-level thread에서는 system call로 구현


'System > Etc.' 카테고리의 다른 글

[OS 기초] Process Synchronization  (0) 2015.09.25
[OS기초] CPU Scheduling  (0) 2015.09.22
[OS기초] 개요  (0) 2015.09.17
KVM/KSM  (0) 2015.08.13
RAID  (0) 2015.08.12

+ Recent posts