Linux Kernel Overview
커널 구조, 프로세스/메모리 관리, syscall, sysctl 튜닝
🎯 커널이란?
Kernel: 하드웨어와 소프트웨어 사이에서 자원을 관리하는 운영체제의 핵심
flowchart TB subgraph UserApps["User Applications"] Apps["nginx, java, python, docker"] end subgraph UserSpace["User Space"] Libs["libc, libraries"] end SCI["System Call Interface (syscall)"] subgraph KernelSpace["Kernel Space"] PM["Process Manager"] MM["Memory Manager"] FS["File System"] NS["Network Stack"] DD["Device Drivers"] end subgraph HW["Hardware"] Hardware["CPU, RAM, Disk, NIC"] end UserApps --> UserSpace --> SCI --> KernelSpace --> HW
커널이 하는 일:
- 프로세스 관리: 프로그램 실행, CPU 시간 분배
- 메모리 관리: RAM 할당, 가상 메모리
- 파일시스템: 디스크 읽기/쓰기
- 네트워크: TCP/IP 스택, 소켓
- 디바이스 드라이버: 하드웨어 제어
🔒 Kernel Space vs User Space
CPU는 두 가지 모드로 동작함.
| 구분 | Kernel Mode | User Mode |
|---|---|---|
| 권한 | 모든 하드웨어 접근 가능 | 제한된 접근 |
| 메모리 | 전체 메모리 접근 | 자기 영역만 |
| 실패 시 | 시스템 패닉 (커널 패닉) | 프로세스만 죽음 |
| 실행 주체 | 커널 코드 | 애플리케이션 |
분리하는 이유:
- 보안: 악성 프로그램이 시스템 전체를 망가뜨리지 못하게 함
- 안정성: 하나의 프로그램 버그가 전체 시스템에 영향 주지 않게 함
- 격리: 프로세스 간 서로 간섭 방지
📞 System Call (syscall)
System Call: 유저 프로그램이 커널 기능을 요청하는 인터페이스
유저 프로그램은 직접 하드웨어를 제어할 수 없음. 파일을 읽거나 네트워크 통신을 하려면 반드시 커널에게 요청해야 함.
sequenceDiagram participant App as User Application participant Libc as libc (glibc) participant Kernel as Kernel participant Disk as Disk App->>Libc: read(fd, buf, size) Libc->>Kernel: syscall(SYS_read, ...) Note over Libc,Kernel: CPU 모드 전환: User → Kernel Kernel->>Disk: 실제 디스크에서 데이터 읽기 Disk-->>Kernel: 데이터 반환 Note over Libc,Kernel: CPU 모드 전환: Kernel → User Kernel-->>Libc: 결과 반환 Libc-->>App: 데이터 반환
주요 시스템 콜:
| 분류 | syscall | 설명 |
|---|---|---|
| 프로세스 | fork, exec, exit, wait | 프로세스 생성/종료 |
| 파일 | open, read, write, close | 파일 I/O |
| 메모리 | mmap, brk | 메모리 할당 |
| 네트워크 | socket, bind, connect, send, recv | 소켓 통신 |
# 프로세스가 호출하는 syscall 추적
strace -c ls
# 출력 예시:
# % time calls syscall
# ------ -------- --------
# 25.00 12 openat
# 20.00 10 read
# 15.00 8 close
# ...인프라 관점에서 중요한 이유:
- syscall은 오버헤드가 있음 → 과도한 syscall은 성능 저하 유발
- strace로 애플리케이션의 시스템 콜 패턴 분석 가능
- 컨테이너는 호스트 커널의 syscall을 공유 → 보안 고려 필요
⚙️ Process & Thread
프로세스 (Process)
프로세스: 실행 중인 프로그램의 인스턴스
각 프로세스가 가지는 독립 자원:
- 고유한 PID (Process ID)
- 독립된 메모리 공간 (가상 주소 공간)
- 파일 디스크립터 테이블
- 환경 변수
flowchart TB subgraph ProcessA["Process A (PID 1234)"] A1["Code (Text) - 실행할 명령어"] A2["Data - 전역 변수"] A3["Heap - 동적 할당 메모리"] A4["Stack - 함수 호출 스택"] end subgraph ProcessB["Process B (PID 1235)"] B1["별도 메모리 공간"] end ProcessA <-->|완전히 분리됨| ProcessB
스레드 (Thread)
스레드: 프로세스 내에서 실행되는 흐름의 단위
같은 프로세스의 스레드들은 메모리를 공유함.
flowchart TB subgraph Process["Process (PID 1234)"] Shared["Code, Data, Heap (공유)"] subgraph Threads["Threads"] T1["Thread 1"] T2["Thread 2"] T3["Thread 3"] end Shared --> Threads end
프로세스 vs 스레드
| 구분 | Process | Thread |
|---|---|---|
| 메모리 | 독립 (격리) | 공유 |
| 생성 비용 | 높음 | 낮음 |
| 통신 | IPC 필요 (복잡) | 메모리 공유 (쉬움) |
| 안정성 | 하나 죽어도 다른 것 영향 없음 | 하나 죽으면 전체 영향 |
| 예시 | nginx worker | Java 스레드 풀 |
프로세스 상태
stateDiagram-v2 [*] --> Created: fork() Created --> Ready: 스케줄러 선택 Ready --> Running: CPU 할당 Running --> Ready: time slice expired Running --> Waiting: I/O 요청 등 Waiting --> Ready: I/O 완료 Running --> Zombie: exit() Zombie --> [*]: wait()
| 상태 | 설명 |
|---|---|
| Running (R) | CPU에서 실행 중 |
| Ready (R) | 실행 가능, CPU 대기 |
| Waiting (S/D) | I/O 등 이벤트 대기 |
| Zombie (Z) | 종료됨, 부모가 수거 안 함 |
| Stopped (T) | 중지됨 (SIGSTOP) |
# 프로세스 상태 확인
ps aux
# STAT 컬럼:
# R: Running
# S: Sleeping (Interruptible)
# D: Disk Sleep (Uninterruptible) ← I/O 대기, kill 안 됨
# Z: Zombie
# T: StoppedZombie 프로세스가 많으면?
- 부모 프로세스가 wait()를 안 하고 있음
- 직접적 리소스 소모는 적지만 PID 고갈 가능
- 부모 프로세스 점검 필요
D 상태가 많으면?
- 디스크 I/O 병목 가능성
- NFS 등 네트워크 스토리지 문제 의심
- 스토리지 성능 확인 필요
🧠 Memory Management
가상 메모리 (Virtual Memory)
각 프로세스는 자신만의 가상 주소 공간을 가짐. 실제 물리 메모리(RAM)와 매핑되어 동작함.
flowchart LR subgraph ProcessA["Process A - Virtual Memory"] A1["Stack"] A2["Heap"] A3["Data"] A4["Code"] end subgraph Physical["Physical Memory (RAM)"] F1["Frame"] F2["Frame"] F3["Frame"] F4["Frame"] end subgraph ProcessB["Process B - Virtual Memory"] B1["Stack"] B2["Heap"] B3["Data"] B4["Code"] end A1 --> F1 A2 --> F2 B1 --> F3 B4 --> F4 MMU["MMU가 변환"]
가상 메모리의 장점:
- 프로세스 간 격리 (서로의 메모리 접근 불가)
- 물리 메모리보다 큰 주소 공간 사용 가능
- 메모리 단편화 해결
- Copy-on-Write로 효율적인 fork()
Swap
Swap: 물리 메모리가 부족할 때 디스크를 메모리처럼 사용하는 메커니즘
flowchart TB subgraph RAM["Physical RAM (가득참)"] R1["A-1"] R2["B-1"] R3["A-2"] R4["C-1"] R5["B-2"] end RAM -->|RAM 부족<br/>오래 안 쓴 페이지를 Swap으로| Swap subgraph Swap["Swap (Disk)"] S1["A-1"] S2["C-1"] end
# Swap 사용량 확인
free -h
# 출력:
# total used free shared buff/cache available
# Mem: 16Gi 8.0Gi 2.0Gi 500Mi 6.0Gi 7.5Gi
# Swap: 4.0Gi 500Mi 3.5Gi
# Swap 사용 프로세스 확인
for f in /proc/*/status; do
awk '/VmSwap|Name/{printf $2 " " $3}END{print ""}' $f
done | sort -k 2 -n | tail -10Swappiness: 커널이 얼마나 적극적으로 Swap을 사용할지 결정하는 값
# 현재 값 확인 (0-100, 기본값 60)
cat /proc/sys/vm/swappiness
# 값 변경 (런타임)
sysctl vm.swappiness=10
# 영구 설정 (/etc/sysctl.conf)
vm.swappiness=10| 값 | 동작 |
|---|---|
| 0 | Swap 거의 안 씀 (OOM 위험) |
| 10-30 | 서버 권장 (메모리 우선) |
| 60 | 기본값 |
| 100 | 적극적으로 Swap 사용 |
OOM Killer (Out of Memory)
메모리가 완전히 부족하면 커널이 프로세스를 강제 종료함.
flowchart TB A["Memory 사용량 증가"] --> B["RAM 부족<br/>Swap도 부족"] B --> C["OOM Killer 발동"] C --> D["oom_score가 높은<br/>프로세스 선택"] D --> E["프로세스 SIGKILL(9)"]
# OOM 발생 로그 확인
dmesg | grep -i "out of memory"
journalctl -k | grep -i oom
# 프로세스별 OOM 점수 확인 (높을수록 죽을 확률 높음)
cat /proc/<PID>/oom_score
# OOM 점수 조정 (-1000 ~ 1000, -1000이면 OOM 대상 제외)
echo -500 > /proc/<PID>/oom_score_adjOOM 방지 전략:
- 메모리 충분히 확보
- Swap 적절히 설정
- 중요 프로세스 oom_score_adj 낮추기
- cgroup으로 메모리 제한 (컨테이너)
💾 I/O Scheduler
디스크 I/O 요청을 어떤 순서로 처리할지 결정함.
I/O 스케줄러 종류
| 스케줄러 | 특징 | 적합한 환경 |
|---|---|---|
| none (noop) | 순서대로 처리, 스케줄링 없음 | NVMe SSD, 가상화 |
| mq-deadline | 데드라인 보장, 기아 방지 | 범용, DB 서버 |
| bfq | 공정한 대역폭 분배 | 데스크탑, 멀티미디어 |
| kyber | 저지연 목표 | 빠른 응답 필요 |
# 현재 스케줄러 확인
cat /sys/block/sda/queue/scheduler
# 출력: [mq-deadline] kyber bfq none
# 스케줄러 변경 (런타임)
echo "none" > /sys/block/sda/queue/scheduler
# NVMe는 보통 none이 기본
cat /sys/block/nvme0n1/queue/schedulerSSD vs HDD:
- HDD: mq-deadline 권장 (헤드 이동 최소화)
- SSD: none 또는 mq-deadline (물리적 탐색 없음)
- NVMe: none (충분히 빠름)
🌐 Network Stack
리눅스 네트워크는 커널에서 처리됨.
flowchart TB App["Application (nginx, curl)"] Socket["Socket Layer"] Transport["TCP / UDP (Transport)"] Network["IP (Network)"] Driver["Device Driver (e1000, ixgbe)"] NIC["NIC Hardware (Physical)"] App -->|"socket(), send(), recv()"| Socket Socket --> Transport --> Network --> Driver --> NIC
주요 네트워크 버퍼:
# 수신 버퍼 크기
cat /proc/sys/net/core/rmem_max
cat /proc/sys/net/core/rmem_default
# 송신 버퍼 크기
cat /proc/sys/net/core/wmem_max
cat /proc/sys/net/core/wmem_default
# TCP 버퍼 (min, default, max)
cat /proc/sys/net/ipv4/tcp_rmem
cat /proc/sys/net/ipv4/tcp_wmem🔧 Kernel Parameter Tuning (sysctl)
커널 파라미터는 /proc/sys/ 아래에 파일로 노출됨. sysctl 명령으로 조회/변경 가능함.
주요 튜닝 파라미터
네트워크
# 동시 연결 수 (기본 128, 웹서버는 높여야 함)
sysctl net.core.somaxconn=65535
# TCP 연결 대기 큐
sysctl net.ipv4.tcp_max_syn_backlog=65535
# TIME_WAIT 소켓 재사용 (주의해서 사용)
sysctl net.ipv4.tcp_tw_reuse=1
# 로컬 포트 범위 (클라이언트 연결 많을 때)
sysctl net.ipv4.ip_local_port_range="1024 65535"
# IP 포워딩 (라우터/NAT 역할)
sysctl net.ipv4.ip_forward=1메모리
# Swappiness (서버는 낮게)
sysctl vm.swappiness=10
# 파일시스템 캐시 비율
sysctl vm.dirty_ratio=20
sysctl vm.dirty_background_ratio=5
# OOM 시 패닉 (선택)
sysctl vm.panic_on_oom=0파일 & 프로세스
# 최대 열 수 있는 파일 수 (시스템 전체)
sysctl fs.file-max=2097152
# inotify 감시 수 (컨테이너 환경)
sysctl fs.inotify.max_user_watches=524288영구 설정
# /etc/sysctl.conf 또는 /etc/sysctl.d/*.conf
# 웹서버 튜닝 예시
net.core.somaxconn = 65535
net.ipv4.tcp_max_syn_backlog = 65535
net.ipv4.ip_local_port_range = 1024 65535
vm.swappiness = 10
fs.file-max = 2097152
# 적용
sysctl -p실무 시나리오별 튜닝
웹서버 (Nginx, Apache):
net.core.somaxconn = 65535
net.ipv4.tcp_max_syn_backlog = 65535
net.core.netdev_max_backlog = 65535DB 서버:
vm.swappiness = 1
vm.dirty_ratio = 40
vm.dirty_background_ratio = 10컨테이너 호스트:
fs.inotify.max_user_watches = 524288
fs.inotify.max_user_instances = 512
net.bridge.bridge-nf-call-iptables = 1📊 커널 정보 확인 명령어
# 커널 버전
uname -r
# 출력: 5.15.0-91-generic
# 커널 상세 정보
uname -a
# 커널 모듈 목록
lsmod
# 특정 모듈 정보
modinfo <module_name>
# 커널 로그
dmesg
journalctl -k
# 커널 파라미터 전체 조회
sysctl -a
# 시스템 콜 통계 (프로세스별)
strace -c <command>
# CPU 정보
cat /proc/cpuinfo
# 메모리 정보
cat /proc/meminfo🔗 관련 포스트
- Linux Fundamentals — 리눅스 기초
- Container Virtualization — 컨테이너 (커널 공유)
- Hypervisor & VM — 하이퍼바이저