운영체제는 무엇을 하는가?

컴퓨터를 사용할 때 우리는 브라우저를 열고, 터미널에서 명령을 실행하고, 파일을 저장한다. 이러한 행위가 자연스럽게 느껴지는 이유는 운영체제가 하드웨어의 복잡성을 숨기고 있기 때문이다.

운영체제의 역할은 크게 두 가지로 요약할 수 있다. 첫째, 하드웨어를 추상화하여 응용 프로그램이 하드웨어의 세부 사항을 몰라도 동작할 수 있게 하는 것이다. 둘째, 여러 프로그램이 동시에 실행될 때 CPU, 메모리, 디스크 같은 자원을 공정하게 분배하는 것이다. 이 두 가지를 수행하지 못하면 모든 프로그램이 하드웨어를 직접 제어해야 하며, 프로그램 간의 충돌을 피할 방법이 없어지는 것이다.

커널이란?

운영체제의 핵심 구성 요소가 커널이다. 커널은 하드웨어와 직접 소통하는 유일한 소프트웨어이며, 모든 응용 프로그램은 커널을 통해서만 하드웨어에 접근할 수 있다.

그렇다면 커널과 운영체제는 같은 것인가? 엄밀히 말하면 다르다. 운영체제는 커널에 더해 시스템 라이브러리, 셸, 유틸리티 등을 포함하는 더 넓은 개념이다. 리눅스라는 이름은 공식적으로 커널만을 가리킨다. 우리가 흔히 리눅스라고 부르는 것은 이 커널 위에 GNU 도구와 각종 소프트웨어를 올린 배포판인 것이다.

유저 공간과 커널 공간

리눅스는 메모리를 유저 공간과 커널 공간으로 나눈다. 이 분리가 존재하는 이유는 안정성과 보안 때문이다.

유저 공간에서 실행되는 프로그램은 하드웨어에 직접 접근할 수 없다. 파일을 읽으려면 시스템 콜을 통해 커널에 요청해야 한다. 커널은 이 요청이 정당한지 확인한 후 하드웨어 조작을 수행하고 결과를 돌려준다. 만약 이런 분리가 없다면 어떤 프로그램이든 디스크의 아무 영역을 덮어쓸 수 있고, 다른 프로세스의 메모리를 읽을 수 있을 것이다.

┌─────────────────────────────────────┐
│         유저 공간 (User Space)        │
│  ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐  │
│  │ bash│ │nginx│ │ python│ │ java│  │
│  └──┬──┘ └──┬──┘ └──┬──┘ └──┬──┘  │
├─────┴───────┴───────┴───────┴──────┤
│          시스템 콜 인터페이스          │
├────────────────────────────────────┤
│        커널 공간 (Kernel Space)       │
│  프로세스 관리 │ 메모리 관리 │ VFS     │
│  네트워킹     │ 디바이스 드라이버      │
├────────────────────────────────────┤
│              하드웨어                │
└────────────────────────────────────┘

CPU 자체가 이 분리를 하드웨어 수준에서 지원한다. x86 아키텍처에서는 Ring 0(커널 모드)과 Ring 3(유저 모드)라는 권한 수준이 존재하며, 유저 모드에서 커널 모드의 명령을 실행하려 하면 CPU가 예외를 발생시킨다. 이러한 하드웨어 보호 메커니즘이 운영체제의 안정성을 보장하는 기반이 되는 것이다.

리눅스 커널의 주요 서브시스템

리눅스 커널은 여러 서브시스템으로 구성되어 있으며, 각각이 특정 영역의 자원 관리를 담당한다.

서브시스템역할
프로세스 관리프로세스 생성, 스케줄링, 종료
메모리 관리가상 메모리, 페이징, 메모리 할당
파일 시스템 (VFS)파일 읽기/쓰기, 다양한 파일 시스템 추상화
네트워킹TCP/IP 스택, 소켓, 패킷 처리
디바이스 드라이버하드웨어 장치와의 통신
IPC프로세스 간 통신 (파이프, 시그널, 공유 메모리)

이 서브시스템들이 서로 협력하여 하나의 통합된 환경을 제공하는 것이다. 예를 들어, 웹 서버가 클라이언트의 요청을 처리하려면 네트워킹 서브시스템이 패킷을 수신하고, 프로세스 관리가 워커 프로세스를 스케줄링하고, 파일 시스템이 정적 파일을 읽어 메모리에 올리는 과정이 동시에 일어난다.

모놀리식 커널 vs 마이크로커널

커널 설계에는 크게 두 가지 접근법이 있다. 모놀리식 커널은 모든 서브시스템이 하나의 큰 바이너리로 컴파일되어 같은 주소 공간에서 실행되는 구조이다. 마이크로커널은 최소한의 기능만 커널에 두고 나머지를 유저 공간의 서버 프로세스로 분리하는 구조이다.

과연 마이크로커널이 더 우수한 설계인가? 이론적으로는 그렇다. 모듈 간의 격리가 더 강하고, 하나의 서브시스템이 죽어도 전체 시스템이 멈추지 않는다. 하지만 현실에서는 유저 공간과 커널 공간 사이의 빈번한 컨텍스트 스위칭으로 인한 성능 저하가 문제가 된다.

리눅스는 모놀리식 커널을 채택하고 있다. 다만, 커널 모듈이라는 메커니즘을 통해 기능을 동적으로 로드하고 언로드할 수 있어 순수한 모놀리식의 경직성을 보완하고 있다. 디바이스 드라이버가 대표적인 예로, 필요할 때만 메모리에 올리고 사용하지 않을 때는 해제할 수 있는 것이다.

이 시리즈에서 다루는 것

이 시리즈는 리눅스 커널의 내부 동작을 하나씩 살펴본다. 프로세스가 어떻게 생성되고 스케줄링되는지, 가상 메모리가 어떻게 동작하는지, 파일 시스템이 데이터를 어떻게 관리하는지, 그리고 최종적으로 컨테이너가 이 모든 커널 기능을 어떻게 활용하는지까지를 다룰 것이다.

다음 포스트에서는 리눅스의 프로세스와 스레드를 살펴본다.