Priv's Blog

3. 프로세스와 스레드 본문

Dev. Study Note/OS Introduction

3. 프로세스와 스레드

Priv 2021. 10. 12. 23:36


 

 

1. 프로세스의 개념

1.1) 등장 배경

다중 프로그래밍이 등장하면서 여러 프로그램들이 서로의 자원을 공유하기 시작했다.

이로 인해 여러 프로그램들을 함께 제어하기 위한 방법이 필요해졌다.

1960년대, '멀틱스' 시스템 설계자가 처음으로 '프로세스'라는 용어를 사용함.

 

1.2) 프로세서의 정의

가장 보편적인 프로세서의 정의는 '실행 중인 프로그램'이다.

  • 프로그램: 코드와 정적 데이터들로 이루어진 것.
  • 프로세스: 프로그램이 메모리에 적재된 것.

프로그램의 코드가 얼마 되지 않더라도, 프로세스 상에서 연산을 처리할 때, 해당 프로그램이 자원을 과하게 먹는 오류가 발생할 수도 있다.

프로세스와 프로그램은 같은 개념이 아님에 주의하자.

 

1.3) 시스템 관점에서 본 프로세스

프로그램이 프로세스가 된다는 것장치, 메모리, 프로세서, 기타 HW로 프로그램을 실행한다는 것을 의미한다.

이러한 제어 및 관리는 운영체제가 담당한다.

 


 

2. 프로세스의 주소 공간

프로세스의 주소 공간은 다음과 같이 4가지로 구성되어 있다.

  • 실행 스택: 호출된 프로시저(함수)의 복귀 주소, 지역 변수 등 일시적인 데이터들을 저장하는 영역
  • 실행 힙: 시스템 호출을 통해 동적으로 메모리 공간을 할당/해제할 수 있는 자유로운 영역
  • 데이터: 정적 또는 전역 변수들을 저장하는 영역
  • 코드(텍스트): 프로그램의 코드를 저장하는 영역

위의 코드를 이에 적용해보면, 다음과 같다.

// 프로그램 코드; 코드(텍스트) 영역에 저장됨
// 코드를 1개씩 실행하면서 프로그램 포인터가 다음 실행할 코드를 가리킴

// 전역 변수; 데이터 영역에 저장됨
char notInit[10000];
char init[] = "Hello world";



int main(int argc, char *argvp[]) { // 함수 호출; 함수 실행을 위해 스택 영역 할당
	
    // 지역 변수; 스택 영역에 저장됨
    float f;
    int i;
    
    // 동적 메모리 할당; 실행 힙에 저장됨
    char *cp = malloc(10000);
    
    // g() 함수 호출; g() 실행을 위한 스택 영역이 새로 할당
    g();
}

g( ) 함수를 위한 스택 영역이 메모리 상에 새로 추가되고, 실행을 마쳤을 때 스택이 삭제된다.

스택이 1개 더 추가되었으므로, 스택 포인터프로그램 포인터와 더 가까워진다.

이처럼 프로세스 주소 공간First in - Last out (FILO: 선입 후출) 구조를 지니고 있다.

 


 

3. 프로세스의 종류

3.1) 시스템 프로세스

커널 프로세스 또는 운영체제 프로세스라고도 부른다.

모든 시스템 메모리에 접근이 가능하며, 모든 프로세서의 명령을 수행할 수 있다.

시스템 운영에 필요한 작업들을 수행한다.

  • 프로세스 실행 순서 제어
  • 프로세스의 다른 사용자 영역 또는 운영체제 영역 침범 감시 등

 

3.2) 사용자 프로세스

사용자의 코드를 수행한다.

자신의 영역만을 사용할 수 있으며, 다른 사용자 프로세스 또는 시스템 프로세스에는 접근할 수 없다.

만약 다른 프로세스에 접근이 필요한 경우, 운영체제에 접근 권한을 요청해야 한다.

 

3.3) 사용자/시스템 프로세스의 보호 메커니즘

시스템 프로세스와 사용자 프로세스가 동시에 동작하는 과정에서 서로 가질 수 있는 접근 권한이 다르다.

대부분의 운영체제들은 프로세서가 가지고 있는 서로 다른 실행 모드를 통해 프로세스 보호 메커니즘을 구현한다.

  • 커널 모드/시스템 모드 (0bit): 시스템 프로세스는 커널 모드에서 실행된다. 커널 모드에서는 특권 명령어를 포함하여 모든 명령어를 실행할 수 있다.

  • 사용자 모드 (1bit): 사용자 프로세스는 사용자 모드에서 실행된다. 사용자가 접근할 수 있는 영역이 제한되며, 프로그램 자원을 함부로 침범할 수 없도록 한다.

 

3.4) 독립 프로세스

병행 프로세스들이 서로 영향을 주고받지 않는다.

서로 데이터나 자원을 공유하지 않기 때문에 동기화, 통신 수단과 같은 상호작용 기능이 불필요하다.

 

3.5) 협력 프로세스

병행 프로세스들이 서로 영향을 주고받는다.

서로 데이터나 자원을 공유하기 때문에 동기화, 통신 수단과 같은 상호작용 기능이 필요하다.

 


 

4. 프로세스의 상태 변화와 상태 정보

4.1) 프로세스의 상태 변화 (간단)

프로세스의 생성에서 종료까지의 과정은 운영체제가 관리한다.

프로세스가 실행되기 위해서는 프로세서(CPU)를 차지해야 한다.

프로세스가 실행되다가 프로세서를 내놓게 되면, 인터럽트가 발동되어 비실행 단계가 된다.

 

4.2) 프로세스의 상태 변화 (세부)

생성 단계 ~ 종료 단계까지 준비, 실행, 대기 단계를 반복하며 연산이 처리된다.

여러 프로세스들이 프로세서를 공유해야 하므로, 자발적으로 프로세서를 내놓지 않은 상태에서 프로세서 점유 시간이 만료되면, 해당 프로세스는 대기 상태에 빠진다.

입출력 명령을 실행해야 하거나, 이벤트 대기 상태가 되어도 프로세서를 점유할 수 없기 때문에 대기 상태로 넘어가게 된다.

준비, 대기 상태에서는 를 사용해 프로세스의 순서를 관리한다.

프로세스가 사용하는 각 장치마다 대기 큐가 존재한다.

 

4.3) 프로세스 상태 변화 4단계

프로세스의 상태 변화 종류는 다음과 같이 4단계로 구성되어 있다.

  • dispatch(프로세스 이름): 준비 상태의 프로세스가 프로세서를 할당 받음으로써 실행 상태가 된다. 프로세스 스케줄러준비 상태인 프로세스 중 1개를 선택한다. 디스패처(dispatcher)는 선택된 프로세스에 프로세서를 할당한다.
  • timeout(프로세스 이름): 실행 상태의 프로세스가 프로세서를 자발적으로 반납하기 전프로세서 할당 시간이 만료되면, 해당 프로세스는 준비 상태가 된다. 인터럽트 시간을 두어서 특정 프로세스가 프로세서를 독점하지 못하도록 막는다. 일정 시간이 경과하면 인터럽트를 발생, 실행 중인 프로세서를 준비 상태로 넘기는 것이다.
  • block(프로세스 이름): 실행 상태의 프로세스가 자신에게 할당된 시간이 끝나기 전, 입출력, 새로운 자원 요청 등 어떤 이벤트가 발생하는 것을 기다려야 할 때, 프로세서를 양도한 후 대기 상태가 된다. 이는 프로세서가 직접 처리한다.
  • wakeup(프로세스 이름): 기다리던 조건(입출력 등)이 만족되면, 대기 상태의 프로세스가 준비 상태로 넘어간다.

대부분의 프로세스들은 준비/대기 상태를 지니며, 어느 한순간에 1개의 프로세스만 실행 상태가 된다.

 


 

5. 프로세스 제어 블록 (PCB: Process control Block)

PCB란, OS가 각각의 프로세스를 관리하는데 필요한 정보들을 저장해두는 자료구조이다.

모든 프로세스를 메인 메모리에 올려두기에는 용량에 한계가 있기 때문에, PCB를 통해 프로세스 관리에 필수적인 함축된 정보만을 메인 메모리에 업로드해야 한다.

PCB는 1개의 프로세스를 정의한다.

즉, 프로세스를 1개 생성하면, PCB도 1개 생성되며, 생성된 PCB는 메인 메모리에 유지된다.

PCB에는 프로세스의 우선순위가 저장되고, 이는 프로세서가 프로세스를 처리하는 순서를 의미한다.

 


 

6. 프로세스 문맥 교환

프로세스 문맥 교환이란, 인터럽트나 시스템 호출 등으로 인해 현재 프로세스가 사용 중인 프로세서의 제어권을 다른 프로세스에게 넘기는 과정을 말한다.

인터럽트가 종료되어 프로세서의 제어권이 다시 복귀했을 때, 이전의 작업 지점으로 돌아갈 수 있도록 프로세서의 레지스터에 있던 내용 등을 PCB에 저장한다.

여기서 문맥 교환에 필요한 시간과 메모리 등 다양한 자원들을 오버헤드라고 부른다.

 


 

7. 프로세스 관리

7.1) 프로세스 계층 구조

프로세스 실행 도중, 프로세스 생성 시스템을 호출하면 새로운 프로세스를 생성할 수 있다.

부모 프로세스가 자식 프로세스를 생성하는 과정을 반복하며 계층 구조를 형성한다.

 

7.2) 프로세스의 생성

프로세스는 운영체제 또는 다른 프로세스의 요청에 의해 생성된다.

프로세스의 생성 단계는 다음과 같다.

  • 새로운 프로세스에 프로세스 식별자 할당
  • 프로세스의 주소 공간, 프로세스 제어 블록(PCB) 할당
  • 프로세스 제어 블록(PCB) 초기화
  • 큐(Queue)에 프로세스 삽입 (프로세스 상태 변화 참고)

프로세스에 자원을 할당하는 방법은 다음과 같다.

  • 자식 프로세스에게 필요한 자원을 운영체제가 직접 할당
  • 자식 프로세스가 부모 프로세스의 자원 일부를 사용

프로세스 실행 방식은 다음과 같다.

  • 부모 프로세스와 자식 프로세스가 동시에 실행되는 방식
  • 부모 프로세스가 자식 프로세스들이 모두 종료될 때까지 기다리는 방식

프로세스의 주소 공간은 다음과 같이 관리된다.

  • 자식 프로세스가 부모 프로세스의 주소 공간을 복사
  • 자식 프로세스가 별도의 프로그램을 적재

 

7.3) 프로세스의 종료

프로세스가 마지막 명령을 실행하면 종료되며, 운영체제에 프로세스 제거를 요청한다.

부모 프로세스는 다음과 같은 방법을 통해 자식 프로세스를 종료할 수 있다.

  • 부모 프로세스가 종료되면, 운영체제가 자식 프로세스도 종료한다. (연속 종료)
  • 부모 프로세스가 abort 시스템을 호출하면 자식 프로세스가 종료된다.

프로세스가 종료되는 이유는 다음과 같다.

  • 프로세스가 제 역할을 다하여 정상적으로 종료
  • 명시된 전체 시간을 초과
  • 파일 검색 실패, 입출력 실패 등 실패 상황 발생
  • 오류 발생
  • 메모리 부족, 접근 위반 발생 등

 

7.4) 프로세스의 제거

프로세스 제거란, 프로세스를 종료한 뒤, 프로세스를 파괴하는 것을 의미한다.

프로세스를 파괴하면, 해당 프로세스가 점유하고 있던 자원들이 시스템으로 반납된다.

프로세스가 파괴되면, 시스템 리스트나 테이블에서 사라진다.

프로세스가 제거되어도 프로그램은 디스크에 저장되어 있는 상태를 유지한다.

부모 프로세스를 제거하면, 자식 프로세스도 제거된다.

 

7.5) 프로세스의 중단과 재시작

프로세스의 중단 상태는 2가지가 있다.

  • 중단된 대기(Suspended Waiting): 프로세스가 보조 기억 장치에 있으면서, 이벤트를 대기 중인 상태
  • 중단된 준비(Suspended Ready): 프로세스가 보조 기억 장치에 있지만, 즉시 메인 메모리로 로드하여 실행할 수 있는 상태

프로세스 중단과 재시작 발생 상황은 다음과 같다.

  • 시스템 부하 조정이 필요할 때, 프로세스 몇 개를 중단했다가 시스템이 다시 정상으로 돌아오면 재시작.
  • 시스템에 장애 발생 시, 실행 중인 프로세스를 잠시 중단했다가 시스템 기능이 회복되면 재시작.
  • 프로세스의 어느 부분이 의심스러울 때, 실행 중인 프로세스를 중단하여 확인한 후, 재시작/정지.

 


 

8. 프로세스 우선순위

프로세스 스케줄러는 프로세스 제어 블록에 기록된 우선순위를 이용하여 준비 리스트의 프로세스들을 처리한다.

우선순위를 구분하는 방법은 2가지가 있다.

  • 프로세서 중심 프로세스: 낮은 우선순위를 부여한다. 프로세서를 1번 차지하면 오래 사용하는 경향이 있다. 시간 할당량을 길게 함으로써, 프로세서 사용 횟수는 적지만 1번에 오래 사용한다.
  • 입출력 중심 프로세스: 높은 우선순위를 부여한다. 프로세서를 1번 차지하면 짧게, 자주 사용하는 경향이 있다.

 


 

9. 프로세스의 문맥 교환

9.1) 인터럽트 (Interrupt)

현재 실행 중인 프로세스와 별도로 외부에서 발생되는 여러 종류의 이벤트(입출력 동작 종료 등)에 의해 발생한다.

인터럽트 처리 과정은 다음과 같다.

  • 실행 중인 프로세스로부터 인터럽트 처리 루틴으로 제어가 넘어간다.
  • 인터럽트 유형에 따라 관련 루틴으로 분기된다.

분기되는 인터럽트 유형은 다음과 같다.

  • 입출력 인터럽트: 입출력 완료를 확인한 후, 이벤트를 기다리던 프로세스를 준비 상태로 바꾼다. 이후 실행할 프로세스를 결정한다.
  • 클록 인터럽트: 실행 중인 프로세스를 준비 상태로 바꾸고, 다른 프로세스를 실행 상태로 전환한다.

 

9.2) 트랩 (Trap)

부적절한 파일 접근, 현재 실행 중인 프로세스의 오류나 예외 상황으로 인해 발생한다.

치명적인 오류라면 프로세스를 종료한 후, 프로세스 문맥 교환을 실시한다.

 

9.3) 문맥 교환

실행 중이던 프로세스의 레지스터 내용을 보관, 다른 프로세스의 레지스터 내용을 CPU 레지스터에 적재하여 프로세스를 교환한다.

문맥 교환은 시간과 비용이 들어가는 오버헤드이다.

메모리 속도, 레지스터 수, 특수 명령어 유무에 따라 오버헤드가 달라진다.

운영체제 설계 시, 불필요한 문맥 교환을 줄이는 것이 주요 목표이다.

 


 

10. 스레드

초기에는 프로그래머들이 동시에 수행할 활동들을 명시할 방법이 없었다.

명시적 병행성(Explicit Concurrency)이 1970~19980년대 초, 미 국방부에서 개발한 에이다(Ada) 언어를 통해 처음 도입되었다.

현재는 프로그래머가 응용 프로그램을 동시에 수행할 활동(스레드)들을 포함시킬 수 있다.

 

10.1) 스레드의 개념

프로세서를 사용하는 기본 단위이며, 명령어를 독립적으로 수행할 수 있는 하나의 제어 흐름을 나타낸다.

프로세스에서 실행 제어만 분리한 실행 단위라고 볼 수 있다.

 

10.2) 프로세스와 스레드

각 프로세스는 스레드를 1개 이상 가진다.

스레드는 같은 프로세스 내의 스레드들과 다음 항목들을 공유한다.

  • 코드
  • 주소 공간
  • 운영체제의 자원(파일, 신호) 등

스레드는 각자 다음 항목들을 지닌다.

  • 스레드 실행 정보: 레지스터, 스레드 우선순위, 스레드 상태 등
  • 실행 스택

스레드를 Java로 구현해보면 다음과 같다.

public class Main {
	public static void main(String[] args) {
		Thread t1 = new MyThread('A', 3);
		Thread t2 = new MyThread('B', 4);
        
		t1.start();
		t2.start();
        
		for (int i = 0; i < 5; i++)
			System.out.print('M');
	}
}


class MyThread extends Thread {
	private char ch;
	private int num;
    
	public MyThread(char ch, int num) {
		this.ch = ch;
		this.num = num;
	}
    
	public void run() {
		for (int i = 0; i < num; i++)
			System.out.print(ch);
	}
}

 

스레드는 아래와 같은 환경에서 사용될 수 있다.

  • 워드 편집기 프로세스: 멀티 스레딩으로 생산성, 상호작용성 향상 (사용자 키 입력 응답 스레드, 이미지 및 텍스트 출력 스레드, 정기 백업 스레드 등)
  • 웹 서버 프로세스: 멀티 스레딩으로 처리량, 반응 속도 향상

 

10.3) 다중 스레드 프로그램 (Multithreaded Program)

 

'스레드 실행'을 포함하도록 응용 프로그램을 작성한다.

프로그램의 일부이면서, 병행으로 실행될 작업들을 각각의 스레드로 지정한다.

다중 스레드 프로그램 작성은 고도의 기술이 필요한 힘겨운 작업이다.

다중 프로세서 시스템에서 실행 시, 단일 프로세서 시스템에서보다 실행 속도가 빠르다.

다중 스레드 프로세스에서는 스레드들이 해당 프로세스의 자원을 공유할 수 있다.

스레드들은 서로 독립적이지 않다.

  • 1개의 프로세스에 포함된 모든 스레드들은 그 프로세스의 모든 주소에 접근할 수 있다.

스래드들 간의 보호 문제는 중요하지 않다.

  • 프로세스: 다수의 사용자에게서 발생하므로 서로 보호가 필요하다.
  • 스레드: 한 사용자가 프로세스 내 여러 스레드를 모두 소유하므로 스레드들은 서로 도움을 주는 관계이다.

다중 스레드 장점은 다음과 같다.

사용자에 대한 응답성 증가

  • 프로세스 일부 스레드가 봉쇄되거나 긴 작업을 수행해도 프로세스 실행을 계속 허용한다.

자원 공유로 시스템 성능 향상

  • 프로세스에 속한 스레드는 자원과 메모리를 공유하므로, 응용 프로그램 1개가 같은 주소 공간에서 여러 스레드를 실행해 세스템 성능 향상 및 편리함을 제공할 수 있다.

경제성

  • 스레드는 한 프로세스의 자원을 공유하므로, 자원을 할당해 프로세스를 생성하는 것보다 스레드를 생성하는 것이 경제적이다.

다중 처리(Multi-Processing)로 성능 및 효율 향상

  • 다중 프로세서 구조를 활용하여 각 스레드를 다른 프로세서에서 병렬로 실행할 수 있다.

 

10.4) 스레드 상태 변화

프로세스와 마찬가지로 생성, 준비, 실행, 대기, 종료 상태로 나눠진다.

실행 스레드가 대기 상태로 변할 때, 스레드가 속한 프로세스 전체가 대기 상태가 되는 것이 아니다.

즉, 동일한 프로세스 내에 속한 다른 스레드는 계속 실행할 수 있다.

 

10.5) 스레드 제어 블록 (Thread Control Block)

운영체제가 각 스레드를 관리하기 위해 필요한 스레드 정보를 저장한다.

각 스레드마다 1개의 TCB가 생성된다.

1개의 프로세스에 속한 TCB들은 TCB 리스트를 구성한다.

PCB는 TCB 리스트를 가리키는 구조를 띄고 있다. (PCB -> [TCB1 - TCB2 - TCB3 - TCB4 - ~ - TCBn])

TCB에 포함되는 내용은 다음과 같다.

  • 스레드 ID
  • 레지스터 값
  • 프로그램 카운터
  • 스택 포인터
  • 스케줄링 정보: 상태(실행, 준비, 대기), 우선순위, 프로세서 시간
  • 스레드가 속한 프로세스의 PCB에 대한 포인터

 


 

11. 스레드 구현

스레드의 구현은 운영체제에 따라 방법이 다양하다.

스래드 구현 방법 3가지는 다음과 같다.

  • 사용자 수준 스레드: 다대일 스레드 매핑. 스레드 라이브러리를 이용해 작동
  • 커널 수준 스레드: 일대일 스레드 매핑. 커널에서 스레드 지원
  • 혼합형 스레드: 다대다 스레드 매핑. 사용자 수준 스레드와 커널 수준 스레드의 혼합

 

11.1) 사용자 수준 스레드

커널 스레드를 지원하지 않는 운영체제에서 사용한다.

커널은 스레드가 아닌, 프로세스 1개 단위로 인식하여 스케줄링하고, 1개의 상태를 할당한다.

여러 사용자 수준 스레드들이 1개의 커널 스레드(프로세스)로 매핑되므로, 다대일 매핑이라고 부른다.

사용자 영역의 스레드 라이브러리로 구현한다.

스레드 라이브러리는 스레드 생성과 종료, 스레드 간의 메시지 전달, 스레드 스케줄링과 문맥 등의 정보를 보관한다.

스레드와 관련된 모든 행위를 사용자 영역에서 처리한다.

스레드 교환에 커널이 개입하지 않기 때문에 커널에서 사용자 영역으로의 전환이 불필요하다.

기본 커널 변경 없이 모든 운영체제에 적용이 가능하여 이식성이 높다.

스케줄링, 동기화를 위해 커널을 호출하지 않으므로 커널 전환 오버헤드가 적다.

스레드 라이브러리에서 스케줄링을 제어하므로, 응용 프로그램에 맞게 스케줄링을 유연하게 사용할 수 있다.

프로세스에 속한 스레드 1개가 대기 상태가 되면, 프로세스 전체가 블록 되므로 프로세스에 준비 상태 스레드가 있더라도 실행이 불가능하다. (시스템이 동시성을 지원하지 않음)

커널이 한 프로세스에 속한 여러 스레드들을 프로세스 1개로 관리하므로, 여러 프로세서에서 나누어 처리가 불가능하다. (다중 프로세서 시스템 규모 확장 제약)

스레드 라이브러리에서 스레드 간 보호를 제공해야 프로세스 수준에서 보호가 가능하다. (스레드 간 보호에 커널 보호 기법 사용 불가)

 

11.2) 커널 수준 스레드

사용자 스레드마다 1개의 커널 스레드를 매핑하므로, 일대일 스레드 매핑이라고 부른다.

스레드 관련 모든 작업을 커널이 지원한다.

프로세스와 스레드에 대한 실행 문맥 정보를 커널 영역에 유지한다.

사용자 영역에는 스레드 관리를 위한 코드가 없다.

프로세스에 속한 스레드 1개가 블록 되어도 다른 준비 상태 스레드가 실행될 수 있다. (시스템의 동시성 지원)

다중 처리 환경에서 1개의 프로세스의 여러 스레드가 각각 프로세서를 할당받아 병렬 실행하여 성능의 향상을 기대할 수 있다. (병렬 실행으로 성능 향상 가능)

스케줄링과 동기화를 위한 커널 호출 오버헤드가 발생한다.

사용자 스레드 수 증가 시, 막대한 수의 커널 스레드를 관리해야 하는 오버헤드가 발생한다.

자원 제한으로 사용자 수준 스레드 생성에 따라 커널 스레드를 무한히 생성할 수 없다.

성능 저하를 막기 위해서는 커널 스레드 수를 제한해야 한다.

시스템이 달라지면 해당 운영체제에서 제공하는 스레드 API를 사용해 프로그램을 수정해야 하므로, 이식성이 떨어진다.

 

11.3) 혼합형 스레드

사용자 수준 스레드 + 커널 수준 스레드

여러 사용자 수준 스레드를 1개 그룹의 커널 수준 스레드에 다대다로 매핑한다.

다대일 스레드 매핑의 동시성 저하 문제를 해결할 수 있다.

일대일 스레드 매핑의 스레드 수 문제를 해결할 수 있다.

 

11.4) 스레드 풀링 기법

시스템이 관리하는 스레드의 풀을 응용 프로그램에 제공, 스레드를 효율적으로 사용하게 만든다.

미리 생성한 스레드의 재사용으로 스레드 생성 시간을 줄인다.

동시 생성 가능한 스레드 수를 제한한다.

 


 


수고하셨습니다!


 

'Dev. Study Note > OS Introduction' 카테고리의 다른 글

6. 프로세스 스케줄링  (0) 2021.11.04
5. 교착 상태와 기아 상태  (0) 2021.10.16
4. 병행 프로세스와 상호 배제  (0) 2021.10.12
2. 운영체제  (0) 2021.10.12
1. 컴퓨터 시스템  (0) 2021.10.04
Comments