리눅스 메모리 사용량을 확인하는 방법(프로세스별)
안녕하세요
오늘은 리눅스 메모리 사용량을 확인하는 방법 두 번째 편을 한번 설명드려 보도록 할게요
이전에는 시스템 전체를 기준으로 봤는데요
이번에는 프로세스별로 알아보고 또 시스템 전체를 볼 때도 퍼센트는 우리가 계산을 하는게직접 계산을 했었어야 되는데 스크립트를 통해서 프로세스별 뿐만 아니라 전체 시스템에 대한 퍼센트도 계산할 수 있는스크립트를 한번 얘기를 해보려고 합니다
시스템 내에서 메모리를 차지하는 것을 확인하는 방법을 한번 검색을 해보셨을 거에요
저도 당연히 검색을 해봤구요 블로그가 굉장히 많습니다
많은 곳에서 많이 설명하는게 보통 PS 명령어를 많이 얘기를 하게 되죠
단순하게 전체적으로 메모리를 볼 때는 free 명령어라든지 free -H 이런 명령어라든지 혹은 htop 이라든지 이런걸 통해서 전체적으로 메모리를 얼마나 사용하는지를 볼 수가 있겠고 그리고 또 퍼센트도 나오게 되죠
메모리에 대해서 어느정도 몇 퍼센트를 쓰고 있는지 이런것들을 볼 수가 있는데 이거를 조금 더 구체적으로 프로세스별로 확인할 때
이렇게 ps - ef 가 아니라 전체 every process가 아니라 옵션을 주어서 이거를 이제 어떤 그 pid 별로 볼 건지 뭐 가상 메모리 사이즈 별로 볼 건지 rss 물리 메모리 얼마 만큼 쓰는지 이런 여러가지 단위들 옵션을 주어서 명령을 입력하시게 되면 프로세스 별로 볼 수 있는데 여기서의 핵심은 사실 pmem입니다
pmem, comm에 이런 식으로 보실 수가 있어요
특정 명령어가 메모리를 얼마만큼 사용하고 있는지에 대한 퍼센트를 볼 수가 있겠고요
말씀드린 대로 이 옵션을 여러 개를 더 추가 하실 수가 있습니다
예를 들어서 여기에 pid, comm 추가하고 pid, comm, pmem 뒤에 다 두실 수도 있고
그리고 이 명령어를 심플하게 명령어(프로그램명) 뿐만아니라 명령어에 대한 아규먼트(Argument)같이 보고 싶으시면 이 커맨드 뿐만 아니라 args 이런 것들도 보게 되면은
그 명령어의 전체 길이 (Full Length)를 전부 다 들어가요
그러니까 명령을 입력하면 뒤에 옵션들도 들어갈 수 있잖아요
그래서 그런 것들도 다 포함해 가지고 확인할 수도 있겠고 당연히 옵션은 굉장히 많습니다 이 매뉴얼 페이지 안에서 맨 PS 안에서도 다 확인이 가능해요
예를 들어서 여기서 pmen 같은 거 보시게 되면은 옵션을 넣을 수 있는 것들이 있어요
지금 왼쪽에 보이는 것들이 있죠 이런 것들을 다 넣을 수가 있어요
그래서 이 안에서 지금 우리가 ps -eo 했을 때 , , , , 를 이어서 세부적인 것을 보실 수 있습니다. 이거를 많이 보셨을 거에요 블록으로 이것만 설명하면 좀 아쉽죠 좀 차별점이 있어야 될 거 잖아요 보시게 되면 여기에 치명적인 단점이 있습니다 이게 뭐가 단점이 있냐면 한번 보여드리도록 할게요
자 여기서 만약에 ps -eo pid, pmem, comm grep nginx만 보고 싶어서 이렇게 봤어
그런데 nginx가 하나가 아닌 여러 개로 되어있는 경우들이 있단 말이에요
아무래도 뭐 쓰레드(Thread)를 통해서 혹은 멀티 프로세스 방식으로 여러가지로 어쨌든 사실상 같은 프로세스인데 여러 개의 프로세스로 나눠져서 여러 개의 쓰레드(Thread)로 나눠져 가지고 구동 될 수도 있단 말이에요
그렇게 되면 이게 다 분산 되서 보이기 때문에 좀 계산하기가 힘듭니다
하나하나 다 더해서 봐야 되거든요 심지어 여기서 이제 가상 메모라를 얼마나 쓰고 있는지 물리 매물을 얼마나 쓰고 있는지까지 본다고 하더라도 각각의 사이즈를 다 계산해 가지고 직접 더해야 되요 그래서 굉장히 좀 불편하죠 실제로 우리가 보고 싶었던 건 nginx가 얼마만큼 사용하고 있는지 이런 것들을 보고 싶은 거거든요 그리고 전체적으로 메모리를 얼마나 쓰고 있고 사용량이 몇 퍼센트인지 이런 것들을 좀 보고 싶은 게 먼저 이잖아요
그래서 제가 또 스크립트를 준비를 해놨습니다 저도 다른 스크립트를 조금 참고, 수정해 가지고 만든 스크립트구요 이 파이썬 스크립트는 일단 기본적으로 파이썬 2.7기준(또는 파이썬 3)으로 구동될 수 있는 스크립트입니다
스크립트는 블로그에다가 한번 게시(파이썬3 버전 스크립트 포함)를 해놓도록 할게요
그래서 제가 링크를 여기다 걸어 놓으려고 하거든요
그러면 이제 참고해서 소스코드 링크를 걸어 놓을 테니까 복사 하셔가지고
쓰시게 되면은 다음과 같은 이 스크립트를 받으실 수가 있겠구요 실제로 이런 식으로 보실 수가 있습니다
파이썬 2.7버전용
#!/usr/bin/env python import os, sys a = os.popen("ps -eo pmem,comm | grep -v '%MEM COMMAND'").read().split("\n") pmem_list = list() entire = False if len(sys.argv) > 1: if sys.argv[1] == "all" or sys.argv[1] == "-a" or sys.argv[1] == "a" or sys.argv[1] == "l": entire = True def check_and_append(p): plen = len(p) for i in pmem_list: if plen > len(i['pname']): sname = i['pname'] lname = p['pname'] else: sname = p['pname'] lname = i['pname'] if sname in lname: p['pname'] = sname p['pmem'] += i['pmem'] pmem_list.remove(i) pmem_list.append(p) total=0.0 try: for p in a: if p == "": break; p = p.split() pmem = float(p[0]) # omit 0.0% mem usage if pmem == 0.0: continue total += pmem pname = p[1] if pname[:1] != '/' and pname[:1] != '_' and pname[:1] != '-' and pname[:1] != '(': pname = p[1].split('-')[0].split('/')[0].split('_')[0] p = {"pname" : pname, "pmem" : pmem} check_and_append(p) except IOError as e: pass import re meminfo = open('/proc/meminfo').read() matched = re.search(r'^MemTotal:\s+(\d+)', meminfo) if matched: mem_total_kB = int(matched.groups()[0]) kB_to_gB = 1000000 usage = (mem_total_kB * total) / (kB_to_gB * 100) t = float(mem_total_kB) / kB_to_gB print "%17s" % "total: " + "%4s" % str(total) + "% (" + str(round(usage,1)) + "G/" + str(round(t,2)) + "G)" sorted_pmem_list = sorted(pmem_list, key=lambda k: k['pmem'], reverse=True) etc = 0.0 for p in sorted_pmem_list: if not entire and p['pmem'] < 1.0: etc += p['pmem'] continue usage = (mem_total_kB * p['pmem']) / (kB_to_gB * 100) output = "%15s" % p['pname'] + ": %4s" % str(p['pmem']) if round(usage,1) < 1.0: print output + "% (" + str(round(usage,4) * 1000) + "MB)" else: print output + "% (" + str(round(usage,1)) + "G)" if not entire: print "..." usage = (mem_total_kB * etc) / (kB_to_gB * 100) output = "%17s" % "the rest: " + "%4s" % str(etc) if round(usage,1) < 1.0: print output + "% (" + str(round(usage,4) * 1000) + "MB)" else: print output + "% (" + str(round(usage,1)) + "G)"
파이썬 3버전용
#!/usr/bin/env python3 import os import sys a = os.popen("ps -eo pmem,comm | grep -v '%MEM COMMAND'").read().split("\n") pmem_list = list() entire = False if len(sys.argv) > 1: if sys.argv[1] == "all" or sys.argv[1] == "-a" or sys.argv[1] == "a" or sys.argv[1] == "l": entire = True def check_and_append(p): plen = len(p) for i in pmem_list: if plen > len(i['pname']): sname = i['pname'] lname = p['pname'] else: sname = p['pname'] lname = i['pname'] if sname in lname: p['pname'] = sname p['pmem'] += i['pmem'] pmem_list.remove(i) pmem_list.append(p) total = 0.0 try: for p in a: if p == "": break p = p.split() pmem = float(p[0]) # omit 0.0% mem usage if pmem == 0.0: continue total += pmem pname = p[1] if pname[:1] != '/' and pname[:1] != '_' and pname[:1] != '-' and pname[:1] != '(': pname = p[1].split('-')[0].split('/')[0].split('_')[0] p = {"pname": pname, "pmem": pmem} check_and_append(p) except IOError as e: pass import re meminfo = open('/proc/meminfo').read() matched = re.search(r'^MemTotal:\s+(\d+)', meminfo) if matched: mem_total_kB = int(matched.groups()[0]) kB_to_gB = 1000000 usage = (mem_total_kB * total) / (kB_to_gB * 100) t = float(mem_total_kB) / kB_to_gB print("%17s" % "total: " + "%4.1f%%" % total + " (" + "%.1fG" % round(usage, 1) + "/" + "%.2fG" % round(t, 2) + ")") sorted_pmem_list = sorted(pmem_list, key=lambda k: k['pmem'], reverse=True) etc = 0.0 for p in sorted_pmem_list: if not entire and p['pmem'] < 1.0: etc += p['pmem'] continue usage = (mem_total_kB * p['pmem']) / (kB_to_gB * 100) output = "%15s" % p['pname'] + ": %4.1f%%" % p['pmem'] if round(usage, 1) < 1.0: print(output + " (" + "%.1fMB" % (round(usage, 4) * 1000) + ")") else: print(output + " (" + "%.1fG" % round(usage, 1) + ")") if not entire: print("...") usage = (mem_total_kB * etc) / (kB_to_gB * 100) output = "%17s" % "the rest: " + "%4.1f%%" % etc if round(usage, 1) < 1.0: print(output + " (" + "%.1fMB" % (round(usage, 4) * 1000) + ")") else: print(output + "% (" + str(round(usage,1)) + "G)")
python pmem.py 제가 이제 미리 복사를 해놨거든요 이런 식으로 아웃풋을 보실 수가 있습니다
보시기가 훨씬 편하죠 좀전에 PS 명령어를 통해서도 물론 볼 수 있었지만 이거를 직접 이 스크립트가 다 계산을 합니다
계산을 해서 전체적으로 몇 퍼센트를 사용하고 있는지 그리고 또 그 사이즈가 어느 정도인지
또 어떤 프로세스들이 몇 퍼센트를 쓰고 있는지 그리고 몇 메가(M, mega), 기가(G, giga)만큼 쓰고 있는지 이런 것들에 대한 단위까지도 다 출력을 해주는 그런 스크립트에요 그래서 훨씬 보기가 편하실 거예요
그리고 이 프로세스가 아까 말씀드린 대로 k3s 이렇게 보시게 되면 k3s는 k8s 경량화버전 입니다 쿠버네티스 버전이에요 프로세스가 지금 하나가 아니죠 좀 여러 개로 되어 있단 말이에요 그러다 보니까 이거를 하나씩 하나씩 다 계산을 해야 되잖아 직접 계산해서 k3s, k8s 쿠버네티스 경량화 버전이 몇 퍼센트를 쓰고 있는지 얼마나 쓰는지 이런 것들을 정확하게 볼 수가 있기 때문에 이해하기가 훨씬 더 좋습니다
이 스크립트 같은 경우에는 ppt 상에서도 이렇게 명시를 하긴 했지만 좀 길어요
그래서 이거는 말씀드린 대로 블로그에다 명시를 하고 링크를 제가 걸어 두도록 하겠습니다 그래서 링크 참조해서 보시면 될 것 같구요 이 스크립트 파일은 대략적으로 이런 식으로 생겼습니다
pmem .PY 으로 생겼어요 그래서 이거를 기준으로 해서 출력해서 볼 수가 있겠죠
기본적으로 이것은 파이썬2.7 기준으로 되어 있기 때문에 참고해서 보시면 될 거 같고요
그다음으로 smem 이라는 명령으로 한번 소개를 드리려고 합니다
smem 일단 설치가 안되어 있으면 설치를 해 주셔야 되고요 그래서 저도 한번 설치를 해 본다면 sudo apt install -y smem
smem 설치를 해주시게 되면 smem을 실행할 수가 있습니다
smem에도 다양한 옵션이 있어서 man 페이지 같은 메뉴얼 페이지를 통해서 이제 smem의 사용법들 옵션들 이런 것들 하나씩 보실 수 있겠구요
기본적으로 몇 가지만 설명을 드리려고 합니다
- r도 있구요
r 기준으로 보게 되면 일단은 전체적으로 확인을 해 볼 수가 있는데 process 단위로 해서 얼마만큼 사용하고 있는지 볼 수 있습니다
여기서 보이는 단위들이 조금 생소한 것들이 보이실 거에요 저도 여기서 한번 해볼게요 그냥 smem 이라고도 하고요
sudo를 기준으로도 볼 수 있겠구요 그리고 -r 까지도 해서 보시게 되면 전체적으로 확인도 다 가능합니다
어쨌든 이제 USS, PSS, RSS 이런 것들 보이시잖아요
여기다가 제가 부가 설명으로 적어 놨는데요 USS 같은 경우에는 우리가 ps -eo pid로 많이 보는 것들이 vsz,rss 이런 것들 많이 봐요
예를 들어서 ps -eo pid,comm,vsz,rss ㅣ head -2 이렇게 보시게 되면 시스템 D라고 하는 1번 프로세스 init 프로세스가 init프로세스
init프로세스가 systemd라고 하는 프로세스 1번프로세스 모든 프로세스의 아버지 프로세스 최초 프로세스죠 조상 프로세스 이 프로세스가 가상 메모리로 얼마만큼 쓰고 있고 할당을 해서 사용하고 있고 이거를 실질적으로 물리메모리에 맵핑해서 사용하고 있는 거 이거는 가상 메모리 메커니즘 알고 있으면 좀 더 정확하게 이해하실 수 있는데요
기회가 되면 다른 영상으로 한번 올려 놓으면 좋을 것 같아요 가상 메모리 메커니즘에 대해서요 그런데 여기서 간단하게 설명드려보면 우리가 malloc 배열을 선언하든 모든 메모리에 대한 동작 프로그램 상에서 c언어, java, python 다 가상 메모리 영역을 기준으로 할당하고 해제하게 됩니다
그런데 이때 실제로 읽고 쓰는 동작이 벌어지게 되면 실시간적으로 프로그램이 동작하는 과정에서 단순히 할당하는 게 아니라 거기에다가 쓰거나 혹은 읽거나 이렇게 메모리를 사용하게 되면은 rss가 할당되게 되요 그러니까 물리메모리 영역이 할당되게 되고 이 가상메모리 주소가 원래는 깡통이었다가 물리주소랑 맵핑이 되서 물리 메모리 공간을 할당하게 되고 사용하게 됩니다
이런 식으로 얼마만큼 사용했는지를 이제 확인할 수는 있단 말이죠 그런데 여기의 문제는 뭐냐면요 프로세스가 라이브를 사용한다던가 이렇게 되면은 공유하고 있는 공간들도 많이 있단 말이에요
그러니까 다른 프로세스들하고 공유하고 있는 공간인데도 불구하고 그냥 전부 다 어쨌든 이 프로세스로 인해서 사용되는 메모리공간을 전부 다 여기서 표현을 하고 있기 때문에 지금 우리가 좀 헷갈린다는 거죠 RSS가 RSS가 내가 생각한 것보다 조금 높은 데 라고 생각하실 수 있을 겁니다.
이 프로세스가 정도로 메모리를 많이 쓰나? 라고 생각하실 수도 있단 말
왜? 공유하고 있는 공간까지도 전부 다 때려 넣어서 여기다 넣어 놨기 때문에 사실상 다른 프로세스하고 좀 중복되는 공간 까지도 여기에다 표현이 되고 있다는 말이에요 사실 RSS도 중요한 좋은 수치인 건 맞지만 공유 메모리 영역을 사실은 제외하고 생각을 해야 하는 것도 필요할 수 있거든요 그래서 smem 쓰는 겁니다
smem쓰게 되면은 공유메모리 공간은 완전히 제거하고 진짜로 순수하게 내가 쓰고 있는 양만 보는 거죠
그게 USS 입니다 만약에 제가 여기다가 smem을 하고 확인을 해서 grep systemd 사용해서 본다면 systemd를 기준으로 해서 지금 실행되고 있는 프로세스들을
이제 확인을 해보고 있는 거구요 systemd 프로세스를 기준으로 해서 USS하고 PSS, RSS 양을 다 구별해서 보기 때문에 공유 메모리 영역을 완전히 제거하고 있는 unique set size 하고그리고 또 PSS 그러니까 공유 메모리 부분을 좀 적당하게 나누는 거에요
예를 들어서 standard C 라이브러리 같은 경우에는 특히 여러 프로세스가 나눠서 쓰거든요
예를 들어서 10개의 프로세스가 쓰고 있다 그럼 10개의 프로세스 만큼 나누는 거죠 그래서 실제로 만약에 10메가를 쓰고 있으면 나는 1메가를 쓰고 있다고 나눠서 10분의 1로 나눠서 계산을 한 수치입니다 그래서 적당하게 나눠 가지고 계산한게 PSS 라고 볼 수 있겠고 그냥 그런건 상관 안하고 무조건 나 때문에 사용하는 메모리 공간을 전부 다 생각하는게 RSS입니다
그래서 RSS는 조금 많이 잡힐 수가 있다는 것을 생각할 수가 있어요 그리고 뿐만 아니라 이제 smem은 -u 옵션이라든지 이런 것들도 같이 사용할 수가 있겠구요
그렇게 되면은 사용하고 있는 개정기준으로도 확인해 볼 수가 있습니다 예를 들어서 저도 한번 해본다면 smem -u 이런 식으로도 볼 수가 있겠구요
그리고 w옵션을 사용하시게 되면 시스템별로도 볼 수가 있는데요 예를 들어서 smem - w를 하시게 되면은 유저 메모리 그리고 커널메모리 이런 것들을 구분해서 메모리 사용량을 보실 수도 있습니다.
여기까지 ps명령을 하던지 그리고 제가 제공해드리는 스크립트라던지 그리고 또 smem 통해서 프로세스별로 조금 더 세부적인 관점으로 메모리는 얼마만큼 사용하고 있는 지를 확인해서 봤습니다 그래서 여러분들 문제 해결 하시거나 메모리에 대해서 체크를 하실 때 이런 것들 잘 활용하시게 되면 리눅스를 사용하실 때 많이 도움이 되실 거라고 생각이 됩니다 영상은 여기까지 하고 마무리 짓도록 하겠습니다 감사합니다
본내용에 대해 더 자세히 배우고 싶으시다면 리얼리눅스 강의중 시스템핵심정리를 추천드립니다.
https://reallinux.co.kr/course/course/se_system