포스트

운영체제란

운영체제란

운영체제(Operating Sytstem, OS)

컴퓨터 하드웨어와 사용자(또는 응용 프로그램) 사이의 인터페이스 역할을 하는 핵심 시스템 소프트웨어입니다. OS의 가장 근본적이 두 가지 목적은 자원 관리자(Resource Manager) 로서 CPU, 메모리, 저장 장치 같은 하드웨어 자원을 효율적이고 공정하게 배분 및 관리하고, 추상화 계층(Abstraction Layer) 으로서 복잡한 하드웨어의 세부 동작을 숨기고 응용 프로그램에게 일관되고 사용하기 쉬운 서비스(API, 주로 시스템 콜 형태)를 제공하는 것입니다.

운영체제의 종류

운영체제는 컴퓨터 시스템의 어떤 프로그램이 언제 CPU를 사용할지, 어떤 데이터가 메모리의 어느 부분에 저장될지, 디스크 파일은 어떻게 관리할지 등 모든 자원 관련 결정을 내리고 실행합니다.
설계 목적, 처리 방식, 사용 환경에 따라 다양하게 분류됩니다.

일괄 처리 시스템(Batch System)

초기 컴퓨터 시스템에서 사용되던 방식이며, 사용자의 작업을 모아서 한 번에 처리합니다. 사용자 상호작용이 없으며, 시스템 처리율(Throughput) 을 극대화하는 것이 목표였습니다.

시분할 시스템(Time-Sharing System) / 다중 작업(Multitasking)

현대 대부분의 PC와 서버 OS(Windows, macOS, Linux, Unix)의 기반입니다.
CPU의 처리 시간을 매우 짧은 단위(Time Slice 또는 Quantum)로 쪼개어 여러 프로세스에 번갈아 할당합니다. 사용자는 여러 프로그램들이 ‘동시에’ 실행되는 것처럼 느낍니다. 이는 동시성(Concurrency) 을 구현하는 핵심 원리 입니다.

  • Unix (유닉스)
    • 시분할 시스템의 원형이자 현대 OS의 근간입니다. macOS, Linux 등 수많은 현대 OS의 기반 역할을 합니다.
      1. 높은 이식성(Portability) : 커널의 90% 이상이 C언어로 작성되어, 특정 하드웨어(CPU)에 종속되지 않고 다양한 머신에 빠르게 이식될 수 있었습니다.
      2. 설계 철학 : “모든 것은 파일이다(Everything is a file)”, “작고 날카로운 도구들을 파이프로 연결한다”라는 철학은 강력한 모듈성과 확장성을 제공했습니다.
  • macOS (맥OS)
    • 애플의 OS로, 사용자 친화적인 GUI와 강력한 Unix 기반을 결합했습니다.
      1. 하이브리드 커널 (XNU) : BSD Unix의 핵심 기능(파일 시스템, 네트워크, POSIX 호환성)과 Mach 마이크로커널의 장점(메모리 관리, IPC, 스케줄링)을 결합한 독특한 구조입니다.
      2. 안정성 : 유닉스(BSD) 계열의 검증된 안정성과 보안 모델을 계승했습니다.
  • Windows (윈도우)
    • 개인용 PC 시장을 지배하는 OS로, Unix와는 전혀 다른 계열(VMS)에서 발전했습니다.
      1. NT 커널 아키텍처 : Unix와 마찬가지로 모듈형 하이브리드 커널 구조를 가집니다. (단, 그 구현과 기반 철학은 다릅니다.)
      2. Win32 API : 방대한 응용 프로그램 생태계의 기반이 되는 핵심 API입니다.
      3. 강력한 하위 호환성 : 과거 버전의 소프트웨어가 새 OS에서도 동작하도록 보장하는 것을 매우 중요하게 여깁니다.

실시간 운영체제(Real-Time OS, RTOS)

작업 처리에 엄격한 시간 제약(Deadline) 이 있는 시스템에 사용됩니다. (항공기 제어, 로봇 팔, 미사일 유도 등)
단순히 ‘빠른’ OS가 아니라, 정해진 시간 안에 반드시 응답을 보장하는 예측 가능성(Predictability) 이 핵심입니다.

  • 경성 실시간(Hard RTOS) : 마감 시간을 지키지 못하면 치명적인 시스템 실패로 이어집니다.
  • 연성 실시간(Soft RTOS) : 마감 시간을 지키지 못해도 성능이 저하될 뿐, 시스템 전체가 실패하지는 않습니다. (동영상 스트리밍 등)

분산 운영체제(Distributed OS)

네트워크로 연결된 여러 독립적인 컴퓨터들을 마치 하나의 단일 시스템처럼 보이게 만듭니다.
사용자에게 투명성(Transparency) 을 제공하여, 자원이 물리적으로 어디에 있는지 몰라도 사용할 수 있게 합니다.

임베디드 운영체제(Embedded OS)

특정 목적을 위해 설계된 하드웨어(예: 가전제품, 자동차, IoT 기기)에 내장됩니다.
매우 적은 메모리와 저전력으로 동작해야 하는 자원 제약적 환경에 최적화되어 있습니다.

모바일 운영체제(Mobile OS)

스마트폰, 태블릿 등 모바일 기기에 특화되었습니다. (Android, iOS 등)
터치 인터페이스, 전력 관리, 무선 통신(Wi-Fi, 5G), 앱 격리(Security) 등을 중점적으로 관리합니다.

운영체제의 하드웨어 관리 방식

운영체제는 ‘자원 관리자’로서 컴퓨터의 4대 핵심 자원(CPU, 메모리, I/O 장치, 파일)을 관리합니다.

프로세서 (CPU) 관리 : 스케줄링 (Scheduling)

CPU는 하나(혹은 몇 개)뿐인데 여러 프로그램이 동시에 실행되길 원합니다. OS는 이 CPU 시간을 아주 짧은 단위인 타임 슬라이스(Time Slice) 로 쪼개어 여러 프로세스(Process) 에 공정하고 효율적으로 배분합니다.

시스템 전체의 처리율(Throughput) 을 극대화하고, 사용자에 대한 응답 시간(Response Time) 은 최소화하는 것이 주된 목표입니다. 이 둘은 종종 트레이드오프 관계에 있습니다.

메모리 (RAM) 관리 : 가상 메모리 (Virtual Memory)

OS는 각 프로세스에게 ‘가상 메모리’ 라는 독립적이고 거대한 전용 메모리 공간을 제공합니다. 이는 실제 물리 메모리(RAM)의 크기나 다른 프로세스의 존재와 상관없이, 모든 프로세스가 자신만의 주소 공간(0번지부터)을 가진 것처럼 ‘추상화’하는 것입니다.

  • 메모리 보호(Protection) : 한 프로세스가 다른 프로세스나 커널의 메모리 영역을 침범하지 못하도록 격리합니다.
  • 효율성(Efficiency) : 페이징(Paging) 기법을 통해 실제 물리 메모리보다 더 큰 프로그램을 실행하고(디스크 스왑 공간 활용), 필요한 부분만 메모리에 올립니다.

디스크 (I/O 장치) 관리 : 추상화 및 효율화

OS는 복잡하고 다양한 I/O 장치들(디스크, 키보드, 네트워크 카드 등)을 장치 드라이버(Device Driver) 를 통해 관리합니다. 이를 통해 응용 프로그램은 read(), write() 같은 일관된 시스템 콜만으로 장치 종류와 상관없이 데이터를 읽고 쓸 수 있습니다.

디스크의 경우, 데이터 접근 시간(Access Time)이 메모리보다 수천~수만 배 느립니다. 이 병목 현상을 완화하기 위해 OS는 디스크 스케줄링(엘리베이터 알고리즘 등)을 사용하여 디스크 헤드의 물리적 움직임을 최소화하고 접근 순서를 최적화합니다.

파일 시스템 (File System) 관리

디스크 관리가 ‘하드웨어 블록’ 단위의 접근을 다룬다면, 파일 시스템은 그 상위 계층입니다. OS는 디스크의 물리적 블록들을 ‘파일(File)’과 ‘디렉터리(Directory)’라는 논리적인 단위로 추상화합니다.

데이터에 영속성(Persistence) 을 부여하고, 사용자가 데이터를 계층 구조로 쉽게 구성, 검색, 접근(권한 관리 포함)할 수 있도록 돕습니다.

프로그램 수행 과정

프로세스 생성(Process Creation)실행 중인 프로세스의 I/O 요청(I/O Request) 이 발생하는 과정입니다.

1단계 : 프로세스 생성(Process Creation)

  1. 사용자 명령 : 사용자가 셸(Shell)에서 ./my_program을 입력하고 엔터를 누릅니다.
  2. 프로세스 생성 요청 : 셸(부모 프로세스)이 OS 커널에 시스템 콜(fork()exec())을 호출하여 새 프로세스(자식 프로세스) 생성을 요청합니다.
  3. 커널의 준비 작업 (커널 모드) :
    • OS 커널이 PCB(Process Control Block)라는 전용 데이터 구조를 생성합니다. (PID 할당 등)
    • 새로운 가상 주소 공간을 할당합니다.
    • 로더(Loader) 가 실행됩니다. 로더는 디스크의 실행 파일(my_program)을 읽어, 프로그램의 코드(.text)와 데이터(.data)를 이 가상 주소 공간에 매핑(연결)합니다.
    • 사용자 스택을 초기화합니다.
  4. 준비 완료 : 모든 준비가 끝나면, OS는 이 새 프로세스의 상태를 Ready (준비) 로 설정하고, ‘준비 큐(Ready Queue)’의 끝에 넣습니다. (이때 프로세스는 아직 CPU를 받지 못했습니다.)

2단계 : 첫 실행 및 I/O 요청(Execution & System Call)

  1. 스케줄링 : CPU 스케줄러가 Ready 큐를 확인합니다. (가령, 다른 프로세스의 Time Slice가 끝나거나, 방금 생성된 my_program의 우선순위가 높을 수 있습니다.) 스케줄러가 my_program을 실행하기로 결정합니다.
  2. 문맥 교환 (Context Switch) : (만약 다른 프로세스 B가 실행 중이었다면) B의 상태를 PCB에 저장하고, my_program의 상태(PCB 정보)를 CPU 레지스터로 로드합니다.
  3. 실행 시작 (사용자 모드) : CPU 제어권이 my_program으로 넘어갑니다. 프로세스 상태가 Running (실행) 으로 변경됩니다. 프로그램은 자신의 코드를 순차적으로 실행합니다. (예: int a = 10;, int b = 20; …)
  4. I/O 요청 발생 : 프로그램이 파일에서 데이터를 읽어야 하는 read(file_fd, buffer, ...) 함수를 만납니다.
  5. 시스템 콜 (Trap) :
    • read() 라이브러리 함수는 커널의 도움이 필요하므로, 내부적으로 SYSCALL (또는 INT 0x80) 같은 특수 명령을 실행합니다.
    • 이 명령은 CPU에 ‘트랩(Trap)’ 을 발생시킵니다.
    • CPU는 즉시 ‘커널 모드’로 전환되고, OS 내의 미리 정해진 ‘시스템 콜 핸들러’ 로 점프합니다. (CPU 제어권이 OS로 넘어감)

3단계 : OS의 I/O 처리 및 프로세스 차단(Blocking)

  1. 커널 작업 수행 (커널 모드) :
    • 시스템 콜 핸들러는 my_program이 ‘디스크 읽기’를 요청했음을 파악합니다.
    • OS는 디스크 컨트롤러(하드웨어)에게 “이 데이터를 읽어서 메모리 버퍼로 가져와라”라고 명령합니다.
  2. 프로세스 상태 변경 :
    • 디스크 작업은 CPU에 비해 수백만 배 느립니다. OS는 이 프로세스가 I/O를 기다리며 CPU를 낭비하게 둘 수 없습니다.
    • OS는 my_program의 상태를 Running에서 Blocked (대기) 또는 Wait (대기) 상태로 변경합니다.
    • my_program을 CPU 스케줄링 대상에서 제외하고, ‘I/O 대기 큐(Wait Queue)’로 이동시킵니다.
  3. 스케줄링 (다른 프로세스 실행) :
    • OS는 CPU가 놀고 있으므로 즉시 스케줄러를 다시 호출합니다.
    • 스케줄러는 Ready 큐에 있던 다른 프로세스(예: Program_C)를 선택합니다.
    • 문맥 교환(Context Switch) 을 통해 CPU 제어권을 Program_C에게 넘깁니다. (이제 Program_CRunning 상태가 되어 실행됩니다.)

4단계 : I/O 완료 및 작업 재개(Interrupt & Wake-up)

  1. I/O 작업 완료 (비동기 발생) :
    • (시간이 한참 흐른 뒤) 디스크 컨트롤러가 요청받은 데이터 읽기를 완료하고, 해당 데이터를 메모리의 커널 버퍼로 전송합니다.
    • 작업이 끝난 디스크 컨트롤러는 CPU의 ‘인터럽트 라인’ 에 전기 신호를 보냅니다. (이것이 바로 ‘하드웨어 인터럽트’ 입니다.)
  2. 인터럽트 감지 (커널 모드) :
    • CPU는 (현재 Program_C를 실행하던 중) 이 인터럽트 신호를 감지합니다.
    • CPU는 Program_C즉시 멈추고, 현재 상태를 저장한 뒤, ‘커널 모드’ 로 전환하여 OS의 ‘인터럽트 서비스 루틴(ISR)’ 으로 점프합니다. (CPU 제어권이 다시 OS로 넘어감)
  3. 인터럽트 처리 :
    • ISR은 인터럽트의 원인(“디스크 I/O 완료”)을 파악합니다.
    • 커널은 I/O 완료를 기다리던 my_program을 ‘I/O 대기 큐’에서 찾아냅니다.
    • (필요시) 커널 버퍼의 데이터를 my_programread() 함수가 지정한 buffer로 복사합니다.
  4. 프로세스 상태 변경 (Wake-up) :
    • OS는 my_program의 상태를 Blocked에서 다시 Ready (준비) 상태로 변경하고, Ready 큐로 이동시킵니다. (주의: 즉시 실행되는 것이 아니라 다시 스케줄러의 선택을 기다려야 합니다.)
  5. 작업 복귀 :
    • ISR의 모든 작업이 끝나면, OS는 아까 멈췄던 Program_C로 복귀(Context Switch)하여 마저 실행합니다. (사용자 모드)
  6. 실행 재개 :
    • (또 시간이 흐른 뒤) 스케줄러가 Ready 큐에 와있던 my_program을 다시 선택합니다.
    • 문맥 교환을 통해 my_program이 CPU를 얻습니다. (상태가 Ready -> Running이 됨)
    • my_program은 아까 멈췄던 read() 시스템 콜 _직후_부터 실행을 재개합니다. (이제 buffer에는 디스크에서 읽어온 데이터가 들어있습니다.)

함께 알아두면 좋은 지식

시스템 콜(System Call)과 듀얼 모드(Dual Mode)

운영체제 보안과 추상화의 가장 근본적인 원리입니다.

  • 듀얼 모드 (Mode Bit) : CPU는 최소 두 가지 모드, 즉 사용자 모드(User Mode)커널 모드(Kernel Mode) 로 동작합니다.
    • 사용자 모드 : 응용 프로그램(Word, Chrome, 게임)이 실행되는 모드. 하드웨어 접근이나 다른 프로세스 메모리 접근 같은 위험한 작업이 ‘하드웨어적’으로 금지됩니다.
    • 커널 모드 (or 슈퍼바이저 모드) : OS 커널 코드가 실행되는 모드. 모든 하드웨어와 메모리에 접근할 수 있는 ‘절대 권한’을 가집니다.
  • 시스템 콜 (System Call): 사용자 모드의 응용 프로그램이 파일 읽기, 네트워크 통신, 프로세스 생성 등 커널의 도움이 필요한 작업을 수행할 때, 커널에게 ‘요청’하는 유일한 합법적인 통로입니다.
    • 작동 원리:
      1. 프로그램이 read() 같은 라이브러리 함수를 호출합니다.
      2. 이 함수는 내부적으로 SYSCALL (또는 INT 0x80) 같은 특수 기계어 명령을 실행합니다.
      3. CPU는 이 명령을 받으면, 하드웨어적으로 모드를 ‘커널 모드’로 전환하고, CPU 제어권을 커널 내의 미리 정해진 특정 주소(시스템 콜 핸들러)로 점프시킵니다.
      4. 커널 핸들러는 프로그램이 요청한 작업(예: 디스크 읽기)을 ‘대신’ 수행합니다.
      5. 작업이 완료되면, 커널은 모드를 다시 ‘사용자 모드’로 전환하고, CPU 제어권을 원래 프로그램이 멈췄던 지점으로 복귀시킵니다.
    • 이 ‘모드 전환’ 메커니즘은 응용 프로그램이 OS의 통제 없이 하드웨어를 망가뜨리거나 다른 프로그램을 훔쳐보는 것을 원천적으로 방지합니다.

동시성 (Concurrency) vs 병렬성 (Parallelism)

운영체제는 동시성을 관리함으로써, 병렬성을 가진 하드웨어(멀티 코어)의 성능을 최대로 끌어낼 수 있게 지원합니다.

  • 동시성 (Concurrency): 논리적인 개념입니다. 싱글 코어 CPU에서도 여러 작업을 매우 빠르게 번갈아 처리(Context Switching)하여 ‘동시에’ 실행되는 것처럼 보이게 만드는 것입니다. OS 스케줄러가 관리하는 영역입니다.
  • 병렬성 (Parallelism): 물리적인 개념입니다. 멀티 코어(Multi-core) 시스템에서 두 개 이상의 작업이 ‘물리적으로’ 정확히 같은 순간에 실행되는 것입니다.

동기식/비동기식 입출력

동기식 입출력 (Synchronous I/O)
응용 프로그램(프로세스)이 I/O 작업을 요청(시스템 콜 호출)했을 때, 해당 작업이 완전히 완료되어 결과를 반환받을 때까지 응용 프로그램의 로직이 멈추는(Blocked) 방식입니다.

  • 작동 방식
    1. 프로세스가 read() 시스템 콜을 호출합니다.
    2. 커널이 I/O를 시작하고, 해당 프로세스를 ‘대기(Blocked)’ 상태로 만듭니다.
    3. OS 스케줄러는 다른 프로세스를 실행합니다. (이 덕분에 CPU가 낭비되지 않습니다.)
    4. I/O가 완료되고 데이터가 준비되면, 커널은 프로세스를 ‘준비(Ready)’ 상태로 되돌립니다.
    5. 프로세스가 다시 스케줄링되면 read() 호출이 반환되고 다음 코드를 실행합니다.
  • 프로그래머 관점에서 read() 함수 하나가 끝날 때까지 기다립니다. 코딩이 직관적입니다.

비동기식 입출력 (Asynchronous I/O)
응용 프로그램이 I/O 작업을 요청(시스템 콜 호출)할 때, 커널이 작업 시작만 시켜놓고 즉시 반환하는 방식입니다. 응용 프로그램은 I/O가 완료되기를 기다리지 않고(Non-Blocking) 다음 코드를 계속 실행합니다.

  • 작동 방식
    1. 프로세스가 aio_read() (비동기 I/O 함수 예시)를 호출합니다.
    2. 커널은 I/O 요청을 ‘접수’하고 즉시 “요청 접수됨”을 알리며 반환합니다.
    3. 프로세스는 차단(Block)되지 않고 다음 로직을 계속 수행합니다.
    4. (백그라운드) I/O 작업이 완료되면, OS는 콜백(Callback) 함수, 시그널(Signal), 이벤트 큐 등 약속된 방식으로 응용 프로그램에게 “작업이 완료되었음”을 알려줍니다.
  • I/O 요청과 결과 수신이 분리됩니다. 단일 프로세스/스레드로도 높은 동시 처리가 가능하지만(고성능 웹서버 등), 프로그래밍 모델이 복잡해집니다.
이 기사는 저작권자의 CC BY-NC 4.0 라이센스를 따릅니다.