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\n", 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\n",

                        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\n",

                        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\n", 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...\n";

  write (1, str, strlen(str));

  done = 1;

}

 

void child(void)

{

  int i;

  for (i = 0; i < 3; i++){

    kill(getppid(), SIGRTMIN);

    printf("child - BANG!\n");

  }

  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 \n");

  

  int r = sleep(30);

  

  printf("woke up! r=%d\n", r);

  

  sigprocmask(SIG_SETMASK, &oldset, NULL);

  

  while (!done){

  };

  

  printf("exiting\n");

  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

我們看到主動切換和被動切換有了明顯的變化.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章