운영체제 심화 학습은 단순 개념 암기를 넘어서, "이 개념이 실제로 어떻게 연결되는가"를 묻습니다. 자주 나오는 심화 질문들을 실제 관점에서 정리합니다.


프로세스와 스레드

Q: "스레드 수를 어떻게 결정하나요?"

PLAINTEXT
CPU 집약적 작업: 스레드 수 = CPU 코어 수
  → 코어보다 많으면 컨텍스트 스위칭 비용만 증가

I/O 집약적 작업: 스레드 수 = CPU 코어 수 × (1 + 대기시간/처리시간)
  → I/O 대기 시간이 길수록 더 많은 스레드가 유리

예시: 4코어, I/O 대기 90% → 4 × (1 + 9) = 40 스레드

Q: "프로세스가 좀비 상태가 되면 어떻게 하나요?"

BASH
# 좀비 프로세스: 종료되었지만 부모가 wait()을 안 함
# 메모리는 해제되었지만 프로세스 테이블 엔트리(PCB)가 남아있음

# 좀비 프로세스 확인
ps aux | grep Z

# 해결: 부모 프로세스에 SIGCHLD 전송 또는 부모 종료
kill -SIGCHLD $PARENT_PID

Q: "Java에서 Thread.sleep(0)은 무슨 의미인가요?"

현재 스레드의 남은 타임 슬라이스를 포기하고, 같은 우선순위의 다른 스레드에게 CPU를 양보합니다. 스케줄러에게 재스케줄링 기회를 줍니다.


메모리

Q: "Java 프로세스의 메모리가 -Xmx보다 많이 사용되는 이유는?"

PLAINTEXT
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: "메모리 릭을 어떻게 찾나요?"

BASH
# 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: "데드락을 어떻게 감지하고 해결하나요?"

PLAINTEXT
감지:
- 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 문제를 어떻게 해결하나요?"

PLAINTEXT
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: "서버 응답이 느릴 때 어떻게 진단하나요?"

BASH
# 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 소켓이 너무 많을 때?"

BASH
# 현재 상태 확인
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에서 확인해야 합니다.

BASH
# 컨테이너의 실제 메모리 제한
cat /sys/fs/cgroup/memory.max

# 실제 CPU 제한
cat /sys/fs/cgroup/cpu.max

Q: "컨테이너가 VM보다 보안이 약한 이유는?"

커널을 공유하기 때문입니다. 커널 취약점이 있으면 컨테이너 탈출(escape)이 가능합니다. VM은 하이퍼바이저 레벨에서 격리되므로 공격 표면이 더 작습니다. 이를 보완하기 위해 seccomp, AppArmor, gVisor 등을 사용합니다.


학습 전략

  1. 개념 → 구현 → 활용: "가상 메모리란..."으로 시작해서 "이래서 JVM의 -Xmx 설정이..."으로 연결
  2. 트레이드오프 언급: 모든 기술 선택에는 트레이드오프가 있음을 보여주기
  3. 숫자 감각: "컨텍스트 스위칭은 수 마이크로초", "TLB 미스는 수십 나노초" 등

정리

OS 핵심은 개념을 외우는 것이 아니라, 왜 그렇게 설계되었는지 설명하고 실제와 연결하는 것 입니다. 프로세스/스레드 → 동기화 → 메모리 → I/O → 리눅스 실제 순서로 준비하면 대부분의 질문을 커버할 수 있습니다.

댓글 로딩 중...