Post

[OS] Processes and Threads

프로세스와 스레드에 대해서

[OS] Processes and Threads

✏️ Process

프로세스란 실행 중인 프로그램이다.


■ MultiProgramming

MultiProgramming이란, 하나의 CPU에서 여러개의 프로그램을 실행하는 기법이다.

⚠️ 사실은 동시에 실행하는 것이 아니다. CPU가 매우 빠른 속도로 돌아가면서 프로그램을 실행하는 것이다.

  • 병렬 VS 병행 병렬 - 진짜 여러개가 동시에 실행 병행 - 논리적으로 동시에 실행

MultiProgramming이 어떻게 가능한가? ➜ Dipatcher 사용

  • Dispatcher는 현재 실행 중인 프로세스의 컨텍스트를 저장하고, 다음 프로세스의 컨텍스트를 로드하여 실행한다.
    프로세스 A,B,C는 각각 자원을 따로따로 받는다.
    1. Process A 실행 PC: 5000번대
    2. 5005번 라인 실행 중 타이머 인터럽트 왔음 5005번 라인 끝내고 5006을 스택에 push 한 뒤 인터럽트 서비스 루틴으로 점프
    3. 서비스 루틴은 100번지 (Dispatcher) A 프로세스의 내용을 보관해야됨 (Register 값들을 스택에 푸쉬) 100번지의 타이머 서비스 루틴 실행 한 뒤에 8000번대로 점프
    4. 원래는 5000번대로 돌아가야 하지만, Dispatcher의 스케쥴링으로 인해서 B로 감 Process B 실행. ProgramCounter는 8000번대
  • 실제로는 I/O 대기 시간이 대부분을 차지한다.
  • 대부분의 프로그램은 사용 시간 대비 CPU 활용도가 5%도 안된다.
  • 나머지는 I/O 대기 시간에 사용한다.

■ Process Lifecycle

모든 프로세스는 init 프로세스의 자식이다.

프로세스의 종료는 시스템 호출로 운영체제에게 부탁 해야한다. (exit 시스템 호출)

► 프로세스의 종료도 운영체제가 담당한다.


자발적 종료 비자발적 종료
normal exit(0번) fatal error
Error exit Killed by another process


  • normal exit: 정상 종료
  • Error exit: 잘못된 입력 등

  • fatal error: CPU가 실행할 수 없는 명령을 만나면 스스로 인터럽트를 걸어서 운영체제로 점프함 ex) Divide by 0, Null pointer Exception
  • Killed by another process: 다른 프로세스가 kill signal을 보내서 죽임


■ Process State

  • Running: 프로세스 실행 상태 종료하면 Exit 상태가 된다. (Running과 Exit 사이를 Zombie 상태라 부른다)

  • Blocked: 실행 중에 입출력 명령을 호출해서 프로세스를 실행할 수 없는 상태 입출력이 끝나면 Ready 상태로 돌아감

  • Ready: Blocked 상태에서 입출력이 종료되거나, Running 상태에서 스케줄링된 시간이 끝나면 Ready 상태가 된다.


추가로, Suspend라는 상태도 존재한다.

  • Suspend: 프로그램 실행을 잠깐 멈춘 상태

  • Ready, Block 상태의 프로그램을 Suspend로 만들 수 있다.
    Blocked 상태에서 중지하면 Blocked/Suspend Running, Ready 상태에서 중지하면 Ready/Suspend
    Block Suspend ➜ Ready: Activate + I/O 끝 Ready Suspend ➜ Ready: Activate

  • 프로세스가 너무 많으면 OS가 프로세스를 Suspend로 만들어버릴 수 있다.


■ Scheduling

  • Scheduling은 Queue로 구현된다.
  • Scheduling을 담당하는 Scheduler는 OS가 갖고 있다.

Scheduling Example

  1. Ready Queue의 제일 첫번째 프로그램(A)을 dispatch해서 CPU(Processor)가 실행
  2. 시간이 지남
  3. A가 Ready Queue의 제일 끝으로 들어감
  4. 그 다음 프로그램을 dispatch …

    If, A 실행 중에 I/O가 발생한다면?

  5. 입출력 호출 시 해당되는 입출력 장치 Queue로 들어간다. (block 상태 - Ready Queue로 못감)
  6. Ready Queue에서 다음 프로그램(B)를 dispatch해서 실행
  7. B 실행 중에 A 입출력이 끝남
  8. 일단 B 를 끝냄
  9. 인터럽트가 걸림
  10. Event Queue에서 기다리고 있던 A를 Ready Queue 제일 뒤에 넣음

    If, 프로세스를 종료하고 싶다면?

    exit 시스템 호출 시 좀비 상태가 된 다음에 자원을 반납하고 종료된다.




✏️ Process의 구현

OS는 프로세스를 관리하기 위해 Process Table을 사용한다.

  • Process Table은 OS의 핵심적인 자료구조이다.
  • 프로세스 하나당 하나의 Process Controll Block을 차지한다.
  • 블록에 저장되는 것은, 【 Ready, Blocked ➜ Running 】 을 위해 필요한 정보이다.


Process Controll Block에 저장되는 것들

프로세스 관리 정보 메모리 관리 정보 파일 관리 정보
레지스터 값 메모리 - 텍스트 영역 정보 Root Dir
PC 값 메모리 - 데이터 영역 정보 Working Dir
Stack Pointer 값 메모리 - 스택 영역 정보 File Descriptors
Process State … UID, GUID
Signal   …
PID    
Process 시작 시간    
…    




✏️ Thread

스레드는 프로세스 내의 실행 단위이다. 모든 스레드가 종료될 때 프로세스가 종료된다.

  • 자원은 프로세스에게 주고, 스케쥴링은 Thread를 함
  • 스케쥴링의 단위는 Thread이다!!


■ Multi Thread

스레드를 여러개 만들 수 있다. 운영체제가 스레드를 병행 실행 해준다.

Multi Thread Example - Web Server

웹 서버를 멀티 스레드로 구현할 수 있다. dispatcher Thread + worker Threads

  1. dispatcher Thread가 요청을 받음
  2. worker Thread 지정, dispatcher Thread는 다시 대기 상태로
  3. worker가 캐시를 확인하고 응답을 보냄
  • 멀티 스레드는 자원을 공유한다.
  • 동기화가 필요하다.


If, 멀티 스레드가 지원이 안되면?

멀티 프로세스로 만들면 되긴 한다. dispatcher: fork() 해서 자식 프로세스 만들고 각 자식 프로세스가 exec 하면 된다.
But, 너무 오버헤드가 크다


■ Classic Thread Model

옛날 컴퓨터 시스템은 프로세스의 실행의 흐름이 하나였다. (Single Thread) ➜ main 함수를 실행하고 끝난다.

그러다 불편하니까 멀티 스레드를 만들게됨 스레드는 자원을 공유한다. But! 상태는 각자 다르다.

공유하는 자원 개인 자원
주소 공간 PC
전역 변수 Register
오픈된 파일 Stack
자식 프로세스 State
signal  


■ Posix Thread

POSIX Thread란 Poix 표준에서 정의된 스레드 라이브러리이다. POSIX 스레드는 유닉스 계열 운영 체제에서 표준 스레드 라이브러리로 사용된다.

Thread Call Description
Pthread_create 새로운 스레드를 만듦
Pthread_exit 스레드 종료
Pthread_join 특정 스레드가 종료될 때까지 기다림
Pthread_yield 다른 스레드를 위해 양보함 (프로세스는 양보하지 않음)
Pthread_attr_init 스레드의 속성 구조 만듦
Pthread_attr_destroy 스레드의 속성 구조 없앰




✏️ Thread 구현

~~ 먼 옛날 ~~ MultiThread 필요해 ► OS가 모두 Single Thread라 다 뜯어 고쳐야돼. 오래 걸려 ► 일단 User-Level Thread 패키지로 간이 Multi Thread를 구현하자


■ User-Level Thread

사용자 수준 스레드(User-Level Thread)

사용자 수준 스레드(User-Level Thread)는 운영 체제 커널의 개입 없이 사용자 공간에서 구현되는 스레드이다.

  • 멀티 스레드가 프로세스 내부의 Run-time system 을 사용해서 구현된다. Run-time System: POSIX Thread 같이 스레드를 관리해주는 프로시저의 모음 Run-time System은 Thread Table을 가지고 있다. ➜ 각 프로세스는 자신만의 스레드 테이블 가짐

  • 커널은 스레드 존재를 모르고, 프로세스만 스케줄링함.

  • 스레드가 유저 스페이스에서 관리됨


■ 하이브리드

User level thread package + multiThread

가격 비교 프로세스 여러개 만들기 > 스레드 여러개 만들기 커널이 스레드 여러개 만들기 > 스레드가 User level Threads 만들기

∴ 멀티 스레드가 각자 User level Threads 를 만들어서 사용함


■ Single Threaded Code to MultiThreaded

~~ C언어는 multithread를 생각하지 않고 만들었다. ~~ ► Multi Thread를 사용하려고 하니까 오류가 나네 ► 시스템 호출하고 나서 errno에 에러 정보를 세팅해줌, 근데 Thread가 여러개면 각 스레드가 errno를 적음

► 어떻게 하지? ► 글로벌 변수(errno)도 스레드마다 따로 가지게 하자.


~~ 리눅스는 멀티 스레드를 생각하지 않고 만들었다.~~ ► 우리도 지원해야 하는데… 어떻게 하지? ► Thread 포크해서 code data heap만 같이 쓰게 하자. stack만 따로 쓰게 하자. ► 간이 멀티 Thread 구현 완성




REFERENCE

📚 Modern Operating Systems, Third Edition - Andrew S. Tanenbaum

This post is licensed under CC BY 4.0 by the author.