Microsoft MVP성태의 닷넷 이야기
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일
 

(시리즈 글이 5개 있습니다.)
.NET Framework: 442. C# - 시스템의 CPU 사용량 및 프로세스(EXE)의 CPU 사용량 알아내는 방법
; https://www.sysnet.pe.kr/2/0/1684

Linux: 56. 리눅스 - /proc/pid/stat 정보를 이용해 프로세스의 CPU 사용량 구하는 방법
; https://www.sysnet.pe.kr/2/0/13215

Windows: 260. CPU 사용률을 나타내는 2가지 수치 - 사용량(Usage)과 활용률(Utilization)
; https://www.sysnet.pe.kr/2/0/13582

Windows: 261. CPU Utilization이 100% 넘는 경우를 성능 카운터로 확인하는 방법
; https://www.sysnet.pe.kr/2/0/13583

닷넷: 2233. C# - 프로세스 CPU 사용량을 나타내는 성능 카운터와 Win32 API
; https://www.sysnet.pe.kr/2/0/13589




리눅스 - /proc/pid/stat 정보를 이용해 프로세스의 CPU 사용량 구하는 방법

아래의 글에 보면,

Calculate the Total CPU Usage of a Process From /proc/pid/stat
; https://www.baeldung.com/linux/total-process-cpu-usage

다음과 같은 스크립트를 소개하고 있습니다.

#!/bin/bash
PID=$1
if [ -z "$PID" ]; then
    echo Usage: $0 PID
    exit 1
fi

PROCESS_STAT=($(sed -E 's/\([^)]+\)/X/' "/proc/$PID/stat"))
PROCESS_UTIME=${PROCESS_STAT[13]}
PROCESS_STIME=${PROCESS_STAT[14]}
PROCESS_STARTTIME=${PROCESS_STAT[21]}
SYSTEM_UPTIME_SEC=$(tr . ' ' </proc/uptime | awk '{print $1}')

CLK_TCK=$(getconf CLK_TCK)

let PROCESS_UTIME_SEC="$PROCESS_UTIME / $CLK_TCK"
let PROCESS_STIME_SEC="$PROCESS_STIME / $CLK_TCK"
let PROCESS_STARTTIME_SEC="$PROCESS_STARTTIME / $CLK_TCK"

let PROCESS_ELAPSED_SEC="$SYSTEM_UPTIME_SEC - $PROCESS_STARTTIME_SEC"
let PROCESS_USAGE_SEC="$PROCESS_UTIME_SEC + $PROCESS_STIME_SEC"
let PROCESS_USAGE="$PROCESS_USAGE_SEC * 100 / $PROCESS_ELAPSED_SEC"

echon TCK == ${CLK_TCK}, The PID $PID has spent ${PROCESS_UTIME_SEC}s in user mode, ${PROCESS_STIME_SEC}s in kernel mode. Total CPU usage is ${PROCESS_USAGE_SEC}s
echo The process has been running for ${PROCESS_ELAPSED_SEC}s. So, the process has used ${PROCESS_USAGE}% of CPU

그런데, 이게 좀 말이 안 됩니다. 프로세스가 구동된 시간(PROCESS_ELAPSED_SEC) 대비 CPU를 소비한 시간(PROCESS_USAGE_SEC)을 계산하고 있는데요, 그럼, 프로세스를 실행한 지 오래될수록 CPU 소비 시간이 줄어드는 계산밖에는 안 나옵니다. 그런데 달리 생각해 보면 말이 되기도 합니다. 프로세스가 실행된 이후로 시스템의 프로세스를 얼마나 소비했느냐를 알 수 있다는 것인데... 대개의 경우 별 쓸모없는 데이터에 불과합니다.

위와 같은 계산에 따르면, CPU 100%를 자주 치는 프로세스가 아닌 다음에야, 대부분의 경우 시간이 지날수록 출력 결과는 1%에 가까워지게 됩니다. 게다가 오래된 프로세스에 CPU 100%를 치는 코드를 돌려도 이미 지난 시간의 값이 크기 때문에 1%에서 2%로 가는 것조차 시간이 걸립니다. 실제로 위의 코드를 while 루프로 바꾸게 되면,

while [ -z "" ];
do
    sleep 1

    ...[생략]...
    echo The PID $PID has spent ${PROCESS_UTIME_SEC}s in user mode, ${PROCESS_STIME_SEC}s in kernel mode. Total CPU usage is ${PROCESS_USAGE_SEC}s
    echo The process has been running for ${PROCESS_ELAPSED_SEC}s. So, the process has used ${PROCESS_USAGE}% of CPU
done

화면에는 이런 식의 출력만 보게 됩니다.

The process has been running for 942s. So, the process has used 1% of CPU
TCK == 100, The PID 21702 has spent 8s in user mode, 9s in kernel mode. Total CPU usage is 17s
The process has been running for 943s. So, the process has used 1% of CPU
TCK == 100, The PID 21702 has spent 8s in user mode, 9s in kernel mode. Total CPU usage is 17s
The process has been running for 944s. So, the process has used 1% of CPU
TCK == 100, The PID 21702 has spent 8s in user mode, 9s in kernel mode. Total CPU usage is 17s
The process has been running for 945s. So, the process has used 1% of CPU
TCK == 100, The PID 21702 has spent 8s in user mode, 9s in kernel mode. Total CPU usage is 17s
The process has been running for 946s. So, the process has used 1% of CPU




그렇다면, 위의 코드를 우리가 잘 알고 있는 "작업 관리자"처럼, 혹은 "top"처럼 보고 싶다면 어떻게 해야 할까요? ^^

방법은 예전에 설명한 윈도우의 CPU 사용량과 유사합니다.

C# - 시스템의 CPU 사용량 및 프로세스(EXE)의 CPU 사용량 알아내는 방법
; https://www.sysnet.pe.kr/2/0/1684

즉, 시간 차에 따른 증가량을 CPU 사용량으로 보면 되는 건데요, 이를 위해 위의 코드에서 /proc/[pid]/stat 파일로부터 구한 user(PROCESS_UTIME), kernel(PROCESS_STIME) 시간을 1초마다 바뀐 차이를 계산하면 됩니다.

#!/bin/bash
# proc_cpu.sh

let OLD_PROCESS_TIME=0

while [ -z "" ];
do
    sleep 1
    
    PROCESS_STAT=($(sed -E 's/\([^)]+\)/X/' "/proc/$PID/stat"))
    PROCESS_UTIME=${PROCESS_STAT[13]}
    PROCESS_STIME=${PROCESS_STAT[14]}
    
    let PROCESS_TIME="$PROCESS_UTIME + $PROCESS_STIME"
    if [ $OLD_PROCESS_TIME -eq 0 ]; then
        OLD_PROCESS_TIME=$PROCESS_TIME
        continue
    fi
   
    let ELAPSED="$PROCESS_TIME - $OLD_PROCESS_TIME"
    OLD_PROCESS_TIME=$PROCESS_TIME
    
    echo $ELAPSED
done
/* 출력 결과
0
4
0
...[생략]...
*/

저 값은 CLK_TCK가 반영된 것이니 이 값을 정식으로는 다음과 같이 계산한 다음,

x = $ELAPSED / $CLK_TCK 

퍼센트로 바꾸기 위해 이렇게 계산하면,

usage = x * 100 / 1(초)

바로 저 값이 프로세스의 CPU 사용률이 됩니다. 그런데, 대개의 경우 $CLK_TCK가 100이기 때문에 $ELAPSED 자체가 백분율로 된 값으로 나옵니다.




그런데, 실제로 저렇게 구한 값을 top과 비교해 보면 좀 맞지 않습니다.

[1초마다 $ELAPSED]
0, 0, 5, 0, 0, 3, 0, 0, 4

[top에서 보이는 값]
대체로 1.0 ~ 1.3 정도의 값

도대체 무슨 차이일까요? 위의 테스트에 사용한 응용 프로그램은 CPU 사용을 약 1초마다 한 번씩 하고 있습니다. 실제로 /proc/pid/stat 파일의 값 변화를 체크해 봐도 그렇게 나옵니다.

그리고 왠지 top의 화면 변화는 약 3초로 보이는데요, 그래서 -d 옵션을 줘서 top을 1초 갱신으로 다시 실행해 보면,

linux_cpu_usage_1.png

거의 동일한 값을 보여주고 있습니다. 그러니까 결국 top은 /proc/pid/stat 값의 변화를 설정된 refresh 주기로 나눠서 "%CPU"에 보여주는 것이었습니다. 그렇기 때문에 기본 refresh 주기인 3초마다 나온 5, 3, 4의 값을 3으로 나눈 1.0 ~ 1.3 정도의 값이 나온 것입니다.

정리해 보면, 여러분들이 /proc/pid/stat을 이용해 CPU 사용량을 구하는 경우 차등 값을 사용해야 CPU 사용량을 구할 수 있습니다. 대개의 경우, 차등 값은 1초마다 구하게 될 텐데요, 그런 경우 (기본) 3초마다 평균을 보여주는 리눅스의 top 명령어와는 결과가 다를 수 있다는 점만 알아두시면 되겠습니다.




참고로, /proc/pid/stat 파일의 갱신 주기는 어떻게 될까요? 아래의 Q&A 글에 보면,

/proc/[pid]/stat refresh period
; https://stackoverflow.com/questions/31219317/proc-pid-stat-refresh-period

문서를 인용하며, OS/kernel 데이터가 바뀌는 순간에 바로 반영된다고 하니 아마도 CPU 사용량의 경우라면 이상적인 경우 시스템 타이머의 주기에 맞춰 바뀔 듯합니다. 예를 들어, CPU 100%를 소비하는 예제를 실행한 다음 위의 proc_cpu.sh을 사용하면 구하는 시간마다 값이 바뀌는 것을 볼 수 있습니다. 반면, CPU를 거의 사용하지 않으면 해당 파일도 바뀌지 않은 채로 그 시간만큼 유지됩니다.





[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]







[최초 등록일: ]
[최종 수정일: 10/13/2023]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer at outlook.com

비밀번호

댓글 작성자
 



2023-03-20 10시30분
파이썬 시스템 정보 확인 방법 (CPU, MEMORY, DISK)
; https://blog.naver.com/cjinnnn/223047217336
정성태

... 106  107  108  109  110  111  112  113  114  115  116  117  118  [119]  120  ...
NoWriterDateCnt.TitleFile(s)
10949정성태4/28/201619844.NET Framework: 575. SharedDomain과 JIT 컴파일파일 다운로드1
10948정성태4/28/201623779.NET Framework: 574. .NET - 눈으로 확인하는 SharedDomain의 동작 방식 [3]파일 다운로드1
10947정성태4/27/201621642.NET Framework: 573. .NET CLR4 보안 모델 - 4. CLR4 보안 모델에서의 조건부 APTCA 역할파일 다운로드1
10946정성태4/26/201624495VS.NET IDE: 106. Visual Studio 2015 확장 - INI 파일을 위한 사용자 정의 포맷 기능 (Syntax Highlighting)파일 다운로드1
10945정성태4/26/201618211오류 유형: 327. VSIX 프로젝트 빌드 시 The "VsTemplatePaths" task could not be loaded from the assembly 오류 발생
10944정성태4/22/201619468디버깅 기술: 80. windbg - 풀 덤프 파일로부터 텍스트 파일의 내용을 찾는 방법
10943정성태4/22/201624323디버깅 기술: 79. windbg - 풀 덤프 파일로부터 .NET DLL을 추출/저장하는 방법 [1]
10942정성태4/19/201619635디버깅 기술: 78. windbg 사례 - .NET 예외가 발생한 시점의 오류 분석 [1]
10941정성태4/19/201619536오류 유형: 326. Error MSB8020 - The build tools for v120_xp (Platform Toolset = 'v120_xp') cannot be found.
10940정성태4/18/201622790Windows: 116. 프로세스 풀 덤프 시간을 줄여 주는 Process Reflection [3]
10939정성태4/18/201623831.NET Framework: 572. .NET APM 비동기 호출의 Begin...과 End... 조합 [3]파일 다운로드1
10938정성태4/13/201623403오류 유형: 325. 파일 삭제 시 오류 - Error 0x80070091: The directory is not empty.
10937정성태4/13/201631624Windows: 115. UEFI 모드로 윈도우 10 설치 가능한 USB 디스크 만드는 방법
10936정성태4/8/201642295Windows: 114. 삼성 센스 크로노스 7 노트북의 운영체제를 USB 디스크로 새로 설치하는 방법 [3]
10935정성태4/7/201626609웹: 32. Edge에서 Google Docs 문서 편집 시 한영 전환키가 동작 안하는 문제
10934정성태4/5/201625351디버깅 기술: 77. windbg의 콜스택 함수 인자를 쉽게 확인하는 방법 [1]
10933정성태4/5/201630956.NET Framework: 571. C# - 스레드 선호도(Thread Affinity) 지정하는 방법 [8]파일 다운로드1
10932정성태4/4/201623253VC++: 96. C/C++ 식 평가 - printf("%d %d %d\n", a, a++, a);
10931정성태3/31/201623518개발 환경 구성: 283. Hyper-V 내에 구성한 Active Directory 환경의 시간 구성 방법 [3]
10930정성태3/30/201621460.NET Framework: 570. .NET 4.5부터 추가된 CLR Profiler의 실행 시 Rejit 기능
10929정성태3/29/201631583.NET Framework: 569. ServicePointManager.DefaultConnectionLimit의 역할파일 다운로드1
10928정성태3/28/201637291.NET Framework: 568. ODP.NET의 완전한 닷넷 버전 Oracle ODP.NET, Managed Driver [2]파일 다운로드1
10927정성태3/25/201626521.NET Framework: 567. System.Net.ServicePointManager의 DefaultConnectionLimit 속성 설명
10926정성태3/24/201626045.NET Framework: 566. openssl의 PKCS#1 PEM 개인키 파일을 .NET RSACryptoServiceProvider에서 사용하는 방법 [10]파일 다운로드1
10925정성태3/24/201620367.NET Framework: 565. C# - Rabin-Miller 소수 생성 방법을 이용하여 RSACryptoServiceProvider의 개인키를 직접 채워보자 - 두 번째 이야기파일 다운로드1
10924정성태3/22/201620984오류 유형: 324. Visual Studio에서 Azure 클라우드 서비스 생성 시 Failed to initialize the PowerShell host 에러 발생
... 106  107  108  109  110  111  112  113  114  115  116  117  118  [119]  120  ...