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 ModeUser 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 스레드

구분ProcessThread
메모리독립 (격리)공유
생성 비용높음낮음
통신IPC 필요 (복잡)메모리 공유 (쉬움)
안정성하나 죽어도 다른 것 영향 없음하나 죽으면 전체 영향
예시nginx workerJava 스레드 풀

프로세스 상태

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: Stopped

Zombie 프로세스가 많으면?

  • 부모 프로세스가 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 -10

Swappiness: 커널이 얼마나 적극적으로 Swap을 사용할지 결정하는 값

# 현재 값 확인 (0-100, 기본값 60)
cat /proc/sys/vm/swappiness
 
# 값 변경 (런타임)
sysctl vm.swappiness=10
 
# 영구 설정 (/etc/sysctl.conf)
vm.swappiness=10
동작
0Swap 거의 안 씀 (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_adj

OOM 방지 전략:

  • 메모리 충분히 확보
  • 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/scheduler

SSD 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 = 65535

DB 서버:

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

🔗 관련 포스트


🔗 참고 자료