Linux에서 CPU가 커널 코드를 실행한다고 해서 모두 같은 경로와 제약을 갖는 것은 아니다. 사용자 프로세스가 요청한 시스템 콜, 장치가 일으킨 하드웨어 인터럽트, 커널이 미룬 softirq, worker thread가 처리하는 workqueue는 진입 원인과 실행 문맥이 다르다.
이 차이를 알아야 함수 안에서 잠들 수 있는지, 어떤 지연이 사용자 요청에 포함되는지, 성능 도구의 숫자를 어떻게 해석할지 판단할 수 있다.
사용자 함수가 곧바로 시스템 콜이 되는 것은 아니다
애플리케이션 함수는 먼저 사용자 공간에서 실행된다. 그 함수가 커널의 서비스가 필요할 때 시스템 콜 경계를 통과한다.
애플리케이션 함수
→ 언어 런타임·C 라이브러리
→ 필요할 때 시스템 콜 진입
→ 커널 처리
→ 사용자 공간 복귀
예를 들어 printf는 출력 내용을 사용자 공간 버퍼에 모았다가 나중에 write 계열 시스템 콜을 사용할 수 있다. malloc도 관리 중인 메모리 영역에서 요청을 해결하면 그 호출마다 커널에 들어가지 않는다. 그래서 소스의 함수 호출 수와 시스템 콜 수를 같다고 보면 안 된다.
Linux man-pages는 시스템 콜을 애플리케이션과 커널 사이의 기본 인터페이스라고 설명하면서, 대부분은 glibc 같은 라이브러리 래퍼를 통해 호출한다고 구분한다.
시스템 콜은 프로세스 문맥에서 시작한다
시스템 콜은 실행 중인 스레드가 파일, 메모리, 네트워크 같은 커널 서비스를 요청하는 동기적 진입이다. 아키텍처별 명령과 진입 코드를 거쳐 사용자 모드에서 커널 모드로 전환하고, 커널은 syscall 번호와 인자, 권한을 확인해 처리한다.
시스템 콜은 일반적으로 요청한 작업의 프로세스 문맥을 가진다. 그래서 실행 경로의 조건이 허용하면 I/O 완료나 lock을 기다리며 sleep할 수 있다. 다만 커널 코드라는 이유만으로 언제나 sleep할 수 있는 것은 아니다. spinlock을 잡았거나 preemption·interrupt가 제한된 구간처럼 현재 문맥의 제약을 확인해야 한다.
하드 IRQ는 비동기 장치 이벤트에서 들어온다
네트워크 카드, 저장 장치, 타이머 같은 하드웨어 이벤트는 CPU가 어떤 코드를 실행 중이든 인터럽트를 일으킬 수 있다. Linux의 하드 IRQ 처리 경로는 아키텍처별 진입 코드를 거쳐 등록된 IRQ handler를 실행한다.
하드 IRQ 문맥은 사용자 프로세스의 시스템 콜과 다르다. 보통 짧게 처리해야 하고 sleep할 수 없다. 오래 걸리는 일을 전부 하드 IRQ handler에서 처리하면 다른 인터럽트와 작업의 지연을 키울 수 있어, 나머지 처리를 다른 문맥으로 미룬다.
타이머 인터럽트도 하드웨어 인터럽트 계열이다. 이를 “소프트 인터럽트가 스케줄러를 호출한다”라고 한 문장으로 묶으면 진입 원인과 지연 처리 메커니즘이 섞인다.
softirq는 시스템 콜을 뜻하는 소프트웨어 인터럽트가 아니다
Linux의 softirq는 일부 커널 작업을 지연 처리하기 위한 정해진 메커니즘이다. 하드 IRQ가 끝나는 경로 등에서 pending softirq를 처리할 수 있고, 부하가 이어지면 ksoftirqd 커널 스레드가 처리하기도 한다.
대표적으로 네트워크 송수신과 타이머 관련 처리가 softirq를 사용할 수 있다. 하지만 softirq도 일반적인 프로세스 문맥과 같지 않아 sleep하면 안 된다. 이름에 soft가 들어간다는 이유로 시스템 콜, 예외, 사용자가 발생시킨 모든 이벤트를 softirq라고 부르면 안 된다.
장치 이벤트
→ hard IRQ: 급한 확인과 최소 처리
→ softirq: 지연된 비수면 처리
→ 필요하면 더 긴 작업을 thread 문맥으로 넘김
실제 드라이버는 threaded IRQ, NAPI와 여러 동기화 기법을 조합할 수 있다. 위 그림은 진입 문맥을 구분하기 위한 단순화다.
workqueue는 worker thread 문맥에서 일을 처리한다
workqueue는 커널이 작업 함수를 나중에 worker thread에서 실행하도록 큐잉하는 비동기 실행 메커니즘이다. 일반적인 workqueue는 process context에서 실행하므로 필요할 때 sleep 가능한 작업을 맡길 수 있다.
다만 “workqueue는 항상 sleep 가능하다”라고 외우는 것도 위험하다. 작업 함수가 어떤 lock을 잡는지, 어떤 workqueue 속성과 실행 문맥을 쓰는지 확인해야 한다. Linux에는 softirq 문맥에서 실행되는 BH workqueue 같은 별도 형태도 있다.
중요한 것은 함수 이름보다 그 함수가 어떤 문맥에서 호출됐는가다. 같은 커널 함수도 프로세스 문맥에서 호출될 때와 IRQ 경로에서 호출될 때 허용되는 동작이 달라질 수 있다.
네 실행 문맥을 한 표로 비교한다
| 경로 | 주된 시작점 | 요청한 프로세스 문맥 | 일반적인 sleep 가능 여부 |
|---|---|---|---|
| 시스템 콜 | 실행 중인 스레드의 커널 서비스 요청 | 있음 | 경로와 lock 조건이 허용하면 가능 |
| hard IRQ | 장치·타이머의 비동기 인터럽트 | 없음 | 불가 |
| softirq | 커널의 지연 처리 pending 상태 | 없음 | 불가 |
| 일반 workqueue | 커널이 worker thread에 작업 큐잉 | worker thread 문맥 | 조건이 허용하면 가능 |
이 표는 설계 원칙을 잡는 출발점이다. PREEMPT_RT, threaded interrupt와 세부 커널 설정에 따라 실행 방식은 달라질 수 있으므로 특정 커널의 지연을 분석할 때는 빌드 설정과 실제 trace를 함께 봐야 한다.
CPU 시간과 CPU 이벤트도 분리해서 측정한다
벽시계 경과 시간, 프로세스 CPU 시간, 하드웨어 성능 카운터는 서로 다른 값이다. 고수준 언어의 elapsed-time 타이머로 잰 시간이 CPU cycle 수를 뜻하지는 않는다. 대기, 스케줄링, 다른 작업의 간섭도 포함될 수 있다.
Linux에서는 목적에 따라 관찰 지점을 나눈다.
# 프로세스가 호출한 시스템 콜의 분포
strace -c -- command
# IRQ와 softirq 누적 카운터
cat /proc/interrupts
cat /proc/softirqs
# 지원되는 환경에서 하드웨어·소프트웨어 이벤트 집계
perf stat -e cycles,instructions,context-switches,cpu-migrations -- command
perf 이벤트는 CPU 모델과 커널 설정, 권한에 따라 지원 여부와 의미가 달라진다. multiplexing이 발생하면 카운터가 추정될 수 있고, 가상 머신에서는 일부 하드웨어 이벤트를 제공하지 않을 수도 있다. 명령 하나의 숫자를 원인으로 단정하기보다 trace와 시스템 상태를 함께 본다.
운영체제의 자원 관리와 시스템 콜은 이 경계의 입문 설명이고, CPU·Linux task·goroutine 구분은 실행 단위를 더 자세히 다룬다.
참고 자료
'배움과 성장 > 시스템·성능' 카테고리의 다른 글
| 시간 단위 변환 기준: 초·밀리초·나노초부터 날짜 계산까지 (0) | 2024.12.08 |
|---|---|
| 프로세스 메모리 구조: 힙·스택·가상 메모리 도식의 한계 (0) | 2024.12.07 |
| Linux Kernel 동작 흐름: System Call·Task·Virtual Memory·VFS 연결하기 (0) | 2024.12.06 |
| Linux man 페이지 섹션과 syscall 추적: man 1·2·3·5·7·8 (5) | 2024.12.06 |
| JIT 컴파일과 가상 머신: bytecode·ISA·profiling·deoptimization 구분 (0) | 2024.12.05 |
댓글