본문 바로가기

CS/면접을 위한 CS 전공노트

[운영체제] 프로세스와 스레드/ 멀티프로세싱과 멀티스레딩

반응형

2023년 4월 22일 157p~172p

 

3.3 프로세스와 스레드

기본적으로 프로세스마다 최소 1개의 스레드를 소유 (메인 스레드 포함)한다. 하나의 프로세스가 생성될 때, 기본적으로 하나의 스레드가 같이 생성된다.

프로세스(process) : 실행 중인 프로그램으로, 디스크로부터 메모리에 적재되어 CPU 의 할당을 받을 수 있는 것
스레드(thread)  : 프로세스의 실행 단위

https://mrlazydev.tistory.com/entry/%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4-%EA%B0%9C%EB%85%90


그림처럼 프로그램이 메모리에 올라가면 프로세스가 되는 인스턴스화가 일어나고, 이후 운영체제의 CPU 스케줄러에 따라 CPU가 프로세스를 실행한다.

 

3.3.1 프로세스와 컴파일 과정 (예시 : C언어)

프로세스는 프로그램이 메모리에 올라가 인스턴스화 된 것을 말한다. 예를 들어 프로그램은 구글 크롬 프로그램과 같은 실행 파일이며, 이를 두 번 클릭하면 구글 크롬 프로세스로 변환되는 것이다.

프로그램을 만드는 과정은 만드는 언어마다 다를 수 있으며 C언어 기반으로 설명하자면, 컴파일러가 컴파일 과정을 통해 컴퓨터가 이해할 수 있는 기계어로 번역하여 실행할 수 있는 파일을 만들게 된다.

컴파일 과정은 다음과 같다.

  • 전처리
    소스 코드의 주석을 제거하고 #include, #define 등 헤더 파일을 병합하여 매크로를 치환한다.
    ex) #define... 로 시작되는 전처리 문장을 매크로라고 하며 #define PI 3.14는 PI를 3.14로 치환하는 것을 정의한다.

  • 컴파일러 
    오류 처리, 코드 최적화 작업을 하며 어셈블리어(컴퓨터 언어)로 변환한다.

  • 어셈블러
    어셈블리어는 목적 코드로 변환된다.
    ex) 리눅스의 경우 cs.c 라는 파일을 만들었을 때 sc.o 라는 파일이 만들어지게 된다.

  • 링커 
    프로그램 내에 있는 라이브러리 함수 또는 다른 파일들과 목적 코드를 결합하여 실행파일을 만든다.
    ex) 실행 파일의 확장자는 .exe 나 .out이라는 확장자를 갖는다.

라이브러리는 정적 라이브러리와 동적 라이브러리로 나뉜다.

  • 정적 라이브러리 
    프로그램 빌드 시 라이브러리가 제공하는 모든 코드를 실행 파일에 넣는 방식이다.
    시스템 환경 등 외부 의존도가 낮은 장점이 있지만 코드 중복 등 메모리 효율성이 떨어지는 단점이 있다.

  • 동적 라이브러리 
    프로그램 실행 시 필요할 때만 DLL(Dynamic Link Library) 이라는 함수정보를 통해 참조하여 쓰는 방식이다.
    메모리 효율성에서 장점을 지니지만 외부 의존도가 높아진다는 단점이 있다.

3.3.2 프로세스의 상태 

프로세스의 상태는 여러가지의 상태 값을 갖는다.

시스템이 부팅된 후 최초의 프로세스는 운영체제가 직접 생성하지만 그 다음부터는 이미 존재하던 프로세스가 다른 프로세스를 복제하여 생성한다. 항상 자식 프로세스가 먼저 종료되고 난 후 부모 프로세스가 종료된다. 후손이 여러 개인 (자식의 자식으로 이어지는 계층도가 긴) 프로세스의 경우에는 연쇄적으로 모든 후손 프로세스들을 종료시킨 뒤에 종료된다.

생성된 프로세스가 작업을 수행하기 위해서는 자원이 필요하다. 운영체제로부터 직접 자원을 할당받는 경우도 있고 부모 프로세스와 자식 프로세스가 자원을 공유하는 경우(일부만 공유하는 경우 / 전부를 공유하는 경우)도 있다. 프로세스가 수행되는 모델도 부모와 자식이 공존하며 수행되는 모델이 있고, 부모가 자식이 종료될때까지 기다리는 모델도 있다. 전자의 경우에는 CPU 획득을 위해 서로 경쟁하는 관계가 된다. 반면 후자의 경우에는 자식 프로세스가 종료되기 전까지 부모 프로세스는 봉쇄 상태에 머물러 있다가 자식 프로세스가 종료되면 준비 상태가 되어 CPU를 얻을 권한이 생기게 된다.

  • 생성 
    생성 상태는 프로세스가 생성된 상태를 의미하며 fork() 또는 exec() 함수를 통해 생성한다. 이때 PCB(Process Control Block)가 할당된다.

    * PCB : 특정 프로세스에 대한 중요한 정보를 저장 하고 있는 운영체제의 자료구조이다. 운영체제는 프로세스를 관리하기 위해 프로세스의 생성과 동시에 고유한 PCB 를 생성 한다. 프로세스는 CPU 를 할당받아 작업을 처리하다가도 프로세스 전환이 발생하면 진행하던 작업을 저장하고 CPU 를 반환해야 하는데, 이때 작업의 진행 상황을 모두 PCB 에 저장하게 된다. 그리고 다시 CPU 를 할당받게 되면 PCB 에 저장되어있던 내용을 불러와 이전에 종료됐던 시점부터 다시 작업을 수행한다.
  • fork() : 부모 프로세스의 주소 공간을 그대로 복사하며, 새로운 자식 프로세스를 생성하는 함수이다.
    프로세스가 생성되면 독자적인 주소 공간을 갖게 된다. 부모 프로세스와 자식 프로세스는 별도의 주소 공간을 가지게 되고, 자식 프로세스는 부모 프로세스의 주소 공간 내용을 그대로 복사하여 생성된다. 주소 공간만 복사할 뿐 부모 프로세스의 비동기 작업 등을 상속하지 않는다.
  • exec() : 새로운 프로세스를 생성하는 함수이다.
    fork를 통해 복제가 완료되면 그 위에 새로운 프로그램을 덮어씌울 수도 있다.

자식 프로세스를 생성하는 과정은 다음과 같다.

운영체제는 자식 프로세스의 생성을 위해 fork() 시스템 콜을 제공한다. fork() 시스템 콜을 하게 되면 fork() 함수를 호출한 프로세스와 똑같은 프로세스가 생성된다. 이렇게 생성된 자식 프로세스는 부모 프로세스의 모든 문맥을 동일하게 가지고 있으며 부모 프로세스의 주소 공간을 비롯해 프로그램 카운터 등 레지스터 상태, PCB 및 커널 스택 등 모든 문맥을 그대로 복제해 자식 프로세스의 문맥을 형성한다. 즉, 자식 프로세스는 부모 프로세스의 처음부터 수행하는 것이 아니라 현재 수행한 시점(PC 지점)부터 수행하게 된다. 이 둘을 구분하기 위해 부모 프로세스 식별자(pid)는 양수이며 자식 프로세스의 식별자는 0이라는 차이가 존재한다.

* 커널 : 운영체제 중 항상 메모리에 올라가 있는 운영체제의 핵심 부분으로써, 하드웨어와 응용 프로그램 사이에서 인터페이스를 제공하는 역할을 하며 컴퓨터 자원들을 관리하는 역할

자식 프로세스가 부모와 다른 독자적 프로그램을 수행하도록 만들기 위해선 exec() 시스템 콜이 필요하다. exec() 시스템 콜은 지금까지 수행했던 상태를 잊어버리고 그 주소 공간을 완전히 새로운 프로그램으로 덮어씌운 후 처음부터 시작하도록 만들어준다. 즉, exec() 이전 시점까지 프로그램을 실행하다가 exec()을 만나면 새로운 프로그램으로 넘어가 그 프로그램을 실행하는 것이다.

wait() 시스템 콜을 통해 부모 프로세스와 자식 프로세스 간의 동기화가 가능하다. wait() 시스템 콜이 이루어지면 부모 프로세스는 자식 프로세스가 종료될 때까지 봉쇄상태에 들어가는데, 일반적인 봉쇄 상태에서와는 다르게 자원을 기다리며 줄 서 있지 않고 자식 프로세스가 종료될 때까지 수면 상태에 머무른다. 자식 프로세스가 종료되는 순간 준비 큐에 재진입하여 CPU를 얻을 권한을 획득한다.

자식 프로세스가 부모 프로세스와 달라지지 않는다면 굳이 복사를 진행할 필요가 없다. 따라서 자식 프로세스에서 변경이 진행될 때 복사를 진행하는 방식을 Copy-On-Write(a.k.a. COW)라고 한다.

  • 대기 
    대기 상태는 메모리 공간이 충분하면 메모리를 할당받고 아니면 아닌 상태로 대기하고 있으며, CPU 스케줄러로 부터 CPU 소유권이 넘어오기를 기다리는 상태이다.

  • 대기 중단 
    대기 중간 상태는 메모리 부족으로 일시 중단되어 있는 상태이다.

  • 실행 상태 
    실행 상태는 CPU 소유권과 메모리를 할당받고 인스트럭션을 수행 중인 상태를 의미한다. 이를 CPU burst 가 일어났다고도 표현한다.

  • 중단 상태 
    중단 상태(blocked)는어떠한 이벤트가 발생한 이후 기다리며 프로세스가 차단된 상태이다. I/O 디바이스에 의한 인터럽트로 이러한 현상이 발생하기도 한다.
    ex) 프린터 인쇄 버튼을 눌렀을때 프로세스가 잠시 멈춘 느낌

  • 일시 중단 상태 
    일시 중단 상태(blocked suspended)는 대기 중단과 유사하다. 중단된 상태에서 프로세스가 실행되려고 했지만 메모리 부족으로 일시 중단된 상태이다.

  • 종료 상태 
    종료 상태는 메모리와 CPU 소유권을 모두 놓고 가는 상태를 말한다.
    프로세스의 종료는 크게 자발적 종료와 비자발적 종료로 구분된다. 자발적 종료가 이루어지는 경우, 프로세스는 명령을 모두 수행한 후 exit()이라는 시스템 콜을 진행한다(exit()은 프로그래머가 명시적으로 호출하지 않아도 프로그램 종료 지점에 컴파일러가 자동으로 삽입). 운영체제에게 exit()을 통해 자신이 종료될 수 있음을 알리면 운영체제는 자원을 회수하고 시스템 내에서 프로세스를 정리한다. 반면 비자발적 종료의 경우에는 부모 프로세스가 자식 프로세스의 수행을 강제로 종료하는데, 이는 abort()라는 함수를 통해 이루어진다.

* 프로세스가 강제 종료되는 경우

  1. 자식 프로세스가 한계치를 넘는 자원을 요구할 때
  2. 자식 프로세스에게 할당된 작업이 더 이상 필요하지 않을 때
  3. 부모 프로세스가 종료될 때

위와 다르게 부모 프로세스가 종료된 이후에도 실행되어야 할 프로세스가 있다면 해당 프로세스를 다른 프로세스의 자식으로 이양하는 작업이 필요하다.

3.3.3 프로세스 메모리의 구조

위 그림과 같이, 프로그램이 실행되기 위해서는 운영체제(OS)가 프로그램의 정보를 메모리에 로드 해야 한다. 또한 프로그램이 실행되는 동안 CPU가 코드를 처리하기 위해서는, 메모리가 명령어와 데이터들을 저장해야 한다.

운영체제는 프로세스에 적당한 메모리를 할당하는데 다음 구조를 기반으로 할당한다.

프로세스의 메모리 구조스택(stack), (heap), 데이터 영역(Data segement), 코드 영역(code segement)으로 나눠진다.

* 왜 이렇게 구역을 나눈건가요?
최대한 데이터를 공유하여 메모리 사용량을 줄여야 한다.
Code는 같은 프로그램 자체에서는 모두 같은 내용이기 때문에 따로 관리하여 공유한다.
Stack과 데이터를 나눈 이유는, 스택 구조의 특성과 전역 변수의 활용성을 위함이다.
=> 프로그램의 함수와 지역 변수는, LIFO(가장 나중에 들어간게 먼저 나옴)특성을 가진 스택에서 실행된다. 따라서 이 함수들 안에서 공통으로 사용하는 '전역 변수'는 따로 지정해주면 메모리를 아낄 수 있다.

스택은 높은 주소(0xFFFFFFFF) 로 부터 할당되고, 힙은 낮은 주소로 부터 할당된다.

* 런타임, 컴파일이란?
런타임(Runtime)과 컴파일타임(Compiletime)은 소프트웨어 프로그램개발의 서로 다른 두 계층의 차이를 설명하기 위한 용어이다. 프로그램을 생성하기 위해 개발자는 첫째로 소스코드를 작성하고 컴파일이라는 과정을 통해 기계어코드로 변환 되어 실행 가능한 프로그램이 되며, 이러한 편집 과정을 컴파일타임(Compiletime) 이라고 부른다.
컴파일과정을 마친 프로그램은 사용자에 의해 실행되어 지며, 이러한 응용프로그램이 동작되어지는 때를 런타임(Runtime)이라고 부른다.

- 스택 과 힙

스택 과 힙은 동적 할당되며, 동적 할당런타임 단계에서 메모리를 할당 받는 것을 말한다.

스택은 지역 변수, 파라미터, 실행되는 함수에 의해 늘어나거나 줄어드는 프로그램이 자동으로 사용하는 임시 메모리 영역을 말한다.

함수 호출 시 생성되는 지역 변수파라미터가 저장되고, 함수 호출이 완료되면 사라진다. 이때 스택 영역에 저장되는 함수의 호출 정보를 스택 프레임(stack frame) 이라고 한다.

스택 영역에서 푸시(push) 로 데이터를 저장하고, 팝(pop) 으로 데이터를 인출한다. 스택 영역은 후입 선출(LIFO, Last-In First-Out) 의 방식으로, 가장 나중에 들어온 데이터가 가장 먼저 인출 된다. 이는 스택 영역이 메모리의 높은 주소에서 낮은 주소의 방향으로 할당 되기 때문이다.

가장 아래(높은 주소)부터 차곡차곡 위(낮은 주소)의 방향으로 데이터가 쌓인다고 생각하면 된다.

* 왜 스택은 높은 주소에서 낮은 주소 순으로 데이터가 쌓여?!

1.  높은 주소부터 거꾸로 스택을 채워나가면 스택영역이 커널영역에 침범할 일이 없어진다.
만약 작은 주소부터 할당되었다면 접근 불가 영역인 커널에 도달하려는 일이 생길지도?
 
2. 힙과 스택이 중간에 공유라이브러리 영역을 두고 효율적으로 메모리 할당을 하기 위해서.
[ 힙→ 공유라이브러리 ←스택 ] 구조로 되어있기 때문에

예를 들어 main() 함수 안의 변수들도 메모리를 할당받는데 (<- 늘어남) 함수가 종료되면 메모리에 할당된 변수들을 메모리에서 해제한다(<- 줄어듬). 재귀함수를 호출할 때 StackOverflow는 재귀가 실행되면서 변수를 메모리에 계속 할당하다가 할당된 메모리 자원 영역을 넘어버리면 발생하는 것이다.

은 프로그래머가 직접 공간을 할당, 해제하는 메모리 공간이다. 힙 영역에서 malloc() 또는 new 연산자를 통해 메모리를 할당하고, free() 또는 delete 연산자를 통해 메모리를 해제한다.

힙 영역은 선입선출(FIFO, First-In First-Out)의 방식으로, 가장 먼저 들어온 데이터가 가장 먼저 인출 된다. 이는 힙 영역이 메모리의 낮은 주소에서 높은 주소의 방향으로 할당되기 때문이다.

위(낮은 주소)부터 차곡차곡 아래(높은 주소) 방향으로 데이터가 쌓인다고 생각하면 된다.

오버 플로우란 영어로 넘쳐흐른다는 뜻 이다. 말 그대로, 한정된 메모리 공간이 부족하여 메모리 안에 있는 데이터가 넘쳐 흐르는 현상이다.

오버 플로우의 종류에는 힙 오버 플로우와 스택 오버 플로우가 있다.

힙은 메모리 위쪽 주소(낮은 주소)부터 할당되고, 스택은 메모리 아래쪽 주소(높은 주소)부터 할당되기 때문에 각 영역이 상대 공간을 침범하는 일이 발생할 수 있다. 이때 힙이 스택을 침범하는 경우를 힙 오버 플로우라 하고, 스택이 힙을 침범하는 경우를 스택 오버 플로우라고 한다.

- 데이터 영역과 코드 영역

이 영역은 정적 할당되는 영역이다. 정적 할당컴파일 단계에서 메모리를 할당하는 것을 말한다.

데이터 영역은 BSS segement 와 Data segement, code text / segement 로 나뉘어서 저장된다. 프로그램의 시작과 함께 할당되며 프로그램이 종료되면 소멸한다.

  1. BSS Segement는 전역 변수 또는 static, const로 선언되어 있고 0으로 초기화 되어있거나 어떠한 값으로도 초기화 되어 있지 않은 변수가 이곳에 할당된다.
  2. Data Segement는 전역 변수 또는 static, const로 선언되어 있고 변수가 0이 아닌 값으로 초기화 되어 있을 때 이곳에 할당된다.
  3. code segement에는 프로그램의 코드가 들어간다. 텍스트 영역이라고 부르기도 한다. CPU는 코드 영역에 저장된 명령을 하나씩 가져가서 처리하게 된다. 프로그램이 시작하고 종료될 때 까지 메모리에 계속 남아있는다.

3.3.4 PCB(Process Metadata)

PCB는 운영체제에서 프로세스에 대한 메타데이터를 저장한 '데이터'를 말한다. 프로세스의 가장 중요한 정보를 포함하고 있기 때문에 일반 사용자가 접근하지 못하도록 커널 스택의 가장 앞부분에서 관리된다.

프로그램 실행 → 프로세스 생성 → 프로세스 주소 공간에 (코드, 데이터, 스택) 생성 
→ 이 프로세스의 메타데이터들이 PCB에 저장

* 메타데이터 : 데이터에 관한 구조화된 데이터이자 데이터를 설명하는 작은 데이터, 대량의 정보 가운데에서 찾고 있는 정보를 효율적으로 찾아내서 이용하기 위해 일정한 규칙에 따라 콘텐츠에 대해 부여되는 데이터

* PCB는 왜 있는걸까?
CPU는 프로세스가 여러 개일 때, CPU 스케줄링을 통해 이를 관리한다. CPU는 각 프로세스들이 누군지 알아야 관리가 가능하다.
이때, 프로세스들의 특징을 갖고있는 것이 바로 PCB이다.

CPU에서는 프로세스의 상태에 따라 교체 작업이 이루어진다. (interrupt가 발생해서 할당받은 프로세스가 wating 상태가 되고 다른 프로세스를 running으로 바꿔 올릴 때)
이때, 앞으로 다시 수행할 대기 중인 프로세스에 관한 저장 값을 PCB에 저장해두는 것이다.

* PCB는 어떻게 관리되나요?
Linked List 방식으로 관리한다.
PCB List Head에 PCB들이 생성될 때마다 붙게 된다. 주소값으로 연결이 이루어져 있는 연결리스트이기 때문에 삽입 삭제가 용이하다. 즉, 프로세스가 생성되면 해당 PCB가 생성되고 프로세스 완료시 제거된다.

이렇게 수행 중인 프로세스를 변경할 때, CPU의 레지스터 정보가 변경되는 것을 Context Switching 이라고 한다.

- PCB의 구조

PCB는 프로세스 스케줄링 상태, 프로세스 ID 등의 다음과 같은 정보로 이루어져 있다.

  • 프로세스 스케줄링 상태
    '준비', '일시 중단' 등 프로세스가 CPU에 대한 소유권을 얻은 이후의 상태

  • 프로세스 ID
    프로세스 ID, 해당 프로세스의 자식 프로세스 ID

  • 프로세스 권한
    컴퓨터 자원 또는 I/O 디바이스에 대한 권한 정보

  • 프로그램 카운터
    프로세스에서 실행해야 할 다음 명령어의 주소 포인터

  • CPU 레지스터
    프로세스를 실행하기 위해 저장해야 할 레지스터에 대한 정보

  • CPU 스케줄링 정보
    CPU 스케줄러에 의해 중단된 시간 등에 대한 정보

  • 계정 정보
    프로세스 실행에 사용된 CPU 사용량, 실행한 유저의 정보

  • I/O 상태 정보
    프로세스에 할당된 I/O 디바이스 목록

- 컨텍스트 스위칭

컨텍스트 스위칭이란 PCB를 교환하는 과정을 말한다. 한 프로세스의 할당한 시간이 끝나거나 인터럽트에 의해 발생한다.

CPU가 이전의 프로세스 상태를 PCB에 보관하고, 또 다른 프로세스의 정보를 PCB에 읽어 레지스터에 적재하는 과정

싱글코어 기준으로 봤을 땐 컴퓨터가 많은 프로그램을 동시에 처리하는 것처럼 보이는데, 사실 실행되는 프로세스는 한 개이며
컨텍스트 스위칭이 매우 빠르게 일어나기에 동시에 구동되는 것처럼 보이는 것이다.

1. 프로세스 A가 실행하다 멈춘다.
2. A의 PCB를 저장하고 프로세스 B를 로드하여 실행한다.
3. 다시 프로세스 B의 PCB를 저장하고 프로세스 A의 PCB를 로드한다.

컨텍스트 스위칭이 일어나면 기다리는 동안 유휴시간 및 컨텍스트 스위칭에 드는 비용인 캐시미스가 발생한다.

* 캐시미스  : 컨텍스트 스위칭이 일어날 때 프로세스가 가지고 있는 메모리 주소가 그대로 있으면 잘못된 주소 변환이 생기므로 캐시클리어 과정을 겪게 되고 이 때문에 캐시미스가 발생

스레드에서도 컨텍스트 스위칭이 일어나는데 스레드는 스택 영역을 제외한 모든 메모리를 공유하기에 비용 및 시간이 적게 든다. 
스레드는 독립적인 실행 흐름을 가져야 독립적인 함수 호출이 보장 되므로 스택이 스레드 마다 독립적으로 할당된다.

Context Switching 중에는 CPU가 프로세스 작업을 하지 않기 때문에 오버헤드가 발생하게 된다. 따라서 이 과정을 쓸데없이 자주 반복하지 않도록 하고, 필요한 순간에 적절하게 하도록 하는 것이 운영체제 스케쥴러이다.

* 스케줄러

프로세스를 스케줄링하기 위한 Queue 에는 세 가지 종류가 존재한다.

  • Job Queue : 현재 시스템 내에 있는 모든 프로세스의 집합
  • Ready Queue : 현재 메모리 내에 있으면서 CPU 를 잡아서 실행되기를 기다리는 프로세스의 집합
  • Device Queue : Device I/O 작업을 대기하고 있는 프로세스의 집합

CPU 스케줄링 척도는 다음과 같다.

  • Response Time : 작업이 처음 실행되기까지 걸린 시간
  • Turnaround Time : 실행 시간과 대기 시간을 모두 합한 시간으로 작업이 완료될 때 까지 걸린 시간

https://rebas.kr/852

<프로세스의 상태전이>
✓ 승인 (Admitted) : 프로세스 생성이 가능하여 승인됨.
✓ 스케줄러 디스패치 (Scheduler Dispatch) : 준비 상태에 있는 프로세스 중 하나를 선택하여 실행시키는 것.
✓ 인터럽트 (Interrupt) : 예외, 입출력, 이벤트 등이 발생하여 현재 실행 중인 프로세스를 준비 상태로 바꾸고, 해당 작업을 먼저 처리하는 것.
✓ 입출력 또는 이벤트 대기 (I/O or Event wait) : 실행 중인 프로세스가 입출력이나 이벤트를 처리해야 하는 경우, 입출력/이벤트가 모두 끝날 때까지 대기 상태로 만드는 것.
✓ 입출력 또는 이벤트 완료 (I/O or Event Completion) : 입출력/이벤트가 끝난 프로세스를 준비 상태로 전환하여 스케줄러에 의해 선택될 수 있도록 만드는 것.

각각의 Queue 에 프로세스들을 넣고 빼주는 스케줄러에도 크게 세 가지 종류가 존재한다.

1. 장기스케줄러(Long-term scheduler or job scheduler)
메모리는 한정되어 있는데 많은 프로세스들이 한꺼번에 메모리에 올라올 경우, 대용량 메모리(일반적으로 디스크)에 임시로 저장된다. 이 pool 에 저장되어 있는 프로세스 중 어떤 프로세스에 메모리를 할당하여 ready queue 로 보낼지 결정하는 역할을 한다.

- 메모리와 디스크 사이의 스케줄링을 담당.
- 프로세스에 memory(및 각종 리소스)를 할당(admit)
- degree of Multiprogramming 제어(실행중인 프로세스의 수 제어)
- 프로세스의 상태
new -> ready(in memory)
cf) 메모리에 프로그램이 너무 많이 올라가도, 너무 적게 올라가도 성능이 좋지 않은 것이다. 참고로 time sharing system 에서는 장기 스케줄러가 없다. 그냥 곧바로 메모리에 올라가 ready 상태가 된다.

2. 단기스케줄러(Short-term scheduler or CPU scheduler)
- CPU 와 메모리 사이의 스케줄링을 담당.
- Ready Queue 에 존재하는 프로세스 중 어떤 프로세스를 running 시킬지 결정.
- 프로세스에 CPU 를 할당(scheduler dispatch)
- 프로세스의 상태
ready -> running -> waiting -> ready

3. 중기스케줄러(Medium-term scheduler or Swapper)
- 여유 공간 마련을 위해 프로세스를 통째로 메모리에서 디스크로 쫓아냄 (swapping)
- 프로세스에게서 memory 를 deallocate
- degree of Multiprogramming 제어
- 현 시스템에서 메모리에 너무 많은 프로그램이 동시에 올라가는 것을 조절하는 스케줄러.
- 프로세스의 상태
ready -> suspended

Suspended(stopped) : 외부적인 이유로 프로세스의 수행이 정지된 상태로 메모리에서 내려간 상태를 의미한다. 프로세스 전부 디스크로 swap out 된다. 
blocked 상태는 다른 I/O 작업을 기다리는 상태이기 때문에 스스로 ready state 로 돌아갈 수 있지만 이 상태는 외부적인 이유로 suspending 되었기 때문에 스스로 돌아갈 수 없다.

하지만 프로세스 작업 중에는 OverHead를 감수해야 하는 상황이 있다.

프로세스를 수행하다가 입출력 이벤트가 발생해서 대기 상태로 전환시킴
이때, CPU를 그냥 놀게 놔두는 것보다 다른 프로세스를 수행시키는 것이 효율적

Context Switching을 통해 CPU가 놀지 않도록 만들고, 사용자에게 빠르게 일처리를 제공한다.

3.3.5 멀티프로세싱

멀티프로세싱은 여러 개의 '프로세스' 즉, 멀티프로세스를 통해 2가지 이상의 일을 수행할 수 있는 것을 말한다.

장점 : 독립된 구조로 안정성 높음. 프로세스 중 하나에 문제가 생겨도 다른 프로세스에 영향을 주지 않음.
단점 : 독립된 메모리 영역이기 때문에 작업량이 많을수록 문맥 교환이 잦아 오버헤드가 발생할 수 있음.

웹 브라우저

웹 브라우저는 멀티프로세스 구조를 가지고 있다.

  • 브라우저 프로세스
    주소 표시줄, 북마크 막대, 뒤로 가기 버튼, 앞으로 가기 버튼 등을 담당하며 네트워크 요청이나 파일 접근 권한을 담당한다.

  • 렌더러 프로세스
    웹 사이트가 '보이는' 모든 부분의 모든 것을 제어한다. (보이는 모든 것을 그린다.)

  • 플러그인 프로세스
    웹 사이트에서 사용하는 플러그인을 제어한다.

  • GPU 프로세스
    GPU를 이용해서 화면을 그리는 부분을 제어한다.

IPC

멀티프로세스는 IPC(Inter Process Communication)가 가능하다. IPC프로세서끼리 데이터를 주고받고 공유 데이터를 관리하는 메커니즘을 뜻한다. 예를 들어, 클라이언트 서버 관계에서 클라이언트는 데이터를 요청하고 서버는 클라이언트 요청에 응답하는 것이 있다.

IPC 종류는 공유 메모리, 파일, 소켓, 익명 파이프, 명명 파이프, 메세지 큐가 있으며 이들은 모두 메모리가 완전히 공유되는 스레드보다는 속도가 떨어진다.

  • 공유 메모리
    공유 메모리는 여러 프로세스가 동일한 메모리 블록에 접근 권한이 부여되어 프로세스가 서로 통신할 수 있도록 공유 메모리를 생성해서 통신하는 것을 이야기한다. 프로세스가 공유 메모리 할당을 커널에 요청하면, 커널은 해당 프로세스에 메모리 공간을 할당해주고 이후 모든 프로세스는 해당 메모리 영역에 접근할 수 있게 된다.
    메모리 자체를 공유하기에 불필요한 복사의 오버헤드가 발생하지 않고 가장 빠르며, 메모리 영역을 프로세스가 공유해야 하기에 동기화가 필요하다.
    하드웨어 관점에서의 공유 메모리는 CPU가 접근할 수 있는 큰 메모리인 RAM을 말한다.

  • 파일
    파일은 디스크에 저장된 데이터 또는 파일 서버에서 제공한 데이터를 말하고, 이를 기반으로 프로세스 간 통신한다.

  • 소켓
    동일한 컴퓨터의 다른 프로세스나 네트워크의 다른 컴퓨터로 네트워크 인터페이스를 통해 전송하는 데이터를 의미한다. 클라이언트와 서버가 소켓을 통해서 통신하는 구조로, 원격에서 프로세스 간 데이터를 공유할 때 사용한다.
    TCP 와 UDP가 있다.

  • 익명 파이프
    익명 파이프는 프로세스 간의 FIFO(First In First Out)방식으로 읽히는 임시 공간인 파이프를 통해 데이터를 주고받는다.
    단방향 방식으로 읽기 전용, 쓰기 전용으로 만들어 사용한다. 한 방향으로만 통신이 가능해서 반이중통신이라고도 부른다. 따라서 양쪽으로 송수신하고 싶으면 두 개의 파이프가 필요하다.
    부모와 자식 프로세스 사이에서만 사용 가능하며 다른 네트워크상에서는 사용이 불가하다.

  • 명명된 파이프
    익명 파이프는 통신할 프로세스를 명확히 알 수 있는 경우에 사용한다(부모-자식 프로세스 간 통신처럼). 명명된 파이프는 전혀 모르는 상태의 프로세스들 사이 통신에 사용한다. 즉, 익명 파이프의 확장 상태로 부모 프로세스와 무관한 다른 프로세스도 통신이 가능하다. 
    명명된 파이프는 파이프 서버와 하나 이상의 파이프 클라이언트 간의 통신을 위한 명명된 단방향 또는 양방향 파이프를 말한다.
    클라이언트/서버 통신을 위한 별도의 파이프를 제공하며 여러 파이프를 동시에 사용할 수 있다. 컴퓨터의 프로세스끼리 또는 다른 네트워크의 프로세스와도 통신이 가능하다. 보통 파이프 서버와 클라이언트를 연결하기 위해 파이프 인스턴스를 한 개 또는 여러 개를 기반으로 통신하다.

  • 메시지 큐
    메시지 큐는 메시지를 큐(queue) 데이터 구조 형태로 관리하는 것을 의미한다. 이는 커널의 전역변수 형태 등 커널에서 전역적으로 관리되며 다른 IPC 방식에 비하여 사용이 쉽고 매우 직관적이다.

    입출력 방식은 명명된 파이프와 동일하나 메시지 큐는 파이프처럼 데이터의 흐름이 아니라 메모리 공간이다. 사용할 데이터에 번호를 붙이면서 여러 프로세스가 동시에 데이터를 쉽게 다룰 수 있다.

    공유 메모리를 통해 IPC를 구현할 때 쓰기 및 읽기 빈도가 높으면 동기화 때문에 기능을 구현하는 것이 매우 복잡해지는데, 이때 대안으로 메시지 큐를 사용하기도 한다.

이러한 IPC 통신에서 프로세스 간 데이터를 동기화하고 보호하기 위해 세마포어와 뮤텍스를 사용한다. (공유된 자원에 한번에 하나의 프로세스만 접근시킬 때)

3.3.6 스레드와 멀티스레딩 

스레드프로세스의 실행 가능한 가장 작은 단위이다. 프로세스는 여러개의 스레드를 가질 수 있다.

코드 영역, 데이터 영역, 힙 영역, 스택 영역을 각각 생성하는 프로세스와는 달리, 스레드는 코드, 데이터, 힙은 스레드끼리 서로 공유한다.

멀티스레딩프로세스 내 작업을 여러 개의 스레드, 멀티스레드로 처리하는 기법이며 스레드끼리 자원을 공유하기에 효율성이 높다.

예를 들어 웹 요청을 처리할 때 새 프로세스를 생성하는 대신 스레드를 사용하는 웹 서버의 경우 훨씬 적은 리소스를 소비하며, 한 스레드가 중단되어도 다른 스레드는 실행 중일 수 있기 때문에 중단되지 않는 빠른 처리가 가능하다. 또한 동시성에도 큰 장점이 있다.

* 동시성 : 서로 독립적인 작업들을 작은 단위로 나누고 동시에 실행되는 것처럼 보여주는 것

다만, 한 스레드에 문제가 생기면 다른 스레드에도 영향을 끼쳐 스레드로 이루어져 있는 프로세스에 영향을 줄 수 있다.

장점 : 프로세스 내 자원들이 메모리를 공유해 자원 소모 줄어듦.
단점 : 자원을 공유하기 때문에 동기화 문제가 발생할 수 있음. 하나의 스레드에 문제가 생기면 전체 프로세스가 영향을 받음

멀티스레드의 예로 웹 브라우저의 렌더러 프로세스를 들 수 있다.

* 멀티 스레드와 멀티 프로세스의 차이점

멀티 스레드는 멀티 프로세스보다 작은 메모리 공간을 차지하고 Context Switching이 빠른 장점이 있지만, 동기화 문제와 하나의 스레드 장애로 전체 스레드가 종료될 위험을 갖는다.

멀티 프로세스는 하나의 프로세스가 죽더라도 다른 프로세스에 영향을 주지 않아 안정성이 높지만, 멀티 스레드보다 많은 메모리 공간과 CPU 시간을 차지하는 단점이 있다.

이 두 가지는 동시에 여러 작업을 수행한다는 점에서 같지만, 적용해야 하는 시스템에 따라 적합/부적합이 구분된다. 따라서 대상 시스템의 특징에 따라 적합한 동작 방식을 선택하고 적용해야 한다.

반응형