通过 /proc/[pid]/ 查看进程状态

来源:互联网 发布:剑网三怎么在淘宝买金 编辑:程序博客网 时间:2024/05/20 23:30

参考资料

[/proc官方手册] http://man7.org/linux/man-pages/man5/proc.5.html
[解读/proc/$PID/status中各种参数] https://my.oschina.net/aiguozhe/blog/125477
[查看Linux & Android中内存占用方法] http://blog.csdn.net/myarrow/article/details/7703296
[Measuring Memory Usage] http://locklessinc.com/articles/memory_usage/
[Linux下的/proc/[pid]目录下的文件分析] https://github.com/NanXiao/gnu-linux-proc-pid-intro
[Linux中查看进程占用内存的情况] http://hutaow.com/blog/2014/08/28/display-process-memory-in-linux/


/proc/[pid]/ 目录

在这篇文章中 http://man7.org/linux/man-pages/man5/proc.5.html 详细讲解了/proc 目录下各个文件的意义以及系统调用的方法。
当我们查看进程的时候 #ps –e#ps可以列举出当前进程ID以及名称。
ps 命令的使用可以参考:http://man7.org/linux/man-pages/man1/ps.1.html
其中PID为1的固定为init进程,2的为kthreadd进程。前面的几个进程基本上都是固定的。在 /proc 目录下会有与PID一致的目录名,目录里面的信息就包含该进程的所有信息。对 /proc/[pid]/ 目录的解释如下:

这里写图片描述

比如说 PID=1的init进程的信息存放在 /proc/1/ 的目录如下。
这里写图片描述


/proc/[pid]/status

这里写图片描述

/proc/[pid]/stat/proc/[pid]/statm 的内容以用户可以直观分析的格式打印出来。这里只列举了其中的几个参数的意义:

* Name: Command run by this process.* State: Current state of the process.  One of "R (running)","S (sleeping)", "D (disk sleep)", "T (stopped)", "T (tracingstop)", "Z (zombie)", or "X (dead)".* Pid: Thread ID (see gettid(2)).* VmPeak: Peak virtual memory size.* VmSize: Virtual memory size.* VmLck: Locked memory size (see mlock(3)).* VmPin: Pinned memory size (since Linux 3.2).  These arepages that can't be moved because something needs todirectly access physical memory.* VmHWM: Peak resident set size ("high water mark").* VmRSS: Resident set size.  Note that the value here is thesum of RssAnon, RssFile, and RssShmem.* RssAnon: Size of resident anonymous memory.  (since Linux4.5).* RssFile: Size of resident file mappings.  (since Linux 4.5).* RssShmem: Size of resident shared memory (includes System Vshared memory, mappings from tmpfs(5), and shared anonymousmappings).  (since Linux 4.5).* VmData, VmStk, VmExe: Size of data, stack, and textsegments.* VmLib: Shared library code size.* VmPTE: Page table entries size (since Linux 2.6.10).* VmPMD: Size of second-level page tables (since Linux 4.0).* VmSwap: Swapped-out virtual memory size by anonymous privatepages; shmem swap usage is not included (since Linux2.6.34).* Threads: Number of threads in process containing thisthread.
  • VmPeak: 表示进程所占用最大虚拟内存大小
  • VmSize: 表示进程当前虚拟内存大小
  • VmLck: 表示被锁定的内存大小
  • VmHWM: 表示进程所占用物理内存的峰值
  • VmRSS: 表示进程当前占用物理内存的大小(与procrank中的RSS)
  • VmData: 表示进程数据段的大小
  • VmStk: 表示进程堆栈段的大小
  • VmExe: 表示进程代码的大小
  • VmLib: 表示进程所使用共享库的大小
  • VmPTE: 表示进程页表项的大小

如果我们只想要获取其中某几个参数的值,可以使用命令:
#cat /proc/1/status | grep -E 'VmSize|VmRSS'

有这么一个需求:要统计各个进程中占用的物理内存的大小,我总不能一条条去执行cat命令吧,于是我们可以写一个应用程序循环读取每个进程的某个状态值。于是就有了下面的 rdstatus 应用程序


cat /proc/[pid]/stat

stat这个文件包含了很多信息,总共有52个参数。
这里写图片描述

这些内容在内核代码中:<kernel_dir>/fs/proc/array.c 中实现。


rdstatus 应用程序

rdstatus.c 只能用来获取 /proc/[pid]/status 中的内容。通过读取 /proc 目录下的所有文件,然后提取其中[pid]的目录,获取到 /proc/[pid]/status 的路径,再组合成 cmd 命令传递给system() 执行。

#if 1#include <stdio.h>#include <stdlib.h>#include <time.h>#include <unistd.h>#include <fcntl.h>#include <unistd.h>#include <sys/mman.h>#include <string.h>#include <sys/stat.h>#include <sys/types.h>#include <sys/wait.h>#include <errno.h>#include <unistd.h>#include <dirent.h>#define PROC_DIR_PATH "/proc"#define DIR_MAX  256#define DIR_NAME_MAX 40#define CMD_NAME_MAX 200#define RD_BUFFER_MAX 512#if 0 // 介绍目录操作http://songlee24.github.io/2014/09/20/linux-get-directory/在头文件<dirent.h>中定义了两种主要的数据类型。DIR:代表一个目录流的结构。struct __dirstream{    void *__fd;              /* 'struct hurd_fd' pointer for descriptor.*/    char *__data;            /* Directory block.  */    int __entry_data;        /* Entry number `__data' corresponds to.*/    char *__ptr;             /* Current pointer into the block.*/    int __entry_ptr;         /* Entry number `__ptr' corresponds to.*/    size_t __allocation;          /* Space allocated for the block.*/    size_t __size;                /* Total valid data in the block.*/    __libc_lock_define (, __lock) /* Mutex lock for this structure.*/};typedef struct __dirstream DIR;struct dirent:包含一个文件或目录信息的结构体。struct dirent{    long d_ino;                 /* inode number 索引节点号 */    off_t d_off;                /* offset to this dirent 在目录文件中的偏移 */    unsigned short d_reclen;    /* length of this d_name 文件名长 */    unsigned char d_type;       /* the type of d_name 文件类型 */        char d_name [NAME_MAX+1];   /* file name 文件名,最长255字符 */}DIR* opendir(const char* dirname);/* 打开一个目录:        成功 - 返回指向DIR类型对象的指针。        失败 - 返回NULL    */int closedir(DIR *dirp);/* 关闭目录流:        成功 - 返回0        失败 - 返回-1    */struct dirent *readdir(DIR *dirp);/* 读取目录流:        成功 - 返回指向struct dirent对象的指针。        失败 - 返回NULL(出错或流末尾)  */int readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result);/* 读取目录流:用 dirp 当前位置的目录初始化entry,并让 result 指向 entry。        成功 - 返回0        失败 - 返回error number   */void rewinddir(DIR *dirp);/* 重置目录流的位置到开头 */void seekdir(DIR *dirp, long int loc);/* 设置目录流的位置,设置以后readdir()会读取到loc位置的目录。 */long int telldir(DIR *dirp);/* 返回目录流的当前位置 */#endifint translat(char c){    if(c <= '9'&&c >= '0')        return 1;    else        return -1;}// 判断字符串是否是数字int isStringNum(char *str){    int length = strlen(str);    int i,n=0;    int cnt=0;    if(length == 0)        return 0;    for(i=0; i<length; i++) {        cnt += translat(str[i]);    }    if(cnt == length)        return 1;    else        return -1;}void ShowHelp(void){    printf("show this help:\n");    printf("  -h : show this help\n");    printf("  -a : cat all pid all message\n");    printf("  -c : cycle yes or not, 0 for not, 1 for yes\n");    printf("  -n : process name\n");    printf("  -p : pid number\n");    printf("  -r : pid rang, pid_min ~ pid_max\n");    printf("  -s : cat status, VmSize VmRSS VmPeak etc\n");    printf("\n");    // 命令控制只能采用以下的格式    printf("for example:\n");    printf("  ./rdstatus -h\n");        // flag = 1    printf("  ./rdstatus -c 1 -a\n");   // flag = 2    printf("  ./rdstatus -c 1 -n init -s VmSize\n");// flag = 3    printf("  ./rdstatus -c 1 -p 1 -s VmSize\n");       // flag = 4    printf("  ./rdstatus -c 1 -r 51 77 -s VmSize\n");   // flag = 5    printf("\n");    printf("argument number must 1 & 4 & 7 or more & 8 or more\n");}int GetRdFlag(int l_argc, char **l_argv){    int flag = 1;    if(l_argc == 2)        flag = 1;    else if(l_argc == 4)        flag = 2;    else if((l_argc >= 7) && (!strcmp(l_argv[3], "-n")))        flag = 3;    else if((l_argc >= 7) && (!strcmp(l_argv[3], "-p")))        flag = 4;    else if((l_argc >= 8) && (!strcmp(l_argv[3], "-r")))        flag = 5;    else        flag = 1;    return flag;}// 通过 process name 判断与当前 pid 是否匹配int isProcessMatchPid(char *pid, char *name){    char path[CMD_NAME_MAX];    int ret = 0;    int fd = 0;    char rd_buf[RD_BUFFER_MAX];    //printf("pid = %s, process_name = %s\n", pid, name);    sprintf(path, "/proc/%s/status", pid);    //printf("path = %s\n", path);    fd = open(path, O_RDWR);    if(fd < 0) {        printf("open %s error\n", path);        return -1;    }    ret = read(fd, rd_buf, 30);    //printf("rd_buf = %s\n", rd_buf);    if(strstr(rd_buf, name) != NULL){        //printf("Match process name ok\n");        close(fd);        return 1;    } else {        //printf("Match process name failure\n");        close(fd);        return -1;    }}void SetCmd(int l_argc, char **l_argv, char *path, char *cmd, char *name){    int j = 0;    strcpy(path, PROC_DIR_PATH);        // 调用 strcpy() 可重新定位到 status_path 的开头处    strcat(path, "/");    strcat(path, name);    strcat(path, "/");    strcat(path, "status");             // 只支持读取 /proc/[pid]/status 的内容    // 确定执行的命令    strcpy(cmd, "cat ");    strcat(cmd, path);    strcat(cmd, "| grep -E 'Name");     // 固定显示 Name 字段    strcat(cmd, "|");    for(j=6; j<l_argc; j++) {        strcat(cmd, l_argv[j]);         // 获取 -s 之后的内容        if(j != (l_argc-1))         // 最后不添加 '|'            strcat(cmd, "|");    }    strcat(cmd, "'");    //printf("cmd = %s\n", cmd);}int main(int argc, char **argv){    DIR *dp;    struct dirent *dirp;    int dir_num = 0;                        // /proc 目录下总的文件数    int i = 0;    int j = 0;    int ret = 0;    int pid_min = 0;                        // pid 进程最大值    int pid_max = 0;                        // pid 进程最大值    int pid = 0;    int temp_pid = 0;    int rd_flag = 0;                        // 读取的方式标记    int cycle_flag = 0;                 // 循环读取标记    char dir_name[DIR_MAX][DIR_NAME_MAX];   // 存放 /proc 目录下所有的文件名    char temp_name[DIR_NAME_MAX];    char process_name[DIR_NAME_MAX];        // 进程名称    char status_path[DIR_NAME_MAX];         // 保存 /proc/[pid]/status 的路径    char cmd[CMD_NAME_MAX];                 // 执行的命令    rd_flag = GetRdFlag(argc, argv);    if(rd_flag <= 1) {        ShowHelp();        return 0;    }    //printf("rd_flag = %d\n", rd_flag);    // 获取循环标记    cycle_flag = atoi(argv[2]);    //printf("cycle_flag = %d\n", cycle_flag);    if(cycle_flag < 0 || cycle_flag > 1) {        printf("cycle_flag error, please check it!\n");        return -1;    }    switch(rd_flag) {        case 3:            strcpy(process_name, argv[4]);            //printf("process = %s\n", process_name);            break;        case 4:            pid = atoi(argv[4]);            //printf("pid = %d\n", pid);            break;        case 5:            pid_min = atoi(argv[4]);            pid_max = atoi(argv[5]);            //printf("pid_min = %d, pid_max = %d\n", pid_min, pid_max);            if(pid_min > pid_max){                printf("pid range error, please check it\n");                return -1;            }            break;        default:            break;    }    if((dp = opendir(PROC_DIR_PATH)) == NULL)        printf("Can't open %s\n", PROC_DIR_PATH);    while((dirp = readdir(dp)) != NULL) {        strcpy(&dir_name[dir_num++][0], dirp->d_name);    }    //printf("dir_num = %d\n", dir_num);    do {        for(i=0; i<dir_num; i++) {            strcpy(temp_name, &dir_name[i][0]);            ret = isStringNum(temp_name);            if(ret == 1) {                              // 获取 /proc/[pid] 成功                temp_pid = atoi(temp_name);                //printf("temp_pid = %d\n", temp_pid);                if(rd_flag == 3) {                    ret = isProcessMatchPid(temp_name, argv[4]);                    if(ret > 0) {                        //printf("Process match pid ok\n");                        SetCmd(argc, argv, status_path, cmd, temp_name);                        system(cmd);                        printf("\n");                        break;                    }                }                else if(rd_flag == 4 && temp_pid == pid) {  // 通过 pid 来 cat 状态                    SetCmd(argc, argv, status_path, cmd, temp_name);                    system(cmd);                    printf("\n");                    break;                }else if(rd_flag == 5 && (temp_pid >= pid_min && temp_pid <= pid_max)) {    // 通过 pid 范围 cat 状态                    SetCmd(argc, argv, status_path, cmd, temp_name);                    system(cmd);                    printf("\n");                }            }        }        sleep(1);        printf("===================\n");    }while(cycle_flag == 1);    closedir(dp);    return 0;}#endif
原创粉丝点击