![[운영체제] 운영체제란 무엇인가?](/images/os-1-1.png)
[운영체제] 운영체제란 무엇인가?
이 두 역할은 가상화, 동시성, 영속성이라는 세 테마로 조직됩니다. 각 섹션에서 이 원리가 어떻게 구체적인 메커니즘으로 드러나는지 살펴보세요.
1. 운영체제가 없다면?
추상화 제공자와 자원 관리자
운영체제가 없는 세상을 상상해봅시다. 프로그래머가 파일 하나를 읽으려면 디스크 컨트롤러의 레지스터 주소를 알아야 하고, 정확한 프로토콜에 따라 명령을 보내야 하며, 디스크 종류가 바뀌면 코드를 처음부터 다시 작성해야 합니다.
여기에 여러 프로그램이 동시에 실행된다면 문제는 더 심각해집니다. 프로그램 A가 쓴 메모리를 B가 덮어쓸 수 있고, 하나가 CPU를 독점하면 나머지는 영원히 실행되지 못합니다.
이로부터 운영체제의 두 가지 핵심 역할이 도출됩니다:
하드웨어의 복잡한 세부사항을 감추고, 깔끔하고 일관된 인터페이스를 제공합니다.
CPU, 메모리, 디스크 등을 여러 프로그램 사이에서 공정하고 효율적으로 분배합니다.
2. OS를 이해하는 세 가지 테마
OSTEP의 프레임워크: 가상화, 동시성, 영속성
운영체제는 방대한 소프트웨어이지만, OSTEP은 이를 가상화(Virtualization), 동시성(Concurrency), 영속성(Persistence)이라는 세 가지 테마로 조직합니다.
3. 인터럽트
CPU가 기다리지 않는 비결
CPU와 디스크의 속도 차이는 수만~수십만 배에 달합니다. 디스크가 데이터를 읽는 동안 CPU가 멈춰서 기다린다면 엄청난 낭비입니다.
폴링(polling)은 CPU가 "끝났어?"를 반복적으로 확인하는 방식입니다. 단순하지만 CPU를 낭비합니다. 인터럽트는 디스크가 작업을 마치면 CPU에게 전기 신호를 보내는 방식입니다. CPU는 그때까지 다른 일을 할 수 있습니다.
CPU가 프로그램 B의 코드를 실행하고 있습니다.
4. I/O 구조
CPU의 부담을 점진적으로 줄여나가는 세 가지 방식
동기 I/O는 프로그래머에게 예측 가능성을 제공합니다 — read() 다음 줄에서 데이터가 반드시 준비되어 있습니다. 비동기 I/O는 CPU 효율적이지만 코드가 복잡해집니다. 실제로는 절충안을 사용합니다 — 프로그램에겐 동기로 보이지만 OS 내부에서는 비동기로 처리합니다.
I/O 방식은 폴링 → 인터럽트 → DMA로 발전해왔으며, 반복되는 패턴은 하나입니다: CPU가 무언가를 기다리지 않고 다른 일을 계속 실행하도록 하는 구조.
CPU가 디스크 상태를 반복 확인합니다. 디스크 작업이 끝날 때까지 CPU는 다른 일을 할 수 없습니다.
5. 이중 모드와 시스템 콜
왜 아무 프로그램이나 하드웨어를 만지면 안 되는가
인터럽트 핸들러가 하는 일 — 상태 저장, 데이터 이동, 실행 상태 변경 — 은 모두 특권적 작업입니다. 일반 프로그램이 이런 작업을 할 수 있다면 시스템 전체가 위험해집니다.
이중 모드: CPU 내부의 모드 비트가 해결책입니다. 커널 모드(0)에서는 모든 명령어와 하드웨어 접근이 가능하고, 사용자 모드(1)에서는 제한된 명령어만 실행할 수 있습니다.
프로그램이 커널 서비스를 요청하려면 트랩(trap)이라는 특별한 CPU 명령어(syscall)를 실행합니다. 이것이 시스템 콜 — 사용자 프로그램이 커널 서비스를 요청하는 유일한 공식 통로입니다.
트랩과 하드웨어 인터럽트 모두 커널 모드 전환을 유발하고, 상태 저장 → 핸들러 → 복귀라는 동일한 처리 구조를 따릅니다.
트랩은 동기적(프로그래머가 의도적으로 발생), 하드웨어 인터럽트는 비동기적(예측 불가능한 시점에 발생).
사용자가 셸에 프로그램 실행 명령을 입력합니다.
트랩을 통해 커널 모드 진입 → 새 프로세스를 생성합니다.
fork()가 완료되면 사용자 모드로 돌아옵니다.
트랩을 통해 커널 모드 진입 → 프로그램을 메모리에 로드합니다.
로드된 프로그램이 사용자 모드에서 실행됩니다.
파일 읽기를 위해 트랩 → 커널 모드 진입 → 디스크 요청 → 프로세스 대기.
디스크 작업 완료 후 하드웨어 인터럽트 발생 → 커널이 데이터를 전달합니다.
데이터가 준비되어 프로그램이 사용자 모드에서 계속 실행됩니다.
트랩을 통해 커널 모드 진입 → 프로세스를 종료합니다.
셸이 자식 프로세스의 종료를 확인하고 자원을 회수합니다.
6. 저장 장치 계층구조
속도, 용량, 비용의 트레이드오프
빠르면서 큰 저장 장치를 만들 수 없는 이유는 속도↑ 용량↓ 비용↑의 트레이드오프 때문입니다. 그래서 컴퓨터는 여러 종류의 저장 장치를 계층적으로 조합합니다.
이 계층이 효과적인 이유는 프로그램의 데이터 접근 패턴에 지역성(locality)이 있기 때문입니다. 시간적 지역성: 최근 접근한 데이터는 곧 다시 접근할 가능성이 높고 (예: for 루프), 공간적 지역성: 어떤 주소에 접근했으면 근처 주소도 곧 접근합니다 (예: 배열 순회).
7. 멀티프로그래밍과 멀티태스킹
CPU를 놀리지 않고, 누구도 독점하지 못하게
멀티프로그래밍: 여러 프로그램을 메모리에 동시에 올려놓고, 하나가 I/O를 기다릴 때 다른 프로그램으로 전환합니다. 동기는 CPU 활용률입니다.
그러나 한계가 있습니다. 프로그램이 I/O 없이 계산만 하면 CPU를 독점할 수 있습니다.
멀티태스킹(시분할): 타이머 인터럽트로 강제 전환합니다. 프로그램의 의지와 무관하게 일정 시간이 지나면 끊어줍니다. 동기는 CPU 활용률에 더해 응답성과 공정성입니다.
타이머 인터럽트는 앞서 배운 하드웨어 인터럽트의 구체적인 예시입니다. 충분히 빠른 전환(초당 수십~수백 번)으로 동시 실행의 환상을 만들어내는 것이 CPU 가상화의 구체적 메커니즘입니다.
8. strace로 보는 시스템 콜
echo hello 하나에 시스템 콜이 38개?
strace echo hello를 실행하면 놀라운 결과를 볼 수 있습니다. 단순히 "hello"를 출력하는 프로그램인데, 38개의 시스템 콜이 발생합니다.
크게 세 덩어리로 나뉩니다: execve(프로그램 실행 요청), 라이브러리 로딩(openat, read, mmap, close 등으로 libc.so.6 로딩), 그리고 실제 목적(write(1, "hello\n", 6) — 단 1개!).
운영체제가 "프로그래머가 printf 한 줄 쓰면 뒤에서 수십 번의 시스템 콜을 처리한다"는 추상화의 실체입니다.