공룡책 Ch 3 요약 (원서를 읽고 요약하는 과정에서 잘못된 내용이 있을 수 있습니다.)
1. Process Concept
1-1. The Process
- 프로세스는 메모리에 올라가 실행중인 프로그램을 의미함(program in execution)
- 프로세스의 현재 실행에 관한 정보는 프로그램 카운터와 프로세서의 레지스터 구성으로 나타낼 수 있음
- 프로세스의 메모리 구조는 크게 텍스트, 데이터, 힙&스택 영역으로 나눌 수 있음
- 텍스트 영역 : 실행 가능한 코드 데이터가 저장됨
- 데이터 영역: 정적/전역 변수가 저장됨
- 힙 영역: 프로그램 런타임 시점에 동적으로 생성된 객체 등이 저장됨
- 스택 영역: 함수 호출시 일시적으로 매개변수, 리턴 주소, 지역변수 등이 저장됨
-
텍스트 및 데이터 영역의 크기는 고정되어 있으며, 스택과 힙 영역은 프로그램 실행 시점에 크기가 가변적임
- 함수가 호출될 때마다 지역변수, 매개변수, 리턴주소 등을 포함한
activation record가 스택에 추가(push)됨 - 비슷하게 객체가 생성되면 힙 메모리 영역에 적재되며, 쓰임이 다한 후 메모리의 해제외 함께 힙 영역의 크기도 줄어듦
- 함수가 호출될 때마다 지역변수, 매개변수, 리턴주소 등을 포함한
-
프로세스 자체가 다른 코드의 실행 환경이 될 수도 있음
-
JVM이 대표적인 예로, 가상머신이 로드된 자바 코드를 읽어들인 후 가상머신 자체의 명령어를 통해 코드 대신 행동을 취하는 방식임The JVM executes as a process that interprets the loaded Java code and takes actions(via native machine instructions) on be half that code.
-
예를 들어
java Program이라는 명령을 실행했을 때,java는 JVM을 프로세스로서 실행시킨다는 것을 의미하며,Program은 JVM 환경에서 해당 프로그램을 실행시킨다는 것을 의미함
-
1-2. Process State
- 프로세스가 실행된다는 것은 프로세스의 상태(state)가 바뀐다는 것을 의미
- 프로세스의 상태는
생성(New),실행(Running),대기(Waiting),준비(Ready),종료(Terminated)의 5가지로 나뉨- 생성: 프로세스가 말 그대로 새롭게 생성된 상태를 의미
- 실행 : 명령(Instructions)들이 실행되는 상태를 의미
- 대기 : 프로세스가 입출력과 같은 특정 이벤트를 위해 대기하는 상태를 의미
- 준비 : 프로세스가 실행을 위해 프로세서에 할당되는 것을 기다리는 상태를 의미
- 종료 : 프로세스가 모든 실행을 종료한 상태를 의미
- 프로세스 혹은 코어 하나당 하나의 프로세스가 실행 상태가 될 수 있음
1-3. Process Control Block
-
각각의 프로세스는 운영체제 상에서 프로세스 제어 블록(
PCB) 으로 대표되며, 여기에는 개별 프로세스의 구체적인 정보들이 포함되어 있음 -
프로세스 상태(
Process State) : 바로 위에서 언급한 생성,준비,실행,대기,종료 등의 상태 정보 -
프로그램 카운터(
Program Counter) : 카운터는 프로세스 내에서 다음에 수행되야 할 명령의 주소를 가리킴 -
일반 레지스터 및 CPU 레지스터(
Registers) :레지스터는 프로세서에서 자료를 보관하기 위한 장소로 사용되며, 위의 구조에서는 일반적인 레지스터와 함께 스택 포인터나 상태 코드 정보 등을 저장하는 레지스터가 포함되있음- 인터럽트 발생시 레지스터에 상태 정보가 제대로 저장되어 있어야, 나중에 프로세스가 재개될 때 정확한 상태로 수행될 수 있음
-
CPU 스케줄링 정보(
CPU-scheduling information) : 스케줄링 관련 정보를 포함하며, 여기에는 프로세스 우선순위나 스케줄링 큐에서 가리킬 포인터 등이 있음 -
메모리 관리 정보(
Memory-management information) : 페이지 및 세그먼트 테이블이나 레지스터의 공간 제한 등의 프로세스 메모리 관련 정보를 포함하고 있음 -
프로세스 계정 정보(
Accounting information) : CPU 사용량, 시간 제한, 계정 혹은 프로세스 번호 등의 정보를 포함하고 있음 -
입출력 상태 정보(
I/O status information) : 프로세스에 할당된 입출력 디바이스 목록이나, 열린 파일 목록 등의 정보를 포함하고 있음
1-4. Threads
-
스레드는 프로세스 내에서 개별 실행 흐름의 작은 단위를 의미함
-
지금 까지 언급된 프로세스는 엄밀히 말해 싱글 스레드 기반의 수행(
single thread of execution) 을 말하는 것이며, 오늘날 보통의 프로세스 컨셉은 프로세스가 다수의 스레드를 사용하는 것을 허용함 -
이는 특히 멀티코어 환경에서 여러 스레드가 병렬적으로 작업을 수행하게 할 수 있다는 점에서 이점이 있음
-
멀티 스레딩을 지원하는 환경에서 프로세스 제어 블록은 개별 스레드의 정보까지 포함하는 식으로 확장됨
2. Process Scheduling
- 멀티 프로그래밍의 목적은 결국 CPU 자원의 활용을 최대한으로 높이고자 프로세스가 항상 실행될 수 있도록 하는 데 있음
- 이를 위해 프로세스 스케줄러(
process scheduler) 은 적절한 프로세스들을 선택하여 자원을 분배하여 실행될 수 있도록 함 - 코어 하나 당 하나의 프로세스를 실행시킬 수 있기 때문에, 실행되야 할 프로세스의 수가 코어의 수를 초과하면 나머지 프로세스들은 사용 가능한 코어가 나올 때 까지 대기 상태로 있어야 하는데, 스케줄러가 여기서 자원 분배 과정을 조정하는 것
- 여기서 메모리에 올라와 있는 프로세스의 수를
degree of multiprogramming으로 표현함
- 여기서 메모리에 올라와 있는 프로세스의 수를
2-1. Scheduling Queues
- 일단 프로세스가 실행을 위해 메모리에 올라가면 준비 큐(
ready queue)에 올라가서 CPU 코어와 같은 자원을 배분받기 위해 기다리게 됨 - 준비 큐 외에 대기 큐(
wait queue) 라는 것도 존재하는데, 이는 특정 이벤트의 발생을 위해 대기 상태로 있는 프로세스들이 보관된 큐임 - 프로세스가 처음 생성되면 준비 큐로 들어가 실행 상태가 될 때 까지 기다리며, 자원을 배분받으면(
dispatched) 실행 도중에 여러 이벤트를 발생시킴 - 이후 입출력 요청과 같은 이벤트가 발생하여 프로세스는 대기 큐로 들어가 이벤트가 끝날 때 까지 대기할 수도 있고, 프로세스가 자식 프로세스(
child process) 를 생성하여 자식 프로세스가 종료될 때까지 대기할 수도 있음 - 혹은 실행중이던 프로세스가 코어에서 인터럽트 발생이나 시간 초과 등으로 인해 강제로 제거될 경우에는 다시 준비 큐로 들어갈 수도 있음
2-2. CPU Scheduling
- CPU 스케줄러의 역할은 앞에서 언급했듯 준비 큐에 있는 프로세스 중 적절한 것들을 선택해서 자원을 분배하는 것임
- 최대한 많은 프로세스들이 실행될 수 있도록, 스케줄러는 빈번히 실행되면서 특정 프로세스로부터 강제로 자원을 회수하여 다른 프로세스에 할당할 수도 있음
- 문제는 메모리에 올라갈 수 있는 프로세스의 수는 한정되있기 때문에, 기존에 너무 많은 메모리가 실행 혹은 대기 상태로 있을 경우에 새로운 프로세스를 준비 큐에 올리지 못하는 상황이 발생할 수 있음
- 이를 위해 몇몇 운영체제들은 메모리에서 특정 프로세스를 제거하여 멀티프로그래밍의 정도를 줄인 후 나중에 프로세스가 재개되야 할 시점에 이를 다시 메모리에 올릴 수 있도록 하는데, 이를 가리켜
swapping이라고 표현함- 프로세스를 메모리로부터 제거하는 것은
swapping out이라고 표현
- 프로세스를 메모리로부터 제거하는 것은
- 스와핑은 결국 메모리 공간이 부족하여 가용 공간을 확보해야 하는 상황에 유용한 것
2-3. Context Switch
- 앞에서 언급했듯이 인터럽트가 발생하면 운영체제는 CPU 코어를 현재 작업을 수행하던 것에서 커널 실행을 위한 모드를 바꾸도록 변경함
- 이 경우 시스템 상에서 변경된 코어에서 실행중이던 프로세스의 상황 정보(
context of process)를 추후 프로세스의 재개를 위해 저장해둘 필요가 있는데, 이는 프로세스 제어 블록에 저장됨- 다시 말해 상황 정보에는 앞서 언급한 프로세스 제어 블록에 저장되는 상태 정보와 같은 것들이 포함되는 것
- 이렇게 프로세스에서 자원을 회수하고 상황 정보를 잠시 프로세스 제어 블록에 저장한 후, 자원을 다시 다른 프로세스에 배분하여 실행될 수 있도록 하는 것을
Context Switching이라고 표현함 - 커널이 기존 프로세스의 상황 정보를 프로세스 제어 블록에 저장시킨 후, 새로 실행될 프로세스의 상황 정보를 로드해서 실행될 수 있도록 하는 것
- 컨텍스트 스위칭이 수행되는 시간은 이것이 수행되는 동안 시스템이 다른 작업을 수행하지 않기 때문에 완전한 오버헤드로 간주됨
3. Operations on Processes
3-1. Process Creation
-
프로세스가 새로운 프로세스를 생성할 수 있는데, 여기서 만드는 것이 부모 프로세스이고 만들어진 것이 자식 프로세스에 해당됨
- 부모 프로세스가 자식 프로세스를 생성하는 과정은 위의 그림과 같이 결과적으로 트리 형태로 나타남
-
유닉스 계열의 경우 보통 새로운 프로세스는
fork()시스템 호출을 통해 생성된 후 실행되며, 실행이 끝나면exec()호출을 통해 실행이 끝났음을 알린 후 프로세스의 메모리 공간에 기존의 프로그램을 새로운 프로그램으로 대체하여 새로운 작업을 실행할 수 있도록 함(After a fork() system call, one of the two processes typically uses the exec() system call to replace the process’s memory space with a new program.)
-
대부분의 운영체제는 여러 프로세스를 프로세스 식별자(
Process Identifie, PID)를 통해 구분하는데 보통 정수값(integer value)을 가짐- 리눅스의 경우 시스템이 시장되면
systemd프로세스가 여러 프로세를 생성하게 되는데, 이것이 곧 최초 부모 프로세스에 해당되며 pid 값이 1이 됨
- 리눅스의 경우 시스템이 시장되면
-
프로세스가 보통 자식 프로세스를 생성하게 되면, 자식 프로세스는 필요한 자원을 운영체제로부터 얻을 수도 있고 부모 프로세스의 자원을 일부 얻어 한정적으로 사용할 수도 있음
-
자식 프로세스의 실행은 보통 부모 프로세스와 자식 프로세스들이 동시적으로 실행되거나(execute concurrently) , 자식 프로세스가 모두 종료될 때 까지 부모 프로세스가 대기하는 형태가 될 수도 있음
- 만일 부모 프로세스를 대기시켜야 하는 경우라면
wait()시스템 호출을 통해 자식 프로세스가 종료될 때까지 대기시킴
- 만일 부모 프로세스를 대기시켜야 하는 경우라면
-
또한, 자식 프로세스 생성으로 인한 주소 공간(Address space)은 부모 프로세스와 동일한 프로글매 및 데이터를 복제하여 적재하거나, 자식 프로세스가 새로운 프로그램을 메모리에 적재하여 실행하는 형태가 될 수도 있음
- 부모 프로세스를 복제하는 것은 부모 프로세스와 자식 프로세스 간의 통신(communication)을 더욱 쉽게 한다는 이점이 있음
3-2. Process Termination
-
위에서 언급했듯 프로세스는 마지막 구문(final statement)의 실행이 끝나면 종료 상태가 되면서 운영체제로 하여금
exit()시스템 호출을 통해 스스로를 제거할 것을 요청함 -
자식 프로세스가 종료되는 경우에는 정수 형태의 상태값을
wait()을 통해 대기중인 부모 프로세스에게 반환하고 물리, 가상 메모리와 입출력 머퍼 등을 포함한 모든 프로세스 자원은 운영체제에 의해 메모리에서 해제됨 -
프로세스의 종료는 윈도우의
TerminateProcess()호출과 같이 부모 프로세스에 의해 종료되거나, 유저나 잘못 실행중인 프로세스가 임의로 다른 유저의 프로세스를 종료할 수도 있음- 부모 프로세스에 의해 종료될 경우에는 부모 프로세스가 반드시 자식 프로세스의 신원을 알아야 하기 때문에 자식 프로세스를 새롭게 생성할 때 pid와 같은 신원정보가 전달되야 함
-
부모 프로세스가 자식 프로세스를 종료하는 것은 보통 다음과 같은 경우로 나눌 수 있음
- 자식 프로세스가 허용된 자원의 범위를 초과하여 사용한 경우
- 자식 프로세스에 할당된 작업이 더 이상 필요한 것이 아니라고 판단될 경우
- 부모 프로세스가 종료되야 할 때, 운영체제가 자식 프로세스가 계속해서 실행되는 것을 허용하지 않는 경우(
Cascading termination)
-
wait()호출은 자식 프로세스가 종료되면서 남긴 상태값을 얻을 수 있도록 하는 매개변수(parameter)을 전달하는데, 결과적으로 종료된 자식 프로세스의 pid와 같은 신원을 리턴하도록 함으로써 부모 프로세스가 어떤 자식 프로세스가 종료되었는 지 알 수 있도록 함pid_t pid; int status; pid = wait(&status);- 만일 자식 프로세스의 실행이 끝났는데도 부모 프로세스가
wait()을 호출하지 않은 경우에는 이를zombie process라고 부름 - 유닉스 계열에서는 이런 식으로 자식 프로세스가 사실상 고아 상태로 방치될 경우
init프로세스를 이러한 프로세스의 새로운 부모 프로세스가 되도록 하고 있음 init프로세스가 주기적으로wait()을 호출하여 실행이 끝난 후exit()을 호출한 상태인 프로세스를 모아 이들의 pid와 프로세스 테이블 항목을 모두 할당 해제하는 것
- 만일 자식 프로세스의 실행이 끝났는데도 부모 프로세스가
4. Interprocess Communication
- 보통 프로세스가 다른 프로세스와 그 어떤 데이터도 공유하지 않을 경우 두 프로세스가 서로 독립적이라고 표현함(independant)
- 반대로 프로세스가 실행을 통해 서로에게 영향을 끼치는 경우에는 두 프로세스 간에 협력한다고 표현함(cooperating)
- 프로세스가 서로 협력적인 데에는 정보 교환, 더욱 빠른 작업 수행, 시스템 모듈화 등의 이유가 있음
- 협력적인 프로세스는 프로세스 간 통신 매커니즘(
Interpersonal communication mechanism, IPC)을 필요로 하는데, 이는 간단히 말해 프로세스 간에 데이터를 서로 공유할 수 있는 매커니즘을 의미함 - 이러한 IPC는 크게 공유 메모리 모델(
Shared memory)과 메시지 전달 모델(Message passing)로 나눠볼 수 있음 - 공유 메모리 모델은 운영체제에 의해 프로세스들이 특정 메모리 공간을 공유하는 형태 이며, 여기서 프로세스들은 해당 공간에 있는 데이터를 읽거나 씀으로써 서로 간에 데이터를 교환하게 됨
- 메시지 전달 모델에서는 프로세스들이 서로 간에 메시지를 보내는 방식으로 커뮤니케이션이 이뤄짐
- 비교적 작은 크기의 데이터를 교환하는 경우에는 충돌을 피해야 하는 경우가 적기 때문에 메시지 전달이 적합하며, 데이터의 크기가 비교적 크거나 빠른 시간 동안 커뮤니케이션이 이루어져야 하는 경우에는 공유 메모리 모델이 적합함
- 공유 메모리 모델에서는 공유 영역에 대한 쓰기 결과가 즉시 보여지며, 커널의 간섭도 없기 때문
5. IPC in Shared-Memory Systems
- 공유 메모리 모델 하에서 프로세스 간 통신은 우선적으로 공유 메모리 영역을 구축하는 것으로부터 시작됨
- 보통 운영체제는 프로세스가 다른 프로세스의 메모리 영역에 접근하는 것을 허용하지 않지만, 공유 메모리 영역을 사용할 경우에는 두 프로세스 간에 이러한 제한을 없애게 됨
- 따라서 공유 메모리 영역의 데이터 및 위치 등의 형식은 운영 체제가 아닌 프로세스에 의해 결정됨
- 공유 메모리 영역에의 읽기 및 쓰기를 통해 데이터의 교환이 이루어지지만, 두 프로세스가 동시에 같은 곳에 쓰기를 실행하면 안됨 (동기화 문제)
- 두 프로세스 간의 관계를 생산자-소비자 관계로 보는 생산자-소비자 문제 (
consumer-producer problem) 는 버퍼 사용을 통해 이러한 동기화 문제 해결의 대표적인 매커니즘임- 데이터를 생산하는 쪽이 생산자, 소비하는 쪽이 소비자가 되며 보통 웹서비스에서 웹서버가 생산자, 클라이언트가 소비자로 흔히 비유됨
- 생산자가 데이터를 생성하면 이를 공유 메모리 영역의 버퍼에 보관하게 되며, 소비자는 버퍼에서 데이터를 꺼내 소비하는 구조로 데이터의 생성이 완료된 후에 소비가 이루어지는 순서가 담보됨으로써 동기화가 가능해지는 것
- 여기서 버퍼는 버퍼 사이즈의 제한 유무에 따라 비한정 버퍼(
unbounded buffer) 와 한정 버퍼(bounded buffer) 의 두 가지로 나뉘며, 한정 버퍼를 사용할 경우 소비자와 생산자는 각각 버퍼가 비어있거나 가득차있을 때 대기해야 함- 현실 세계에서 버퍼의 사이즈는 당연히 제한적이기 때문에, 생산자-소비자 문제를 다른 말로 한정 버퍼 문제로 부르기도 함
6. IPC in Message-Passing Systems
- 메시지 교환 모델에서는 공유 메모리 영역을 사용하지 않고 두 프로세스 간의 커뮤니케이션 과정에서의 동기화를 이룸
- 이는 커뮤니케이션을 하는 두 프로세스가 네트워크로 연결된 서로 다른 두 컴퓨터에 존재하는 등의 분산 환경에서 동기화를 실현할 수 있다는 점에서 유용함
- 교환되는 메시의 크기는 고정될 수도, 가변적일 수도 있으며 보통
send()와receive()의 두 연산의 실행을 사용함 - 두 프로세스가 메시지를 교환하기 위해서는 우선적으로 커뮤니케이션 링크(
communication link) 가 둘 사이에 반드시 존재해야 함
6-1. Naming
Direct Communication
- 두 프로세스가 직접적으로 커뮤니케이션할 때, 기본적으로 이름과 같은 서로의 신원 정보를 같이 넘기도록 대칭적(symmetric)으로 구성됨
- 여기서 커뮤니케이션 링크는 자동적으로 단 한개만 정확히 구축되며, 이로 인해 프로세스는 커뮤니케이션을 위해서 오로지 신원정보만 알면 됨
send(P, message) // 프로세스 P에게 전송
receive(Q, message) // 프로세스 Q로부터 수신
- 여기서 조금 변형을 가할 경우, 송신자만 수신자 정보를 넘기고 수신자는 송신자 정보를 꼭 넘기지 않아도 되도록 비대칭적(asymmetric)으로 구성할 수도 있음
send(P, message) // 프로세스 P에게 전송
receive(id, message) // 일단 들어온 메시지를 무조건 수신(송신자는 모름)
-
위의 코드에서
id변수는 커뮤니케이션이 발생한 프로세스의 이름으로 설정되는 것The variable id is set to the name of the process with which communication has taken place
-
직접 커뮤니케이션 방식은 프로세스의 신원 정보가 바뀌면 이와 연관된 커뮤니케이션상의 다른 프로세스들도 모두 수정해야 한다는 번거로움이 있음
Indirect Communication
- 두 프로세스의 간접적인 커뮤니케이션은 메일박스(
mailbox) 혹은 포트의 사용으로 설명할 수 있음 - 메일박스들은 고유의 신원정보를 가지며, 프로세스는 서로 다른 메일박스의 수 만큼 다른 프로세스와 통신할 수 있음
- 단, 두 프로세스가 서로 메일 박스를 공유하고 있다는 전제 하에서만 정상적인 커뮤니케이션이 일어나는 것이며 커뮤니케이션 링크 또한 공유되는 메일박스가 있어야 구축됨
send(A, message) // 메일박스 A로 전송
receive(A, message) // 메일박스 A로부터 수신
-
단, 하나의 메일박스를 세 개 이상의 프로세스가 바라보고 있는 경우에는 메일박스로부터 메시지를 수신할 때 어떤 프로세스가 이를 가져갈 것인 지를 정해야 함
-
메일박스의 소유권이 프로세스에 있을 경우에는 메시지를 받기만 하는 소유자와 보내기만 하는 사용자를 반드시 구분해야 하고, 운영체제에 소유권이 있을 경우에는 메일 박스는 그 자체로 독립적이며 그 어떤 프로세스에도 종속되지 않음
- 이는 다시 말해, 메일 박스가 특정 프로세스에 종속되어 있을 경우 해당 프로세스가 종료되면 메일박스도 사라짐을 의미함
-
보통은 프로세스가 메일박스를 생성할 경우, 해당 프로세스가 소유권을 가져가는 것이 기본이지만 필요에 따라 적절한 시스템 호출을 통해 다른 프로세스로 소유권을 넘길 수도 있음
The process that creates a new mailbox is that mailbox’s owner by default. Initially, the owner is the only process that c an receive messages through this mailbox. However, the ownership and receiving privilege may be passed to other processes through appropriate system calls.
6-2. Synchronization
- 보통은
send()연산과receive()연산의 두 가지가 기본이지만, 이를 동기적(synchronous) 혹은 비동기적(asynchronous) 인 방식으로 세분화할 수도 있음 - 동기적 송신(Blocking send) : 메시지가 완전히 수신되기 전까지 송신할 수 없음
- 비동기적 송신(Nonblocking send) : 송신 과정이 일어나면서 다른 작업도 수행할 수 있음
- 동기적 수신(Blocking receive) : 메시지를 온전히 수신할 수 있을 때까지 다른 작업을 차단하는 것
- 비동기적 수신(Nonblocking receive) : 수신자는 메시지가 유효하지 않거나
null이더라도 다른 작업을 차단하지 않음
6-3. Buffering
- 커뮤니케이션의 직, 간접 여부에 관계없이 교환되는 메시지들은 모두 일시적인 큐에 저장되는데, 이를 버퍼라고 부름
- 버퍼의 사이즈가 0일 경우에는 데이터를 보관할 수 없는 상태이기 때문에 송신자는 반드시 수신자가 메시지를 받을 때 까지 차단해야 함
- 버퍼의 사이즈가 0보다 큰 값으로 한정된 경우에는 버퍼가 가득 차지 않는 이상 송신자는 계속 메시지를 보낼 수 있으며, 가득 찰 경우에는 데이터를 넣을 여유 공간이 확보될 때까지 차단해야 함
- 버퍼의 사이즈가 무한한 경우에는 메시지가 끊임 없이 큐에 저장될 수 있기 때문에 송신자가 굳이 차단할 필요 없음
Reference
-
Abraham Silberschatz, Greg Gagne, Peter B. Galvin Operating System Concepts , Wiley