테마
Chapter 01. 운영체제 개요와 하드웨어 기초
운영체제(Operating System)가 무엇인지, 하드웨어와 어떻게 소통하는지, 그리고 인터럽트(Interrupt)와 시스템 콜(System Call)이라는 핵심 메커니즘을 이해한다.
1.1 운영체제란 무엇인가?
정의
운영체제(Operating System, OS) 는 하드웨어를 직접 제어하고 관리하면서, 그 위에서 동작하는 애플리케이션(Application)을 지원하는 플랫폼 소프트웨어(Platform Software) 이다.
쉽게 말하면, OS는 두 가지 역할을 동시에 수행한다.
| 방향 | 역할 | 비유 |
|---|---|---|
| 위로 (애플리케이션 방향) | 프로그램이 하드웨어를 몰라도 동작할 수 있도록 서비스를 제공 | 호텔 프론트 데스크 |
| 아래로 (하드웨어 방향) | CPU, 메모리, 디스크, 네트워크 등 하드웨어를 직접 제어하고 보호 | 건물 관리인 |
애니메이션 개념 그림
운영체제는 유저 모드와 하드웨어 사이의 관문이다
1 / 4시스템 콜
애플리케이션은 하드웨어를 직접 만지지 않고 시스템 콜로 커널에게 요청한다.
스스로 확인
일반 프로그램이 하드웨어를 직접 제어하지 못하게 막는 이유는 무엇일까?
컴퓨터의 3계층 구조
컴퓨터 시스템은 크게 하드웨어, 커널 모드(Kernel Mode), 유저 모드(User Mode) 세 계층으로 나뉜다.
- 유저 모드(User Mode): 일반 애플리케이션이 동작하는 공간. 하드웨어에 직접 접근할 수 없다.
- 커널 모드(Kernel Mode): OS의 핵심(커널)이 동작하는 공간. 하드웨어를 직접 제어할 수 있는 특권 모드이다.
- 하드웨어(Hardware): 실제 물리적 장치들.
왜 분리하는가? 만약 아무 프로그램이나 하드웨어를 직접 건드릴 수 있다면, 악성 프로그램 하나가 시스템 전체를 망가뜨릴 수 있다. 그래서 OS는 "관문"을 세워 놓고, 반드시 시스템 콜(System Call)이라는 정해진 절차를 통해서만 하드웨어에 접근하도록 한다.
멀티태스킹(Multitasking)
현대 OS는 여러 프로세스(Process)를 동시에 실행하는 것처럼 보이게 한다. 실제로 CPU는 매우 빠른 속도로 프로세스 사이를 전환(Context Switch)하며, 사용자 눈에는 동시에 실행되는 것처럼 느껴진다.
Physical vs Logical/Virtual
| 구분 | Physical (물리적) | Logical / Virtual (논리적 / 가상) |
|---|---|---|
| 의미 | 실제 하드웨어가 존재 | 소프트웨어로 구현된 것 |
| 예시 | 실제 CPU, 실제 메모리 8GB | 가상 메모리(Virtual Memory), 가상 CPU |
| 특징 | 만질 수 있다 | 만질 수 없지만 있는 것처럼 동작한다 |
가상화(Virtualization)
가상화란 물리적 하드웨어 자원을 소프트웨어로 추상화하여 구현하는 기술이다.
예를 들어:
- 가상 메모리(Virtual Memory): 실제 RAM이 8GB인데, 프로그램에게는 훨씬 큰 메모리가 있는 것처럼 보여준다.
- 가상 머신(Virtual Machine): 하나의 물리 서버 위에 여러 개의 가상 컴퓨터를 띄운다.
플랫폼(Platform)
플랫폼은 CPU 아키텍처 + 운영체제의 조합을 의미한다.
| 플랫폼 예시 | CPU 아키텍처 | 운영체제 |
|---|---|---|
| x86-64 + Windows | 64비트 Intel/AMD | Windows 10/11 |
| x86-64 + Linux | 64비트 Intel/AMD | Ubuntu, CentOS |
| ARM64 + macOS | 64비트 Apple Silicon | macOS Ventura/Sonoma |
| ARM64 + Linux | 64비트 ARM | Raspberry Pi OS |
프로그램을 컴파일(Compile)할 때 어떤 플랫폼용인지를 지정해야 한다. 64비트 CPU에서는 64비트 OS가, 64비트 OS에서는 64비트 애플리케이션이 동작하는 것이 자연스럽다.
OS의 두 가지 역할 요약
- 위로: 애플리케이션에게 API와 서비스를 제공한다 (서포트 역할).
- 아래로: 하드웨어를 직접 제어하고, 자원을 효율적으로 관리한다 (관리자 역할).
1.2 I/O의 전체 흐름: printf("Hello World")의 여정
프로그램에서 printf("Hello World")를 호출하면 어떤 일이 벌어질까? 화면에 글자가 나타나기까지의 전체 여정을 따라가 보자.
전체 흐름 개요
단계별 상세 설명
① ~ ② 유저 모드에서의 시작
c
printf("Hello World"); // 유저 모드에서 호출printf()는 C 표준 라이브러리(Standard Library)가 제공하는 API(Application Programming Interface) 이다. 이 함수는 내부적으로 포맷 문자열을 처리한 뒤, 결과를 파일(File) 이라는 추상화된 인터페이스를 통해 내보낸다.
핵심 개념: 유닉스/리눅스에서는 모든 것이 파일(Everything is a File) 이다. 모니터 출력도 표준 출력(stdout, fd=1) 이라는 "파일"에 쓰는 행위로 추상화된다.
③ 시스템 콜(System Call) 발생
printf() 내부에서는 결국 write() 시스템 콜을 호출한다.
c
write(1, "Hello World", 11);
// fd=1: 표준 출력(stdout)
// "Hello World": 출력할 데이터
// 11: 데이터 길이이 순간 CPU의 실행 모드가 유저 모드 → 커널 모드로 전환된다.
④ ~ ⑥ 커널 내부 처리
커널은 파일 디스크립터(File Descriptor) 1번이 표준 출력임을 확인하고, 해당 터미널이나 콘솔의 디바이스 드라이버(Device Driver)를 찾아 데이터 출력을 요청한다. 디바이스 드라이버가 하드웨어(비디오 카드)와 직접 통신하여 데이터를 전송한다.
⑦ ~ ⑨ 인터럽트를 통한 완료 통보
하드웨어가 데이터 전송을 완료하면, 인터럽트(Interrupt) 를 발생시켜 CPU에게 "작업 끝났어!" 라고 알린다. 이 신호를 받은 커널은 결과를 정리한다.
⑩ ~ ⑪ 유저 모드로 복귀
시스템 콜의 결과(기록한 바이트 수)가 유저 모드로 반환되고, printf()가 최종 반환된다.
전체 흐름의 핵심: 내려갔다 올라오는 구조
I/O 흐름은 항상 이 패턴이다:
- 유저 모드 → 커널 모드 → 하드웨어 (내려감)
- 하드웨어 → 커널 모드 → 유저 모드 (올라옴)
Blocking I/O vs Non-blocking I/O
| 구분 | Blocking I/O | Non-blocking I/O |
|---|---|---|
| 동작 방식 | 전체 I/O 과정이 끝날 때까지 대기 | 요청만 보내고 즉시 반환, 나중에 결과 확인 |
| 비유 | 음식점에서 음식 나올 때까지 자리에서 기다림 | 주문 후 번호표 받고 다른 일 하다가, 진동벨 울리면 가져옴 |
| 장점 | 구현이 단순하고 직관적 | CPU를 놀리지 않고 효율적으로 사용 |
| 단점 | 대기 시간 동안 CPU가 아무것도 못 함 | 구현이 복잡, 결과 확인 로직 필요 |
| 예시 | 일반적인 read(), write() 호출 | select(), poll(), epoll(), async/await |
1.3 인터럽트(Interrupt)
정의
인터럽트(Interrupt) 는 CPU의 현재 작업을 일시적으로 중단시키는 신호이다.
비유: 집에서 열심히 공부하고 있는데, 갑자기 "띵동!" 초인종이 울린다. 하던 공부를 잠깐 멈추고, 문을 열어 택배를 받은 뒤, 다시 공부로 돌아온다. 이것이 바로 인터럽트이다!
- 초인종 소리 = 인터럽트 신호
- 공부 = CPU가 하던 기존 작업
- 택배 수령 = 인터럽트 처리 루틴(ISR) 실행
- 다시 공부 = 원래 작업으로 복귀
왜 필요한가?
컴퓨터와 주변기기(키보드, 마우스, 디스크, 네트워크 카드 등) 사이의 입출력(I/O) 통신에서 필수적이다. 만약 인터럽트가 없다면, CPU가 "키보드 눌렸나? 아직? 키보드 눌렸나? 아직?" 하고 끊임없이 확인(Polling) 해야 하므로 매우 비효율적이다.
IRQ (Interrupt Request)
각 하드웨어 장치에는 고유한 인터럽트 번호(IRQ) 가 할당되어 있다. CPU는 이 번호를 보고 어떤 장치가 인터럽트를 보냈는지 식별한다.
| IRQ 번호 | 장치 | 설명 |
|---|---|---|
| 0 | CPU 타이머 | 스케줄링의 기본 단위, 주기적으로 발생 |
| 1 | 키보드 | 키를 누르거나 뗄 때마다 발생 |
| 2 | 캐스케이드 | 두 번째 인터럽트 컨트롤러 연결용 |
| 5 | 사운드 카드 | 오디오 처리 완료 시 |
| 12 | PS/2 마우스 | 마우스 이동/클릭 시 |
| 14 | 프라이머리 IDE | 디스크 읽기/쓰기 완료 시 |
인터럽트의 종류와 우선순위
인터럽트에도 우선순위(Priority) 가 있다. 전원 이상은 가장 급하고, 소프트웨어 인터럽트는 상대적으로 덜 급하다.
각 종류를 자세히 살펴보자:
1) 전원 이상 (Power Failure) - 최고 우선순위
전원이 나가면 모든 데이터가 날아가므로, 가장 먼저 처리해야 한다. 전원 이상이 감지되면 즉시 현재 상태를 비휘발성 저장장치에 백업하려 시도한다.
2) 기계 착오 (Machine Check)
CPU 내부에서 하드웨어적 결함이 감지된 경우이다. 예를 들어 메모리 패리티 에러(Parity Error)가 발생하면 이 인터럽트가 발생한다.
3) 외부 인터럽트 (External Interrupt)
CPU 바깥의 하드웨어 장치가 보내는 신호이다.
- I/O 인터럽트: 키보드 입력, 디스크 읽기 완료, 네트워크 패킷 도착 등
- 타이머 인터럽트: 일정 시간 간격으로 발생, OS 스케줄러의 심장 박동
- 사용자 개입:
Ctrl+Alt+Del같은 긴급 조작
4) 내부 인터럽트 / 트랩 (Internal Interrupt / Trap)
프로그램 실행 도중 발생하는 예외(Exception)이다.
- 0으로 나누기(Divide by Zero):
10 / 0연산 시도 - 오버플로우(Overflow): 표현 가능한 범위를 초과
- 언더플로우(Underflow): 표현 가능한 범위 아래로 내려감
- 잘못된 메모리 접근: 허용되지 않은 주소 참조
5) Supervisor Call (SVC) - 소프트웨어 인터럽트
프로그램이 OS에게 서비스를 요청할 때 의도적으로 발생시키는 인터럽트이다. 시스템 콜(System Call)이 바로 이것이다.
인터럽트 처리 과정
인터럽트가 발생했을 때 CPU가 처리하는 과정을 자세히 살펴보자.
단계별 설명
① 인터럽트 발생: 하드웨어 장치나 소프트웨어가 CPU에게 신호를 보낸다.
② CPU 일시정지: CPU는 현재 실행 중인 명령어를 마저 완료한 뒤, 다음 명령어 실행을 멈춘다.
③ 현재 상태 백업 (PCB에 저장): CPU의 현재 상태(프로그램 카운터(PC), 각종 레지스터(Register) 값, 상태 플래그(Flag) 등)를 PCB(Process Control Block) 에 저장한다.
PCB란? 프로세스의 "이력서" 같은 것이다. 프로세스가 어디까지 실행했고, 어떤 상태인지를 기록해 두는 자료구조이다. 나중에 복귀할 때 이 정보를 보고 원래 상태로 돌아간다.
④ ISR 주소 검색: 인터럽트 벡터 테이블(Interrupt Vector Table, IVT) 에서 해당 인터럽트 번호에 대응하는 처리 루틴의 주소를 찾는다.
⑤ ISR 실행: ISR(Interrupt Service Routine) 으로 점프하여 인터럽트를 처리한다. 예를 들어 키보드 인터럽트라면, 눌린 키 값을 읽어와 버퍼에 저장한다.
⑥ ~ ⑧ 복귀: ISR 실행이 끝나면, PCB에 백업해 둔 상태를 복원하고, 중단되었던 지점부터 원래 작업을 재개한다.
인터럽트 핵심 요약 표
| 항목 | 설명 |
|---|---|
| 인터럽트(Interrupt) | CPU의 현재 작업을 중단시키는 신호 |
| IRQ | 인터럽트 요청, 각 장치마다 고유 번호 |
| IVT | 인터럽트 번호 → ISR 주소 매핑 테이블 |
| ISR | 인터럽트를 실제로 처리하는 루틴(함수) |
| PCB | 프로세스 상태를 백업/복원하는 자료구조 |
1.4 시스템 콜(System Call)
정의
시스템 콜(System Call) 은 유저 모드에서 커널 모드로 진입하기 위한 유일한 합법적 관문이다.
비유: 유저 모드와 커널 모드 사이에는 요단강(삼도천) 이 흐르고 있다. 일반 프로그램은 절대 혼자서 이 강을 건널 수 없다. 오직 시스템 콜이라는 다리를 통해서만 건널 수 있다. 이 다리를 건너면 커널의 특권 영역에 들어가 하드웨어를 제어할 수 있게 된다.
모든 I/O의 본질: read와 write
수많은 API가 존재하지만, 커널 수준에서 I/O의 본질은 단 두 가지로 귀결된다.
| 시스템 콜 | 의미 | 예시 |
|---|---|---|
read() | 외부 → 프로그램으로 데이터를 읽어들이는 것 | 키보드 입력, 파일 읽기, 네트워크 수신 |
write() | 프로그램 → 외부로 데이터를 내보내는 것 | 화면 출력, 파일 쓰기, 네트워크 전송 |
API와 시스템 콜의 관계
일반 프로그래머가 직접 시스템 콜을 호출하는 일은 드물다. 대신 API(Application Programming Interface) 를 사용하면, API 내부에서 알아서 시스템 콜을 호출해 준다.
| 우리가 부르는 것 (API) | 내부에서 호출되는 것 (시스템 콜) |
|---|---|
printf("Hello") | write(1, "Hello", 5) |
scanf("%d", &n) | read(0, buf, size) |
fopen("file.txt", "r") | open("file.txt", O_RDONLY) |
malloc(1024) | brk() 또는 mmap() |
CreateFile() (Windows) | NtCreateFile() (NT 시스템 콜) |
핵심 통찰: OS의 정체성은 시스템 콜의 집합으로 결정된다. 만약 누군가 Windows의 시스템 콜을 100% 완벽하게 호환하는 새로운 OS를 만든다면, 그 위에서 MS Word, Excel 같은 Windows 프로그램이 수정 없이 그대로 동작할 수 있다. 실제로 Wine(리눅스에서 Windows 프로그램 실행)이나 WSL(Windows에서 Linux 프로그램 실행)이 이런 원리를 응용한 것이다.
시스템 콜 발생 과정 (내부 동작)
① 유저 모드에서 API 호출 (예: printf)
② API 내부에서 시스템 콜 번호를 CPU 레지스터에 설정
③ 소프트웨어 인터럽트 명령어 실행 (x86: int 0x80 또는 syscall)
④ CPU가 커널 모드로 전환
⑤ 커널이 시스템 콜 번호를 보고 해당 함수 실행
⑥ 결과를 레지스터에 담아 유저 모드로 복귀시스템 콜은 결국 앞서 배운 소프트웨어 인터럽트(Supervisor Call) 의 한 형태이다. 프로그램이 의도적으로 인터럽트를 발생시켜 커널 모드로 진입하는 것이다.
핵심 정리
주요 용어 정리 표
| 용어 | 영문 | 설명 |
|---|---|---|
| 운영체제 | Operating System (OS) | 하드웨어를 제어하고 애플리케이션을 지원하는 플랫폼 소프트웨어 |
| 커널 | Kernel | OS의 핵심 부분, 하드웨어를 직접 제어할 수 있는 특권 코드 |
| 유저 모드 | User Mode | 일반 애플리케이션이 동작하는 비특권 모드 |
| 커널 모드 | Kernel Mode | OS 커널이 동작하는 특권 모드, 하드웨어 직접 접근 가능 |
| 가상화 | Virtualization | 물리 하드웨어를 소프트웨어로 추상화하여 구현하는 기술 |
| 플랫폼 | Platform | CPU 아키텍처 + OS의 조합 |
| 멀티태스킹 | Multitasking | 여러 프로세스를 동시에 실행하는 것처럼 보이게 하는 기법 |
| 인터럽트 | Interrupt | CPU의 현재 작업을 중단시키는 신호 |
| IRQ | Interrupt Request | 고유 번호가 부여된 인터럽트 요청 |
| ISR | Interrupt Service Routine | 인터럽트를 실제로 처리하는 루틴(핸들러 함수) |
| IVT | Interrupt Vector Table | 인터럽트 번호와 ISR 주소를 매핑하는 테이블 |
| PCB | Process Control Block | 프로세스의 상태 정보를 저장하는 자료구조 |
| 시스템 콜 | System Call | 유저 모드에서 커널 모드로 진입하는 유일한 합법적 관문 |
| API | Application Programming Interface | 시스템 콜을 감싸 사용하기 쉽게 만든 인터페이스 |
| 디바이스 드라이버 | Device Driver | 특정 하드웨어를 제어하는 커널 모듈 |
| Blocking I/O | Blocking I/O | I/O 완료까지 프로세스가 대기하는 방식 |
| Non-blocking I/O | Non-blocking I/O | I/O 요청 후 즉시 반환, 나중에 결과를 확인하는 방식 |
이 장의 핵심 요약
1. OS = 위로는 애플리케이션 서포트, 아래로는 하드웨어 제어/관리
2. I/O 흐름 = 유저 모드 → (시스템 콜) → 커널 모드 → 하드웨어 → (인터럽트) → 복귀
3. 인터럽트 = CPU에게 보내는 "알림" 신호, 우선순위가 존재
4. 시스템 콜 = 유저 모드와 커널 모드 사이의 유일한 다리
5. 모든 I/O의 본질 = read (읽기) 아니면 write (쓰기)