OS 프로세스와 쓰레드

 

프로세스

dispatcher 디스패처
  • 스케줄러(scheduler)라는 함수를 의미하는 용어
  • cpu가 실행할 작업을 다른 프로세스로 변경하는 커널 함수

처리 도중 타임슬라이스가 끝나거나, 다른 이유로 context switch가 일어나야할 때, 디스패처를 호출한다.

  system call Interrupt
처리 도중 context switch O X (반드시 단기간에 완료해야 함)

context switch가 일어나는 위치

  • 시스템 콜 실행 중
  • 시스템 콜 완료 후
  • 인터럽트 완료 후

인터럽트 처리 중에는 절대 context switch가 일어나지 않는다 !!

context switch는 반드시 커널 모드 수행 시에 이루어지므로 프로세스는 반드시 커널 안에서 죽고 커널 안에서 살아난다.

swap out (서스펜디드 상태로 만드는 것)을 하는 경우
  • 스와핑(swapping) : 메모리에 공간 부족
  • block 프로세스를 unblock될 때까지 보조기억장치에서 기다리도록
  • 사용자가 디버깅 등의 목적으로 명시적으로 서스펜디드 상태로 보내도록 요청
  • 주기적으로 가끔 실행되는 프로세스인 경우, 실행되지 않는 기간 동안
  • 부모 프로세스가 요청
PCB (Process Contorol Block) 프로세스 컨트롤 블록
  • 각 프로세스마다 하나씩 존재.
  • 리눅스의 경우 ‘프로세스 디스크립터’라고 부름 : 계층적
  • 커널에 존재하는 모든 프로세스들은 Double LinkedList로 연결되어 프로세스 리스트를 형성함.
  • 프로세스 리스트에 들은 프로세스들 중에 레디 상태인 프로세스들은 따로 Double LinkedList로 연결하여 런큐(run_queue)를 구성하다가 2.6버전 이후 우선순위 값마다 별도의 런큐를 구성하여 보다 빨리 스케줄링할 수 있게 됨.
  • 구성
    • 프로세스 식별정보
      • pid, ppid, user id, pgid
    • 상태 정보

프로세스 컨텍스트 - 1

프로세스 컨텍스트는 크게 세 부분으로 구성된다.

  • 프로세스의 정보를 유지하기 위해 커널이 할당한 자료구조들 : PCB, Segment Table, Page Table, File Diskrupter Table, File Table 등
  • 프로세스의 수행 이미지 : 코드 + 데이터 + 스택 + 힙.
    • 메모리와 디스크에 나눠서 존재한다.
  • 하드웨어 레지스터 컨텍스트를 저장하는 메인 메모리 자료구조.
    • context switch할 때에 프로세스가 중단되었던 당시의 cpu 레지스터 값들을 저장한다.

컨텍스트 세이브란, 현재 프로세스를 실행하는 과정에서 생겨난 프로세서 레지스터들의 값(eip, eflags, eax, …)을 PCB에 저장하는 일을 말한다.

컨텍스트 리스토어란, 프로세스의 수행 정보를 프로세서 레지스터에 복원하는 과정이다.

context switch를 하는 과정 중에 컨텍스트 세이브와 컨텍스트 리스토어가 일어나야 한다.

프로세스 컨텍스트 - 2

프로세스 컨텍스트 = 유저 컨텍스트(user context) + 시스템 컨텍스트(system context)

  • 유저 컨텍스트 : 유저 스택 + 전역 변수 + 함수 호출 시 필요한 임시 정보
  • 시스템 컨텍스트 : 커널 스택 + PCB

유저 스택과 커널 스택에 저장되는 정보와 용도는 동일하다.

  • arguments + 지역 변수 + 호출 당시의 프로세서 레지스터 값

fork() 시스템 콜을 호출하면, 해당 프로세스와 동일한 유저 컨텍스트를 갖는 새로운 프로세스를 생성한다. - 공유하지 않는다.

a.out

실행가능 파일인 a.out을 실행시키면, a.out의 구성 요소인 텍스트, 데이터, 스택 영역 정보를 복사해서 프로세스가 만들어 진다. 따라서, 프로세스도 텍스트, 데이터, 스택 영역으로 이루어지며 이들을 프로세스의 유저 컨텍스트라고 한다.

각 프로세스는 버추얼 어드레스 스페이스(virtual address space)라고 불리는 가상의 4GB 주소공간이 보조기억장치에 만들어진다.

버추얼 어드레스 스페이스(virtual address space)

보조기억장치에 실제 4GB를 할당하는 것이 아니라, 데이터나 스택 과같은 일부만 보조기억장치에 할당하고 다른 영역은 정보만 유지한다. 코드는 이미 있는 코드 부분을 공유하고, 커널은 RAM에 있는 것을 매핑함으로써 있는척한다. 또한, 각 영역의 시작과 끝 주소를 PCB에 저장하여 있는 척을 하는 것이다.

즉, 실제로 디스크에는 코드, 데이터, 스택 만이 있고 커널은 메모리에 있다.

여기서 0~3GB는 유저 컨텍스트, 나머지 3~4GB는 커널. 이 커널 부분 또한 프로세스 주소공간의 일부이므로 사용자 프로세스가 접근할 수 있다. 해당 커널 부분을 실행하는 동안은 커널 모드 또는 시스템 모드에서 실행한다고 부른다. 커널 모드로 변경하는 것을 모드 체인지라고 부르는데, 커널 역할을 하는 별도의 프로세스가 있는 것이 아니라 사용자 프로세스가 필요시 커널로 변신해 운영체제를 실행하기 때문에 커널이 프로세스 공간의 일부로 간주하는 것이다. 커널 자체는 메모리에 하나 밖에 없고, 이 커널 하나가 각 프로세스의 3G~4G 영역에 매핑되는 것이다.

프로세스 A 가 User 모드에서 작동하다가 시스템콜이나 인터럽트에 의해 커널모드로 진입시에는 유저모드에서의 레지스터 컨텍스트가 프로세스 A 의 커널스택에 저장되고, 커널스택에 저장되어있던 프로세스 A 의 커널모드 레지스터 컨텍스트가 복원된다.

프로세스 A 가 Context Switching 을 할때는 이미 프로세스 A 가 반드시 커널모드인 상태이다. 이 경우는 프로세스 A 의 커널모드 레지스터 컨텍스트가 메모리에 저장되면서 다음번 스케줄될 프로세스 B 의 레지스터 컨텍스트(커널모드의) 가 복원된다

유저 프로세스가 돌다가 갑자기 한번에 스케줄이되서 프로세스 B 로 넘어가는것 같지만 정확한 순서는

  1. 유저프로세스 A 가 커널모드로 진입하면서 유저모드 레지스터 컨텍스트를 A 의 커널스택에 저장
  2. 프로세스 A 는 커널컨텍스트를 가진상태로 Schedule 을 호출
  3. A 의 커널컨텍스트가 메모리에 저장되고 프로세스 B 의 저장된 커널컨텍스트를 레지스터에 복원
  4. B 의 커널컨텍스트가 메모리에 저장되면서 B 의 유저컨텍스트를 레지스터에 복원하면서 유저모드로 리턴

프로세스를 생성하는데 할 일 (1 ~ 5 : PCB)

  1. 프로세스에 대한 정보를 담는 자료구조 PCB 만들기.

    1. pid + 부모 프로세스의 PCB 값들
  2. 지금 만들어지는 PCB를 기존 프로세스들의 PCB와 연결. (프로세스 리스트)

    1. 이 (자식) 프로세스의 PCB와 부모 프로세스 PCB를 연결한다. (주의! 유저 컨텍스트 아님)
    2. 형제 프로세스가 있다면 이들도 같이 연결한다.
    • pstree로 init 프로세스(1번) 밑의 프로세스들을 확인할 수 있다.
  3. 유저 컨텍스트 생성.

    1. 부모 프로세스의 데이터와 스택 영역은 복사. (코드는 공유)
  4. 구성된 프로세스를 레디 큐에 넣는다. (레디 큐에 들어간 PCB와 자식 프로세스의 PCB를 연결)

  5. 부모 프로세스에게 fork()의 실행 결과로서 자식 프로세스의 프로세스 아이디 값을 리턴해준다.

COW(Copy On Write) 자식 프로세스용 데이터 영역과 스택 영역을 복사하는 일을 최대한 뒤로 미룬다. 데이터, 스택까지 공유하다가 값을 바꾸게될 때, 복사하여 쓴다.

프로세스를 끝내는데 할 일 (exit 호출)

  1. 모드 체인지를 해서 커널로 들어간다
  2. 컴퓨팅 자원을 PCB를 제외하고 전부 회수한다.
  3. 부모 프로세스에게 death-of-child 시그널을 보낸다
  4. PCB의 상태를 기록하는 필드에 종료 상태를 기록한다.
  5. 새로운 프로세스를 위한 스케줄링
  6. 부모 프로세스가 wait() 시스템 콜을 부르면 없어진다. (좀비는 init이 없애준다)

프로세스간 통신 IPC

메세지 패싱 (message passing)

메세지를 보내거나 받을 때 모드 체인지를 해야하기 때문에 시간이 오래 걸린다.

  1. OS에게 요청해 메시지 큐를 만든다.
  2. 전달받을 프로세스를 fork함으로써 프로세스를 만든다.
  3. 메세지 큐의 식별자 정보를 물려받는다.
쉐어드 메모리 (shared memory)
  1. OS에게 요청해 쉐어드 메모리를 만든다. : A와 B 모두 사용가능한 데이터 영역으로 가상주소공간에 추가된다.
  2. 이후 메세지 패싱과 같음.
  메세지 패싱 쉐어드 메모리
장점 편리 빠르다 (각 프로세스의 가상주소공간 유저 영역에 있음)
단점 오래 걸린다 (커널에 있어 모드체인지 많이 필요) 프로세스간 동기화(시그널, 세마포어, 락)가 필요하여 프로그래밍하기 어렵다 (충돌 방지)
시그널 (동기화 방법 중 1)

이벤트가 발생했을 때 프로세스에게 알리는 방법.

I/O 인터럽트는 HW장치에서 이벤트가 발생하면 OS에게 알리는 것

인터럽트나 트랩은 커널(OS)에게 알리는데, 시그널은 일반 프로세스에게도 알릴 수 있다. 물론 커널을 통해서.

쓰레드

쓰레드는 프로세스 내부 실행단위이다.

쓰레드가 실행되는데 직접적으로 필요한 것은 스택CPU 레지스터 값들을 저장하기 위한 메모리 공간이다.

멀티쓰레드
  • code, data, files(실제 파일과 각종 컴퓨터자원을 통칭한 것)는 공유하고, 레지스터와 스택은 각 쓰레드마다 가진다.
  • 프로세스가 실행될 때, 요청을 하지 않아도 OS는 파일 3개를 할당해준다.
    • STDIN, STDOUT, STDERR

3 개의 프로세스로 구현할 경우 IPC를 해야하므로 오래 걸리고, 자원도 많이 낭비한다. (code, data, files 중복)

쓰레드를 사용할 때의 장점
  1. 프로세스를 만들 때보다 시간이 적게 걸린다.
  2. context switch가 빠르다. 쓰레드간 전환할 때에는 유저 컨텍스트가 변하지 않기 때문. (공유)
  3. 쓰레드간 통신할 시 IPC를 이용하지 않기 때문에 더 빠르다. 데이터 영역(bss)의 전역 변수 이용

커널레벨 쓰레드와 유저레벨 쓰레드

쓰레드가 생성될 때 레지스터 값이 저장되는 공간과 스택을 만들어야 한다.

유저 레벨 쓰레드

  • 유저 도메인에서 돌아가는 쓰레드 라이브러리가 쓰레드 생성 및 관리
  • 생성, 관리 실행 속도가 커널 레벨 쓰레드보다 빠르다
  • 그러나, 유저 레벨 쓰레드가 여러개 만들어 진다고 해도, 커널 입장에선 그냥 프로세스 하나일 뿐

커널 레벨 쓰레드

  • 운영체제(커널)이 쓰레드 생성 및 관리. (clone() in linux)
  • 커널 내부에 쓰레드를 생성하여 cpu 스케줄링 대상이므로, 동시수행성, 즉 컨커런트(concurrent) 실행성이 더 좋다
  유저 레벨 쓰레드 커널 레벨 쓰레드
장점 커널 레벨 쓰레드 기능을 제공하지 않는 OS에서도 사용 가능
빠르다
사용자가 스케줄링을 제어 가능 - 유저모드 이므로
동시수행성 GOOD
단점 동시수행성이 크게 제약됨 - 커널 입장에선 그냥 프로세스 하나일 뿐 느리다

마이크로 커널

모노리딕 커널
  • 커널의 모든 주요 기능이 커널 모드에서 실행되는 구조
  • OS 커널의 기능이 한 덩어리로 뭉쳐져 사용자 프로세스 안에 포함된다.
마이크로 커널
  • I/O 관리, 파일 관리 등과 같은 전통적인 OS 기능이 커널 외부의 사용자 프로그램 형태로 실행된다.
    • 가상 메모리, File System이나 Protocol, 디바이스 관리기능 등은 커널 외부에 있어도 무방
  • 장점
    • 커널의 크기가 작아진다. - OS가 차지하는 메모리 공간을 줄일 수 있다.
    • 확장성이 좋다. - HW 장치에 따라 필요한 프로세스만 실행시킬 수 있다.
  • 단점
    • 모노리딕 커널에 비해 OS 실행속도가 늦다. - 모드체인지가 많고, IPC도 많다

🔥모드 체인지 vs 컨텍스트 스위치

출처 : https://daehee87.tistory.com/473

Context 스위칭과 kernel-user 모드체인지는 다르다. 두가지 경우 모두 현재 레지스터 컨텍스트를 메모리에 저장함과동시에 기존에 저장해놨던 레지스터 컨텍스트를 메모리로부터 복원한다는 점이 동일해서 햇갈리기가 쉬운데, 둘을 잘 구분할 필요가 있다.

Context 스위치는 프로세스간 실행. 모드 체인지는 하나의 프로세스 안에서 쓰레드간 실행.

프로세스 A 가 User 모드에서 작동하다가 시스템콜이나 인터럽트에 의해 커널모드로 진입시에는 유저모드에서의 레지스터 컨텍스트가 프로세스 A 의 커널스택에 저장되고, 커널스택에 저장되어있던 프로세스 A 의 커널모드 레지스터 컨텍스트가 복원된다.

프로세스 A 가 Context Switching 을 할때는 이미 프로세스 A 가 반드시 커널모드인 상태이다. 이 경우는 프로세스 A 의 커널모드 레지스터 컨텍스트가 메모리에 저장되면서 다음번 스케줄될 프로세스 B 의 레지스터 컨텍스트(커널모드의) 가 복원된다.

즉 유저 프로세스가 돌다가 갑자기 한번에 스케줄이되서 프로세스 B 로 넘어가는것 같지만 정확한 순서는

  1. 유저프로세스 A 가 커널모드로 진입하면서 유저모드 레지스터 컨텍스트를 A 의 커널스택에 저장

  2. 프로세스 A 는 커널컨텍스트를 가진상태로 Schedule 을 호출

  3. A 의 커널컨텍스트가 메모리에 저장되고 프로세스 B 의 저장된 커널컨텍스트를 레지스터에 복원

  4. B 의 커널컨텍스트가 메모리에 저장되면서 B 의 유저컨텍스트를 레지스터에 복원하면서 유저모드로 리턴