日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

linux监测指定进程的CPU及物理内存消耗情况(c程序)

發布時間:2023/12/15 linux 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 linux监测指定进程的CPU及物理内存消耗情况(c程序) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

近日,由于工作要求,研究了一下如何在linux系統下對某個指定的單個進程進行監測,分析其CPU及物理內存的使用情況,并基于c語言寫了一個獨立的模塊,完整的實現上述功能。現將整個模塊的代碼貼上,以便日后借鑒,并與同道中人分享。源碼在ubuntu 16.04、嵌入式主板等多個系統下運行親測正常,各類注釋都比較齊全,對模塊的使用方法也有介紹,此處就不再贅述,若有問題,自行閱讀源碼即可。
注:另有一份代碼是基于bash腳本的,同樣實現上述的功能,貼于另一篇博客,如有需要,可點擊此處跳轉。

/************************************************************************************************** ** ** 文件名稱: res_monitor.c ** 文件描述: 指定進程的[cpu/內存]資源使用情況監測 ** =============================================================================================== ** 創建信息: | 2019-2-1 | LEON | 創建本模塊 ** =============================================================================================== ** 修改信息: 單擊此處添加.... **************************************************************************************************/ #include <time.h> #include <ctype.h> #include <stdio.h> #include <fcntl.h> #include <errno.h> #include <assert.h> #include <printf.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <signal.h> #include <dirent.h> #include <sys/time.h> #include <sys/types.h>/*************************************************************************************************/ // CPU占用率計算原理: // 1、讀取/proc/pid/stat文件,其中記錄了從開機到現在,本進程所占用的CPU時間(單位jiffies) // 2、然后再讀取/proc/stat文件,其中記錄了從開機到現在,系統所占用的CPU時間(單位jiffies) // 3、取兩個時間點,這兩個時間點的進程耗時差,除以系統耗時差,得到的就是該進程的CPU占用率 /*************************************************************************************************/ // 內存占用率計算原理: // 讀取/proc/pid/status文件,其中以VmRSS開頭的行,記錄了該進程的物理內存值 /*************************************************************************************************//*************************************************************************************************/ // 下面這段話詳細的解釋了RSS內存的意義,及其與VSZ內存的區別 // // RSS(Resident Set Size),常駐內存集大小,表示進程在RAM中占用了多少內存,并不包含在SWAP中占用的虛擬內存 // 即使是在內存中的使用了共享庫的內存大小也一并計算在內,包含了完整的在stack和heap中的內存 // // VSZ(Virtual Memory Size),虛擬內存大小,表明了該進程可以訪問的所有內存,包括被交換的內存和共享庫內存 // // 如果進程A的二進制文件大小為500KB,并且鏈接到了2500KB的共享庫,有200KB的stack/heap大小 // 這200KB中又有100KB位于內存中,100KB位于SWAP空間中,并且加載了1000KB的共享庫和400KB的自身二進制文件。則 // RSS: 400K + 1000K + 100K = 1500K; VSZ: 500K + 2500K + 200K = 3200K /*************************************************************************************************//*************************************************************************************************/ // 模塊宏定義 /*************************************************************************************************/ #define CPU_START_POS 14 /* stat文件的有效起始行數 */ #define READ_BUF_SIZE 512 /* 讀取文件的緩存空間大小 */#define LOG_ENABLE "logon" /* 啟用日志輸出的命令 */#define MONITR_DATA_PATH "monitor_log" /* 日志文件的子目錄 */ #define MONITR_DATA_BEXT ".log" /* 日志文件的后綴名 *//*************************************************************************************************/ // 模塊靜態變量定義 /*************************************************************************************************/ static long s_cur_pro_cpu, s_pre_pro_cpu; /* 指定程序的本輪/前輪CPU時間 */ static long s_cur_sys_cpu, s_pre_sys_cpu; /* 整個系統的本輪/前輪CPU時間 */static int s_needlogfile; /* 是否要輸出到日志文件 */ static char s_recfilepath[128]; /* 日志文件的路徑信息 *//************************************************************************************************** ** 函數名稱: excute_cmd ** 功能描述: 執行shell腳本,并解析出最終的執行結果 ** 輸入參數: 腳本命令 ** 輸出參數: 無 ** 返回參數: 返回1表示腳本命令執行成功,返回-1表示執行失敗 **************************************************************************************************/ static int excute_cmd(char *comand) {int status;status = system(comand);if (status == -1) { /* 系統子進程創建失敗 */return -1;}if (WIFEXITED(status) == 0) { /* shell拉起失敗或未正常執行結束 */return -1;}if (WEXITSTATUS(status) != 0) { /* mkdir命令執行失敗 */return -1;}return 1; }/************************************************************************************************** ** 函數名稱: target_is_exist ** 功能描述: 檢查目標是否存在 【目標可以是目錄和文件】 ** 輸入參數: 無 ** 輸出參數: 無 ** 返回參數: 返回1表示目標存在,返回-1表示目標不存在 **************************************************************************************************/ static int target_is_exist(char *target_path) {int result;result = access(target_path, F_OK);return result; }/************************************************************************************************** ** 函數名稱: make_sudir ** 功能描述: 創建目錄 【可以建立多級子目錄】 ** 輸入參數: 子目錄路徑 ** 輸出參數: 無 ** 返回參數: 返回0表示該目錄已經存在,返回1表示創建成功,返回-1表示創建失敗 **************************************************************************************************/ static int make_sudir(char *fpath) {char *comand;int pathlen, result;if (target_is_exist(fpath) == 1) { /* 目標文件夾已經存在 */return 0;}pathlen = strlen(fpath) + 16; /* 16的長度是為shell命令而留 */comand = (char *)malloc(pathlen);assert(comand != NULL);memset(comand, 0, pathlen);sprintf(comand, "mkdir -p %s", fpath);result = excute_cmd(comand);free(comand); /* 這里要記得釋放內存 */return result; }/************************************************************************************************** ** 函數名稱: write_recfile ** 功能描述: 將信息寫入到日志文件中 ** 輸入參數: 無 ** 輸出參數: 無 ** 返回參數: 無 **************************************************************************************************/ static void write_recfile(char *log_path, char *log_text) {FILE *log_hdl;log_hdl = fopen(log_path, "a");assert(log_hdl != NULL); /* 打開日志記錄文件 */assert(fseek(log_hdl, 0, SEEK_END) == 0); /* 定位到尾開始寫入 */assert(fwrite(log_text, strlen(log_text), 1, log_hdl) == 1); /* 寫入信息內容 */assert(fclose(log_hdl) == 0); /* 關閉日志記錄文件 */ }/************************************************************************************************** ** 函數名稱: generate_rfile_path ** 功能描述: 生成文件名路徑信息 ** 輸入參數: 無 ** 輸出參數: 無 ** 返回參數: 無 **************************************************************************************************/ static void generate_rfile_path(char *target) {struct timeval tv;struct tm tm;char timestr[32];memset(timestr, 0, sizeof(timestr));gettimeofday(&tv, NULL);tm = *localtime(&tv.tv_sec);sprintf(timestr, "%.4d%.2d%.2d_%.2d%.2d%.2d", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);assert(make_sudir(MONITR_DATA_PATH) != -1);memset(s_recfilepath, 0, sizeof(s_recfilepath));sprintf(s_recfilepath, "%s/%s_%s%s", MONITR_DATA_PATH, target, timestr, MONITR_DATA_BEXT);printf("log is enabled. log_file \"%s\"\n", s_recfilepath); }/************************************************************************************************** ** 函數名稱: record_loginfo ** 功能描述: 將信息記錄到日志文件,并進行打印輸出 ** 輸入參數: 無 ** 輸出參數: 無 ** 返回參數: 無 **************************************************************************************************/ static void record_loginfo(char *info) {struct timeval tv;struct tm tm;int tlen;char *wstr;tlen = strlen(info) + 32; /* 32用于存放時間戳 */wstr = malloc(tlen);assert(wstr != 0);gettimeofday(&tv, NULL);tm = *localtime(&tv.tv_sec);sprintf(wstr, "[%04d/%02d/%02d %02d:%02d:%02d] %s", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, info);printf("%s", wstr); /* 輸出到終端顯示 */if (s_needlogfile > 0) {write_recfile(s_recfilepath, wstr); /* 記錄到日志文件 */} }/************************************************************************************************** ** 函數名稱: find_pid_by_name ** 功能描述: 根據進程名得到進程ID ** 輸入參數: 無 ** 輸出參數: 無 ** 返回參數: 無 **************************************************************************************************/ int find_pid_by_name(char* pidName) {DIR *prodir;FILE *status;struct dirent *next;char finame[READ_BUF_SIZE];char tmpbuf[READ_BUF_SIZE];char pcname[READ_BUF_SIZE];prodir = opendir("/proc"); /* proc中包括當前的進程信息 */assert(prodir != NULL);while ((next = readdir(prodir)) != NULL) { /* 逐個檢索所有目錄 */if (strcmp(next->d_name, "..") == 0) {continue;}if (!isdigit(*next->d_name)) { /* 進程目錄必須是數字的 */continue;}sprintf(finame, "/proc/%s/status", next->d_name); /* 拼湊出完整的目錄名稱 */if (!(status = fopen(finame, "r"))) {continue;}if (fgets(tmpbuf, READ_BUF_SIZE - 1, status) == NULL) { /* 讀取目錄下的文件 */fclose(status);continue;}fclose(status);sscanf(tmpbuf, "%*s %s", pcname); /* 提取出其中的有效內容 */if (strcmp(pcname, pidName) == 0) { /* 與所輸入的進程名符合 */return strtol(next->d_name, NULL, 0);}}return 0; }/************************************************************************************************** ** 函數名稱: get_items_by_pos ** 功能描述: 在字符串中尋找第N次空格出現的地方 ** 輸入參數: 無 ** 輸出參數: 無 ** 返回參數: 無 **************************************************************************************************/ char *get_items_by_pos(char *buff, unsigned int numb) {char *crpos;int i, ttlen, count;crpos = buff;ttlen = strlen(buff);count = 0;for (i = 0; i < ttlen; i++) {if (' ' == *crpos) { /* 以空格為標記符進行識別 */count++;if (count == (numb - 1)) { /* 全部個數都找完了 */crpos++;break;}}crpos++;}return crpos; }/************************************************************************************************** ** 函數名稱: get_pro_cpu_time ** 功能描述: 獲取某個進程的CPU時間(從開機到現在,單位jiffies) ** 輸入參數: 無 ** 輸出參數: 無 ** 返回參數: 無 **************************************************************************************************/ long get_pro_cpu_time(unsigned int pid) {FILE *fd;char *vpos, buff[1024];long utime, stime, cutime, cstime;sprintf(buff, "/proc/%d/stat", pid); /* 讀取進程的狀態文件 */fd = fopen(buff, "r");assert(fd != NULL);assert(fgets(buff, sizeof(buff), fd) != NULL); /* 讀取文件內容到緩沖區 */vpos = get_items_by_pos(buff, CPU_START_POS); /* 讀取指定的條目內容 */sscanf(vpos, "%ld %ld %ld %ld", &utime, &stime, &cutime, &cstime); /* 將條目內容拆分成實際的數據 */fclose(fd);return (utime + stime + cutime + cstime); }/************************************************************************************************** ** 函數名稱: get_sys_cpu_time ** 功能描述: 獲取整個系統的CPU時間(從開機到現在,單位jiffies) ** 輸入參數: 無 ** 輸出參數: 無 ** 返回參數: 無 **************************************************************************************************/ long get_sys_cpu_time(void) {FILE *fd;char name[32], buff[1024];long user, nice, syst, idle;fd = fopen("/proc/stat", "r"); /* 讀取系統的狀態文件 */assert(fd != NULL);assert(fgets(buff, sizeof(buff), fd) != NULL); /* 讀取文件內容到緩沖區 */sscanf(buff, "%s %ld %ld %ld %ld", name, &user, &nice, &syst, &idle); /* 將條目內容拆分成實際的數據 */fclose(fd);return (user + nice + syst + idle); }/************************************************************************************************** ** 函數名稱: get_cpu_stat ** 功能描述: 獲取進程的CPU使用率 ** 輸入參數: 無 ** 輸出參數: 無 ** 返回參數: 本輪時間片里,該進程的CPU使用率,單位百分比 **************************************************************************************************/ float get_cpu_stat(unsigned int pid) {float ratio;s_cur_pro_cpu = get_pro_cpu_time(pid);s_cur_sys_cpu = get_sys_cpu_time();if ((s_cur_pro_cpu == s_pre_pro_cpu) || (s_cur_sys_cpu == s_pre_sys_cpu) || (s_cur_pro_cpu == 0) || (s_cur_sys_cpu == 0)) {ratio = 0;} else {ratio = (100.0 * (s_cur_pro_cpu - s_pre_pro_cpu)) / (s_cur_sys_cpu - s_pre_sys_cpu);}s_pre_pro_cpu = s_cur_pro_cpu;s_pre_sys_cpu = s_cur_sys_cpu;return ratio; }/************************************************************************************************** ** 函數名稱: get_mem_stat ** 功能描述: 獲取進程的內存使用情況 ** 輸入參數: 進程ID值 ** 輸出參數: 無 ** 返回參數: 此刻,該進程的物理內存占用量,單位KB **************************************************************************************************/ unsigned int get_mem_stat(unsigned int pid) {FILE *fd;int vmrss;char *valid, sbuff[32], tbuff[1024];sprintf(tbuff, "/proc/%d/status", pid); /* 在proc目錄下查找進程對應文件 */fd = fopen(tbuff, "r");assert(fd != NULL);while (1) { /* 對文件內容進行逐行搜索 */assert(fgets(tbuff, sizeof(tbuff), fd) != NULL); /* 文件讀取出錯 */valid = strstr(tbuff, "VmRSS"); /* 在該行內容中搜索關鍵詞 */if (valid != NULL) { /* 結果非空則表示搜索成功 */break;}}sscanf(tbuff, "%s %d", sbuff, &vmrss); /* 將該行內容拆成符合需要的格式 */fclose(fd);return vmrss; }/************************************************************************************************** ** 函數名稱: main ** 功能描述: 主函數 ** 輸入參數: 需要監測的進程名稱,刷新的頻率,以及是否要輸出到日志文件 ** 輸出參數: 無 ** 返回參數: 無 ** 使用方法: 輸入"monitor_pc cheese 1",表示以1s的頻率監控cheese進程的CPU及物理內存使用情況 ** 使用方法: 如果需要將監控數據同步輸出到日志文件,則輸入"monitor_pc cheese 1 logon"即可 **************************************************************************************************/ int main(int argc, char *argv[]) {unsigned int pid, rtime;char *name_pos, *proc_name, sstr[128];if (argc < 3) {printf("invalid cmd!!! should specify \"proc_name\" and \"refresh_time\"\n");return 1;}proc_name = argv[0]; /* 獲取進程名稱 */name_pos = strrchr(proc_name, '/');if (NULL != name_pos){ /* 提取出路徑最后部分的文件名 */proc_name = name_pos + 1;}printf("proc \"%s\" starting...\n", proc_name);rtime = atoi(argv[2]); /* 獲取刷新時間 */if ((rtime == 0) || (rtime > 60)) {printf("invalid refresh_time(%d)!!! valid range: 0-60\n", rtime);return 1;}s_needlogfile = 0; /* 默認不需要啟用日志輸出功能 */if ((argc == 4) && (strcmp(argv[3], LOG_ENABLE) == 0)) { /* 判斷用戶輸入的命令是否符合要求 */generate_rfile_path(argv[1]); /* 生成日志文件的路徑信息 */s_needlogfile = 1;}while (1) {pid = find_pid_by_name(argv[1]);if (pid == 0) {sprintf(sstr, "proc \"%s\" not found...\n", argv[1]);} else {sprintf(sstr, "cpu %.2f%%, mem %d KB\n", get_cpu_stat(pid), get_mem_stat(pid));}record_loginfo(sstr);sleep(rtime);}return 0; }

總結

以上是生活随笔為你收集整理的linux监测指定进程的CPU及物理内存消耗情况(c程序)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。