这里我们主要介绍进程的状态,进程的状态可以通过/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\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 <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\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 <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\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 <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...\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

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

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

Linux下进程信息的深入分析[转]的更多相关文章

  1. Linux下进程信息的深入分析

    这里我们主要介绍进程的状态,进程的状态可以通过/proc/PID/status来查看,也可以通过/proc/PID/stat来查看. 如果说到工具大家用的最多的ps也可以看到进程的信息.这里我们通过/ ...

  2. Linux下进程信息/proc/pid/status的深入分析

    https://blog.csdn.net/beckdon/article/details/48491909

  3. Linux下进程隐藏的方法及其对抗

    零.背景 在应急响应中,经常碰到ps命令和top命令查不到恶意进程(异常进程)的情况,会对应急响应造成很大的影响.轻则浪费时间,重则排查不出问题,让黑客逍遥法外.所以这篇博客研究学习如何对抗linux ...

  4. 【网络编程基础】Linux下进程通信方式(共享内存,管道,消息队列,Socket)

    在网络课程中,有讲到Socket编程,对于tcp讲解的环节,为了加深理解,自己写了Linux下进程Socket通信,在学习的过程中,又接触到了其它的几种方式.记录一下. 管道通信(匿名,有名) 管道通 ...

  5. 【Linux】Linux下进程间的通信方式

    本文内容: 1.进程通信的目的 2.介绍Linux下进程间的4种通信方式:管道,消息队列,共享内存,信号量 ps:套接字也可以用于进程间的通信,不过是不同物理机器上的进程通信,本章讨论是是同一台物理机 ...

  6. linux 下进程通讯详解

    linux 下进程通讯方法主要有以下六种: 1.管道 2.信号 3.共享内存 4.消息队列 5.信号量 6.socket

  7. 【Linux下进程机制】从一道面试题谈linux下fork的运行机制

    今天一位朋友去一个不错的外企面试linux开发职位,面试官出了一个如下的题目: 给出如下C程序,在linux下使用gcc编译: #include "stdio.h" #includ ...

  8. Linux下进程的建立

    Linux下进程的建立 我们都知道,进程就是正在执行的程序.而在Linux中,可以使用一个进程来创建另外一个进程.这样的话,Linux的进程的组织结构其实有点像Linux目录树,是个层次结构的,可以使 ...

  9. Linux下进程通信的八种方法

    Linux下进程通信的八种方法:管道(pipe),命名管道(FIFO),内存映射(mapped memeory),消息队列(message queue),共享内存(shared memory),信号量 ...

随机推荐

  1. c++中赋值运算符重载为什么要用引用做返回值?

    class string{ public: string(const char *str=NULL); string(const string& str);     //copy构造函数的参数 ...

  2. 使用summernote编辑器上传图片,重写onImageUpload

    JS部分:$('.summernote').summernote({ height: 200,//高度 tabsize: 2,//页面上的summernote编辑框的个数 lang: 'zh-CN', ...

  3. Sean McGinnis

    * Loaded log from Wed Nov 25 22:19:43 2015 * Now talking on #openstack-smaug* [smcginnis] (~smcginni ...

  4. MATLAB特殊矩阵以及矩阵转置

    特殊矩阵 通用特殊矩阵 zeros函数:产生全0矩阵,即零矩阵. ones函数:产生....1矩阵,即幺矩阵. eye函数:产生对角线为1的矩阵,当矩阵是方正时,得到单位矩阵. rand函数:产生(0 ...

  5. JEECMS站群管理系统-- 首页的加载过程

    在浏览器中输入http://localhost:8080/jeecms,回车 首先进入配置文件web.xml, <context-param> <param-name>cont ...

  6. HDU 4612——Warm up——————【边双连通分量、树的直径】

    Warm up Time Limit:5000MS     Memory Limit:65535KB     64bit IO Format:%I64d & %I64u Submit Stat ...

  7. java学习第十二天

    1:Scanner的使用(了解) (1)在JDK5以后出现的用于键盘录入数据的类. (2)构造方法: A:讲解了System.in这个东西. 它其实是标准的输入流,对应于键盘录入 B:构造方法 Inp ...

  8. 【安全测试】sql注入

    SQL注入攻击是黑客对 数据库 进行攻击的常用手段之一,随着B/S模式应用开发的发展,使用这种模式编写应用程序的程序员越来越多,但是由于程序员水平及经验页参差不齐,相当大部分程序员在编写代码的时候没有 ...

  9. 设计模式在Spring

    设计模式在spring中的使用1.工厂模式,这个很明显,在各种BeanFactory以及ApplicationContext创建中都用到了:2.模版模式,这个也很明显,在各种BeanFactory以及 ...

  10. [LeetCode]14. Longest Common Prefix最长公共前缀

    Write a function to find the longest common prefix string amongst an array of strings. If there is n ...