리눅스 PSI 읽는 법: /proc/pressure의 some, full, avg, total
/proc/pressure의 some과 full, avg10·avg60·avg300, total이 각각 무엇을 재는지, iowait와 무엇이 다른지, 임계값 트리거를 걸 때의 제약을 커널 문서 기준으로 정리합니다.
CPU 사용률이나 메모리 사용량은 자원이 얼마나 쓰이고 있는지를 보여 줍니다. 그런데 작업이 그 자원을 기다리느라 실제로 얼마나 멈춰 있었는지는 이런 숫자만으로 알기 어렵습니다. 리눅스의 PSI(Pressure Stall Information)는 이 “멈춘 시간”을 CPU, 메모리, I/O별로 내보내는 인터페이스입니다. 이 글은 커널 PSI 문서를 기준으로 각 숫자의 뜻과 읽을 때의 한계를 정리합니다.
파일 형식
시스템 전체의 압력 정보는 /proc/pressure/ 아래 cpu, memory, io 세 파일에 있습니다. 형식은 다음과 같습니다.
some avg10=0.00 avg60=0.00 avg300=0.00 total=0
full avg10=0.00 avg60=0.00 avg300=0.00 total=0
| 항목 | 의미 |
|---|---|
some 줄 |
적어도 일부 태스크가 해당 자원 때문에 멈춰 있던 시간의 비율 |
full 줄 |
idle이 아닌 태스크 전부가 동시에 해당 자원 때문에 멈춰 있던 시간의 비율 |
avg10, avg60, avg300 |
각각 10초, 60초, 300초 구간의 최근 추세. 단위는 % |
total |
누적 절대 정지 시간. 단위는 마이크로초(µs) |
full은 some의 부분집합입니다. 커널 문서는 full 상태를 실제 CPU 사이클이 낭비되는 상태로 설명하고, 이 상태에 오래 머무는 워크로드를 스래싱(thrashing)으로 본다고 적고 있습니다. 반대로 some만 올라가 있다면 일부 태스크는 기다리지만 CPU는 여전히 다른 유용한 일을 하고 있을 수 있습니다. 두 줄을 따로 집계하는 이유가 이 구분입니다.
/proc/pressure/cpu의 full은 0이 정상
문서에 따르면 CPU의 full은 시스템 수준에서는 정의되지 않습니다. 다만 5.13부터 이 줄이 출력되기 시작했고, 하위 호환을 위해 값은 0으로 고정됩니다. 그래서 /proc/pressure/cpu의 full이 0이라는 사실로 CPU에 여유가 있다고 판단할 수는 없습니다. CPU 쪽은 some 줄을 봐야 합니다. 이 0 고정 규칙은 문서에서 시스템 수준에 대해 적힌 것입니다.
total로 원하는 구간의 비율 구하기
avg 값은 10초, 60초, 300초 창으로 고정되어 있습니다. 문서는 total을 따로 내보내는 이유로 두 가지를 듭니다. 시간 평균에는 거의 드러나지 않는 짧은 지연 스파이크를 잡기 위해, 그리고 원하는 기간으로 직접 평균을 내기 위해서입니다.
total은 마이크로초 단위 누적값이므로 두 번 읽은 차이를 그 사이 경과 시간으로 나누면 해당 구간의 정지 비율이 됩니다.
cat /proc/pressure/io
sleep 5
cat /proc/pressure/io
예를 들어(설명용 가상 값) some 줄의 total이 1,200,000에서 1,950,000으로 늘었고 그 사이가 5초였다면 다음과 같습니다.
(1,950,000 − 1,200,000) µs ÷ 5,000,000 µs = 0.15 → 15%
이 5초 동안 적어도 일부 태스크가 I/O 때문에 멈춰 있던 시간이 약 15%였다는 뜻입니다.
iowait, procs_blocked, 디스크 통계와 무엇이 다른가
/proc/stat의 CPU 줄에도 iowait 열이 있습니다. 그런데 커널 /proc 문서는 이 값에 대해 스스로 몇 가지 문제를 적어 둡니다.
- CPU가 I/O 완료를 기다리는 것이 아니라 태스크가 기다립니다. 태스크가 I/O를 기다리며 CPU가 idle로 가면 그 CPU에는 다른 태스크가 스케줄될 수 있습니다.
- 멀티코어에서 I/O를 기다리는 태스크는 어느 CPU에서도 실행 중이 아니므로 CPU별 iowait를 계산하기 어렵습니다.
/proc/stat의 iowait 값은 특정 조건에서 감소할 수 있습니다.
문서의 결론은 “/proc/stat에서 읽은 iowait는 신뢰할 수 없다”입니다. 같은 파일의 procs_blocked는 현재 I/O 완료를 기다리며 막혀 있는 프로세스 수로, 읽는 순간의 값입니다. 시간이 얼마나 쌓였는지는 알려 주지 않습니다.
디스크별 통계의 필드 10(I/O에 쓴 밀리초, /proc/diskstats에서는 주·부 번호와 장치 이름 뒤의 10번째 값)은 장치 쪽 관점입니다. iostats 문서에 따르면 이 값은 진행 중인 I/O가 있는 동안 증가하고, 5.0부터는 요청이 시작되거나 완료된 jiffy를 세며, 요청이 2 jiffy보다 오래 걸리면 동시 요청이 있을 때 일부 시간이 누락될 수 있습니다. 장치가 바빴는지와 작업이 I/O 때문에 멈췄는지는 서로 다른 질문이고, PSI의 io 파일은 후자를 다룹니다.
load average는 별도 글에서 다뤘습니다. 메모리 사용량 숫자를 읽는 방법은 free의 available 글을 참고하세요. 사용량이 높다는 사실과 메모리 부족으로 작업이 멈췄다는 사실은 별개이고, 후자를 보는 파일이 /proc/pressure/memory입니다.
임계값 트리거: poll()로 알림 받기
주기적으로 파일을 읽는 대신, 정지 시간이 기준을 넘을 때 깨어나도록 트리거를 등록할 수 있습니다. 해당 /proc/pressure/ 파일을 열고 다음 형식의 문자열을 쓴 뒤, 그 파일 디스크립터를 select(), poll(), epoll()로 기다립니다.
<some|full> <정지 시간 µs> <시간 창 µs>
문서의 예시는 이렇습니다.
/proc/pressure/memory에some 150000 1000000: 1초 창 안에서 부분 메모리 정지가 150ms를 넘으면 알림/proc/pressure/io에full 50000 1000000: 1초 창 안에서 전체 I/O 정지가 50ms를 넘으면 알림
문서에 적힌 제약은 다음과 같습니다.
- 시간 창은 500ms부터 10s까지 허용됩니다. 정지 증가량은 창마다 10번 확인하므로 갱신 간격은 최소 50ms, 최대 1s입니다.
- 권한 없는 사용자도 트리거를 만들 수 있지만 시간 창이 2초의 배수여야 합니다.
- 트리거 하나에 파일 디스크립터 하나가 필요합니다. 같은 파일이라도 트리거마다 따로
open()해야 하고, 이미 트리거가 있는 디스크립터에 다시 쓰면EBUSY로 실패합니다. - 모니터는 해당 지표가 정지 상태에 들어갈 때 활성화되고, 한 번 활성화되면 최소 한 창 동안 유지됩니다. 알림은 창당 한 번으로 제한됩니다.
- 트리거는 등록에 쓴 파일 디스크립터를 닫으면 해제됩니다.
마지막 규칙 때문에 셸에서 echo "some 150000 1000000" > /proc/pressure/memory처럼 쓰면 리다이렉션이 끝나면서 디스크립터가 닫히므로, 감시할 트리거가 남지 않습니다. 디스크립터를 연 채로 기다리는 프로그램이 필요합니다. 문서의 C 예제를 줄이면 핵심은 다음과 같습니다.
const char trig[] = "some 150000 1000000";
struct pollfd fds;
fds.fd = open("/proc/pressure/memory", O_RDWR | O_NONBLOCK);
fds.events = POLLPRI;
write(fds.fd, trig, strlen(trig) + 1);
while (1) {
poll(&fds, 1, -1);
if (fds.revents & POLLERR) break; /* 이벤트 소스가 사라짐 */
if (fds.revents & POLLPRI) printf("event triggered!\n");
}
원문 예제에는 open(), write(), poll()의 오류 처리가 들어 있으니 실제로 쓸 때는 원문을 기준으로 하세요.
cgroup v2에서의 PSI
CONFIG_CGROUPS=y 커널에서 cgroup2 파일 시스템이 마운트되어 있으면 cgroup별로도 PSI가 집계됩니다. 각 cgroup 디렉터리에 cpu.pressure, memory.pressure, io.pressure가 있고 형식은 /proc/pressure/와 같습니다. 트리거도 시스템 전체와 같은 방식으로 등록할 수 있습니다.
cgroup v2 문서에는 두 가지가 더 있습니다.
irq.pressure: IRQ/SOFTIRQ에 대한 압력 정보를 보여 준다고 나열되어 있습니다. 다만 이번에 확인한 PSI 문서 판에는 IRQ 항목에 대한 별도 설명이 없습니다.cgroup.pressure: 기본값은1이고0을 쓰면 해당 cgroup의 PSI 집계가 꺼집니다. 이 설정은 계층적이지 않아서 자손 cgroup에 영향을 주지 않습니다. PSI가 cgroup마다 따로 집계하고 계층마다 합산하기 때문에, 계층이 깊으면 일부 워크로드에서 무시하기 어려운 오버헤드가 생길 수 있어 non-leaf cgroup에서 끌 수 있도록 둔 설정입니다.
읽을 때의 한계
- PSI는 자원별로 멈춘 시간을 합산한 값입니다. 어떤 프로세스가 원인인지는 이 파일만으로 알 수 없습니다. cgroup별 파일을 비교하면 어느 그룹에서 정지가 생겼는지까지는 좁힐 수 있습니다.
- 커널 문서는 몇 %부터 문제라는 기준값을 제시하지 않습니다. 이 글도 기준값을 제시하지 않습니다.
avg값은 최근 추세이므로 짧은 스파이크가 묻힐 수 있습니다. 문서가total을 따로 둔 이유입니다.- 이 글은 2026-10-11에 확인한 docs.kernel.org 문서(7.3.0-rc6 빌드)를 기준으로 합니다. 이 글이 인용한 문서에서 버전이 명시된 것은 “CPU
full은 5.13부터 출력”과 “diskstats 필드 10은 5.0부터 요청이 시작되거나 완료된 jiffy를 셈” 두 가지입니다. 오래된 커널에서는 출력 형식이나 지원 항목이 다를 수 있습니다.