[Operating System] Process (Context, Creation, Switch)
Process Context는 프로세스가 실행될 때 필요한 컴퓨터 내 리소스의 집합을 의미한다.
User Context
운영체제가 직접 만들지 않고, 개발자가 작성한 프로그램이 만들어낸다.
실행 중인 프로그램의 명령어 (Code Segment)
전역 변수, 정적 변수 등 프로그램이 끝날 때 까지 유지되는 데이터 (Global Data)
함수 내부에 선언된 지역 변수들이 저장되는 LIFO 구조 공간 (User Stack)
System Context
운영체제가 직접 관리한다.
Kernel Stack - 커널 모드에서 실행될 때 함수 호출과 시스템 콜을 처리하기 위한 스택으로, 프로세스마다 독립적이다.
Process Control Block - 프로세스의 중요한 정보를 저장하는 자료구조로 프로세스의 상태, pid, 메모리 정보 등을 저장한다.
fork 시스템 콜은 자식 프로세스를 생성하고 생성된 자식 프로세스의 pid를 반환한다.
운영체제는 idle process로 이미 pid 0을 사용하고 있어 이 때 만들어지는 pid 값은 0이 나올 수 없다.
자식 프로세스를 생성한 후 2초동안 sleep 하게 되니 현재 프로세스는 대기 상태가 되고, 자식 프로세스가 메모리에 올라가고 실행된다.
실행되는 자식 프로세스의 코드는 부모 프로세스의 코드와 동일하지만, 자식 프로세스는 fork 시스템 콜이 수행된 직후부터 실행된다.
자식 프로세스가 만들어지면서 부모 프로세스의 PCB 레지스터 값들을 모두 복사받는데, 이 레지스터에는 다음 프로세스를 가리키는 ProgramCounter도 포함되어있으니..
자식 프로세스는 fork 시스템 콜을 실행하지 않았고, pid값은 기본 값인 0으로 초기화 되어 있으니 자식 프로세스는 glob++와 var++ 문장을 수행하게 된다. (이 때 변수는 자식 프로세스의 변수를 가리킨다)
fork 시 부모 프로세스의 메모리 공간이 자식 프로세스에게 복사되지만, 서로는 완전히 독립적인 프로세스임에 집중하자.
전역 변수를 포함한 Data는 모두 독립적으로 작동한다.
소스코드를 컴파일하면 보조 기억 장치에 실행 파일이 저장된다.
리눅스에서 a.out은 ELF 포맷을 따른다.
header - 전체에 대한 정보를 가진다
code - 컴파일된 바이너리 값을 가진다
data - 전역변수를 가진다
bss - 값이 지정되지 않은 전역변수를 가진다 (buffer)
stack - 지역변수를 가진다
실행 파일을 실행시키면 실행 파일의 데이터 구조를 바탕으로 프로세스가 만들어진다.
보조 기억 장치에 Virtual Address Space라는 4GB (2^32.. 32비트 기준) 짜리 공간이 만들어지는데, 0GB ~ 3GB까지는 User Context가, 3GB ~ 4GB 까지는 System Context가 자리잡는다.
이 때 코드는 어차피 변하지 않으니 공간을 효율적으로 사용하기 위해 실행 파일의 코드를 재활용하는 경우도 있다.
시스템 콜을 호출할 때는 code 부분에서 3GB ~ 4GB 사이에 있는 System Context에 엑세스 하게 된다.
프로세스의 상태가 new인 경우 보조 기억 장치에 로드되고, 프로세스가 메인 메모리에 올라가게 되면 해당 내용이 메인 메모리로 복사된다.
CPU는 Virtual Address Space에 있는 프로그램을 실행할 수는 없다. 그냥 프로세스 이미지를 보관하는 용도라고 생각하자.
Virtual Address Space의 커널은 실제로 디스크에 존재하는 커널이 아니다. 실제 커널은 메인 메모리에만 있고, 가상메모리의 공간은 실제 메모리의 커널 데이터를 가리키고 있다고 생각하면 된다.
프로세스의 상태가 ready인 경우 메인 메모리에 데이터가 모두 올려진 상태이다.
커널은 메모리에 하나만 올라가고, 모든 프로세스가 공유해서 사용한다.
Bus를 통해 커널에 접근할 때 동기화 매커니즘을 사용해 Bus Conflict를 방지하고, 여러 CPU 코어가 커널에 충돌 없이 접근할 수 있다.
Ampersand로 백그라운드로 실행한 new 상태인 프로세스의 정보는 보조 기억 장치의 Virtual Address에 올라가있는 상태이고, 시간이 돼 해당 프로세스가 메모리에 올라가면 ready 상태가 된다.
디스크는 단순히 파일만 저장하는게 아니라, 가상 메모리를 관리하는 역할도 수행한다.
독립성과 메모리 공간 확장을 통해 프로세스를 효과적으로 관리하기 위해 가상 메모리가 도입됐다.
Kernel Stack은 커널 내 함수를 호출할 때 사용되고, 스택에 정보를 저장하는 방식은 User Stack과 동일하다.
사용자 코드에서 시스템 콜을 호출하니 시스템 콜이 수행된 후 사용자 코드로 돌아가기 위해 해당 주소를 저장한다.
시스템 콜 처리 중 Disk Interrupt나 Clock Interrupt가 걸린 경우 해당 인터럽트를 처리하기 위해 함수를 호출하는데, 이 때 스택에는 직전 함수의 레지스터 값과 로컬 변수 값을 저장한다.
프로세스는 유저 모드와 커널 모드를 번갈아가면서 실행하게 된다.
프로세스에게 할당된 시간이 끝나거나 시스템 콜을 수행해야 하는 경우 커널 모드로 전환해 다른 프로세스를 할당하거나 시스템 콜을 수행한다.
마치 코루틴처럼.. 매우 짧은 시간에 실행되니 각 프로세스는 동시에 실행되는 것 처럼 보인다.
Mode Change는 같은 실행환경 내에서 유저 모드로 실행할지, 커널 모드로 실행할지를 의미하고
Context Switch는 프로세스 자체를 다른 프로그램으로 변경하는 작업을 의미한다.
실행하던 프로세스가 종료되거나, 현재 프로세스가 입출력 등 Blocking System Call을 호출하거나, 할당된 시간이 끝나거나, I/O Interrupt가 발생해 우선순위가 더 높은 프로세스가 깨어나는 경우 Context Switch가 발생한다.
Context Switch가 발생할 때 각 PCB 레지스터들의 값을 설정하고, PCB를 적절한 큐로 옮긴다.
이후 준비 상태에 있는 프로세스 중 어떤 프로세스를 실행할 지 결정하고, 선정된 프로세스를 실행할 수 있는 환경으로 설정한다.
프로세스 생성 과정을 살펴보자.
1. 새로 만들어질 프로세스의 정보를 담기 위한 PCB를 준비한다.
2. 부모 프로세스로부터 자식 프로세스의 pid를 준비하고 각 프로세스를 연결해준다.
3. User Context를 준비한다.
부모 프로세스의 code data stack을 그대로 자식 프로세스로 가져온다.
단순히 주소값만 복사하는게 아니고, 주소 값을 따로 할당하고 그 주소에 데이터도 넣어준다. (코드 제외)
4. 생성된 프로세스를 Ready 큐에 넣어준다. (PCB에 설정)
5. 자식의 pid를 부모에게 반환한다.
운영체제에서는 프로세스를 생성하는데에 걸리는 시간을 줄이고 메모리 사용 효율을 높이기 위해 자식 프로세스의 data statck 영역을 복사하는 작업을 최대한 늦추는데, 이 방식을 COW (Copy on Write) 이라고 부른다.
COW 방식을 사용하면 바로 data와 stack을 복사하지 않고 잠시동안 부모의 값을 그대로 사용한다.
이후 data 영역의 값을 조작할 경우 복사가 발생하는데.. JPA의 Lazy Loading과 비슷하다.
exec 명령어는 실행 중인 프로세스의 메모리 공간을 새 프로그램으로 덮어씌우는 역할을 수행하는데, COW 방식과 exec를 함께 사용할 때 매우 효과적이다.
운영체제에서 부모 프로세스와 완전히 다른 프로그램을 실행하는 자식 프로세스를 만들 때는 fork와 exec를 조합해서 사용하는데, fork로 만든 프로세스의 데이터를 exec를 통해 기존 User Context를 지우는 방식으로 동작한다.
COW 방식을 사용하면 fork가 호출됐을 때 부모의 메모리를 자식으로 복사하지 않고 공유하게 되며, 자식 프로세스가 exec을 호출하는 순간 기존 메모리를 새로운 실행 파일의 코드로 덮어쓰게 돼 불필요한 data stack 복사를 줄일 수 있다.
컴퓨터를 부팅하면 ROM에 저장된 BIOS가 실행된다.
BIOS는 보조 기억 장치에 있는 부트블럭을 읽어 부트로더를 메인메모리로 올려준다.
메모리에 올라갔으니 이제 CPU가 코드를 읽어서 실행하는데, 부트로더는 다시 보조 기억 장치로부터 운영체제 코드를 읽어 메모리에 올려준다.
CPU는 운영체제 코드를 실행하는데, 운영체제 코드는 pid가 0이며 모든 프로세스의 시조인 swapper process를 만든다. (idle process)
이 프로세스는 fork 등 시스템 콜로 만들어지지 않고, 운영체제가 수동으로 만들어준다.
CPU가 할 일이 없을 때는 저 swapper process가 CPU에게 배정돼 빈 루프를 계속 돈다.
프로세스가 다른 프로세스를 만들 때는 fork를 통해 PCB 등 기본 데이터를 확보하고 exec을 사용해 데이터를 교체한다.
'Computer Science > Operating System' 카테고리의 다른 글
[Operating System] 프로세스와 동시성 제어 (0) | 2025.03.28 |
---|---|
[Operating System] Process Termination / Thread (0) | 2025.03.19 |
[Operating System] 커널 구조와 프로세스 (0) | 2025.03.12 |
[Operating System] 운영체제 개요 (0) | 2025.03.10 |
[Ubuntu] 셸 스크립트 프로그래밍 (0) | 2023.09.11 |
댓글
이 글 공유하기
다른 글
-
[Operating System] 프로세스와 동시성 제어
[Operating System] 프로세스와 동시성 제어
2025.03.28 -
[Operating System] Process Termination / Thread
[Operating System] Process Termination / Thread
2025.03.19 -
[Operating System] 커널 구조와 프로세스
[Operating System] 커널 구조와 프로세스
2025.03.12 -
[Operating System] 운영체제 개요
[Operating System] 운영체제 개요
2025.03.10