디스크에 있는 것은 프로그램, 메모리에 로드된 것은 프로세스라고 한다. 프로세스는 Stack, Heap, Data, Code로 나뉜다.
- Stack : 함수의 매겨변수, 복귀 주소, 지역 변수와 같은 임시 데이터
- Data : 전역 변수
- Heap : 실행 중 동적 할당되는 메모리
- Code : 프로그램 코드
프로세스 상태는 현재의 활동에 따라 달라진다.
- New : 프로세스가 처음 생성될 때
- Ready : 프로세스가 프로세서에 할당되기를 기다릴 때
- Running : 프로세스가 할당되어 실행될 때
- Waiting : 프로세스가 이벤트를 기다릴 때
- Terminated : 프로세스가 실행이 끝날 때
각 프로세스는 운영체제에서 프로세스 제어 블록에 의해 표현된다. PCB가 가지고 있는 정보들
- 프로세스 상태 : 프로세스 상태를 가지고 있음
- 프로그램 카운터 : 프로세스가 다음에 실행할 명령어의 주소를 가르키고 있다
- CPU 레지스터 : 인터럽트가 발생할 때 어디까지 실행되었는지 상태를 가지고 있다
- CPU 스케줄링 정보 : 프로세스 우선 순위를 가지고 있다
- 메모리 관리 정보 : 메모리와 관련된 테이블 정보를 포함한다
- 회계 정보 : CPU 사용시간, 시간 제한, 저장 번호를 포함한다
- 입출력 상태 정보 : 할당된 입출력 장치 정보를 가지고 있다.
프로세스 스케줄링은 멀티 태스킹을 하게 한다. 프로그램이 실행되는 동안 사용자가 상호 작용할 수 있도록 프로세스 사이에서 CPU를 교체한다. 그래서 프로세스 스케줄러는 CPU에서 실행 가능한 여러 프로세스 중 하나의 프로세스를 선택한다.
프로세스가 시스템에 들어오면 Ready Queue 라는 리스트에 유지된다. 이 큐는 LinkedList로 저장된다. 헤더는 리스트의 첫번째와 마지막 PCB 알고 있다.
새로운 프로세스는 Ready Queue에 두고 실행을 위해 CPU를 할당받을 때까지 대기한다. 그럼 아래와 같은 이벤트가 발생할 수 있다.
- 프로세스가 I/O 요청을 공표하고 I/O 대기 큐에 놓인다.
- 프로세스는 새 자식 프로세스를 만들고 다음 자식의 종료를 기다리는 동안 대기 큐에 놓일 수 있다.
- 인터럽트 또는 타임 슬라이스가 만료되어 프로세스가 코어에서 강제로 제거되어 Ready Queue로 돌아갈 수 있다.
처음 두 경우는 대기 상태 -> 준비 상태 -> 대기 큐 로 프로세스가 종료될 때까지 반복한다. 그리고 종료 시점에 큐에서 제거되고 PCB 자원이 반환된다.
CPU 스케줄러의 역할은 Ready Queue에 있는 프로셋 중 선택된 하나의 프로세스에 CPU 코어를 할당하는 것
인터럽트는 운영체제가 CPU 코어를 현재 작업에서 뺏어 내어 커널 루틴을 실행할 수 있게 한다. 인터럽트가 발생한다면 시스템은 인터럽트 처리가 끝난 후에 문맥을 복구할 수 있도록 현재 실행 중인 프로세스의 현재 문맥을 저장할 필요 있다. 이때 문맥은 PCB에 표현된다. 일반적으로 커널 모드건 사용자 모드건 CPU의 현재 상태를 젖아하는 작업을 수행하고(state save) 나중에 연산을 재개하기 위하여 상태 복구 작업을 수행한다. (state restore) 이 작업을 문맥 교환이라고 한다.
운영체제는 프로세스 생성 및 종료를 위한 기법을 제공해야한다.
실행되는 동안 프로세스는 여러 개의 새로운 프로세스 들을 생성할 수 있다. 생성하는 프로세스는 부모 프로세스, 새로운 프로세스는 자식 프로세스라고 한다. 이 결과 프로세스는 트리를 형성한다.
프로세스 아이디, 정수형 시스템의 각 프로세스에 고유한 값을 가지도록 할당된다.
프로세스가 자식 프로세스를 생성 할 때, 자식 프로세스는 자원을 운영체제로 부터 직접 얻거나, 부모 프로세스가 가진 자원의 부분 집합만 사용하도록 제한 할 수 있다.
프로세스가 자식 프로세스를 생성할 때, 두 프로세스를 실행하는 방법은 2가지이다.
- 부모는 자식과 병향하게 실행을 계속한다.
- 부모는 일부 또는 모든 자식이 실행을 종료할 때 까지 기다린다.
주소 공간 측면에서 볼때
- 자식 프로세스는 부모 프로세스의 복사본이다( 자식 프로세스는 부모와 똑같은 프로그램과 데이터를 가진다)
- 자식 프로세스가 자신에게 적재될 새로운 프로그램을 가지고 있다.
프로세스가 마지막 문장의 실행을 끝내고, exit 시스템 콜을 사용하여 운영체제에 자신의 삭제를 요청하면 종료한다. 이 시점에서 프로세스는 자신을 기다리고 있는 부모 프로세스에 (wait 시스템 콜을 통해) 상태값(통상 정수값) 을 반환할 수 있다. 물리 메모리와 가상 메모리, 열린 파일, 입출력 버퍼를 포함한 프로세스의 모든 자원이 할당 해제되고 운영체제에 반환된다.
- 연쇄적 종료 : 몇몇 프로그램에서는 부모 프로세스가 종료한 이후에 자식 프로세스가 존재할 수 없어 모두 종료된다.
- 좀비 프로세스 : 프로세스가 종료할 때, 부모 프로세스가 아직 wait() 을 호출하지 않아 데이터가 남아 있는 경우
- 고아 프로세스 : 부모 프로세스가 wait 을 호출하는 대신 종료한 경우
프로세스는 독립적이거나 협력적인 프로세스일 수 있다. 프로세스가 시스템에서 실행 중인 다른 프로세스들과 데이터를 공유하지 않으면 독립적이고 데이터를 공유하면 협력적이다.
- 정보 공유 : 여러 응용 프로그램이 동일한 정보에 흥미를 느낄 수 있어서, 해당 데이터에 대해 병행적으로 접근할 수 있는 환경을 제공해야함
- 계산 가속화 : 태스크를 빨리 실행하고 싶을 때 서브 태스크로 나누어 각각 다른 서브태스크들과 병렬로 실행되게 한다
- 모듈성 : 스레드, 프로세스들을 나누어 모듈식으로 구성하길 원할 수 있다.
그래서 해당 일을 하기 위한 방식은 두가지가 있다.
- 공유 메모리
- 메세지 전달
공유 메모리를 사용하는 프로세스 간 통신에서는 통신하는 프로세스들이 공유 메모리 영역을 구축해아한다. 통상 공유 메모리 영역은 공유 메모리 세그먼트를 생성하는 프로세스의 주소 공간에 위치한다. 이 공유 메모리를 사용하고자 하는 프로세스는 이 세그먼트를 자신의 공간에 추가해줘야한다. 이때, 공유 메모리는 둘 이상의 프로세스가 접근하지 않게 제어해주어야 한다. “프로세스들이 메모리 영역을 공유할 것을 필요로 하며, 공유 메모리에 접근하고 조작하는 코드가 응용 프로그래머에 의해 명시적으로 작성되어야 한다.
메시지 전달 방식은 통신하는 프로세스 들이 네트워크에 의해 연결된 다른 컴퓨터들에 존재할 수 있는 분산환경에서 유용하다.(chat 프로그래밍) 메시지 전달 시스템은 최소 2가지 연산을 제공한다
- send(message)
- receive(message)
만약, 프로세스 P와 Q가 통신을 원한다면, 반드시 서로 메시지를 보내고 받아야 한다. 이들 사이에 통신 연결이 설정되어 있어야 한다. 하나의 링크와 send()/receive() 연산을 논리적으로 구현하는 방법은 3가지 있다.
- 직접 또는 간접 통신
- 동기식 또는 비동기식 통신
- 자동 또는 명시적 버퍼링
메시지 전달은 봉쇄형(blocking) 이거나 비봉쇄형(nonblocking) 방식으로 전달된다. 이 방식은 각각 동기식, 비동기식이라고 알려져 있다.
- blocking send : 송신하는 프로세스는 메시지가 수신 프로세스 또는 메일 박스에 의해 수신될 때까지 봉쇄된다.
- nonblocking send: 송신하는 프로세스가 메시지를 보내고 작업을 재시작한다.
- blocking receive : 메시지가 이용 가능할 때까지 수신 프로세스가 봉쇄된다.
- nonblocking receive : 송신하는 프로세스가 유효한 메시지 또는 null을 받는다.
통신하는 프로세스들에 의해 교환되는 메시지는 임시 큐에 들어가 있다. 이러한 큐를 구현하는 방식은 3가지 있다.
- 무용량(zero capacity) : 큐의 최대 길이가 0이다. 즉, 링크는 자체 안에 대기하는 메시지를 가질 수 없다.
- 유한 용량(bounded capacity) : 큐는 유한한 길이 n 을 가진다. 즉, 최대 n 개의 메시지가 그안에 들어있을 수 있다.
- 무한 용량( unbounded capacity) : 큐는 잠재적으로 무한한 길이글 가진다. 따라서 메시지들이 얼마든지 큐안에 대기할 수 있다.