본문 바로가기
공부, 취준/기타 프로그래밍

파이썬 파일 입력받아 그래프 그리기 (우분투 리소스 사용률 측정)

by 린레몬 2020. 10. 8.

안녕하세요 레몬입니다.

운영체제 강의 수강중에, 간단한 실습 과제를 해결한 과정을 정리해서 포스팅하려 한다.

원래 학기중에는 블로그에 거의 손을 대지 않는데, 파이썬을 이번에 처음 다루다보니 사용법을 정리하면서 익히는 편이 좋을 것 같아 기록으로 남길 겸 작성하게 되었다.

대신 대충 쓰고 넘길

거라 글의 퀄리티는 보장 못함...


우선 주어진 과제는 다음과 같다.

버추얼머신에서의 우분투 환경에서 sysstat 을 이용해 리소스를 모니터링하고, 그 결과를 파이썬을 이용해 그래프로 출력하기

첫 번째 과제이다보니 다 끝내고 보면 정말 별거아닌데, 우분투 환경과 파이썬을 처음 접해서 익숙하질 않아 시간을 많이 잡아먹었다.

레포트에 첨부할 사진들을 보며 과정을 설명해 보겠다.

1. 우분투에 sysstat 설치하여 리소스 모니터링해보기

sysstat 설치 자체는 매우 간단하다. 우분투에서 여러 유틸을 설치할 때와 마찬가지로

apt-get install sysstat

명령어를 이용해 바로 설치할 수 있다. 이후 sysstat에서 제공하는 sar 명령어를 통해 네트워크, 메모리, CPU, Disk 등의 여러 리소스 정보를 모니터링하고 그 결과를 txt파일로 저장할 수 있다.

§ sar –n DEV 1 > filename.txt //(network)
§ sar –r 1 > filename.txt //(Memory)
§ sar –d –p 1 > filename.txt //(disk.dat)
§ sar –u –P ALL 1 > filename.txt //(CPU)

위 명령어들을 통해 각각의 리소스를 모니터링할 수 있다. (해당 명령어에 대해서는 시키는대로 한거라 자세히 모름)

명령어를 입력하면 계속 모니터링 결과를 파일에 출력하고 있으니, 적당히 기다리다가 ctrl+C를 눌러 종료시켜주자.

출력된 파일은 위와 같은 모양이다.

적절히 행과 열을 구분하여 데이터가 저장되어있어, 파이썬에서 파일을 입력 받아 데이터를 뽑아낼 수 있게 생겼다.

 

2. 리소스 모니터링 중 wc 명령어를 실행하여 리소스 변화 기록

이제 리소스 모니터링 도중에 뭔가 명령어를 실행하여 어떻게 리소스 사용량이 변하는지 알아볼 차례다.

이번 과제에서는 텍스트 파일의 word 개수를 알아내는 wc 명령어를 사용했다. 이를 위해 큰 용량의 텍스트 파일을 아래 링크에서 다운받아 사용하였다.

https://www.kaggle.com/datasnaek/youtubenew?select=USvideos.csv

이런 모습의 csv 파일을 다운받았다.

해당 파일에 wc 명령어를 수행해보면, 이처럼 단어 수가 카운트되어 출력된다. 이제 리소스 모니터링 도중 위 명령어를 수행시켜 변화를 기록할 차례다.

그 과정에서 내가 사용한 명령어들이다. 아무래도 우분투 환경에 익숙하지 않아 딱 봐도 뭔가 엉성하게 한 것 같은데.. 과정은 다음과 같다.

우선 리소스 모니터링을 위해 sar 명령어를 실행시키되, 해당 프로세스를 백그라운드에서 실행하고 새로 명령어를 입력할 수 있도록 맨 끝에 &를 추가한다.

그러면 리소스 모니터링이 시작되고 해당 프로세스의 id가 출력되면서 새로운 명령어를 입력할 수 있게 된다.

그 후 wc 명령어를 실행하여 결과가 출력되는 것을 확인한 뒤, kill 명령어를 이용해 방금 출력된 id의 프로세스를 강제 종료시킨다.

kill -15 에서 -15는 해당 프로세스에 정상적인 신호를 보내 종료시키기 위해 넣은건데.. 그냥 kill해도 크게 다르진 않을 것 같다. 아무튼 이 과정을 네가지 리소스에 대해 반복하여 결과 파일을 얻어냈다.

 

3. 파이썬을 이용해 2번에서 얻은 결과 파일을 입력으로 받아 원하는 데이터를 그래프로 그리기

파이썬을 처음 사용하다보니 가장 시간을 많이 잡아먹은 파트다..

정말 별거아닌 내용인데 하나하나 찾아보며 필요한 기능을 사용하다보니 완성에 시간이 오래 걸렸다.

내가 실제로 이용한 코드를 한줄씩 살펴보며 설명해보겠다.

먼저 8~11행 까지는 필요한 모듈을 import한 부분이다.

matplotlib을 사용하기 위해서는 따로 파이썬 프롬프트 창에서 필요한 파일을 설치해줘야 했다.

python -m pip install -U pip
python -m pip install -U matplotlib

위 두 명령어를 아나콘다 프롬프트에 입력하여 설치한 뒤 사용했다.

matplotlib을 통해 리스트로 저장된 데이터를 MATLAB에서처럼 쉽고 편하게 그래프로 그릴 수 있다.

#열 개수 맞추기 위해 파일의 첫번째 줄 skip / str과 float 섞여있어 dtype=None
cpudata = np.genfromtxt('RUNcpu학번.txt', skip_header=1, dtype=None, encoding = 'UTF8')

#plot될 그래프에서 한글(이름) 출력하기 위한 폰트 설정
path = 'C:\\Users\\사용자\\Downloads\\NanumBarunGothic.ttf' 
fontprop = fm.FontProperties(fname=path, size=18)

#테스트 위한 전체 데이터 출력
#print(cpudata)

파일 입력을 받기 위해 np.genfromtxt 메소드를 활용했다.

파일 입출력은 C와 Java에서 여러번 해봤는데, 파이썬을 써보면서 파일 입력이 이렇게 간단했나 싶어 놀랐다..

주석에 간략히 설명을 붙여놨지만 내부 매개변수에 대해 자세히 얘기해보자면

'RUNcpu학번.txt' : 읽을 파일 이름이다. 파일 위치는 해당 파이썬 파일이 저장된 디렉토리에 저장해 두었다. 다른 곳에 파일이 있으면 파일 주소를 전부 입력해주면 된다.

skip_header=1 : 읽을 파일의 내용을 살펴보자.

내가 필요한 정보는 [초]와 [%idle] 열의 원소들이다.

genfromtxt 메소드는 파일을 입력받아 각 행마다 공백 ' '을 기준으로 str 요소를 리스트로 저장한다.

예를 들어 위 파일을 입력하면

[['Linux', '5.4.0-48-generic', '(sdy-VirtualBox)', '2020년', '10월' '06일' '_x86_64_', '(4' CPU)' ], #1행
['02시', '22분']['02시' '22분' '17초' 'CPU' '%user' '%nice' '%system' '%iowait' '%steal'
  '%idle'], #2행
...]

이런 식으로, [[1행] , [2행] , [3행] ...] 을 저장하는 리스트 안에 리스트가 생성되는 것이다.

하지만 이 때 2차원 리스트가 생성되므로 열의 개수는 모든 행에서 같아야한다. 파일을 살펴보면 첫 줄에 쓸데없는 정보가 적혀있고, 다음 줄부터 내가 필요한 데이터가 행렬 형태로 예쁘게 정리되어 있다. 즉 첫 줄을 버릴 필요가 있다.

이를 위해 skip_header=1를 적어 1개의 줄은 skip하고 넘어가라고 신호를 주는 것이다.

dtype = None : 내가 제일 감탄한 부분이다. genfromtxt는 기본적으로 파일 내 요소의 자료형이 모두 같아야 멀쩡히 동작한다.

하지만 위 파일의 경우 str과 float 형이 섞여있다. 이 때 dtype=None이라고 언급만 해주면, 알아서 리스트 내의 요소를 뭔가 하나의 자료형으로 통일하지 않고 str은 str로, float은 float으로, int는 int로 저장해준다.

이 변수를 입력하지 않았을 때 모든 자료형이 float으로 통일되어 OO시 OO분 과 같은 데이터는 nan(Not a Number)로 표시되는 것을 확인했다.

encoding = 'UTF8' : 이제는 익숙한 UTF8이다. 한글 처리를 위해 필요하다.

바로 이어 나오는

#plot될 그래프에서 한글(이름) 출력하기 위한 폰트 설정
path = 'C:\\Users\\사용자\\Downloads\\NanumBarunGothic.ttf' 
fontprop = fm.FontProperties(fname=path, size=18)

이 코드는 말그대로 그래프에 한글을 출력하기 위해 추가해줬다.

한글 폰트 파일을 다운받고 해당 파일을 넣어줬다. 이걸 하지 않으면 결과 그래프에서 한글이 깨져서 출력된다.

cputime = [] #cpu 그래프에서 x축으로 들어갈 시간
cpuidle = [] #cpu 그래프에서 y축으로 들어갈 %idle

for i in range(0, int(len(cpudata)/6)): #파일의 끝까지 반복하기 위해 cpudata의 전체 행 수 / 6 (파일에서 1초당 6행 작성되므로)
    cputime.insert(i, cpudata[(i*6) + 1, 2]) #초 정보만 뽑아내 cputime 리스트에 저장
    cpuidle.insert(i, 100 - float(cpudata[(i*6) + 1, 9]))

#테스트 위한 각 배열 출력    
print(cputime)
print(cpuidle)

plt.figure(1)
plt.plot(range(0, len(cputime)), cpuidle) #X축은 0초부터 시간의 경과를 나타내므로 0부터 cputime 리스트의 길이만큼
plt.title('이름 학번 ' + datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'), fontproperties=fontprop) #title에 이름/학번 및 현재 시간 출력
plt.xlabel('time')
plt.ylabel('CPU %idle')
plt.savefig('./cpugraph2016124134.png')
plt.show()

이제 그래프를 만드는 부분이다.

레포트는 하나도 안쓰고있으면서 포스팅하는 시간만 길어져서 진짜 빨리 끝내보겠다...

cputime과 cpuidle을 리스트로 선언했다. 여기에 내가 필요한 데이터를 저장할 예정이다.

이 파일 기준 2열의 OO초 정보와 all행 9열의 %idle 값을 뽑아낼 것이다.

for i in range(0, int(len(cpudata)/6)): #파일의 끝까지 반복하기 위해 cpudata의 전체 행 수 / 6 (파일에서 1초당 6행 작성되므로)
    cputime.insert(i, cpudata[(i*6) + 1, 2]) #초 정보만 뽑아내 cputime 리스트에 저장
    cpuidle.insert(i, 100 - float(cpudata[(i*6) + 1, 9]))

파이썬 for문 이용이 뭔가 어색하다. C에서의 for (i ; i<100 ; i++) 형태가 나는 익숙한 것 같다..

아무튼 반복 횟수는 전체 파일을 저장한 cpudata의 행 수를 기준으로 정했다. 파일을 확인하니 내가 18초부터 32초까지 총 15초를 측정한 것을 확인했다.

파일을 보면

데이터이름
all
CPU0
CPU1
CPU2
CPU3

이렇게 1초당 6행이 작성되므로, 전체 행 수를 6으로 나눠 그만큼 반복했다. (그냥 15초 했으니까 15번 해도 되지만 그렇게 값을 고정적으로 정하는건 좋지 않은 프로그래밍이다.)

그리고 cputime과 cpuidle에 insert 메소드를 이용하여 요소를 추가해줬다.

cpudata의 all 행에서 '초' 정보와 '%idle' 정보를 뽑아올 것이므로

i*6 + 1 행 2열, i*6 + 1 행 9열 의 요소를 각각 저장해줬다.

idle값은 100에서 뺀 값으로 사용하라길래 그냥 100에서 빼줬다.

plt.figure(1)
plt.plot(range(0, len(cputime)), cpuidle) #X축은 0초부터 시간의 경과를 나타내므로 0부터 cputime 리스트의 길이만큼
plt.title('이름 학번 ' + datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'), fontproperties=fontprop) #title에 이름/학번 및 현재 시간 출력
plt.xlabel('time')
plt.ylabel('CPU %idle')
plt.savefig('./cpugraph학번.png')
plt.show()

이제 마지막으로 그래프 그리기다.

여러 리소스의 그래프를 각각 출력할 것이므로 figure(1), figure(2)... 이렇게 사용했다.

x축은 0초부터 시작해 경과한 시간을 표시한다. 이 때 사실 파일에서 OO초 정보는 필요가 없었다는 것을 깨달았다...

18초에서 32초까지 측정하든 32초에서 46초까지 측정하든 똑같은 15초이므로 그냥 cpuidle 리스트만 만들어서 그 길이를 x축으로 사용해도 아무 문제 없다. 그냥 이미 작성했으니까 넘어갔음..

사실 [['18초'], ['19초'], ['20초']...]라고 저장된 cputime에서 [18, 19, 20...] 만 뽑아내기 위해 진짜 말그대로 개고생을 했는데 정말 허탈했다. (re.findall, map 등등 온갖걸 끌어와 써먹어서 겨우 구현했는데..)

어쨌든 x축은 저렇게, y축은 그냥 cpuidle값을 그대로 사용하고, 제목이나 x축 y축 이름들을 대충 설정한 뒤 파일을 이미지로 저장하고 출력했다.

출력 결과는 다음과 같다.

타이틀에 현재 시간도 표시하라는 사항이 있어서 표시해줬다. 아무래도 6~8초 사이에 내가 wc 명령어를 실행했나보다.

CPU 파일에 대해 했으니, 메모리 네트워크 디스크 파일에 대해서는 그냥 똑~~~같은 구조로 저장된 파일의 행, 열만 파악해서 반영해주면 끝이다.

결과 이미지들을 첨부한다. 이렇게 대충 설명하고 넘어갈거였으면 왜썼지..?

아 이시간에 레포트나 쓰고 제출할걸~!!!

그런 일이 있을거라고는 정말정말 생각 안하지만 진짜 혹시모르니 업로드는 과제 제출 기한 뒤로 예약해둬야지..

댓글