Process
Last updated
Last updated
실행 중인 프로그램을 프로세스라고 한다. 보조기억장치에 저장된 프로그램을 메모리에 적재하고 실행하는 순간 프로그램이 프로세스가 되는 것이다. 운영체제는 프로세스의 실행 순서를 관리하고, 프로세스에 CPU를 비롯한 자원을 배분하기 위해 프로세스 제어 블록(PCB:Process Control Block)을 이용한다. PCB는 커널 영역에 생성되며 특정 프로세스를 식별하고 해당 프로세스를 처리하는 데 필요한 정보를 판단하기 위해 사용된다. 따라서 프로세스 생성 시 만들어지고 실행이 끝나면 없어진다.
PCB에는 프로세스 아이디, 레지스터 값, 프로세스 상태, CPU 스케줄링 정보, 메모리 관리 정보, 사용한 파일과 입출력장치 목록 정보가 포함된다.
특정 프로세스를 식별하기 위한 고유 번호이다. 같은 일을 수행하는 프로그램이더라도 두번 실행하면 PID가 다르게 부여되며 생성된다.
해당 프로세스가 실행하며 사용했던 프로그램 카운터를 비롯한 레지스터 정보이다. 자신의 실행 차례가 돌아오면 이전까지 사용했던 레지스터 중간값들을 모두 복원하기 때문이다. 그래야 이전까지 진행했던 작업을 이어서 실행할 수 있다.
현재 프로세스가 어떤 상태인지 나타내는 것이다.
생성 상태(new): 저장장치에서 메모리에 적재되고 실행되어 PCB를 할당받은 상태, 프로그램이 프로세스로 된 상태
준비 상태(ready): 실행을 기다리는 프로세스들이 준비 큐에서 차례를 기다리는 상태, 준비 상태에서 실행 상태로 전환되는 것을 디스패치라고 하며 CPU 스케줄링으로 선택
실행 상태(running): CPU를 할당받아 일정 시간동안 프로세스가 실행 중인 상태, 할당된 시간을 모두 사용하는 타이머 인터럽트 발생 시 준비 상태로 이동
대기 상태(waiting): 프로세스 실행 도중 입출력장치를 사용하는 경우 입출력이 끝날때까지 기다리는 상태, 입출력 작업이 완료되면 준비 상태로 이동
종료 상태(terminated): 프로세스가 종료된 상태, 사용되던 자원은 반납되며 메모리가 정리되고 PCB 폐기
프로세스가 언제, 어떤 순서로 CPU를 할당받을지에 대한 정보이다. 위 다이어그램에서 실행 상태(running)에서 대기 상태(waiting)로 이동하는 경우와 프로세스가 종료되는(terminated) 경우는 비선점형 스케줄링에 해당된다. 실행 상태(running)에서 준비 상태(ready)로 이동하는 경우와 대기 상태(waiting)에서 준비 상태(ready)로 이동하는 경우는 선점형 스케줄링에 해당된다.
비선점형 스케줄링
하나의 프로세스가 자원을 사용하고 있다면 종료되거나 스스로 대기 상태에 접어들기 전까지 다른 프로세스가 끼어들 수 없는 방식
장점: 오버헤드는 선점형 스케줄링보다 적음
단점: 한 프로세스의 독점으로 모든 프로세스가 골고루 자원을 사용할 수 없음
선점형 스케줄링
프로세스가 자원을 사용하고 있더라도 운영체제가 프로세스로부터 자원을 강제로 빼앗아 다른 프로세스에 할당할 수 있는 방식
장점: 한 프로세스의 자원 독점을 막고 골고루 자원을 배분 가능
단점: 문맥 교환 과정에서 오버헤드가 발생 가능
CPU를 최대한 활용해 많은 양을 처리하고, 처리/대기/응답 시간을 최소화 하기위해 여러 스케줄링 알고리즘을 사용한다.
First Come First Served(FCFS) Scheduling: 준비 큐에 삽입된 순서대로 프로세스를 처리하는 방식이다. CPU를 오래 사용하는 프로세스가 먼저 도착하면 나머지 프로세스는 긴 시간 기다려야하는 호위 효과(convoy effect)가 나타난다.
Shortest Job First(SJF) Scheduling: 호위 효과를 방지하기 위해 CPU 사용 시간이 긴 프로세스는 나중에 실행하고 짧은 프로세스를 먼저 실행하는 방식이다.
Round Robin Scheduling: FCFS 스케줄링에 타임 슬라이스 개념이 더해진 방식으로, 정해진 타임 슬라이스만큼의 시간 동안 돌아가며 CPU를 이용하는데, 완료되지 않은 경우 다시 큐의 맨 뒤에 삽입되는 방식이다. 문맥 교환에서의 오버 헤드나 호위 효과를 방지하기 위해 타임 슬라이스 크기가 매우 중요하다.
Priority Scheduling: 프로세스에 우선순위를 부여하고 가장 높은 우선순위부터 실행하는 방식이다. 우선순위가 낮은 프로세스는 계속 연기되는 기아 현상이 나타난다.
Multilevel Queue Scheduling: Priority 스케줄링에서는 우선순위 준비 큐를 한 개 두었다면, 우선순위별로 준비 큐를 여러 개 두어 사용하는 방식이다. 우선순위가 가장 높은 큐에 있는 프로세스를 먼저 처리한다.
Multilevel Feedback Queue Scheduling: Multilevel Queue 스케줄링에서는 우선순위가 낮은 프로세스에 기아 현상이 나타날 수 있다. CPU 이용 시간이 긴 프로세스는 낮은 우선순위 큐로 이동시키고, 어떤 프로세스가 낮은 우선순위 큐에서 너무 오래 기다린다면 점차 우선순위가 높은 큐로 이동시키는 에이징 기법을 적용해 기아 현상을 예방하는 방식이다. 구현이 복잡하지만 가장 일반적인 CPU 스케줄링 알고리즘으로 알려져있다.
프로세스마다 메모리에 저장된 위치가 다르기에 어느 주소에 저장되어 있는지에 대한 정보이다.
프로세스가 실행 과정에서 사용한 특정 입출력장치나 파일의 정보이다. 프로세스가 실행되다가 인터럽트를 받아 다른 프로세스를 실행해야할 때 기존 프로세스는 저장되어야한다. 또한 다른 프로세스가 끝나면 다시 불러와 실행하던 부분 이후부터 진행해야한다. 이는 문맥 교환으로 이루어진다.
하나의 프로세스 수행을 재개하기 위해 기억해야 할 정보를 문맥이라고 하는데, PCB에 기록되는 정보가 문맥인 것이다. 인터럽트가 발생하면 운영체제는 PCB를 문맥에 백업하는데, 기존 프로세스의 문맥을 PCB에 백업하고 새로운 프로세스를 실행하기 위해 문맥을 PCB로부터 복구하는 것을 문맥 교환이라고 한다. 문맥 교환을 자주하면 오버헤드가 발생할 수 있어 반드시 좋은 것은 아니다. 프로세스는 실행 도중에도 시스템 호출을 통해 다른 프로세스를 생성할 수 있다.
새 프로세스를 생성한 프로세스를 부모 프로세스, 부모 프로세스에 의해 생성된 프로세스를 자식 프로세스라고 한다. 자식 프로세스는 또 자식 프로세스를 생성할 수 있어 계층적인 구조를 가진다. 부모 프로세스는 **fork()**로 자식 프로세스(자신의 복사본)를 만들고, 자식 프로세스는 **exec()**로 자신의 메모리 공간을 다른 프로그램으로 덮어씌운다. 여기서 fork()와 exec()는 시스템 호출이다. 부모는 자식이 종료될때까지 wait()하는데, 아래와 같은 경우 **abort()**로 자식 프로세스를 강제 종료할 수 있다.
자식 프로세스가 필요 없어진 경우
자식이 할당된 자원을 초과한 경우
부모 프로세스가 종료된 경우
만약 대기중인 부모 프로세스가 없다면(wait()을 호출하지 않은 경우) 프로세스는 좀비(zombie)가 되며, 부모 프로세스가 wait() 호출 없이 종료되었다면 자식 프로세스는 고아(orphan)가 된다.
프로세스를 구성하는 실행 흐름 단위로 하나의 프로세스는 여러 개의 스레드를 가질 수 있다. 스레드는 프로세스 내에서 각기 다른 스레드 ID, 프로그램 카운터 값을 비롯한 레지스터 값, 스택으로 구성된다. 따라서 스레드마다 다른 코드를 실행할 수 있는 것이다. 스레드들은 실행에 필요한 최소한의 정보만을 유지한 채 프로세스 자원을 공유하며 실행된다.(프로세스끼리는 기본적으로 자원을 공유하지 않음!) 지금까지 다룬 프로세스는 단일스레드를 실행하는 것인데, 실제로는 여러개의 스레드로 구성되어 병렬로 실행된다. 이러한 멀티스레드 환경에서는 하나의 스레드에 문제가 생기면 프로세스 전체에 영향을 끼친다. 하지만 프로세스를 생성하는 것보다 스레드를 생성하는 게 더 저렴하고, 스레드 교환은 문맥 교환보다 오버헤드 발생이 적다는 장점을 가진다.
여러 스레드로 프로세스를 동시에 실행하는 것을 멀티스레드라고 한다.
다대일로 많은 사용자 스레드를 하나의 커널 스레드에 매핑하는 방식이다. 쓰레드 관리가 사용자 영역의 스레드 라이브러리에 의해 수행되어 효율적이지만, 하나의 스레드가 시스템 호출을 하는 동안 전체 프로세스가 차단될 수 있다. 또한 하나의 스레드만 커널에 접근할 수 있기때문에 멀티코어 시스템에서 여러 스레드가 병렬로 실행될 수 없다.
일대일로 하나의 사용자 스레드를 하나의 커널 스레드에 매핑하는 방식이다. many-to-one보다 더 많은 동시성을 제공한다. 하나의 스레드가 시스템 호출로 인해 차단되더라도 다른 스레드가 실행될 수 있으며 병렬로 실행 가능하다. 하지만 하나의 사용자 스레드를 만들때마다 커널 스레드도 만들어줘야한다.
많은 사용자 스레드를 많은 커널 스레드에 매핑할 수 있는 방식이다. 커널 스레드 개수를 조절하며 유연하게 매핑하므로 시스템 자원의 활용을 최적화한다.
Many-to-Many모델과 유사하지만 추가로 특정 사용자 스레드가 커널 스레드에 직접 연결될 수 있다.
여러 프로세스를 동시에 실행하는 것을 멀티프로세스라고 한다.
프로그램은 병렬처리가 가능한 부분과 순차적으로(직렬) 처리되는 부분으로 구성되므로 프로세스를 아무리 병렬화 시켜도 더이상 성능이 향상되지 않는 한계가 존재한다는 법칙이다. 만약 어떤 응용프로그램이 75%는 병렬, 25%는 직렬로 2개의 프로세스의 코어를 사용해 실행된다고 가정해보자. 그럼 1 / (0.25 + (1-0.25) / 2) = 1.6으로 프로세스의 코어 1개를 사용할 때 보다 1.6배 빨라진다. 코어 2개 사용했다고 속도도 2배 빨라지는 게 아니라는 것이다.
IPC는 프로세스 간에 데이터나 정보를 주고 받기 위한 매커니즘으로 공유 메모리(shared memory)와 메세지 전달(message passing) 2가지 모델이 있다.
말그대로 프로세스간에 공유가 되도록 설정해 놓은 메모리로 모든 프로세스들이 접근할 수 있다. 다같이 하나의 공간을 공유해서 사용하는 것이다. 처음 생성할때만 시스템 호출을 사용해 속도가 빨라 많은 양의 데이터를 처리할 때 많이 사용된다. 하지만 동기화 문제가 발생한다.
필요한 공유자원을 그때그때 프로세스 사이에서 전달하는 방식이다. 때문에 동기화를 고민할 필요가 전혀 없으며 구현하기가 편하다. 하지만 복사해서 사용하는 만큼 공유 메모리보다 느리며 적은 양의 데이터를 처리할 때 사용한다.