[운영체제] 프로세스 | 프로그램이 실행되기까지의 여정

 

지난 시간에는 운영체제의 세 가지 핵심 역할인 가상화, 동시성, 영속성에 대해 알아보았다. OS가 한정된 하드웨어 자원을 어떻게 추상화하여 여러 프로그램에 '내 것처럼' 쓸 수 있게 해주는지에 대한 이야기였다. 오늘은 그 '프로그램'이 실제로 어떻게 실행되는지, 디스크에 잠자고 있던 코드 덩어리가 어떻게 생명을 얻어 CPU 위를 뛰어다닐 수 있는지에 대한 여정을 따라가 보려 한다.

수업은 네 가지 핵심 질문으로 시작되었다. 이 질문들을 따라가다 보면, 프로그램과 프로세스의 관계를 명확히 이해할 수 있을 것이다.


1. 프로그램 실행이란 무엇인가? (What is program execution?)

가장 근본적인 질문이다. 프로그램 실행이란 간단히 말해 "프로그램에 의해 기술된 계산을 수행하는 것"이다. 우리가 C나 Python으로 작성한 코드는 결국 컴퓨터가 수행해야 할 작업 목록이다. 이 목록을 순서대로 처리하여 결과를 내는 모든 행위가 '프로그램 실행'에 해당한다.

이는 폰 노이만 구조의 '저장 프로그램 컴퓨터(Stored Program Computer)' 개념과 맞닿아 있다. 명령어(Program)와 데이터가 모두 메모리에 저장되어 있고, CPU가 이를 순차적으로 읽어 처리하는 방식이다. 즉, 프로그램은 디스크나 메모리에 저장된 수동적인 명령어의 집합일 뿐이다.

Von Neumann architecture


2. 프로그램은 어떻게 생명을 얻는가? (What happens & How is it executed?)

그렇다면 이 수동적인 명령어 덩어리는 어떻게 실행될까? 우리가 터미널에서 ./a.out을 입력하고 엔터를 치는 순간, 컴퓨터 내부에서는 다음과 같은 일이 순차적으로 일어난다.


Step 1. 메모리에 적재 (Loading)

우선 운영체제(OS)는 로더(Loader)를 통해 디스크에 있던 실행 파일(e.g., a.out)을 메모리로 가져온다. 실행 파일은 단순히 코드만 있는 것이 아니라, ELF(Executable and Linkable Format) 같은 정해진 형식을 따른다. 이 파일 안에는 프로그램의 기계어 코드가 담긴 .text 섹션, 초기화된 전역 변수가 담긴 .data 섹션, 초기화되지 않은 전역 변수를 위한 .bss 섹션 등 다양한 정보가 구역별로 나뉘어 있다. 로더는 이 정보들을 읽어 메모리의 정해진 구조에 맞게 배치한다. 이로써 프로그램은 비로소 실행될 준비를 마친다.

ELF format


Step 2. 명령어 실행 사이클 (Fetch-Decode-Execute Cycle)

프로그램이 메모리에 올라오면, CPU가 바통을 이어받는다. CPU 내부에는 프로그램 카운터(Program Counter, PC) 라는 특별한 레지스터가 있는데, 이는 현재 실행해야 할 메모리 상의 명령어 주소를 가리키고 있다. 

Assembly Programmer’s View

CPU는 이 PC가 가리키는 주소의 명령어를 메모리에서 가져오고(Fetch), 이것이 어떤 명령어인지 해석한 뒤(Decode), 마지막으로 명령어를 실행한다(Execute). 하나의 명령어 실행이 끝나면 PC는 자동으로 다음 명령어의 주소를 가리키게 되고, 이 과정은 프로그램이 종료될 때까지 무한히 반복된다.

Fetch-Decode-Execute Cycle

이 단순한 사이클이 모여 워드프로세서, 웹 브라우저 등 복잡한 프로그램의 실행을 구성하는 것이다.



3. 실행 중인 프로그램, '프로세스' (What is a "program-in-execution"?)

마지막 질문이다. 이처럼 디스크에 있던 정적인 프로그램이 메모리에 올라와 CPU에 의해 실행되고 있는 동적인 상태, 이 '실행 중인 프로그램'을 무엇이라고 불러야 할까?

컴퓨터 과학에서는 이를 프로세스(Process) 라고 정의한다.

프로세스는 단순히 실행 중인 프로그램을 넘어, OS가 해당 프로그램을 관리하기 위해 부여하는 각종 상태 정보를 포함하는 추상적인 개념이다. 즉, 프로그램은 디스크에 저장된 수동적인 실체(passive entity)이고, 프로세스는 메모리에서 실행되는 능동적인 실체(active entity)이다.


프로세스 생성의 5단계

그렇다면 OS는 정확히 어떤 과정을 거쳐 프로그램을 프로세스로 만드는 걸까? 수업에서는 이 과정을 5개의 단계로 나누어 설명했다.


  1. 프로그램 코드와 데이터를 메모리에 적재 (Loading): OS는 디스크에 있는 실행 파일의 내용을 읽어 프로세스의 가상 주소 공간에 로드한다. 현대적인 OS는 효율성을 위해 프로그램 전체를 한 번에 올리지 않고, 실행에 필요한 부분만 먼저 올리는 지연 로딩(Lazy Loading) 방식을 사용하기도 한다.

    Loading

  2. 런타임 스택 생성 (Stack Allocation): 프로세스가 사용할 런타임 스택을 메모리에 생성한다. 이 스택 공간은 지역 변수, 함수 파라미터, 리턴 주소 등을 저장하는 데 사용된다. 우리가 흔히 보는 main(int argc, char *argv[]) 함수의 인자들(argc, argv)도 이 단계에서 스택에 초기화된다.

  3. 힙 생성 (Heap Allocation): 프로세스가 동적으로 메모리를 할당할 수 있는 공간인 힙(Heap) 영역을 생성한다. 프로그램이 실행 중에 malloc()(C)이나 new(C++) 같은 함수를 호출할 때, OS는 이 힙 영역에서 메모리를 할당해준다.

  4. I/O 등 초기화 작업 수행 (Initialization): OS는 프로세스를 위해 다른 초기화 작업들을 수행한다. 대표적으로 각 프로세스는 기본적으로 세 개의 파일 디스크립터(File Descriptor)를 할당받는데, 이는 각각 표준 입력(Standard Input), 표준 출력(Standard Output), 표준 에러(Standard Error)에 해당한다.

  5. 실행 시작 (Execution Start): 모든 준비가 끝나면, OS는 CPU의 제어권을 해당 프로세스에게 넘겨준다. 이로써 프로그램 카운터(PC)가 프로그램의 시작점(entry point, 보통 main 함수)을 가리키게 되고, 마침내 프로그램의 첫 번째 명령어가 실행되며 프로세스의 여정이 시작된다.

    Process State Transition

이처럼 프로세스는 OS의 세심한 준비 작업을 통해 탄생하는 복합적인 결과물이다. OS는 각 프로세스를 효과적으로 관리하기 위해 다음과 같은 핵심 요소들을 함께 다룬다.

  • 메모리 주소 공간 (Address Space): 각 프로세스는 자신만의 독립된 가상 메모리 공간을 할당받는다.

  • 제어 흐름 (Control Flow): 프로세스가 실행 중인 코드의 흐름을 의미하며, 이는 CPU의 레지스터 상태, 특히 프로그램 카운터(PC) 값으로 표현된다.

  • 프로세스 상태 (Process States): 프로세스는 생성부터 소멸까지 Running, Ready, Blocked 같은 여러 상태를 거친다.

  • 프로세스 제어 블록 (Process Control Block, PCB): OS는 각 프로세스의 모든 정보(상태, PC, 레지스터, 메모리 정보 등)를 PCB라는 자료구조에 저장하여 관리한다.



결론: 프로그램에서 프로세스로의 여정

Summary

정리하자면, 디스크에 존재하던 프로그램 파일은 OS의 체계적인 5단계 생성 과정을 거쳐 메모리 위에서 살아 움직이는 '프로세스'로 다시 태어난다. 이렇게 생성된 프로세스를 관리하기 위해 OS는 PCB라는 자료구조를 사용하며, 이를 통해 각 프로세스의 상태와 자원을 철저히 통제한다.

결국 지난 시간에 배운 'CPU 가상화'란 OS가 여러 프로세스의 PCB를 매우 빠른 속도로 번갈아 CPU에 올렸다 내렸다 하며(Context Switching) 이뤄낸 결과였던 셈이다.



추천글:

[시스템프로그래밍] Process & Thread

[운영체제] 운영체제란? | Virtualization, Concurrency, Persistence 핵심 개념 이해


hyeon_B

안녕하세요! AI 기술을 이용해 더 나은 세상을 만들어 나가고 싶은 과기원생 Hyeon이라고 합니다. 저는 앞으로 인공지능 시대에는 지식을 '활용'하는 능력이 중요해질 것이라고 생각합니다. 대부분의 일들은 인공지능이 뛰어난 모습을 보이지만, 인공지능은 데이터로 부터 연관관계를 학습하기 때문에 지식들을 새로 통합해서 활용하는 능력이 부족합니다. 인공지능이 뉴턴 전에 만들어졌다면 사과가 떨어지는 이유에 대답하지 못했을 것이고, 아인슈타인 전에 만들어졌다면 중력이 어떻게 생기는지 설명하지 못했을 것입니다. 따라서 앞으로 우리는 '본질'을 탐구하고 그 본질로부터 다른 곳에 적용하며 인공지능을 현명하게 활용해야 할 것입니다. 함께 인공지능 시대를 준비합시다!

댓글 쓰기

다음 이전

POST ADS1

POST ADS 2