1200字范文,内容丰富有趣,写作的好帮手!
1200字范文 > linux监测指定进程的CPU及物理内存消耗情况(c程序)

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

时间:2020-05-05 04:35:27

相关推荐

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

近日,由于工作要求,研究了一下如何在linux系统下对某个指定的单个进程进行监测,分析其CPU及物理内存的使用情况,并基于c语言写了一个独立的模块,完整的实现上述功能。现将整个模块的代码贴上,以便日后借鉴,并与同道中人分享。源码在ubuntu 16.04、嵌入式主板等多个系统下运行亲测正常,各类注释都比较齐全,对模块的使用方法也有介绍,此处就不再赘述,若有问题,自行阅读源码即可。

注:另有一份代码是基于bash脚本的,同样实现上述的功能,贴于另一篇博客,如有需要,可点击此处跳转。

/****************************************************************************************************** 文件名称: res_monitor.c** 文件描述: 指定进程的[cpu/内存]资源使用情况监测** ===============================================================================================** 创建信息: | -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;charfiname[READ_BUF_SIZE];chartmpbuf[READ_BUF_SIZE];charpcname[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;}

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。