운영체제 심화 — 핵심 질문 모음
운영체제 심화 학습은 단순 개념 암기를 넘어서, "이 개념이 실제로 어떻게 연결되는가"를 묻습니다. 자주 나오는 심화 질문들을 실제 관점에서 정리합니다.
프로세스와 스레드
Q: "스레드 수를 어떻게 결정하나요?"
CPU 집약적 작업: 스레드 수 = CPU 코어 수
→ 코어보다 많으면 컨텍스트 스위칭 비용만 증가
I/O 집약적 작업: 스레드 수 = CPU 코어 수 × (1 + 대기시간/처리시간)
→ I/O 대기 시간이 길수록 더 많은 스레드가 유리
예시: 4코어, I/O 대기 90% → 4 × (1 + 9) = 40 스레드
Q: "프로세스가 좀비 상태가 되면 어떻게 하나요?"
# 좀비 프로세스: 종료되었지만 부모가 wait()을 안 함
# 메모리는 해제되었지만 프로세스 테이블 엔트리(PCB)가 남아있음
# 좀비 프로세스 확인
ps aux | grep Z
# 해결: 부모 프로세스에 SIGCHLD 전송 또는 부모 종료
kill -SIGCHLD $PARENT_PID
Q: "Java에서 Thread.sleep(0)은 무슨 의미인가요?"
현재 스레드의 남은 타임 슬라이스를 포기하고, 같은 우선순위의 다른 스레드에게 CPU를 양보합니다. 스케줄러에게 재스케줄링 기회를 줍니다.
메모리
Q: "Java 프로세스의 메모리가 -Xmx보다 많이 사용되는 이유는?"
JVM 메모리 = Heap(-Xmx) + Metaspace + Thread Stack + Direct Buffer
+ JIT 코드 캐시 + GC 오버헤드 + Native 메모리
→ -Xmx=2G로 설정해도 실제 RSS는 3~4GB가 될 수 있음
→ 컨테이너 메모리 제한 시 여유를 두어야 OOM 방지
Q: "malloc은 언제 실패하나요?"
리눅스의 오버커밋 정책(기본값 0)에서는 거의 실패하지 않습니다. 실제 물리 메모리가 부족하면 malloc은 성공하지만 나중에 OOM Killer가 프로세스를 종료합니다. overcommit_memory=2로 설정하면 물리 메모리 한도 내에서만 할당합니다.
Q: "메모리 릭을 어떻게 찾나요?"
# 1. 프로세스 메모리 추이 모니터링
watch -n 1 "ps -p $PID -o rss,vsz"
# 2. 리눅스 도구
valgrind --leak-check=full ./myapp # C/C++
jmap -histo $PID # Java 힙 덤프
pmap $PID # 메모리 맵 확인
동기화
Q: "데드락을 어떻게 감지하고 해결하나요?"
감지:
- Java: jstack으로 스레드 덤프 → 데드락 자동 감지
jstack $PID | grep -A 5 "deadlock"
- DB: SHOW ENGINE INNODB STATUS (MySQL)
예방:
1. 자원 획득 순서 통일 (가장 실용적)
2. 타임아웃 설정 (tryLock with timeout)
3. 데드락 감지 후 롤백 (DB 방식)
Q: "ConcurrentHashMap은 어떻게 동기화하나요?"
Java 8 이후: 버킷 단위 synchronized + CAS 연산. 전체 맵을 잠그지 않고 해당 버킷(배열 인덱스)만 잠그므로 높은 동시성을 제공합니다.
I/O
Q: "서버에서 C10K 문제를 어떻게 해결하나요?"
C10K = 동시 10,000개 연결 처리
스레드 당 연결 방식의 한계:
- 10,000 스레드 × 1MB 스택 = 10GB 메모리
- 컨텍스트 스위칭 오버헤드
해결 방법:
1. I/O 멀티플렉싱 (epoll, kqueue) — Nginx, Node.js
2. 이벤트 루프 + Non-blocking I/O
3. 코루틴/경량 스레드 (Go goroutine, Java Virtual Thread)
4. io_uring (최신)
Q: "Nginx가 Apache보다 동시 접속에 강한 이유는?"
| Apache (prefork) | Nginx | |
|---|---|---|
| 모델 | 프로세스/스레드 당 연결 | 이벤트 루프 (epoll) |
| 10K 연결 시 | 10K 프로세스/스레드 | 워커 몇 개로 처리 |
| 메모리 | 연결당 수 MB | 연결당 수 KB |
리눅스 실제
Q: "서버 응답이 느릴 때 어떻게 진단하나요?"
# 1단계: 시스템 자원 확인
top # CPU, 메모리, 로드 에버리지
vmstat 1 # CPU, 메모리, I/O, 스왑
iostat -x 1 # 디스크 I/O (await, %util)
# 2단계: 네트워크 확인
ss -s # 소켓 상태 요약
netstat -an | grep TIME_WAIT | wc -l
# 3단계: 프로세스 분석
strace -p $PID -c # 시스템 콜 통계
perf top # CPU 핫스팟
# 4단계: 로그 확인
journalctl -u myservice --since "5 minutes ago"
dmesg | tail # 커널 메시지 (OOM 등)
Q: "TIME_WAIT 소켓이 너무 많을 때?"
# 현재 상태 확인
ss -s | grep TIME-WAIT
# 튜닝 (서버에서)
echo 1 > /proc/sys/net/ipv4/tcp_tw_reuse # TIME_WAIT 소켓 재사용
echo 30 > /proc/sys/net/ipv4/tcp_fin_timeout # FIN_WAIT2 타임아웃 단축
컨테이너와 가상화
Q: "Docker 컨테이너에서 top으로 CPU/메모리를 보면 호스트 값이 나오는 이유는?"
/proc/cpuinfo, /proc/meminfo는 호스트의 값을 보여줍니다 (namespace로 격리되지 않음). 컨테이너의 실제 제한은 cgroup에서 확인해야 합니다.
# 컨테이너의 실제 메모리 제한
cat /sys/fs/cgroup/memory.max
# 실제 CPU 제한
cat /sys/fs/cgroup/cpu.max
Q: "컨테이너가 VM보다 보안이 약한 이유는?"
커널을 공유하기 때문입니다. 커널 취약점이 있으면 컨테이너 탈출(escape)이 가능합니다. VM은 하이퍼바이저 레벨에서 격리되므로 공격 표면이 더 작습니다. 이를 보완하기 위해 seccomp, AppArmor, gVisor 등을 사용합니다.
학습 전략
- 개념 → 구현 → 활용: "가상 메모리란..."으로 시작해서 "이래서 JVM의 -Xmx 설정이..."으로 연결
- 트레이드오프 언급: 모든 기술 선택에는 트레이드오프가 있음을 보여주기
- 숫자 감각: "컨텍스트 스위칭은 수 마이크로초", "TLB 미스는 수십 나노초" 등
정리
OS 핵심은 개념을 외우는 것이 아니라, 왜 그렇게 설계되었는지 설명하고 실제와 연결하는 것 입니다. 프로세스/스레드 → 동기화 → 메모리 → I/O → 리눅스 실제 순서로 준비하면 대부분의 질문을 커버할 수 있습니다.