dh_0e

[OS] Process, Context Switch 본문

Operating System

[OS] Process, Context Switch

dh_0e 2025. 11. 6. 23:53

Scope of OS(운영체제의 범위)

  • 프로세스 관리(Process Management)
  • 메모리 관리(Memory Management)
  • 파일 관리(File Management)
  • I/O 시스템 관리(I/O System Management)
  • 네트워킹(Networking)
  • 보안(Security)

프로그램이 실행되는 과정

 

컴파일러(Compiler)

  • 역할
    • 사람이 이해할 수 있는 프로그래밍 언어로 작성된 Source Code를 컴퓨터(CPU)가 이해할 수 있는 기계어로 표현된 Object 파일로 변환
      • x86 vs ARM vs RISC-V 등 다양한 명령어 셋에 따라 Object 파일의 형태가 달라질 수 있음
    • Object file: 컴퓨터가 이해할 수 있는 기계어로 구성된 파일
      • 자체로는 수행이 이루어지지 못함
      • 프로세스로 변환되기 위한 정보가 삽입되어야 함 (헤더, 텍스트 섹션, 데이터 섹션, 주소 정보 변환 등)
      • Relocatable Addresses(Relative Address: 상대 주소)로 표현
        • 심볼들의 주소가 상대적인 값으로 표현됨 (ex. 시작 주소로부터 26바이트 지점)

Cross-compilier for different platforms

 

링커(Linker)

  • 역할
    • 관련된 여러 Object 파일들과 라이브러리들을 연결하여, 메모리로 로드될 수 있는 하나의 Executable 파일로 변환
    • Executable File: 특정한 환경(OS)에서 수행될 수 있는 파일
      • 프로세스로의 변환을 위한 Header, 실제 작업 내용인 Text, 필요한 데이터인 Data를 포함
      • Absolute Addresses(절대 주소)로 표현됨
        • 심볼들의 주소가 절댓값으로 표현됨 (ex. 32026번 주소)
      • ex) exe file
  • 컴파일러와 링커는 결과물이 수행될 OS와 CPU에 따라 다른 형태의 파일을 만듦

 

로더(Loader)

  • 역할
    • Executable 파일을 실제 메모리에 올려주는 역할을 담당하는 OS의 일부
    • Loading: RAM에 올려서 실행할 준비를 끝내고, 프로세스로서 존재하게 만드는 것
  • 동작 과정
    1. Executable의 Header를 읽어, Text와 Data의 크기 결정
    2. 프로그램을 위한 가상 메모리 공간인 Address Space를 생성
    3. Text와 Data들을 방금 생성한 Address Sapce로 복사
    4. 프로그램이 실행될 때 필요한 추가적인 정보들(Argument)은 Stack 메모리 영역에 복사
    5. CPU 내 임시 저장 공간인 Register를 초기화하고,
      프로그램의 시작 지점(Start-up Routine)으로 Jump 해서
      첫 번째 명령어(PC)를 실행하도록 지시

 

런타임 시스템(Runtime System)

  • 역할
    • 응용 프로그램의 효율적인 실행을 지원하기 위해 프로그램과 연결하여 상호 작용함
  • C Runtime System Program Execution
    • 자동 준비 코드 추가: GCC(GNU Compiler Collection)Start-up Code Object(준비 코드) 파일을 추가여 런타임 시스템 프로그램을 컴파일하며, 이때 기본 라이브러리들도 동적으로 링크
    • 첫걸음 지시: 프로그램이 시작될 때, CPU의 PC에게 "자, 이제 _start(공식 시작점)라는 곳으로 가서 첫 번째 명령을 실행해!"라고 지시함
    • 초기화 작업: _start 함수는 바로 main()을 호출하지 않고, 먼저 _libc_start_main을 호출하여 C 라이브러리 및 스레드 환경을 초기화함
    • 초기화 이후, 프로그램의 $main()$ 함수가 호출됨

Runtime System Program은 GCC(Compiler+Linker)에 의해 코드와 함께 컴파일 됨

 

프로세스 (Process)

  • Execution Unit(실행 단위): OS가 여러 프로그램을 번갈아 가면서 실행시킬 때(스케줄링)의 단위
  • Protection Domain(보호 영역): 각 프로세스는 독립적인 공간을 가져, 서로 영역을 침범하지 못함
구성 요소 설명
Program Counter (PC) CPU 레지스터 중 하나로, 다음에 실행할 명령어의 주소를 가리킴
Stack 함수 호출 정보(리턴 주소, 매개변수), 지역 변수 저장. 함수 호출 시 쌓이고(return 시 사라짐)
Heap malloc, new 같은 동적 메모리 할당 영역
Data Section (.data) 초기화된 전역 변수, 정적 변수 저장
BSS Section (.bss) 초기화되지 않은 전역/정적 변수 저장
Text Section (.text) 프로그램의 기계어 코드(명령어) 저장
프로세스는 디스크에 저장된 프로그램으로부터 변환되어 메모리로 로딩된다.
(하드디스크에 그냥 파일로 누워 있던 프로그램이 실행 버튼을 누르는 순간, 운영체제가 그 내용을 메모리(RAM)로 가져와서 ‘살아 있는 작업(프로세스)’으로 만들어 준다.)

Process Image
Process from Program & Loader

 

프로세스 상태(Process State)

  • 대표적 States
    • New: 프로세스가 생성되고 있는 상태
    • Ready: 프로세서 할당을 기다리는 상태
    • Running: 명령어들이 실행되고 있는 상태
    • Waiting: I/O 완료, 신호 수신과 같은 어떠한 이벤트 발생을 기다리는 상태
    • Terminated: 프로세스 실행이 완료된 상태
  • 커널 내에 Ready Queue, Waiting Queue, Running Queue를 두고 프로세스들을 상태에 따라 관리함
    • Ready Queue: CPU(프로세서)를 기다리는 프로세스들이 할당을 기다리며 줄 서 있는 곳
    • Waiting Queue: 어떤 이벤트가 발생하기를 기다리는 프로세스들이 모여있는 곳
    • Running Queue: 현재 CPU에서 실행 중인 프로세스 (현재 실행 중인 프로세스 자체를 의미)

프로세스 상태 전이 (Transition of Process State)

  • New → Ready: admitted - 새로운 프로세스가 시스템에 추가될 준비가 됨
  • Ready → Running: scheduler dispatch - 스케줄러가 Ready 상태의 프로세스에게 CPU를 할당함
  • Running → Ready: timer interrupt - 할당된 시간이 다 되거나 더 높은 우선순위의 프로세스가 나타나 CPU를 선점당함
  • Running Waiting: I/O or event wait - 프로세스가 I/O 작업 완료 또는 특정 이벤트 발생을 기다림
  • Waiting Ready: I/O or event completion - 기다리던 I/O 작업 또는 이벤트가 완료됨
  • Running Terminated: exit - 프로세스 실행이 완료됨

Process State in xv6

  • enum procstate: 프로세스의 가능한 상태를 정의합니다.
    • UNUSED: 사용되지 않음.
    • EMBRYO: 생성 중인 초기 상태.
    • SLEEPING: 어떤 이벤트를 기다리며 잠들어 있는 상태.
    • RUNNABLE: 실행 준비 완료 상태 (Ready).
    • RUNNING: 현재 실행 중인 상태.
    • ZOMBIE: 실행은 종료되었지만 부모 프로세스가 아직 수확(cleanup)하지 않은 상태.
    • struct proc: 각 프로세스에 대한 정보를 담는 구조체 (프로세스 제어 블록, PCB).
      • uint sz: 프로세스 메모리 크기 (바이트).
      • pde_t* pgdir: 페이지 테이블 (가상 메모리 관리).
      • char *kstack: 커널 스택의 바닥 주소. (마지막 주소: kstack-stack_start=stack_size)
      • enum procstate state: 현재 프로세스 상태.
      • int pid: 프로세스 ID.
      • struct proc *parent: 부모 프로세스 포인터.
      • struct trapframe *tf: 현재 시스템 호출을 위한 트랩 프레임.
      • struct context *context: 프로세스를 실행하기 위한 컨텍스트 (swtch()에 사용).
      • void *chan: (Non-zero일 경우) 어떤 채널에서 잠들어 있는지.
      • int killed: (Non-zero일 경우) 종료 명령을 받았는지.
      • struct file *ofile[NOFILE]: 열린 파일 목록.
      • struct inode *cwd: 현재 작업 디렉토리.
      • char name[16]: 프로세스 이름 (디버깅용).

 

프로세스 제어 블록(Process Control Block)

  • 각 프로세스는 OS에서 PCB로 표현됨

  • 각 프로세스와 관련된 정보
    • Process State: 프로세스의 현재 상태
    • Program Counter: 다음에 실행할 명령어 주소
    • CPU Registers: 프로세스가 실행될 때 CPU 레지스터에 저장된 값들
      • accumulator, index registers, stack pointers 등
    • CPU Scheduling Information: 프로세스 우선순위, 스케줄 큐 포인터 등
    • Memory Management Information: 페이지 테이블 또는 세그먼트 테이블, 메모리 제한 레지스터 값 등
    • Accounting Information: CPU 사용 시간, 실제 사용 시간, 시간제한, 프로세스 번호 등
    • I/O Status Information: 프로세스에 할당된 I/O 장치 목록, 열린 파일 목록 등

 

문맥 전환(Context Switch)

  • CPU가 새로운 프로세스로 전환될 때, 커널이 기존(현재) 프로세스의 상태를 저장하고, 새로운 프로세스를 위해 저장된 상태를 로드하는 과정
  • Overhead: Context Switching 하는 시간
    • 시스템은 전환하는 동안 유용한 작업을 수행하지 못함
  • Context Switching 시간은 하드웨어 지원에 따라 달라짐

Context Switch 도식

 

프로세서 구조에 따른 Context Switch의 차이

CISC(Complex Instruction Set Computer)

  • 복잡한 명령어 셋 구성: 효율을 높이지만, 클럭 속도가 저하될 수 있음
    • 클럭 속도: 프로세서의 작동 속도, CPU가 명령어를 처리하는 빠르기를 결정하는 요소
  • 복잡한 회로: 물리적인 공간을 많이 차지하고, 레지스터 용량이 저하될 수 있음 (레지스터 적음)
  • ex) Intel Pentinum Processor

RISC(Reduced Instruction Set Computer)

  • 간단한 명령어 셋 구성: 클럭 속도가 높고, 빠른 수행 속도를 가짐
  • 절약된 물리적 공간: 더 많은 레지스터를 장착할 수 있음 (레지스터 많음)
  • ex) ARM Processor
  • Register Window(Berkely RISC Design): Context Switch 오버헤드를 줄이기 위한 방법 중 하나
구분 CISC (Complex Instruction Set Computer) RISC (Reduced Instruction Set Computer)
설계 철학 복잡한 일을 한 명령어로 처리 단순한 명령어를 빠르게 많이 처리
명령어 수 많음, 형식도 다양 적음, 형식이 규칙적·단순
명령어 길이 가변 길이 (1바이트 ~ 여러 바이트) 대부분 고정 길이 (예: 32비트)
수행 사이클 한 명령어가 여러 클럭 사이클 사용 한 명령어당 1클럭이 목표 (파이프라인에 유리)
주소 지정 방식 매우 다양, 복잡한 주소 지정 지원 주소 지정 방식이 제한적·단순
하드웨어 복잡도 디코더·마이크로코드 등 하드웨어 복잡 하드웨어 단순, 대신 컴파일러 최적화 중요
코드 길이 한 명령어가 많은 일을 해서 상대적으로 짧을 수 있음 같은 일을 하려면 명령어 개수가 더 많아져 길어질 수 있음
파이프라이닝 복잡한 명령어 때문에 파이프라인 설계가 어려울 수 있음 규칙적인 명령어 덕분에 파이프라인·슈퍼스칼라에 유리
대표 예시 x86, x86-64 (인텔, AMD CPU) ARM, MIPS, RISC-V, PowerPC 등
CISC는 하나의 복잡한 명령어를 처리하는 데 많은 시간이 걸리지만, RISC는 단순한 명령어를 빠르게 처리하여 전반적인 효율을 높임

문맥 전환 측면에서는 RISC가 더 많은 레지스터를 가질 수 있어 효율적일 수 있지만, 그만큼 전환 시 저장/복원해야 할 데이터가 많아질 수 있다는 점이 특징

비유
RISC: “칼, 망치, 드라이버”처럼 도구는 단순하지만 빠름
CISC: “만능 공구”처럼 한 번에 여러 기능 가능하지만 무겁고 느림

 

프로세스 생성(Process Creation)

  • 시스템 내의 프로세스들은 동시에 실행될 수 있으며, 동적으로 생성/종료될 수 있음
  • OS는 프로세스 생성 및 프로세스 종료 메커니즘을 제공함

  • fork(): 유닉스/리눅스 시스템에서 새로운 프로세스를 만드는 가장 대표적인 방법
    • Parent process vs Child process
      • 원래 실행되고 있던 프로세스가 '부모'가 되고, 새롭게 생성된 프로세스가 '자식'이 됨
      • 자식 프로세스는 부모 프로세스를 거의 그대로 복사해서 만들어짐
    • Resource Sharing(자원 공유)
      • 부모와 자식이 모든 자원을 공유(default)
      • 자식이 부모 자원의 일부만 공유받음 
      • 부모와 자식이 아무 자원도 공유하지 않음
    • Execution(실행 방식)
      • 부모와 자식이 동시에 실행됨
      • 부모가 자식이 종료될 때까지 기다림 

 

Process Creation in Memory View

  • 메모리(Text 및 Data 섹션)
    • 자식 프로세스는 부모 프로세스의 복제본임
    • 자식 프로세스에 프로그램이 로드됨
    • in Unix
      1. fork() 시스템 호출로 새로운 프로세스가 생성
        • 새로운(자식) 프로세스는 원본(부모) 프로세스의 메모리를 복사한 것으로 구성됨
      2. exec(2): 새로운 프로그램으로 메모리를 교체
        • 자식 프로세스는 부모와 똑같은 코드를 가지고 있지만, 대부분의 경우 exec()를 호출하여 자신만의 새로운 프로그램의 코드를 메모리에 로드하고 기존의 부모 코드를 지움
        • 완전히 새로운 프로그램을 실행하는 부모와 별개의 프로세스로 거듭남

  • fork()와 exec()는 자주 함께 사용되어 새로운 프로그램을 실행하는 과정을 만듦
    • fork()로 공간을 만들고, exec()로 그 공간에 새로운 프로그램을 채워 넣는 식

 

Process Creation in UNIX

부모 프로세스
자식 프로세스 실행 부분

  • ※ execl이 성공하면 그 시점에서 "지금 돌고 있던 프로그램"이 통째로 사라지고 /bin/ls 프로그램으로 갈아 끼워지기 때문에, 그 아래에 있는 for문, 맨 밑에 wait도 절대 실행되지 않음
    • 현재 프로세스(자식)의 메모리 공간(코드, 데이터, 힙, 스택)을 전부 버리고 새로운 실행 파일(/bin/ls)을 그 자리에 로드
    • 프로세스 ID(pID)는 유지되지만, "프로그램"은 완전히 다른 것으로 바뀜
    • 새 프로그램의 시작점(main 함수)부터 다시 실행을 시작
  • OS 입장에서 단계별
    1. 자식이 execl 시스템 콜을 호출
    2. 커널이 /bin/ls 실행 파일을 찾아서 ELF 헤더 같은 걸 확인
    3. 기존 자식 프로세스의 사용자 영역(code, data, stack 등)을 전부 제거
    4. /bin/ls의 코드와 데이터로 새 주소 공간을 구성
    5. 레지스터, 스택 등을 새 프로그램의 시작 상태로 세팅
    6. 이제부터 이 프로세스는 “ls 프로세스”가 됨. main(int argc, char **argv) 같은 곳부터 실행 시작

Process Creation in XV6

 

프로세스 종료(Process Termination)

  • 프로세스는 최종 명령문을 실행하고 운영체제에 삭제를 요청하기 위해 exit 시스템 호출을 사용하여 종료
    • 부모에게 return: 자식의 출력 데이터는 wait를 통해 부모에게 전달될 수 있음
    • OS의 할당 해제: 프로세스의 자원은 OS에 의해 할당 해제됨
  • abort 함수: 비정상적인 프로세스 종료를 유발
    • 호출 프로세스에게 SIGABRT시그널을 전송
    • Core dump 발생