[프로세스(Process)]
- 실행중인 프로그램으로 크게 두 가지 프로세스로 구분 짓는다.
포그라운드 프로세스(foreground process) - 사용자가 볼 수 있는 공간에서 실행되는 프로세스
백그라운드 프로세스(background process) - 사용자가 볼 수 없는 공간에서 실행되는 프로세스
백그라운드 프로세스는 다시 다음과 같이 두 가지로 분류할 수 있다.
사용자와 직접 상호작용이 가능한 백그라운드 프로세스
사용자와 상호작용하지 않고 그저 정해진 일만 수행하는 프로세스
모든 프로세스는 실행되기 위해선 CPU가 필요하다. 그러나 CPU 는 한정되어있기 때문에 프로세스들은 돌아가면 한정된 시간 만큼만 CPU를 이용한다.
자신의 차례에 정해진 시간만큼 CPU 를 이용하는데 타이머 인터럽트가 발생하면 차례를 양보하면서 서로 CPU 를 차례로 점유하여 사용한다.
[프로세스 제어 블록(PCB, Process Control Block)]
- 빠르게 번갈아 수행되는 프로세스들을 관리해주는 기능으로 프로세스 관련 정보를 저장하는 자료 구조이며, 태그 같이 정보를 제공하는 역할을 하며, 프로세스 생성 시 커널 영역에 생성 및 종료시 폐기된다.
PCB에 담기는 정보(일부)
프로세스 ID (PID) - 특정 프로세스를 식별하기 위해 부여하는 고유한 번호
레지스터 값 - 한정적인 CPU 를 프로세스들이 사이클을 돌면서 사용하는 중에, 사이클을 돌아서 각 프로세스 들이 자신의 차례가 다시 올 때 이전 사이클에서 사용했던 CPU 의 레지스터 상태를 알고 있어야 프로세스를 이어서 수행할 수 있기 때문에 프로세스는 자신의 실행 차례가 오면 이전까지 사용한 레지스터 중간 값을 모두 복원하기위해 (실행을 재개하기 위해), 프로그램 카운터, 스택 포인터 등의 레지스터 값을 저장한다.
프로세스 상태 - 입출력 장치를 사용하기 위해 기다리는 상태, CPU를 사용하기 위해 기다리는 상태, CPU 이용 중인 상태 등을 저장
CPU 스케줄링 정보 - 프로세스가 언제, 어떤 순서로 CPU를 할당 받을지에 대한 정보
메모리 정보 - 각 프로세스가 어느 주소에 저장되어 있는지에 대한 정보, 페이지 테이블 정보 등
사용한 파일과 입출력 장치 정보 - 할당된 입출력장치, 사용 중인 파일 정보 등
[문맥 교환(context switch)]
- 문맥(context)이란 기존에 실행되던 프로세스의 중간 정보(중단되기 전까지의 정보)를 칭하는 것으로 프로그램 카운터등의 각각의 레지스터 값, 메모리 정보, 열었던 파일, 사용한 입출력 장치등의 상태들을 백업해둠으로써 다음 차례가 왔을 때 실행을 재개하기 위해 백업해둔 정보를 해당 프로세스에 맞춰서 바꿔주는 작업이다.
[프로세스의 메모리 영역]
코드(텍스트) 영역 - 실행할 수 있는 코드, 기계어로 이루어진 명령어가 저장되는 영역이며, 데이터가 아닌 CPU가 실행할 명령어가 담기기에 쓰기가 금지된 영역이다(== read-only)
데이터 영역 - 프로그램이 실행되는 동안 유지할 데이터를 저장한다 (전역 변수 등)
코드 영역과 데이터 영역의 크기는 변하지 않기 때문에 정적 할당 영역이라고도 부른다.
힙 영역 - 프로그램을 만드는 사용자, 즉 프로그래머가 직접 할당할 수 있는 저장 공간(할당된 공간은 반환하는 과정 (==가비지 컬렉션)을 해줘야 메모리 누수(Memory Leak)를 관리 할 수 있다)
보통 힙 영역은 낮은 주소 -> 높은 주소 순으로 할당된다.
스택 영역 - 데이터가 일시적으로 저장되는 공간으로 잠깐 사용되는 값들을 저장한다(매개 변수, 직역 변수 등)
보통 스택 영역은 높은 주소 -> 낮은 주소 순으로 할당된다.
힙 영역과 스택 영역의 크기는 변하기 때문에 동적 할당 영역이라고도 부른다.
스택 영역과 힙 영역이 같은 방향으로 할당되면 커질 수 있는 공간이 한계가 있어 충돌우려가 있기 때문에 보통 반대 방향으로 할당된다.
프로세스 상태와 계층 구조
[프로세스 상태]
- 프로세스 상태는 운영 체제마다 차이가 있으나 공통적으로 관리하는 상태가 다음과 같이 다섯 종류가 있다.
생성 상태 - 이제 막 메모리에 적재되어 PCB 를 할당받은 상태로 실행 가능한 상태로 준비된다면 준비 상태로 넘어간다.
준비 상태 - CPU를 할당 받으면 바로 실행이 가능하나 실행 차례가 오지 않았기 때문에 기다리는 상태로, 실행 차례가 된다면 실행 상태로 넘어간다.(==디스패치)
실행 상태 - CPU를 할당받아 실행 중인 상태로 할당된 시간 모두 사용 시(타이머 인터럽트 발생시) 다시 준비 상태로 돌아가고, 실행 도중 입출력장치를 사용하면 입출력 작업이 끝날 때까지 대기 상태로 넘어간다.
대기 상태 - 프로세스가 실행 도중 입출력 장치를 사용하는 경우 입출력 작업은 CPU 에 비해 느리기 때문에 프로세스는 대기 상태가 된다. 입출력이 끝나면(입출력 완료 인터럽트 발생시) 준비 상태로 넘어간다.
종료 상태 - 프로세스가 종료된 상태로 PCB, 프로세스의 메모리 영역을 정리한다.
[프로세스 계층 구조]
- 프로세스는 실행 도중 시스템 호출을 통해 다른 프로세스를 생성 가능하며, Windows 를 제외한 보통의 OS들은 프로세스를 계층 구조로 관리한다.
부모 프로세스 - 새 프로세스를 생성한 프로세스
자식 프로세스 - 부모 프로세스에 의해 생성된 프로세스
부모 프로세스와 자식 프로세스는 별개의 프로세스 이므로 각기 다른 PID 를 가진다. 일부 OS에선 자식 프로세스 PCB에 부모 프로세스의 PID 즉, PPID(Parent PID) 를 명시하기도 한다.
부모 프로세스는 여러 자식 프로세스를 생성할 수 있고, 자식 프로세스 또한 자식 프로세스를 생성 시킬 수 있는데 이렇게 생성시켜 나가다 보면 자연스럽게 계층 구조가 형성된다.
데몬 프로세스 - 컴퓨터 부팅시 생성되는 최초의 프로세스가 생성한 자식 프로세스
최초의 프로세스는 pstree 명령어를 통해서 확인할 수 있다.
[프로세스 생성 기법]
부모 프로세스 = frok 시스템 호출을 통해 자신의 복사본을 자식 프로세스로 생성하며, 부모 프로세스의 자원을 그대로 상속시킨다.
자식 프로세스 = exec 시스템 호출을 통해 자신의 메모리 공간을 다른 프로그램으로 교체(덮어쓴다)하여 코드/데이터 영역은 실행할 프로그램 내용으로 바뀌고 나머지 영역은 초기화된다.
-------------------------------------------------------------------------[ 여 담 ]----------------------------------------------------------------------
모든 프로세스는 컴파일 과정에서 각 코드 파일에 설정된 논리 주소들이 링킹 과정에서 가상 주소로 배치된다. 이후 OS 는 해당 프로세스를 페이지 단위로 나누는데, 이 모든 페이지들이 물리 메모리에 즉시 적재되는 것은 아니기 때문에 MMU 의 매핑 과정에서 페이지 폴트가 발생할 수 있다.
MMU 간단설명-> 메모리 주소 공간 (tistory.com)
페이지 폴트(Page Fault) - MMU 가 OS 에서 제공하는 페이지 테이블을 통해 가상 주소를 물리 주소로 매핑하려 할 때, 해당 페이지가 아직 물리 메모리에 적재되지 않아 매핑할 수 없는 경우를 의미하는 것으로, 페이지 폴트 발생시 OS 는 보조 기억 장치에서 해당 페이지의 내용을 물리 메모리에 로드해줌으로써 해결한다.
(페이지 폴트는 가상 메모리 시스템에서 물리 메모리를 조금 더 효율적이고 사용률을 극대화 시키는 과정에서 발생되는 현상이다. 페이지 폴트로 인해 보조 기억 장치에서 로드되는 과정은 속도가 느리기 때문에 잦은 페이지 폴트는 프로세스 성능에 부정적인 영향을 미칠 수 있다)
'물리 주소로 매핑'하는 것은 페이지의 가상 주소를 해당 페이지가 로드되어있는 물리 메모리의 물리 주소와 연결하여 실제로 물리 메모리 공간을 사용할 수 있게 연결해주는 과정으로
코드, 데이터 영역 - 프로세스 시작 시 물리 주소로 매핑되어 프로세스가 종료될 때까지 유지된다.
스택 영역 - 프로세스 시작시 OS에서 설정된 최대 크기의 물리 주소로 매핑 되지만, 스택 영역에서 실제로 사용하는 공간은 프로세스의 스택 호출에 따라 유동적으로 변한다
힙 영역 - 현재 프로세스가 실행되는 동안 필요한 구간에서만 물리 주소로 매핑되고, 사용이 끝나면 매핑이 해제된다.
프로세스 실행과 동시에 프로세스의 페이지들은 다음과 같은 경우를 제외하고는 최소 한 번의 페이지 폴트가 발생한다고 볼 수 있다.
초기화 페이지 - 프로그램이 시작할 때 반드시 필요한 코드나 데이터를 담고 있는 메모리 페이지로 프로그램 실행 초기에 자주 접근될 가능성이 높아 OS 가 미리 메모리에 로드함으로써 페이지 폴트가 발생하지 않는다
대표적으로 프로그램의 진입점에 위치한 코드 페이지, 필수 라이브러리, 전연 변수 초기화, 스택 초기화 등의 내용이 담긴 페이지가 있다
프리페칭 - OS 가 프로그램이 앞으로 필요로 할 가능성이 높은 데이터를 미리 메모리에 로드함으로써 페이지 폴트가 발생하지 않도록 할 수 있다.
대표적으로 연속적 메모리 접근 패턴(연속적인 자료 구조를 접근하거나 순차적으로 배치된 명령어를 읽을 때)이 있을 때 OS 는 현재 페이지와 인접한 페이지들을 미리 메모리에 로드할 수 있다.
(최초로 읽은 페이지가 미리 로드되지 않은 경우, 페이지 폴트가 발생하지만, 프리페칭을 통해 인접한 페이지가 같이 메모리에 로드되기 때문에, 이 인접한 페이지들은 페이지 폴트 없이 접근할 수 있다.
인접한 페이지를 읽는 범위는 OS 마다 다르게 구현되어 있다)
또한, OS 의 패턴 인식을 통해 프로그램의 이전 또는 과거 메모리 접근 패턴을 분석해 특정 패턴을 예측하여 필요할 경우 미리 메모리에 로드하는 할 수 있다.
페이지 공유 - 이미 메모리에 로드된 페이지를 다른 프로세스와 공유하는 경우
프로그램 시작과 동시에 로드된 메모리나, 위의 초기화 페이지 또는 프리페칭 등을 통해 이미 로드된 메모리를 공유하는 페이지의 경우 당연하게도 페이지 폴트가 발생하지 않으며, fork 로 인한 프로세스 복사, 쓰레드 생성 과정에서 메모리를 공유하는 경우에도 페이지 폴트가 발생하지 않는다.
---------------------------------------------------------------------------------------------------------------------------------------------------------
위 내용을 기반으로 fork 직후의 자식 프로세스는 COW(Copy-On-Write)기법을 통해 부모 프로세스를 복사를 하나 이는 부모 프로세스의 페이지, 즉 가상 주소를 참조하는 것으로, 이후 참조된 페이지를 수정하려(Write) 할 때 해당 페이지에 대한 물리 주소가 매핑 된다.
즉, 수정을 하지 않는다면 자식 프로세스는 물리 주소를 따로 매핑하지 않고 부모 프로세스의 페이지를 그대로 참조하면서 실행되기 때문에 메모리 공간이 절약된다.
메모리에 새로운 프로세스를 처음부터 생성하지 않고 fork 를 통해 부모 프로세스를 복사하면, 부모 프로세스에서 사용했던 자원들을 새 메모리에 다시 할당 해주는 작업과 환경 변수의 복사, 추가 메모리 로드 등 프로세스 생성 과정에서 발생하는 복잡한 연산을 간소화할 수 있어 빠르게 새로운 프로세스를 생성 할 수 있다.
또한, fork 된 프로세스에서 exec 를 호출하면, 기존 프로세스의 가상 메모리 공간(스택, 힙, 데이터, 코드)이 완전히 새로운 프로그램의 메모리 공간으로 대체되며, 그 이후에는 일반적인 프로그램 실행과 동일하게 물리 메모리 매핑 과정이 이뤄진다. 이 과정을 통해서 새 프로그램의 프로세스를 더 빠르게 생성할 수 있다.
부모 프로세스가 자식 프로세스를 실행하고 프로세스의 계층 구조를 이루는 과정은 fork 시스템 호출과 exec 시스템 호출이 반복되는 과정이라고 볼 수 있다. (fork-exec 구조로 Unix 에서 사용되는 프로세스 생성 기법이다)