| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | |||
| 5 | 6 | 7 | 8 | 9 | 10 | 11 |
| 12 | 13 | 14 | 15 | 16 | 17 | 18 |
| 19 | 20 | 21 | 22 | 23 | 24 | 25 |
| 26 | 27 | 28 | 29 | 30 | 31 |
- 공통규약
- springsecurity
- 이벤트핸들러등록
- 로그백
- JPA
- AOP
- DDD
- MVC
- 전전긍긍
- 냄새라도
- 클린아키텍처
- Transaction
- 단위테스트
- OOP
- index
- 공통기술
- 문제선택근거
- 사면초가
- 커뮤니티서버
- 설정세팅
- JDBC
- SpringLegacy
- 디스크i/o
- TDD
- Ajax
- MVC요청플로우
- string
- SEQUENCE
- model
- 이벤트핸들러this
- Today
- Total
개발이군고구마
OS와 CPU의 작동 원리, 그리고 CPU 100% 문제 해결 본문
1. 핵심 개념 정리 — 먼저 용어부터
- 프로그램(Program): 디스크에 저장된 실행 파일 (ex. .jar, .exe)
- 프로세스(Process): 프로그램이 메모리에 올라가 실행 중인 상태. OS가 자원을 할당하는 단위.
- 스레드(Thread): 프로세스 안에서 실제로 코드를 실행하는 흐름. CPU Core가 처리하는 최소 단위.

💡 흔한 오해: "프로세스 = CPU"가 아님. CPU는 하드웨어이고, 프로세스는 CPU 위에서 돌아가는 소프트웨어 개념.
① 컴퓨터의 물리적 구조 — 하드디스크 vs 메모리 vs CPU

- 하드디스크는 "창고", 메모리(RAM)는 "책상", CPU는 "손"임.
- 손(CPU)은 항상 책상(RAM) 위에 있는 것만 처리 가능. 창고(HDD)에서 꺼내서 책상에 올려야 비로소 작업 가능
② OS는 어디에 "위치"하는가 — 부팅 전후 비교
ㄴ OS는 두 곳에 동시에 존재함. 부팅 전/후 상태가 다름.

2. OS는 무엇을 하는가
OS(운영체제)는 CPU라는 하드웨어 위에서 돌아가는 특별한 프로그램. 역할은 크게 두 가지.
- 자원 관리자: CPU, 메모리, 디스크 등 하드웨어 자원을 어떤 프로세스에게 얼마나 줄지 결정
- 중재자: 여러 프로그램이 CPU를 공정하게 나눠 쓸 수 있도록 스케줄링

스케줄링의 핵심 원리: OS는 스레드를 아주 짧은 시간 단위(time slice)로 번갈아 실행함. 사람 눈에는 동시에 실행되는 것처럼 보이지만, 실제로는 빠르게 교체(Context Switch)되는 것.
3. 스레드와 CPU의 관계 — 코드 한 줄이 실행되는 과정

OS 스케줄러는 스레드가 대기 상태일 때 다른 스레드에게 CPU를 양보하도록 조율함. 이것이 멀티태스킹의 실체.
4. 실전 — API 서버 CPU 100% 문제

✳ "Worker Thread 10개"와 "Thread Pool Queue"가 어떻게 다른지부터 짚고 넘어가야 이슈가 이해됨.
- Kafka 토픽 = 주문이 들어오는 주방 벨
- Consumer (1개) = 홀 직원 1명 (주문 접수 담당)
- Thread Pool Queue = 주방 안 대기 주문 슬롯 (메모판)
- Worker Thread 10개 = 요리사 10명
▶ 문제 상황 — Queue 가득 찼을 때 else 없는 루프
while (true) {
List<Event> events = consumer.poll(Duration.ofMillis(100));
for (Event event : events) {
if (workerPool.getQueue().isEmpty()) { // Queue에 여유 있으면
workerPool.submit(() -> process(event)); // Task 제출
}
// else 없음! Queue 가득 차도 그냥 통과
}
// 다시 while 처음으로 → 무한 반복
}
else가 없으면 Queue가 꽉 찼을 때 아무것도 안 하고 루프만 계속 돔. CPU 입장에서는 "일은 없지만 계속 달려야 하는" 상태.

▶ 해결 원리
해결 — else에 Thread.sleep 추가
while (true) {
List<Event> events = consumer.poll(Duration.ofMillis(100));
for (Event event : events) {
if (workerPool.getQueue().isEmpty()) {
workerPool.submit(() -> process(event));
} else {
// Queue 꽉 찼을 때 → CPU 양보하고 잠시 대기
Thread.sleep(50);
}
}
}

Thread.sleep(50)을 호출하는 순간 일어나는 일:
- 해당 스레드가 OS에게 "나 50ms 동안 필요 없어"라고 신호
- OS 스케줄러가 스레드를 RUNNABLE → TIMED_WAITING 상태로 변경
- CPU를 Worker Thread들에게 할당 → Worker들이 밀린 Task를 소화
- 50ms 후 OS가 Consumer 스레드를 깨워 다시 Queue 상태 확인
5. 더 나은 패턴 — Worker Pool + Queue
Thread.sleep은 임시방편. 실무에서는 Worker Pool + BlockingQueue 패턴을 사용함.

// 올바른 패턴: BlockingQueue 사용
BlockingQueue<Task> queue = new LinkedBlockingQueue<>();
// Worker는 take()로 블로킹 대기 — CPU 낭비 없음
while (true) {
Task task = queue.take(); // 큐가 비면 자동으로 Blocked 상태
process(task);
}
BlockingQueue.take()는 OS 레벨에서 스레드를 Blocked 상태로 만들어 줌. 큐에 작업이 생기면 OS가 알아서 스레드를 깨움. Thread.sleep처럼 개발자가 직접 타이머를 관리할 필요 없음.
'SERVER > CS' 카테고리의 다른 글
| 로그 (0) | 2026.06.07 |
|---|---|
| PuTTY로 SSH 접속하면 서버에서 무슨 일이 일어날까 (0) | 2026.03.27 |
| [요청응답 A-Z] 4. 서버 Runtime 환경 (5) | 2025.07.25 |
| [요청응답 A-Z] 3. 서버 Boot (2) | 2025.07.25 |
| [요청응답 A-Z] 2. 네트워크에서 서버의 네트워크로 (0) | 2025.07.24 |