這裏我們主要介紹進程的狀態,進程的狀態可以通過/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 我們看到主動切換和被動切換有了明顯的變化.
Tags: status Linux 信息
文章來源: