C
운영체제/고급/Lesson 05

파일시스템·스케줄링·고급 — IPC·시그널·cgroup·관측

60분·theory

파일시스템·스케줄링·고급 — IPC·시그널·cgroup·관측

🎯 이 lesson 을 읽고 나면

이 lesson 을 다 읽고 나면 아래 3가지를 자신 있게 할 수 있습니다.

  • ✅ Context Switching 비용 (스레드 < 프로세스)
  • ✅ epoll · kqueue · IOCP 이벤트 기반 I/O
  • ✅ Copy-on-Write + fork() 동작

학습 목표를 체크리스트로 두고 다 답할 수 있게 되면 lesson 을 닫으세요.

파일 시스템 — inode와 디렉토리

한 줄: 파일 = inode + 데이터 블록 + 디렉토리는 이름→inode 매핑.

파일 열기 → 읽기 6단계:
1. open("/etc/hosts") — 경로 파싱
2. dentry 캐시 조회 — 디렉토리 항목 캐시 (RAM)
3. inode 로드 — 권한·크기·블록 위치·timestamp 메타데이터
4. 권한 체크 — 사용자/그룹·rwx
5. fd 할당 — 파일 디스크립터 번호 반환 (0,1,2 = stdin/stdout/stderr, 3+ = 사용자)
6. read(fd, buf, n) — 블록을 페이지 캐시 → 사용자 버퍼

파일시스템 종류:

FS용도특징
ext4Linux 표준안정·범용
XFS대용량 서버고성능 (RHEL 기본)
btrfs차세대스냅샷·체크섬
ZFS데이터 무결성Solaris·FreeBSD 기원
APFSmacOSSSD 최적화·스냅샷
NTFSWindows권한·journaling

흔한 함정:

  • ❌ "too many open files" — fd 부족 (ulimit -n 증가)
  • ❌ 파일 동시 쓰기 → 깨짐 (fcntl 락·O_APPEND)
  • ❌ symbolic link 따라가다 무한 루프 (-L 옵션 주의)
  • fsync() 호출로 디스크 sync 보장 (DB·write-ahead log)

CPU 스케줄링 — 누가 먼저 실행되나

Linux CFS (Completely Fair Scheduler) — 기본 스케줄러 (2.6.23+).

원리: 각 프로세스의 virtual runtime 추적. 가장 적게 실행한 프로세스 우선 → 공평성.

스케줄링 정책:

정책용도우선순위 방식
SCHED_NORMAL일반 프로세스nice 값 (-20 ~ +19)
SCHED_FIFO실시간우선순위 + FIFO
SCHED_RR실시간우선순위 + Round Robin
SCHED_IDLE백그라운드다른 작업 없을 때만

Round Robin (RR) 동작:
1. 각 프로세스에 타임 슬라이스 (예: 10ms) 할당
2. 슬라이스 끝나면 큐 끝으로
3. 다음 프로세스 실행
공평하지만 컨텍스트 스위치 비용

스케줄링 함정:

  • ❌ nice 값 잘못 (낮을수록 우선순위 ↑) — 직관과 반대
  • ❌ 실시간 SCHED_FIFO 무한 루프 → 시스템 전체 멈춤
  • ❌ CPU affinity 미설정 → 캐시 무효화 잦음

현대 트렌드:

  • EEVDF (Earliest Eligible Virtual Deadline First) — Linux 6.6+ 의 CFS 후속
  • Pluggable — sched_ext 로 BPF 스케줄러 커스텀 (2024+)

IPC·시그널·좀비 — 프로세스 간 통신

IPC (Inter-Process Communication) 종류:

방식속도용도
파이프부모-자식 단방향 (\ 셸)
명명된 파이프 (FIFO)무관한 프로세스 (mkfifo)
공유 메모리최고DB 캐시·게임 (zero-copy)
메시지 큐시스템 V·POSIX
세마포어빠름동기화만 (데이터 X)
소켓TCP/UDP/Unix domain (가장 범용)
시그널빠름단순 알림 (SIGTERM 등)

시그널 활용:

시그널의미사용
SIGTERM (15)정상 종료 요청kill <PID> 기본
SIGKILL (9)강제 종료 (handler X)kill -9
SIGINT (2)인터럽트 (Ctrl+C)인터랙티브 종료
SIGHUP (1)행업 / 설정 리로드nginx reload
SIGCHLD자식 종료 알림좀비 회수용
SIGUSR1/2사용자 정의앱 설정 다시 읽기

Graceful Shutdown 패턴:

code
SIGTERM 수신 → 새 요청 거부 → 진행 중 요청 완료 대기 → 자원 정리 → exit

좀비 프로세스 (Zombie):

  • 자식 종료했지만 부모가 wait() 호출 안 함 → PCB 만 남아있는 상태
  • ps 에서 Z 상태로 표시
  • 방지: 부모가 SIGCHLD 핸들러 등록 후 waitpid() 호출
  • 고아 프로세스: 부모 먼저 죽으면 init(PID 1)이 입양 → init 가 wait → 정리

cgroup + 관측성 — 컨테이너의 토대

cgroup (Control Group) — Linux 가 프로세스 그룹의 자원을 제한. Docker·K8s 의 핵심.

활용 예:

자원제한 방법
CPUcpu.cfs_quota_us — 100ms 중 50ms 만 → 0.5 코어
메모리memory.limit_in_bytes — 초과 시 OOM Kill
I/Oio.weight — 디스크 대역폭
네트워크net_cls + tc — 트래픽 제어
장치devices.allow — 특정 디바이스만 접근

Docker 예: docker run --memory=1g --cpus=0.5 nginx 가 내부적으로 cgroup 설정.

cgroup v2 (현대 Linux): 통합된 계층 (v1 의 분리된 컨트롤러 통합).

관측성 (Observability) 3축:

도구용도
MetricsPrometheus · Grafana시계열 수치 (CPU·MEM·요청수)
LogsLoki · ELK · Splunk텍스트 이벤트
TracesJaeger · Tempo · Zipkin분산 호출 추적

eBPF (Extended Berkeley Packet Filter):

  • 커널 안에서 안전하게 코드 실행 (커널 패치 X)
  • 시스템 콜·네트워크·디스크 모든 이벤트 관측
  • 도구: bcc·bpftrace·pixie·falco (보안)
  • Cilium (K8s 네트워크), Datadog APM 모두 eBPF 기반

관측 함정:

  • ❌ 메트릭만 (왜 그런지 모름) → trace 도
  • ❌ 로그 폭주 (10TB/day) → 샘플링·필터링
  • ❌ /var/log 가득 → 로그 로테이션 필수 (logrotate)
💻 📌 시나리오로 배우는 시스템 관측
# ============================================================
# 시나리오 1: "디스크가 부족하다는 알람"
# ============================================================
df -h                              # 마운트 포인트별 사용량
# Filesystem      Size  Used Avail Use% Mounted on
# /dev/sda1        50G   42G  6G   88% /
# tmpfs           3.9G  100M 3.8G   3% /run

# 어느 디렉터리가 큰지 — Top 10
du -sh /var/* 2>/dev/null | sort -h | tail -10
# /var/log    가 크면 → 로그 로테이션 안 됨
# /var/lib/docker 크면 → 안 쓰는 이미지·컨테이너 청소
docker system prune -a             # Docker 정리

# inode 부족도 체크 (작은 파일 많을 때)
df -i

# 큰 파일 1GB 이상 찾기
sudo find / -type f -size +1G -exec ls -lh {} \; 2>/dev/null

# ============================================================
# 시나리오 2: "포트 충돌 — 'Address already in use'"
# ============================================================
# 3000 포트 쓰는 프로세스 찾기
lsof -i :3000
# COMMAND   PID  USER  FD  TYPE  NODE NAME
# node    28391  app   25  IPv4  TCP *:3000 (LISTEN)

# 또는 ss (더 빠름)
ss -tlnp | grep :3000

# 죽이고 다시 시작
lsof -ti :3000 | xargs kill -9     # -t = PID 만 출력

# TIME_WAIT 상태 너무 많을 때 (재사용 안 됨)
ss -tan state time-wait | wc -l
# 1000+ 이면 → 커널 파라미터 조정 고려:
#   net.ipv4.tcp_tw_reuse = 1

# ============================================================
# 시나리오 3: "Graceful shutdown 구현"
# ============================================================
# 시그널 핸들러 등록 (Python 예시)
cat > graceful.py <<'EOF'
import signal, time, sys

def shutdown(sig, frame):
    print(f"SIGTERM 받음. 정리 중...")
    # 새 요청 거부·진행 중 요청 완료·DB 연결 close
    time.sleep(2)
    print("정상 종료")
    sys.exit(0)

signal.signal(signal.SIGTERM, shutdown)
signal.signal(signal.SIGINT, shutdown)

print("서버 시작")
while True: time.sleep(1)
EOF

python graceful.py &
PID=$!
sleep 1
kill -TERM $PID                    # 2초 후 정상 종료 메시지

# Docker·K8s 도 같은 패턴:
#   1. SIGTERM 보냄
#   2. terminationGracePeriodSeconds (기본 30초) 대기
#   3. 안 끝나면 SIGKILL

# ============================================================
# 시나리오 4: "systemd 로 서비스 운영"
# ============================================================
# 서비스 유닛 파일: /etc/systemd/system/myapp.service
# [Unit]
# Description=My App
# After=network.target
#
# [Service]
# Type=simple
# User=app
# WorkingDirectory=/opt/myapp
# ExecStart=/usr/bin/node server.js
# Restart=always
# RestartSec=5
# OOMScoreAdjust=-500              # 우선 보호
# LimitNOFILE=65536                # fd 제한
#
# [Install]
# WantedBy=multi-user.target

# 사용
sudo systemctl daemon-reload       # 유닛 수정 후 필수
sudo systemctl enable myapp        # 부팅 시 자동 시작
sudo systemctl start myapp
sudo systemctl status myapp         # 상태 + 최근 로그
sudo systemctl restart myapp
journalctl -u myapp -f             # 로그 실시간 (tail -f)
journalctl -u myapp --since '1 hour ago'

# ============================================================
# 시나리오 5: "컨테이너 자원 제한 (cgroup)"
# ============================================================
# Docker 가 cgroup 으로 처리
docker run --memory=512m --cpus=0.5 --pids-limit=100 myapp
# 내부: /sys/fs/cgroup/memory.max·cpu.max·pids.max 에 기록

# 직접 확인
cat /sys/fs/cgroup/memory.max
cat /sys/fs/cgroup/system.slice/docker-<ID>.scope/memory.current

# 컨테이너 자원 실시간
docker stats                       # 모든 컨테이너
docker stats --no-stream myapp     # 한 번만

# ============================================================
# 시나리오 6: "eBPF 로 시스템 콜 통계 (커널 모드 관측)"
# ============================================================
# bcc·bpftrace — 코드 수정 없이 커널 이벤트 추적
# 어떤 프로세스가 open() 자주 부르나
sudo bpftrace -e 'tracepoint:syscalls:sys_enter_openat { @[comm] = count(); }'

# 100ms 이상 디스크 I/O 만 표시
sudo biolatency-bpfcc

# Python 함수 호출 추적
sudo py-spy record --pid 28391 -o profile.svg

# 보안 — Falco 로 의심 동작 감지 (root 권한 획득·중요 파일 접근)

🤖 AI 에게 이렇게 요청해보세요

이 lesson 의 개념을 알면 AI 에게 구체적으로 지시할 수 있습니다. 막연한 "고쳐줘" 가 아니라 어휘를 가진 요청 — 그게 토큰 절약의 출발점입니다.

  • "이 작업이 컨텍스트 스위칭 비용 큰지 진단해줘"
  • "이 작업을 epoll (Linux) 기반 비동기로 바꾸면 어떤 이점이 있는지 알려줘"

왜 이게 토큰을 줄이나

개념을 모를 땐 AI 답변을 받고도 "그게 뭐예요?" 를 다시 물어야 합니다. 그 "다시 물음" 이 토큰을 잡아먹습니다. 개념 한 번 익혀두면 대화가 한 번에 끝납니다.

먼저 읽으면 좋은 개념: 동시성 — 락·데드락·동기/비동기
다음 추천: 보안
파일시스템·스케줄링·고급 - 운영체제