Ch3. Process
목표
- 프로세스의 개별 구성 요소를 식별하고, 그것들이 운영체제에서 어떻게 표현되고 스케줄되는지?
- 프로세스가 운영체제에서 어떻게 생성되고 종료되는지
- 프로세스 간 통신 (IPC)을 공유 메모리와 메시지 전달 방식에 대해
- 파이프와 POSIX 공유 메모리를 이용해 프로세스 간 통신을 수행하는 프로그램
- 소켁과 원격 프로시저 호출을 사용해 클라이언트-버서 통신을 설명
1. 프로세스란?
실행중인 프로그램이다. -> 프로세스의 실행은 순차적으로 진행되어야 한다. 즉, 디스크에 실행 파일 형태로 저장되어 있는 프로그램이 메모리에 로딩되어 실행되면 프로세스가 된다.
- 구성요소
- 프로그램 코드 (Text Section)
- 현재 수행중인 활동 (PC, processor registers)
- 임시 데이터를 담는 스택 (Stack)
- 함수 매개변수, 반환 주소, 지역 변수 등
- 데이터 섹션 (Data section) : 전역변수들을 포함
- Heap : 실행중 동적으로 할당되는 메모리 영역
1-1. 프로세스의 상태
- 생성 : 프로세그가 생성
- 실행 : 실행중
- 대기 : 어떤 이벤트가 발생하길 기다리는 중
- 준비 : 프로세서에 할당되길 기다리는 중
- 종료 : 실행 종료
1- 2. 프로세스 컨트롤 블럭 (PCB)
각 프로세스의 여러 정보가 담겨있는 블럭
task conrtol block 이라고 하기도함
- 구성요소
- Process state - running, waiting, etc
- Program counter - 다음 실행 명령어의 위치
- CPU registers - 해당 프로세스에 사용되는 모든 레지스터의 값
- CPU 스케줄링 정보 - 우선순위, 스케줄링 큐 포인터 등
- 메모리 관리 정보 - 프로세스에 할당된 메모리 정보
- Accounting information - 사용한 CPU 시간, 시작 이후 경과 시간, 제한 시간 등
- 입출력 상태 정보 - 프로세스에 할당된 I/O 장치, 열린 파일 목록 등
2. 프로세스 스케줄링
CPU 사용률를 극대화하고, 프로세스를 CPU 코어에 빠르게 전환시킴
프로세스 스케줄러는 사용 가능한 프로세스 중에서 다음에 CPU에서 실행 할 것을 선택한다.
- 여러 개의 스케줄링 큐를 유지한다.
- 준비 큐 : 메인 메모리에 있는, 실행 준비가 된 모든 프로세스의 집합
- 대기 큐 : 어떤 이벤트를 기다리는 프로세스들의 집합
- 프로세스는 여러 큐 사이를 이동한다.
2-1. CPU 프로세스간 전환 (Context Switch)
context switch는 CPU가 한 프로세스에서 다른 프로세스로 전환될 때 발생하는 현상을 말
- CPU가 다른 프로세스로 전환할때, 현재 상태를 PCB에 저장하고, 새 프로세스의 저장된 상태를 불러온다.
- 프로세스의 컨텍스트는 PCB에 모두 저장된다.
- 컨텍스트 스위티 시간은 오버헤드이며, 전환중에는 실제 작업을 하지 못한다.
- 운영체제와 PCB가 복잡할수록 -> 컨텍스트 스위치 시간도 길어짐.
- 전환 시간은 하드웨어 지원 여부에 따라도 달라진다.
- 비용 절감을 위해, 어떤 CPU는 모든 레지터를 한 번에 저장/블러오는 단일 명령을 제공함
- 이 경우, 컨텍스트 스위치는 단지 현재 레지스터 집합의 포인터만 바꾸는 것으로 충분.
3. 프로세스의 생성
부모 프로세스가 자식 프로세스를 만든다. 자식 프로세스도 다른 프로세스 생성 가능 -> 프로세스 트리 형성
- 프로세스는 고유한 id를 가진다. -> 운영체제가 이걸로 추적하고 관리함
- 자원 공유 방식 (부모-자식간)
- 모든 자원 공유
- 일부만 공유
- 자원 공유 없음
- 실행방식
- 동시 실행 : 부모와 자식이 동시에 실행됨
- 부모 대기 : 자식 프로세스가 종료될 때까지 부모가 대
- 주소 공간 (Address Space)
- 자식 프로세스는 부모의 복제본으로 생성됨
- 자식 프로세스는 이후에 다른 프로그램을 로딩해서 실행할 수 있음
- Unix Example
- fork() : 새로운 프로세스 생성 system call, 환경변수, 열림 파일 fd 등 모두 상속됨
- exec() : 새 프로그램으로 교체 (덮어쓰기)
- wait() : 자식중 하나 종료되기를 기다다림
4. 프로세스 종료
- 정상 종료
- 프로세스가 마지막 문장을 실행한 후, exit() 시스템 호출을 통해 운영체제에게 종료를 요청함
- 비정상 종료
- abort() 시스템 호출로 강제 종료할 수 있음.
- 종료 이유
- 자식이 할당된 자원을 초과해서 사용하는 경우
- 자식에게 맡긴 작업이 더 이상 필요 없게 된 경우
- 부모가 종료될 예정이며, 우녕체제가 자식이 단독으로 계속 실행되는걸 허용하지 않는 경
- 어떤 시싀템은 부모가 먼저 죽으면 자식도 자동으로 죽이기한다.
- OS가 직접 이렇게함
- 연쇄 종료(Cascading Termination)
- ios 초기 버전이 이 방식
- 정상적인 자식 종료 대기 (wait())
- 좀비 프로세스
- 자식이 먼저 종료됐지만, 부모가 wait 을 호출하지 않음
- 자식이 죽었지만, 상태 정보가 커널에 남아 있음
- 고아 프로세스
- 부모가 먼저 종료됨, 자식은 아직 살아서 실행중
- OS가 pid 1 에게 자식의 관리를 넘김
5. 프로세스 상호간 통신
프로세스 상호간 통신은 두가지로 나눌 수 있다.
- 독립적인 프로세스 (Independent) -> 다른 것과 전혀 상관 없이 작동
- 협력하는 프로세스 (Cooperating) -> 다른 프로세스와 자원 공유 등 영향을 받음 협력 프로세스는 왜 필요할까?
- 정보 공유 (Information sharing)
- 계산 속도 향상 (Computation speedup)
- 모듈성 (Modularity)
- 편리성 (Convenience)
IPC (Interprocess Communication) 를 이용
- Shared Memory
- Messaging Passing
5-1. 생산자-소비자 문제
협력 프로세스에서 발생할 수 있는 문제이다. 생산자(프로세스1)는 데이터를 계속해서 만들고, 소비자(프로세스2)는 데이터를 계속해서 소비한다. 이 두 프로세스는 공유 버퍼에서 데이터를 주고 받는다. 여기서 문제는 생산자가 데이터를 씀과 동시에 소비자가 데이터를 읽게 된다 데이터에 문제가 발생한다. -> Race Condition 발생! -> 그래서 동기화 (Synchronization) 이 필수다.
- Unbounded Buffer (무한 버퍼)
- 이론상 버퍼 크기에 제한이 없음
- Producer는 언제든 데이터를 계속 넣을 수 있음
- 현실적으로는 메모리 한계 때문에 구현이 어려움
- 🔸 Bounded Buffer (유한 버퍼)
- 버퍼 크기가 고정됨 (fixed)
- 버퍼가 꽉 차면 Producer는 기다려야 하고,
- 버퍼가 비면 Consumer도 기다려야 함
5-2. Shared-Memory
프로세스들끼리 공통으로 사용하는 메모리 영역을 말함
- 공유 메모리의 사용은 운영체제가 아닌 사용자 프로세스가 직접 관리
- 즉, 프로세스가 직접 언제, 어떻게 접근할지 스스로 결정해야 함
- 여러 프로세스가 동시에 메모리를 건드리면 충돌이 발생할 수 있음 -> 동기화가 중요함
5-3. Messaging Passing
프로세스 간에 통신(communicate) 하거나 동시에 동기화(synchronize) 하도록 만드는 방법
- 프로세스들이 서로 공유 메모리 없이도 통신할 수 있도록 함
- 모든 통신은 메시지를 주고받는 방식으로 이뤄짐 ✔ 즉, shared variable 없이도 IPC 가능!
5-3-1 Direct Communication
프로세스끼리 메시지를 주고받을 때, 서로의 이름을 명시적으로 알아야 함
1 2 send(P, message) // P에게 메시지를 보냄 receive(Q, message) // Q로부터 메시지를 받음
- 통신이 요청되면 운영체제가 자동으로 링크를 설정함
- 하나의 링크에는 딱 두 프로세스만 연결됨, 두 프로세스간 하나의 링크만 존재
- 단방향일 수 있지만, 보통은 양방향
5-3-2 Indirect Communication
프로세스는 서로를 직접 지정하지 않고, mailbox(메일박스) 또는 port(포트)를 통해 메시지를 주고받음
1 2 send(mailbox_id, message) receive(mailbox_id, message)
- 메일 박스의 특징
- 고유한 id를 가짐
- 공통된 메일박스를 공유한 프로세스끼리만 통신 가능
- 하나의 링크에 여러 프로세스 연결 가능
- 프로세스 쌍은 여러개의 메일박스를 공유할 수 있음
- 링크는 단방향, 양방향 모두 가능
- 발생 가능한 문제
- 메일박스에 도착한 내용을 누구 읽을 것인가?
- 1. 메일박스를 최대 2개 프로세스만 공유하게 제한
- 2. 한 번에 한 프로세스만 receive 실행 허용
- 3. 운영체제가 수신자 자동 선택
5-4 Syncronization (동기화)
메시지 전달은 blocking
or non-blocking
으로 동작할 수 있다.
Blocking
프로세스가 상대가 준비될 때까지 멈추는 방식
- 보낸 쪽(sender)은 상대가 받을 때까지 멈춤
- → 상대가 받을 준비가 될 때까지 기다림
- 받는 쪽(receiver)은 메시지가 도착할 때까지 멈춤
- → 없으면 기다림
Non-Blocking
프로세스가 상대 상태에 상관없이 그냥 진행하는 방식
- 메시지를 보내고 바로 다음 코드 실행
- 만약 받을 준비가 안 되어 있으면 → 메시지 손실 위험 있음
- 따라서 Queue를 이용하면 손실 없이 처리 가능
- 받을 준비가 되어 있지 않아도 그냥 실행
- 결과는:
- 유효한 메시지 받음 (메시지 도착함)
- 또는 Null 메시지 (메시지 없음)
5-5 Buffering
메시지를 바로 받지 못할 경우를 대비해서, 통신 링크에 Queue(대기열) 형태로 메시지를 저장해두는 방식
Zero Capacity (용량 0)
- 버퍼가 없음 → 메시지를 저장할 공간 없음
- 보내는 쪽은 반드시 받는 쪽이 준비되어 있어야 보낼 수 있음
Bounded Capacity (유한한 용량)
- 메시지를 저장할 고정된 크기의 큐가 존재 (ex: 최대 10개)
- 큐가 꽉 차면(sender는 대기) / 큐가 비면(receiver는 대기)
- 현재 가장 많이 사용하는 방식
Unbounded Capacity (무한한 용량)
- 큐의 크기가 이론적으로 무한대
- 아무리 많은 메시지를 보내도 다 저장됨 → sender는 절대 기다리지 않음
6. Pipe (파이프)
파이프는 한 프로세스에서 다른 프로세스로 데이터를 전달하는 통로(conduit)
파이프의 종류
- Ordinary Pipe
- 반드시 부모-자식 관계가 필요
- 파이프를 만든 프로세스 안에서만 사용가능 (외부 사용불가)
- Named Pipe
- 서로 관계가 없어도 사용 가능
- 파이프에 이름이 있으므로 외부에서도 사용가
6-1. Ordinary Pipe
일반적인 파이프는 생산자-소비자 관계로 통신하고 있다 파이프는 한쪽에서 쓰고(생산자), 다른 한쪽에서 읽는다 (소비자) 단방향 형태로 동작한다. (양방향도 가능 - 파이프 두개 이용)
6-2. Named Pipe
이름이 있는 파이프. 파일시스템 상의 특별한 파일로 존재 → 여러 프로세스가 관계 없이 공유 가능