• Linux下进程信息的深入分析


    这里我们主要介绍进程的状态,进程的状态可以通过/proc/PID/status来查看,也可以通过/proc/PID/stat来查看.
    如果说到工具大家用的最多的ps也可以看到进程的信息.这里我们通过/proc/PID/status来分析进程的信息.
    在2.6.18之后的内核,多了capibilty/cpusets等信息.
     
    查看进程状态信息如下:
    more status 
    Name:   rsyslogd
    State:  S (sleeping)
    Tgid:   987
    Pid:    987
    PPid:   1
    TracerPid:      0
    Uid:    0       0       0       0
    Gid:    0       0       0       0
    Utrace: 0
    FDSize: 32
    Groups:
    VmPeak:    36528 kB
    VmSize:    36528 kB
    VmLck:         0 kB
    VmHWM:      1432 kB
    VmRSS:      1420 kB
    VmData:    33980 kB
    VmStk:        88 kB
    VmExe:       320 kB
    VmLib:      2044 kB
    VmPTE:        56 kB
    VmSwap:        0 kB
    Threads:        3
    SigQ:   1/7954
    SigPnd: 0000000000000000
    ShdPnd: 0000000000000000
    SigBlk: 0000000000000000
    SigIgn: 0000000001001206
    SigCgt: 0000000180014c21
    CapInh: 0000000000000000
    CapPrm: ffffffffffffffff
    CapEff: ffffffffffffffff
    CapBnd: ffffffffffffffff
    Cpus_allowed:   3
    Cpus_allowed_list:      0-1
    Mems_allowed:   1
    Mems_allowed_list:      0
    voluntary_ctxt_switches:        1
    nonvoluntary_ctxt_switches:     0
     
     
     
    Name:   rsyslogd
    解释:进程名
     
     
     
    State:  S (sleeping)
    解释:进程的状态我们前文已经做了很详细的分析,各进程的状态代表的意义如下:
    R (running)", "S (sleeping)", "D (disk sleep)", "T (stopped)", "T(tracing stop)", "Z (zombie)", or "X (dead)"
     
     
     
    Tgid:   987
    解释:Tgid是线程组的ID,一个线程一定属于一个线程组(进程组).
     
     
     
    Pid:    987
    解释:这个是进程的ID,更准确的说应该是线程的ID.
    例如:
    UID        PID  PPID   LWP  C NLWP STIME TTY          TIME CMD
    root       987     1   987  0    3 00:18 ?        00:00:00 /sbin/rsyslogd -c 4
    root       987     1   989  0    3 00:18 ?        00:00:00 /sbin/rsyslogd -c 4
    root       987     1   990  0    3 00:18 ?        00:00:00 /sbin/rsyslogd -c 4
    注:
    /proc/pid/status中的Pid就是ps命令的LWP列输出,PID一列其实是进程组,而LWP是轻量级进程,也就是线程,因为所有的进程必须一个线程,那就是它自己.
     
     
     
    PPid:   1
    解释:当前进程的父进程
     
     
     
    TracerPid:      0
    解释:跟踪当前进程的进程ID,如果是0,表示没有跟踪.
    例如:
    用strace跟踪top程序
    strace top
     
    查看top进程
    ps -axjf
    PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
     2491  2500  2500  2491 pts/2     2500 S+       0   0:00          \_ strace top
     2500  2501  2500  2491 pts/2     2500 S+       0   0:00              \_ top
     
    查看top进程的TracerPid位
    cat /proc/2501/stat
    stat    statm   status  
    test1:/proc/2431# cat /proc/2501/status 
    Name:   top
    State:  S (sleeping)
    Tgid:   2501
    Pid:    2501
    PPid:   2500
    TracerPid:      2500
     
     
     
    Uid:    0       0       0       0
    Gid:    0       0       0       0
    解释:
    第一列数字(RUID):实际用户ID,指的是进程执行者是谁.
    第二列数字(EUID):有效用户ID,指进程执行时对文件的访问权限.
    第三列数字(SUID):保存设置用户ID,作为effective user ID的副本,在执行exec调用时后能重新恢复原来的effectiv user ID.
    第四列数字(FSUID):目前进程的文件系统的用户识别码.一般情况下,文件系统的用户识别码(fsuid)与有效的用户识别码(euid)是相同的.
    这里重点说明RUID和EUID,我们用test用户启动top,如下:
    终端1)
    su - test
    top
     
    查看该进程的EUID和RUID,如下:
    终端2)
    cat /proc/`pgrep top|grep -v grep`/status
    前面略
    Uid:    1002    1002    1002    1002
    Gid:    1003    1003    1003    1003
    后面略
     
    注:这里我们看到进程的RUID和EUID都变成了1002.
     
    我们将程序top加上setuid权限,如下:
    chmod +s /usr/bin/top
     
    重新运行top程序,并查看它的RUID和EUID,如下:
    cat /proc/`pgrep top|grep -v grep`/status
    前面略
    Uid:    1002    0       0       0
    Gid:    1003    0       0       0
    后面略
     
    注:我们看到RUID还是1002,说明程序是由test用户(UID=1002)启动的,而程序设定了setuid,那么在程序运行时是用程序的owner权限来运行程序,而不是启动的用户权限.
    由于top的owner是root,那么它的EUID是0.
     
     
     
    FDSize: 32
    解释:
    FDSize是当前分配的文件描述符,这个值不是当前进程使用文件描述符的上限.
    我们看到这里是32,但实际并没有分配32个文件,如下:
    ls -l /proc/`pgrep rsyslogd|grep -v grep`/fd   
    total 0
    lrwx------ 1 root root 64 2011-04-20 20:03 0 -> socket:[5741]
    l-wx------ 1 root root 64 2011-04-20 20:03 1 -> /var/log/auth.log
    l-wx------ 1 root root 64 2011-04-20 20:03 10 -> /var/log/mail.err
    l-wx------ 1 root root 64 2011-04-20 20:03 11 -> /var/log/news/news.crit
    l-wx------ 1 root root 64 2011-04-20 20:03 12 -> /var/log/news/news.err
    l-wx------ 1 root root 64 2011-04-20 20:03 13 -> /var/log/news/news.notice
    l-wx------ 1 root root 64 2011-04-20 20:03 14 -> /var/log/debug
    l-wx------ 1 root root 64 2011-04-20 20:03 15 -> /var/log/messages
    lrwx------ 1 root root 64 2011-04-20 20:03 16 -> /dev/xconsole
    lr-x------ 1 root root 64 2011-04-20 20:03 17 -> /proc/kmsg
    l-wx------ 1 root root 64 2011-04-20 20:03 2 -> /var/log/syslog
    l-wx------ 1 root root 64 2011-04-20 20:03 3 -> /var/log/daemon.log
    l-wx------ 1 root root 64 2011-04-20 20:03 4 -> /var/log/kern.log
    l-wx------ 1 root root 64 2011-04-20 20:03 5 -> /var/log/lpr.log
    l-wx------ 1 root root 64 2011-04-20 20:03 6 -> /var/log/mail.log
    l-wx------ 1 root root 64 2011-04-20 20:03 7 -> /var/log/user.log
    l-wx------ 1 root root 64 2011-04-20 20:03 8 -> /var/log/mail.info
    l-wx------ 1 root root 64 2011-04-20 20:03 9 -> /var/log/mail.warn
    我们看到这里只用到了18个文件描述符.而如果超过32个文件描述符,将以32进行递增,如果是64位系统,将以64进行递增.
    FDSize这个值不会减少,如果我们程序打开了300个文件,并不会因为关闭文件,而减少FDSize这个值.
     
     
     
    Groups: 0
    解释:
    这里的groups表示启动这个进程的用户所在的组.
    我们当前的用户test,现在在两个组(1000,2000)里面,如下:
    id
    uid=1002(test) gid=1002(nagcmd) groups=1000(chenkuo),1002(nagcmd)
     
    用test用户启动top程序,并查看它的groups,如下:
    终端1
    top
     
    终端2
    cat /proc/`pgrep top|grep -v grep`/status
    截取信息如下:
    Groups: 1000 1002 
     
     
     
     
    VmPeak:    36528 kB
    解释:这里的VmPeak代表当前进程运行过程中占用内存的峰值.
    我们用下面的程序申请内存,然后释放内存,最后通pause()函数中止程序的运行,程序源码如下:
     
    #include
    #include
    #include
    #include
     
    int
    main (int argc, char *argv[])
    {
            if (argc != 2)
                    exit (0);
     
            size_t mb = strtoul(argv[1],NULL,0);
     
            size_t nbytes = mb * 0x100000;
            char *ptr = (char *) malloc(nbytes);
            if (ptr == NULL){
                    perror("malloc");
                    exit (EXIT_FAILURE);
            }
     
            printf("allocated %d mb ", mb);
            free(ptr);
            pause();
            return 0;
    }
     
    gcc callmem.c -o callmem
     
    ./callmem 10
    allocated 10 mb
     
    终端2
    我们打开status文件,查看VmPeak值,如下:
    cat /proc/`pgrep callmem|grep -v grep`/status
    Name:   callmem
    State:  S (sleeping)
    Tgid:   2930
    Pid:    2930
    PPid:   2831
    TracerPid:      0
    Uid:    1002    1002    1002    1002
    Gid:    1002    1002    1002    1002
    FDSize: 256
    Groups: 1000 1002 
    VmPeak:    11852 kB
    VmSize:     1608 kB
    VmLck:         0 kB
    VmHWM:       396 kB
    VmRSS:       396 kB
    VmData:       28 kB
    VmStk:        84 kB
    VmExe:         4 kB
    VmLib:      1468 kB
    VmPTE:        12 kB
    下面略
    注:我们看到程序申请了10240kb(10MB)的内存,VmPeak的值为11852kb,为什么不是10MB呢,因为除了我们申请的内存外,程序还会为加载动态链接库而占用内存.
     
     
     
    VmSize:    36528 kB
    解释:VmSize代表进程现在正在占用的内存
    这个值与pmap pid的值基本一致,如果略有不同,可能是内存裂缝所造成的.
     
     
     
    VmLck:         0 kB
    解释:VmLck代表进程已经锁住的物理内存的大小.锁住的物理内存不能交换到硬盘.
    我们用下面的程序进行测试,如下:
    #include
    #include
     
    int main(int argc, char* argv[])
    {
            char array[2048];
     
            if (mlock((const void *)array, sizeof(array)) == -1) {
                    perror("mlock: ");
                    return -1;
            }
     
            printf("success to lock stack mem at: %p, len=%zd ",
                            array, sizeof(array));
     
            sleep(60);
            if (munlock((const void *)array, sizeof(array)) == -1) {
                    perror("munlock: ");
                    return -1;
            }
     
            printf("success to unlock stack mem at: %p, len=%zd ",
                            array, sizeof(array));
     
            return 0;
    }
     
    编译后运行:
    gcc memlock.c -o memlock
     
    我们这里将2048个字节的数组地址空间锁定到了物理内存中.
    接下来我们看下Vmlck值的变化,如下:
    cat /proc/`pgrep memlock|grep -v grep`/status
    Name:   memlock
    State:  S (sleeping)
    Tgid:   3249
    Pid:    3249
    PPid:   3139
    TracerPid:      0
    Uid:    0       0       0       0
    Gid:    0       0       0       0
    FDSize: 256
    Groups: 0 
    VmPeak:     1624 kB
    VmSize:     1608 kB
    VmLck:         4 kB
    VmHWM:       356 kB
    VmRSS:       356 kB
    VmData:       28 kB
    VmStk:        84 kB
    VmExe:         4 kB
    VmLib:      1468 kB
    VmPTE:        16 kB
     
    我们看到Vmlck的值为4Kb,这是因为分配的最少单位是4KB,以后每次递增都是4KB的整数倍.
     
     
     
     
    VmHWM:      1432 kB
    VmRSS:      1420 kB
    解释:
    VmHWM是程序得到分配到物理内存的峰值.
    VmRSS是程序现在使用的物理内存.
    我们用下面的程序进行测试,如下:
    #include
    #include
    #include
    #include
     
    int
    main (int argc, char *argv[])
    {
            if (argc != 2)
                    exit (0);
     
            size_t mb = strtoul(argv[1],NULL,0);
     
            size_t nbytes = mb * 0x100000;
            char *ptr = (char *) malloc(nbytes);
            if (ptr == NULL){
                    perror("malloc");
                    exit (EXIT_FAILURE);
            }
     
            size_t i;
            const size_t stride = sysconf(_SC_PAGE_SIZE);
            for (i = 0;i < nbytes; i+= stride) {
                    ptr[i] = 0;
            }
     
            printf("allocated %d mb ", mb);
            pause();
            return 0;
    }
    编译:
    gcc callmem.c -o test
    注意这个程序在每页都修改一个字节的数据,导致系统必须为它分配占用物理内存.
     
    首先我们查看当前的内存,如下:
    free -m
                 total       used       free     shared    buffers     cached
    Mem:           503         18        484          0          0          5
    -/+ buffers/cache:         12        490
    Swap:         7632          7       7624
     
    我们看到当前有490MB的空闲物理内存.
    运行callmem分配450MB的物理内存,如下:
    ./test 450&
    [1] 2402
    allocated 450 mb
     
    我们查看进程的VmHWM和VmRSS,如下:
    cat /proc/`pgrep test`/status
    VmHWM:    461208 kB
    VmRSS:    461208 kB
    我们看到此时VmHWM和VmRSS是一样的,表示占用了460MB左右的物理内存(因为它会用到动态链接库等).
     
    下面我们查看当前的内存使用情况,如下:
    free -m
                 total       used       free     shared    buffers     cached
    Mem:           503        470         33          0          0          6
    -/+ buffers/cache:        463         40
    Swap:         7632          7       7625
     
    我们看到还有40MB空闲物理内存.
    我们下面再申请100MB的内存,此时系统会通过物理内存和SWAP的置换操作,把第1次运行的test进程所占用的物理内存置换到SWAP,把空出来的物理内存分配给第2次运行的程序,如下:
    mv test test1
    ./test1 100&
    [1] 2419
    allocated 100 mb
     
    再次查看test进程所占用的物理内存,如下:
    cat /proc/`pgrep test`/status
    VmHWM:    461208 kB
    VmRSS:    386704 kB
     
    最后我们看到VmHWM没有变化,因为它表示的是该进程所占用物理内存的峰值,不会因为把内存置换到SWAP,而做改变.
    而VmRSS则由461208KB变成了386704KB,说明它占用的物理内存因为置换所以减少.
     
     
     
    VmData:    33980 kB
    VmStk:        88 kB
    VmExe:       320 kB
    VmLib:      2044 kB
     
    解释:
    VmData:表示进程数据段的大小.
    VmStk:表示进程堆栈段的大小.
    VmExe:表示进程代码的大小.
    VmLib:表示进程所使用LIB库的大小.
     
    关于代码段,堆栈段,数据段:
    代码段可以为机器中运行同一程序的数个进程共享
    堆栈段存放的是子程序(函数)的返回地址、子程序的参数及程序的局部变量
    数据段则存放程序的全局变量、常数以及动态数据分配的数据空间(比如用malloc函数申请的内存)
    与代码段不同,如果系统中同时运行多个相同的程序,它们不能使用同一堆栈段和数据段.
     
    注意:
    堆栈段代表的是程序中的堆区(stack),堆区一般是编译器自动分配释放的.
    我们用malloc申请的内存,它占用的其实是栈区(heap),栈区一般是程序员自已分配释放的,而栈区在这里属于数据段,所以我们看到上面测试程序通过调用malloc函数后,VmData一值有了很大的变化.
     
     
     
    VmPTE:        56 kB
    VmSwap:        0 kB
     
    VmPTE:        56 kB
    解释:
    占用的页表的大小.
     
    VmSwap: 0 kB
    解释:
    进程占用Swap的大小.
     
     
     
    Threads:        3
    解释:
    表示当前进程组有3个线程.
     
     
     
     
    SigQ:   1/7954
    解释:
    表示当前待处理信号的个数,我们用下面和程序进行测试,如下:
    #include
    #include
    #include
    #include
    #include
     
    volatile int done = 0;
     
    void handler (int sig)
    {
      const char *str = "handled... ";
      write (1, str, strlen(str));
      done = 1;
    }
     
    void child(void)
    {
      int i;
      for (i = 0; i < 3; i++){
        kill(getppid(), SIGRTMIN);
        printf("child - BANG! ");
      }
      exit (0);
    }
     
    int main (int argc, char *argv[])
    {
      signal (SIGRTMIN, handler);
      sigset_t newset, oldset;
      
      sigfillset(&newset);
      sigprocmask(SIG_BLOCK, &newset, &oldset);
      
      pid_t pid = fork();
      if (pid == 0)
      child();
      
      printf("parent sleeping ");
      
      int r = sleep(30);
      
      printf("woke up! r=%d ", r);
      
      sigprocmask(SIG_SETMASK, &oldset, NULL);
      
      while (!done){
      };
      
      printf("exiting ");
      exit(0);
    }
     
    编译:
    gcc sig.c -o sig
    本程序会发达三次信号,此后进入sleep,我们可以在这期间来查看待处理信号的个数,如下:
    ./sig
    parent sleeping 
    child - BANG!
    child - BANG!
    child - BANG!
    woke up! r=0
    handled...
    handled...
    handled...
    exiting
     
    cat /proc/`pgrep sig`/status
    SigQ:   4/4294967295
     
    我们发送了三次信号,这里为什么是4呢,因为我们用了fork派生了子进程,子进程结束后会发送SIGCHLD信号.所以这里有4个信号待处理.
     
     
     
    SigPnd: 0000000000000000
    ShdPnd: 0000000000000000
    SigBlk: 0000000000000000
    SigIgn: 0000000001001206
    SigCgt: 0000000180014c21
    解释:
    SigPnd:屏蔽位,存储了该线程的待处理信号,等同于线程的PENDING信号.
    ShnPnd:屏蔽位,存储了该线程组的待处理信号.等同于进程组的PENDING信号.
    SigBlk:存放被阻塞的信号,等同于BLOCKED信号.
    SigIgn:存放被忽略的信号,等同于IGNORED信号.
    SigCgt:存放捕获的信号,等同于CAUGHT信号.
     
     
     
    CapInh: 0000000000000000
    CapPrm: ffffffffffffffff
    CapEff: ffffffffffffffff
    CapBnd: ffffffffffffffff
    解释:
    CapEff:当一个进程要进行某个特权操作时,操作系统会检查cap_effective的对应位是否有效,而不再是检查进程的有效UID是否为0.
    CapPrm:表示进程能够使用的能力,在cap_permitted中可以包含cap_effective中没有的能力,这些能力是被进程自己临时放弃的,也可以说cap_effective是cap_permitted的一个子集.
    CapInh:表示能够被当前进程执行的程序继承的能力.
    CapBnd:是系统的边界能力,我们无法改变它.
     
     
     
    Cpus_allowed:   3
    Cpus_allowed_list:      0-1
    解释:
    Cpus_allowed:3指出该进程可以使用CPU的亲和性掩码,因为我们指定为两块CPU,所以这里就是3,如果该进程指定为4个CPU(如果有话),这里就是F(1111).
    Cpus_allowed_list:0-1指出该进程可以使用CPU的列表,这里是0-1.
    Mems_allowed:   1
    Mems_allowed_list:      0
    内存同CPU一样,进程rsyslogd只是使用了结点0的内存资源.
     
    我们这里调整该进程到CPU0,如下:
    taskset -p 1 987
    pid 987's current affinity mask: 3
    pid 987's new affinity mask: 1
     
    cat /proc/987/status
    Cpus_allowed:   1
    Cpus_allowed_list:      0
    Mems_allowed:   1
    Mems_allowed_list:      0
    注:我们看到Cpus_allowed/Cpus_allowed_list较之前有了变化.Cpus_allowed由3变成了1.表明我们只会用CPU0.
     
     
     
     
    voluntary_ctxt_switches:        1
    nonvoluntary_ctxt_switches:     0
     
    voluntary_ctxt_switches表示进程主动切换的次数.
    nonvoluntary_ctxt_switches表示进程被动切换的次数.
     
    首先查看一下当前进程,如下:
    echo $$
    1544
     
    执行如下命令:
    while ((1)); do echo 1; sleep 1; done
     
    查看该进程的主动切换与被动切换,如下:
    cat status
    voluntary_ctxt_switches:        949
    nonvoluntary_ctxt_switches:     55
    我们看到主动切换和被动切换有了明显的变化.
     
  • 相关阅读:
    VMware虚拟机找不到USB设备该怎么办?
    关于Ubuntu锁屏后,无法输入密码
    设计模式-第一篇之单例模式
    Java-多线程第一篇多线程相关认识(1)
    Quartz-第二篇 使用quartz框架定时推送邮件
    Quartz-第一篇 认识Quartz
    Web Service-第一篇什么是Web Service
    Linux-第二篇常用命令
    Linux-第一篇linux基本认识
    Oracle-常见的错误
  • 原文地址:https://www.cnblogs.com/zl1991/p/6724341.html
Copyright © 2020-2023  润新知