zoukankan      html  css  js  c++  java
  • 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 <stdio.h>

    #include <string.h>

    #include <stdlib.h>

    #include <unistd.h>

    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 <stdio.h>

    #include <sys/mman.h>

    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 <stdio.h>

    #include <string.h>

    #include <stdlib.h>

    #include <unistd.h>

    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 <stdio.h>

    #include <string.h>

    #include <stdlib.h>

    #include <signal.h>

    #include <unistd.h>

    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

    我们看到主动切换和被动切换有了明显的变化.

    来源:http://www.cnblogs.com/eustoma/archive/2012/04/27/2473175.html

  • 相关阅读:
    POJ 3268 Silver Cow Party (Dijkstra)
    怒学三算法 POJ 2387 Til the Cows Come Home (Bellman_Ford || Dijkstra || SPFA)
    CF Amr and Music (贪心)
    CF Amr and Pins (数学)
    POJ 3253 Fence Repair (贪心)
    POJ 3069 Saruman's Army(贪心)
    POJ 3617 Best Cow Line (贪心)
    CF Anya and Ghosts (贪心)
    CF Fox And Names (拓扑排序)
    mysql8.0的新特性
  • 原文地址:https://www.cnblogs.com/tibetanmastiff/p/3974481.html
Copyright © 2011-2022 走看看