Kernel 이란?
앞 글에서는 프로그램이 하드웨어를 쓰기까지 어떤 층을 거치는지, 그 큰 그림을 잡아봤습니다. 이번에는 그 한가운데 있던 Kernel 하나만 놓고 조금 더 들여다보려 합니다.
오늘은 Kernel의 모든 기능을 한꺼번에 공부하지 않습니다. 먼저 운영체제가 왜 Kernel을 필요로 하는지, 그리고 애플리케이션이 파일을 읽을 때 Kernel이 어떤 역할을 하는지까지만 이해해보겠습니다.
데이터 엔지니어 입장에서도 중요한 주제입니다. Spark가 메모리를 쓰거나, Kafka가 데이터를 디스크에 기록하거나, PostgreSQL이 파일을 읽는 과정이 모두 운영체제의 기능과 연결되어 있기 때문입니다.
1. 왜 Kernel이 필요한가
하나의 서버에서 여러 프로그램이 동시에 실행되고 있다고 생각해보겠습니다.
- Kafka가 디스크에 데이터를 기록합니다.
- PostgreSQL이 데이터 파일을 읽습니다.
- Spark가 대량의 데이터를 메모리에 올립니다.
- 그리고 이 모든 프로그램이 CPU를 쓰려고 합니다.
여기서 문제가 생깁니다. CPU, 메모리, 디스크 같은 자원은 한정되어 있는데, 여러 프로그램이 그걸 동시에 쓰려고 하는 것입니다.
만약 모든 프로그램이 하드웨어를 제약 없이 직접 제어할 수 있다면 어떻게 될까요? 예를 들어 프로그램 A가 프로그램 B가 쓰고 있는 메모리 영역에 마음대로 데이터를 쓰면, B의 데이터는 그대로 망가지게 됩니다. 어떤 프로그램이 디스크 장치를 잘못 제어하면 다른 프로그램의 작업까지 영향을 받을 수도 있습니다.

그래서 컴퓨터에는 두 가지 역할이 필요합니다.
- 각 프로그램이 쓸 수 있는 자원을 관리합니다.
- 프로그램이 허용된 범위 안에서만 자원을 쓰도록 통제합니다.
이 두 역할의 중심에 있는 것이 Kernel(커널) 입니다.
2. Kernel이란 무엇인가
Kernel은 운영체제의 핵심 부분으로, 프로그램과 하드웨어 사이에서 시스템 자원을 관리하고, 프로그램이 필요한 작업을 할 수 있도록 지원합니다.
예를 들어 Kernel은 다음과 같은 일을 합니다.
- 어떤 프로그램이 언제 CPU를 쓸지, 실행 순서를 관리합니다.
- 각 프로그램이 쓰는 메모리를 관리합니다.
- 파일을 읽고 쓰는 기능을 제공합니다.
- 네트워크 통신을 처리합니다.
- 하드웨어를 제어하는 장치 드라이버와 협력합니다.
여기서 하나 짚고 갈 점은, Kernel이라는 프로그램 하나가 모든 일을 직접 하는 건 아니라는 것입니다. Kernel 안에는 프로세스 스케줄러, 메모리 관리, 파일 시스템, 네트워크 처리 코드, 장치 드라이버 같은 여러 구성 요소가 있고, 각자 자기 역할을 맡고 있습니다.

3. User Space와 Kernel Space
그럼 프로그램은 어떻게 Kernel에 일을 맡길까요? 이걸 이해하려면 먼저 실행 환경을 크게 두 가지로 나눠서 봐야 합니다.
- User Space(사용자 공간) : 일반 애플리케이션이 실행되는 영역입니다.
- Kernel Space(커널 공간) : Kernel 코드가 실행되면서 시스템 자원을 관리하는 영역입니다.
이 둘은 단순히 메모리의 다른 위치를 뜻하는 것만은 아닙니다. CPU가 허용하는 권한 수준과 메모리 접근 제한으로 보호되는 경계입니다. 예를 들어 x86 CPU에서 Linux는 애플리케이션을 가장 낮은 권한(Ring 3, user mode)으로, Kernel을 가장 높은 권한(Ring 0, kernel mode)으로 실행합니다. 그래서 User Space에 있는 프로그램은 다른 프로그램의 메모리나 장치에 마음대로 손댈 수 없는 것입니다.

그럼 권한이 낮은 프로그램이 파일을 읽으려면 어떻게 해야 할까요? Kernel에게 "이 파일 좀 읽어주세요"라고 요청해야 합니다. 그 요청 통로가 바로 System Call입니다.
4. System Call
System Call(시스템 호출) 은 프로그램이 Kernel에 특정 작업을 요청하는 공식적인 진입 경로입니다. 예를 들어 Linux에서 파일을 읽으려면 프로그램은 read 라는 System Call을 이용합니다.
4.1. 함수 read()와 System Call read는 다르다
여기서 한 가지 구분해야 할 것이 있습니다. 우리가 C 코드에서 read() 를 부르면, 그게 곧바로 System Call인 것은 아닙니다.
- C 라이브러리(glibc)의
read()함수 : 프로그램이 호출하는 평범한 함수입니다. User Space 안에서 실행됩니다. - Linux의
readSystem Call : Kernel에 파일 읽기를 요청하는 인터페이스입니다. 이걸 호출하는 순간 CPU가 kernel mode로 전환됩니다.

일반적인 Linux 환경에서는 C 라이브러리 함수가 System Call을 대신 불러주기 때문에 둘이 같아 보이지만, 함수 호출과 System Call은 같은 개념이 아닙니다. Java나 Python 같은 언어도 결국 JVM이나 인터프리터 안의 라이브러리를 거쳐 같은 System Call에 도착하게 됩니다.
4.2. 전체 구조
지금까지의 내용을 한 장으로 정리하면 다음과 같습니다.

여기서 Driver(장치 드라이버) 는 운영체제가 특정 종류의 하드웨어를 제어할 수 있도록 연결해주는 소프트웨어입니다. SSD, 네트워크 카드처럼 장치마다 동작 방식이 다르기 때문에, Kernel은 Driver를 통해 각 장치를 다룹니다.
단, 이 그림은 개념을 위한 단순화된 모델입니다. 모든 작업이 모든 계층을 순서대로 통과하는 것은 아닙니다.
4.3. 파일 읽기 요청 ≠ 디스크 읽기
이게 무슨 뜻인지 파일 읽기로 보면 바로 이해가 됩니다. 파일 읽기 요청은 먼저 Kernel의 파일 시스템 처리 경로로 들어가는데, 이때 필요한 데이터가 이미 메모리에 올라와 있다면, 실제 저장 장치에는 전혀 접근하지 않고도 처리가 끝날 수 있습니다.

그래서 다음 두 문장은 다른 이야기입니다.
- 파일 읽기를 요청했다.
- 디스크에서 실제 데이터를 읽었다.
이렇게 Kernel이 파일 데이터를 메모리에 올려두는 공간을 Page Cache(페이지 캐시) 라고 하는데, 이 차이는 나중에 Linux의 Page Cache와 I/O를 공부할 때 매우 중요해집니다. 같은 파일을 두 번째 읽을 때 훨씬 빨라지는 것도 대부분 이 Page Cache 덕분이기 때문입니다.
5. Kernel을 거치면 무엇을 얻고, 무엇을 감수하는가
Kernel을 거치는 구조는 공짜가 아닙니다. Kernel이 필요한 이유와 함께, 그 대가로 치르는 비용도 같이 봐야 합니다.
| 구분 | 내용 |
|---|---|
| 장점 1. 보호와 격리 | 프로그램이 다른 프로그램의 메모리나 시스템 자원을 마음대로 침범하지 못하도록 막을 수 있습니다. |
| 장점 2. 공통 인터페이스 | 프로그램마다 디스크나 네트워크 장치를 제각각 제어하는 대신, 운영체제가 주는 공통 기능을 쓰면 됩니다. 개발이 쉬워지고, 다양한 하드웨어를 지원하기도 편해집니다. |
| 단점 1. 실행 비용 | Kernel에 요청할 때마다 실행 권한을 전환하고 인자를 확인하는 비용이 듭니다. 다만 모든 요청이 실제 디스크 접근이나 네트워크 전송까지 가는 것은 아닙니다. |
| 단점 2. 복잡성과 병목 | 여러 프로그램이 자원을 나눠 쓰기 때문에 관리와 동기화가 필요하고, 특정 자원이나 Kernel 내부 경로에 요청이 몰리면 병목이 생길 수 있습니다. |
여기서 동기화(Synchronization) 란 여러 작업이 같은 자원을 동시에 다룰 때 서로 충돌하지 않도록 실행 순서나 접근을 조정하는 것을 말합니다.
결국 Kernel은 프로그램과 하드웨어 사이에 그냥 끼어 있는 계층이 아닙니다. 여러 프로그램이 자원을 안전하게 나눠 쓸 수 있게 해주는 관리 체계라고 보는 게 맞습니다.
6. 실습 : strace로 System Call 관찰하기
이제 개념을 실제 Linux 환경에서 확인해보겠습니다. strace 는 프로그램이 실행되면서 어떤 System Call을 쓰는지, 그리고 그 결과가 무엇이었는지를 한 줄씩 보여주는 도구입니다.

앞 글과 같은 Ubuntu 26.04(WSL2, Kernel 6.18)에서 strace 6.19로 실행했습니다. strace 가 없다면 Ubuntu / Debian 계열에서는 다음처럼 설치할 수 있습니다.
sudo apt update
sudo apt install strace
6.1. cat이 파일 하나를 읽는 과정
/etc/hostname 은 시스템의 호스트 이름이 저장된 파일이고, cat 은 파일 내용을 출력하는 명령어입니다. 파일을 열고, 읽고, 쓰고, 닫는 System Call만 골라서 보겠습니다.
strace -e trace=openat,read,write,close gnucat /etc/hostname
참고로 제 환경의 cat 은 앞 글에서 본 것처럼 Rust로 다시 작성된 uutils 버전이라, read 대신 splice 라는 다른 System Call로 데이터를 옮깁니다. 그래서 여기서는 GNU 버전인 gnucat 으로 실행했습니다. 대부분의 다른 배포판에서는 그냥 cat 이 GNU 버전입니다.
출력의 앞부분에는 라이브러리(libc.so.6)와 언어 설정 파일을 여는 호출이 쭉 나오는데, 그 뒤 우리가 요청한 파일을 다루는 부분만 보면 다음과 같습니다.
openat(AT_FDCWD, "/etc/hostname", O_RDONLY) = 3
read(3, "DESKTOP-BTO4IR0\n", 262144) = 16
write(1, "DESKTOP-BTO4IR0\n", 16) = 16
read(3, "", 262144) = 0
close(3) = 0
| 호출 | 의미 |
|---|---|
openat(...) = 3 |
파일을 열어달라고 요청합니다. 결과로 받은 3 이 이 파일을 가리키는 번호입니다. |
read(3, ...) = 16 |
3번 파일에서 데이터를 읽어달라고 요청합니다. 16바이트를 읽었습니다. |
write(1, ...) = 16 |
읽은 내용을 1번(표준 출력, 즉 화면)에 써달라고 요청합니다. |
read(3, ...) = 0 |
일반 파일에서 0 은 더 읽을 데이터가 없다는 뜻입니다. |
close(3) = 0 |
다 쓴 파일을 닫습니다. |
openat 이 돌려준 3 은 File Descriptor(파일 디스크립터) 입니다. 프로그램이 열린 파일이나 소켓 같은 대상을 구분할 때 쓰는 작은 정수인데, 0, 1, 2번은 각각 표준 입력, 표준 출력, 표준 에러로 이미 쓰이고 있기 때문에 처음 연 파일이 3번을 받게 된 것입니다.
6.2. 여기서 확인할 수 있는 것과 없는 것
여기서 확인할 수 있는 건, cat 이 파일 내용을 마법처럼 읽어오는 게 아니라 Kernel이 제공하는 인터페이스를 통해 파일을 열고, 읽고, 화면에 쓴다는 사실입니다. 화면에 호스트 이름이 찍힌 것도 결국 Kernel에게 "이 16바이트를 써달라"고 요청한 결과입니다.
다만 strace만으로는 실제 디스크에 접근했는지까지 확정할 수 없습니다. 4.3에서 본 것처럼, 이 파일의 데이터가 이미 Page Cache에 올라와 있었다면 read 는 디스크를 건드리지 않고 메모리에서 바로 끝났을 수 있기 때문입니다.
7. 핵심 정리
7.1. 반드시 이해할 것
- Kernel은 CPU, 메모리, 파일, 네트워크 같은 시스템 자원을 관리하는 운영체제의 핵심입니다.
- Kernel 안에는 스케줄러, 메모리 관리, 파일 시스템, 드라이버 같은 여러 구성 요소가 각자 역할을 나눠 맡고 있습니다.
- 애플리케이션은 권한이 낮은 User Space 에서, Kernel은 권한이 높은 Kernel Space 에서 실행됩니다.
- 사용자 프로그램은 System Call 을 통해 Kernel에 작업을 요청합니다. 라이브러리 함수
read()와 System Callread는 다른 개념입니다. - 파일을 읽는 System Call이 일어났다고 해서 반드시 실제 디스크 I/O가 일어나는 것은 아닙니다.
- 애플리케이션부터 하드웨어까지의 계층은 유용한 모델이지만, 모든 작업이 모든 계층을 거치는 것은 아닙니다.
7.2. 장점과 단점
- 장점 : 자원 보호, 프로그램 간 격리, 하드웨어에 대한 공통 인터페이스
- 단점 : 권한 전환 같은 실행 비용, 자원 공유를 관리하면서 생기는 복잡성과 병목
7.3. 직접 해볼 것
-
strace로cat /etc/hostname(또는gnucat) 의 System Call을 관찰합니다. -
openat,read,close가 어떤 순서로 나타나는지 확인합니다. -
read가 돌려준 값과 실제 출력된 파일 내용의 길이를 비교해봅니다.
공부 참고 자료
📑 1. syscalls(2) - Linux manual page
📑 2. read(2) - Linux manual page
📑 3. open(2) - Linux manual page