출처 :http://joinc.co.kr/modules/moniwiki/wiki.php/article/sms_%C7%C1%B7%CE%B1%D7%B7%A5_%C1%A6%C0%DB
출처 :http://joinc.co.kr/modules/moniwiki/wiki.php/Site/system_programing/proc/MakeSmsProg
==================================================================================
article/sms_프로그램_제작
1절. 소개
그동안 몇 번에 걸쳐서 /proc파일시스템에 관한 연재도 했었고 관련되어서 sms정보를 가져오는 강좌도 마련했었다. 이번에는 좀더 폭넓은 시스템정보를 가져오는 프로그램을 작성해보기로 했다.
이 프로그램은 리눅스 kernel-2.4.x에서 코딩/테스트 되었으며, 다른 유닉스들에서는 작동하지 않을것이다.
2절. jsms 소개
만들고자 하는 프로그램의 이름은 jsms으로 정했다. joinc+sms(-.-;)의 줄임말이다. 이 프로그램은 시스템관리를 목적으로 사용하는 df, free등과 유사한 정보를 얻어올 것이다.
즉 디스크정보, 메모리정보, CPU사용율 등의 정보를 출력한다.
2.1절. 시스템정보 수집 방법
각각의 시스템 정보들은 대부분 /proc파일시스템에 있는 파일들을 분석함으로써 얻어오게 된다. 이번 장에서는 각각의 정보별로 어떤식으로 데이타를 얻어올 수 있는지를 설명하도록 할것이다. 이러한 설명은 실제 작동되는 코드를 예제로 만들어가면서 이루어지게 될것이다.
이러한 각각의 예제코드들은 나중에 jsms에 통합되게 될것이다.
2.1.1절. 메모리 정보
메모리는 크게 물리적메모리와 Swap메모리로 구성되며, 우리는 이들 모두에 대한 정보를 가져올 수 있어야 한다. 가져올 데이타는 할당된 전체 블럭크기, 사용되는 블럭크기, 사용할수 있는 블럭크기, shared, buffers, cached된 블럭 크기이다.
이러한 메모리 정보는 /proc파일시스템에 의해서 파일로 제공되어진다. 메모리 정보를 가지는 파일은/proc/meminfo이다. 이 파일은 다음과 같은 메모리의 상세한 정보를 가지고 있다.
[root@localhost proc]# cat meminfo total: used: free: shared: buffers: cached: Mem: 260984832 257720320 3264512 4489216 48381952 8617984 Swap: 542826496 0 542826496 MemTotal: 254868 kB MemFree: 3188 kB ... |
typedef struct _df { // 물리적 메모리 크기 저장용 ulong real_total; ulong real_used; ulong real_buffers; ulong real_cached; // 스왑 메모리 크기 저장용 ulong swap_total; ulong swap_used; } df; |
2.1.2절. CPU 사용율
CPU사용률은/proc/stat를 참고하면 된다.
[root@coco /root]# cat /proc/stat cpu 94964 233 9932 1662334 cpu0 94964 233 9932 1662334 page 467656 48623 swap 4365 11418 ... |
/proc/stat의 출력내용을 보면 cpu와 cpu0과 같이 뒤에 숫자가 붙은 것과 그렇지 않은 것이 있는데, 숫자가 붙지 않은 것은 (cpu가 여러 개 있을 때)전체 cpu에 대한 jiffies총합의 계산을 보여준다. 숫자가 붙은것들은 개별 cpu에 대한 jiffies값을 보여준다. 위의 리눅스시스템의 경우 단지 하나의 cpu로 이루어져 있음을 짐작할 수 있을 것이다.
만약 CPU의 IDLE상태만을 알고 싶다면 다섯번째 필드값만을 조사하면 될것이다.
이렇게 해서 CPU의 jiffies를 가지고 오긴 했지만 이것만으로는 우리가 원하는 정보를 얻어올순 없다. 실제 우리가 원하는 정보는 전체 CPU사용 jiffies중 idle jiffies를 얻어와야 하고 이것을 다시 백분율로 표시해야 하므로 약간의 가공을 해주어야 한다.
방법은 간단하다. 일정시간 소비된 idel jiffies를 총 소비된 jiffies로 나눠 주면 된다.
(idle jiffies)*100 / (idle jiffies + use jiffies + system jiffies + low prio jiffies) |
2.1.3절. 디스크 사용율
보통 디스크 사용율을 확인하기 위해서 여러분은 df시스템 명령어를 사용할 것이다. 다음은 df로 확인해본 필자의 시스템의 디스크 사용율이다.
디스크의 사용율을 검사하기 위해서는 /etc/mtab과 /proc/mounts, /proc/partition의 정보를 이용해야 한다. /proc/mounts는 현재 마운트된 장치와 마운팅경로 파일시스템 종류 정보를 담고 있다.
[root@localhost proc]# mounts /dev/root / ext2 rw 0 0 /proc /proc proc rw 0 0 usbdevfs /proc/bus/usb usbdevfs rw 0 0 /dev/hda1 /boot ext2 rw 0 0 /dev/hda6 /home ext2 rw 0 0 /dev/hda5 /usr ext2 rw 0 0 /dev/hda8 /var ext2 rw 0 0 none /dev/pts devpts rw 0 0 automount(pid556) /misc autofs rw 0 0 |
[root@localhost proc]# cat /etc/mtab /dev/hda7 / ext2 rw 0 0 none /proc proc rw 0 0 usbdevfs /proc/bus/usb usbdevfs rw 0 0 /dev/hda1 /boot ext2 rw 0 0 /dev/hda6 /home ext2 rw 0 0 /dev/hda5 /usr ext2 rw 0 0 /dev/hda8 /var ext2 rw 0 0 none /dev/pts devpts rw,gid=5,mode=620 0 0 automount(pid556) /misc autofs rw,fd=5,pgrp=556,minproto=2,maxproto=3 0 0 |
실제 df의 쏘쓰코드를 보면 mtab와 /proc/mounts를 같이 이용해서 파일시스템 정보를 얻어 오는 걸 확인 할 수 있다.
이렇게 /proc/mounts와 mtab을 이용해서 정확한 마운트된 장치 정보를 얻어왔다면 각 장치의 크기와 사용량등을 검사해야 할 것이다. 이 정보는statfs(2)시스템 함수를 이용하면 된다. 사용방법은 예제를 통해서 알아 볼 것이다.
3절. jsms 작성
우리가 만들 프로그램은 기능의 구현에만 신경을 쓸 것이다. 깔끔, 효율, 가독성 이런건 각자 의 몫으로 남겨 놓도록 하겠다.
3.1절. Source Tree의 구성
원할한 프로젝트의 진행을 위해서 main함수와 함수 구현부분을 분리시키도록 할 것이다. main함수를 포함하는 쏘쓰파일은 최소한의 기능만을 하면서 단지 필요로 하는 함수를 호출 해서 화면에 출력하는 일만을 하도록 할것이다.
./--+-- Makefile | +-- jsms.cc | +-- main.cc | +-- include/ ------ jsms.h |
3.2절. Makefile
이것은 make룰을 담고 있는 파일로 다음과 같이 구성된다. 간단하게 이해 할 수 있을 것이다. Makefile관련 내용은make를 이용한 프로젝트 관리를 참고하기 바란다.
예제 : Makefile
############################################################################# |
3.3절. jsms.h
역시 주석으로 모든 설명을 대신하도록 하겠다.
예제 : jsms.h
#ifndef _JSMS_H_ #define _JSMS_H_ #include <vector> #include <algorithm> #include <unistd.h> #include <stdio.h> #include <set> #include <iostream> #include <sys/vfs.h> #define MOUNT_STR_LEN 255 #define DEVICE_STR_LEN 80 #define MAX_CPU_NUM 32 #define DF_LINE_LEN MOUNT_STR_LEN + DEVICE_STR_LEN using namespace std; // 디스크 정보를 저장하기 위한 구조체 typedef struct _df { int blocks; // 할당된 블럭 int used; // 사용하고 있는 블럭 int avail; // 사용할 수 있는 블럭 char mount[MOUNT_STR_LEN]; // 마운트 디렉토리 char device[DEVICE_STR_LEN]; // 장치명 } df; // cpu 사용율 // 여러개의 CPU를 사용할 수도 있음으로 // CPU의 갯수를 저장하며, // CPU의 사용율은 (CPU갯수 만큼)배열에 저장된다. typedef struct _cpu_usage { int cpuid; // 특별히 사용되지 않는다. int countcpu; // 등록된 CPU의 수 int cpuusage[MAX_CPU_NUM]; // 각 CPU의 사용율 } cpu_usage; // 메모리 정보 // 물리적 메모리와 스왑 메모리 저장용 typedef struct _mem_data { ulong real; // 물리적 메모리 크기 ulong free; // 물리적 메모리 남은 크기 ulong swap_use; // 스왑 사용량 ulong swap_free; // 스왑 남은량 } mem_data; // 나중에 /etc/mtab과 /proc/mounts의 정보를 // 비교하기 위한 목적으로 사용한다. // 사용법은 jsms.cc를 참고 struct ltstr { booloperator()(const df m1, const df m2) const { returnstrcmp(m1.mount, m2.mount) < 0; } }; class JSms { private: // 현재 CPU사용율 (jiffly 갯수) int mcurrent_usage[MAX_CPU_NUM][3]; // 바로전의 CPU사용율 int mold_usage[MAX_CPU_NUM][3]; public : // 디스크 정보를 얻기 위한 메서드 vector<df>GetDf(); // CPU사용율을 얻기 위한 메서드 cpu_usageGetCpuIdle(); // 메모리 정보를 얻기 위한 메서드 mem_dataGetMem(); }; #endif |
3.4절. jsms.cc
JSms의 클래스의 정의를 담고 있다. 코드드와 함께 /proc의 각 파일을 함께 참고하면 좀더 이해하기 쉬울 것이다. /proc의 각 파일로 부터 정보를 얻어오는 것은 scanf계열 함수를 사용했다.
예제 : jsms.cc
#include <jsms.h> #include <vector> #include <algorithm> #include <unistd.h> #include <stdio.h> #include <set> #include <iostream> #include <sys/vfs.h> // /etc/mtab와 /proc/mounts의 파일내용을 이용해서 // 마운트된 디스크의 정보를 얻어온다. vector<df>JSms::GetDf() { FILE *fp = NULL; struct statfs lstatfs; unsigned int i = 0; set<df, ltstr> smtab, smount; vector<df> vmtab; char line[DF_LINE_LEN]; df ldf_data; // 먼저 /etc/mtab을 열어서 각 장치와 // 장치에 마운트된 파일의 이름을 얻어온다. // 얻어온 데이터는 smtab set컨테이너에 저장한다. // smtab에 저장된 데이터는 나중에 /proc/mounts 데이터와 // 비교하게 된다. // 왜냐하면 마운트 파일에 대한 디바이스 이름이 잘못 지정되어 // 있을 수 있음으로 이경우 /proc/mounts의 내용을 참조해야 하기 // 때문이다. fp = fopen("/etc/mtab", "r"); if (fp == NULL) { cout << "Open Error mtab" << endl; exit(0); } while(fgets(line, DF_LINE_LEN, fp) != NULL) { if ((strstr(line, "/dev/") - line) == 0) { sscanf(line, "%s %s", ldf_data.device, ldf_data.mount); smtab.insert(ldf_data); } } fclose(fp); fp = NULL; // /proc/mounts로 부터 디바이스와 마운트파일 정보를 // 얻어온다. fp = fopen("/proc/mounts", "r"); if (fp == NULL) { cout << "Open Error mounts" << endl; exit(0); } while(fgets(line, DF_LINE_LEN, fp) != NULL) { if ((strstr(line, "/dev/") - line) == 0) { sscanf(line, "%s %s", ldf_data.device, ldf_data.mount); smount.insert(ldf_data); } } fclose(fp); fp = NULL; // /proc/mount와 /etc/mtab의 내용을 intersection(교집합)연산을 시킨다. // 연산에 사용할 비교함수는 jsms.h에 정의 되어있는 ltstr이다. // 교집합에 사용될 비교 데이터는 마운트 디렉토리 이름이다. // 이렇게 할경우 비록 /etc/mtab에 있는 장치이름이 잘못되었다고 하더라도 // /proc/mount의 제대로된 장치이름으로 덮어 쓸 수 있다. // 이 데이터는 vmtab에 저장된다. set_intersection( smtab.begin(), smtab.end(), smount.begin(), smount.end(), back_inserter(vmtab),ltstr()); // 마운트된 파일시스템에 대한 디스크 사용율을 구한다. { for (i = 0; i < vmtab.size(); i++) { statfs(vmtab[i].mount, &lstatfs); vmtab[i].blocks = lstatfs.f_blocks*(lstatfs.f_bsize/1024); vmtab[i].avail = lstatfs.f_bavail*(lstatfs.f_bsize/1024); vmtab[i].used = vmtab[i].blocks - vmtab[i].avail; } } return vmtab; } // CPU사용율을 체크한다. // CPU사용율을 위해서는 현재의 jiffies값을 구하고 // 과거의 jiffies의 값과 비교해서 얼마만큼 증가 했는지를 // 판단해서 백분율로 나타내게 된다. // 그러므로 이 메서드는 두번 호출해야 한다. // 처음 호출로 알아낸 jiffies값은 별도의 변수에 저장되고 // sleep를 호출한 후 잠시후 다시 이 메서드를 호출해야 현재의 // jiffies값과 과거의 jiffies를 이용한 계산이 가능해 진다. cpu_usageJSms::GetCpuIdle() { FILE *fp = NULL; char line[80]; cpu_usage lcpu_usage; char cpuid[32]; int nulldata; int totaldiff; int diff[3]; int cpu_num = 0; memset((void *)&lcpu_usage, 0x00,sizeof(cpu_usage)); // /proc/stat파일을 읽어서 jiffies값을 계산한다. fp = fopen("/proc/stat", "r"); if(fp == NULL) { cout << "Cannot open stat" << endl; return lcpu_usage; } while(1) { fgets(line,80, fp); if (strstr(line, "cpu") == NULL) { fclose(fp); // cpu 갯수를 세팅하고 리턴한다. lcpu_usage.countcpu = cpu_num -1; return lcpu_usage; } else { // /proc/stat에서 최초에 읽어 들이는 데이터는 // 전체 CPU에 대한 통계 데이터다. // 그러므로 개별 CPU에 대한 데이터를 얻기 위해서는 처음 라인은 // 무시하고 넘어가야 한다. if (cpu_num > 0) { // sscanf를 이용해서 jiffies값을 얻어온다. // 그리고 과거의 jiffies값과의 연산을 통해서 // 각각의 jiffies가 얼마만큼 변했는지를 확인하고 // 이 값을 백분율로 나타낸다. sscanf(line, "%s %d %d %d %d", cpuid, &mcurrent_usage[cpu_num-1][0], &nulldata, &mcurrent_usage[cpu_num-1][1], &mcurrent_usage[cpu_num-1][2]); diff[0] = mcurrent_usage[cpu_num-1][0] - mold_usage[cpu_num-1][0]; diff[1] = mcurrent_usage[cpu_num-1][1] - mold_usage[cpu_num-1][1]; diff[2] = mcurrent_usage[cpu_num-1][2] - mold_usage[cpu_num-1][2]; totaldiff = diff[0] + diff[1] + diff[2]; lcpu_usage.cpuusage[cpu_num-1] = (diff[2]*100)/totaldiff; memcpy((void *)&mold_usage, (void *)&mcurrent_usage, sizeof(mcurrent_usage)); } cpu_num ++; } } if(fp != NULL) fclose(fp); } // 메모리 정보를 얻어온다. mem_dataJSms::GetMem() { FILE *fp = NULL; int index = 0; char line[128]; mem_data lmem_data; int nulldata; char null[12]; fp = fopen("/proc/meminfo", "r"); if (fp == NULL) { cout << "Meminfo open error" << endl; exit(0); } while(1) { fgets(line, 128, fp); if(index == 0) { index ++; continue; } else if(index == 1) { sscanf(line, "%s %lu %d %lu", null, &lmem_data.real, &nulldata, &lmem_data.free ); } else if(index == 2) { sscanf(line, "%s %lu %lu %lu", null, &nulldata, &lmem_data.swap_use, &lmem_data.swap_free); } else { fclose(fp); return lmem_data; } index ++; } } |
3.5절. main.cc
main함수는 단지 JSms클래스를 이용하는 것 말고는 하는 일이 없다. 프로그램 실행인자를 처리하기 위해서getopt()를 사용했다.
예제 : main.cc
#include <jsms.h> #include <unistd.h> voidhelp() { printf("Usage : jsms -[mcf] \n" "-m : mem info\n" "-c : cpu usage\n" "-h : help message\n" "-f : disk info\n"); exit(0); } intmain(int argc, char **argv ) { class JSms lSms; vector<df> ldf; mem_data lmem_data; int opt; while((opt =getopt(argc, argv, "mcfh")) != -1) { // 메모리 정보를 요청했을때 switch (opt) { case 'm': { ulong swap_real; lmem_data = lSms.GetMem(); printf("REAL : %15lu %15lu %lu\n", lmem_data.real, lmem_data.free, ((lmem_data.real - lmem_data.free)*100)/lmem_data.real); swap_real = lmem_data.swap_use + lmem_data.swap_free; printf("Swap : %15lu %15lu %lu\n", swap_real,lmem_data.swap_free, (swap_real - lmem_data.swap_free)*100/swap_real); break; } // CPU사용율을 요청했을때 //sleep()을 이용해서 시간차를 두고 GetCpuIdle를 두번 호출했음에 // 유의 한다. case 'c': { cpu_usage lcpu_usage; lcpu_usage = lSms.GetCpuIdle(); sleep(1); lcpu_usage = lSms.GetCpuIdle(); for (int i = 0; i < lcpu_usage.countcpu; i++) { printf("[%d] : %d\n", i+1, lcpu_usage.cpuusage[i]); } break; } // 디스크 사용량 체크 case 'f': { ldf = lSms.GetDf(); for (int i = 0; i < ldf.size(); i++) { printf("%10s %-15s %12d %12d\n", ldf[i].device, ldf[i].mount, ldf[i].blocks, ldf[i].used); } break; } // 도움말 case 'h': { help(); break; } default : { help(); break; } } } } |
'Unix Linux' 카테고리의 다른 글
how to create and use program libraries on Linux (0) | 2006.05.11 |
---|---|
[솔라리스] kstat를 이용한 시스템 정보 획득 (0) | 2006.04.18 |
[펌] Solaris Kernel Statistics (0) | 2006.01.26 |