一、程序员的疑惑
大概在十多年前,我当时还是一个产品经理。由于一些工作的原因,需要向运维工程师学习一些linux常用命令。当使用linux ps这个十分常用的命令时,遇到了一个小小的疑惑。有些工程师推荐使用ps aux的命令组合,有些工程师推荐使用ps -aux的命令组合,从输出结果上来看似乎也没有什么不同。考虑到如常用的ls -l命令在内,很多linux命令选项前都要加上一个短横线,这么来看似乎ps -axu是正确的。但是一些早期的linux版本,在执行ps -axu时又会报出如下错误Warning: bad syntax,而ps aux却没有这样的报错信息,这么看来似乎ps aux又是正确的。查阅市面上的一些linux书籍,在介绍linux ps命令示例时,有些说用ps aux,而有些又说用ps -axu。实在是让我这个初学者摸不着头脑。
再后来,随着我加入运维团队对linux ps命令逐步深入使用,学会了查看man page和help,发现其中有ps命令的exmaple,果然是ps aux的用法是正确的。不过随着对linux ps命令使用的逐步深入,对ps命令的其他疑问越来越多。比如,我们天天在使用ps命令时输出的%CPU列到底是什么含义?为什么和top显示的%CPU的值有时候差异非常大?再比如,当我使用ps -el命令时,为什么我的进程名是systemd-journald,而ps命令只显示systemd-journal,弄丢了最后1个字母d。
带着这么多疑惑,3年多前我有机会简单的学习了一下linux ps源代码,逐步解开了上面这些疑惑。后来发现公司的很多同事也对ps命令有各种问题和疑问,于是想把之前对ps命令的学习经验总结成文档,供大家参考。希望能给大家带来帮助。
二、Linux ps命令选项解析
Linux ps命令功能很强大,了解ps命令首先需要从ps命令的选项格式入手。像其他很多linux shell命令一样,ps命令的选项也有长格式和短格式的区别。短选项中也可以带中横线、也可以不带中横线。
根据选项长短和是否有横线的情况,ps命令的选项可以分为以下3类:
- BSD风格语法,必须不能以中横线开头;
- SYSV风格语法,必须仅一个中横线开头;
- GNU风格语法,必须以两个中横线开头;
不过linux ps命令的长选项并不多,而且几乎每个长选项都有一个功能完全相同的短选项对应。在centos7环境运行如下命令可以见。
在本文中我们将主要介绍BSD和SYSV两种风格的ps命令选项。如果大家有对GNU风格的长选项使用的需求,那么可以参考对应的短选项语法即可。需要注意的是GNU风格选项都是带参数值的,例如--sid 1。
各风格的ps命令选项可以混合使用,比如:
Linux ps命令解析SYSV和BSD风格选项时,会分别将每组字符串都解析成单独的字母。以下三个实例,拆分前后的命令都是等价的。
从示例中可以看出,当SYSV风格语法一个中横线之后有多个字母选项时,拆分后需要给每一个字母前都加上一个中横线。也就是说-elL转换为-e -l -L,而不是转换为-e l L。
从上面例子中也可以看出,ps命令选项除了有是否加中横线的区别,字母大小写也表现为不同的选项含义。英文字母一共26个,SYSV风格选项-A到-Z和-a到-z共52个,BSD风格选项A到Z和a到z共52个。于是ps命令就有一共104个命令选项可能性。
不同版本的ps命令选项的使用可能略有出入,本文主要使用主流的centos7上的procps-ng version 3.3.10版本来说明。在这104个命令选项中,未启用的或曾经使用过现在废弃的命令选项有如下40个,分别是A、B、C、D、E、F、G、I、J、K、P、Q、R、W、Y、b、d、i、y、z、-B、-D、-E、-I、-J、-K、-Q、-R、-S、-W、-X、-Y、-b、-h、-i、-k、-r、-v、-x和-z。
既然是未启用或已废弃,那么运行带这个选项的ps命令应该会报错。试运行下大部分确实如此,不过也有几个例外,比如ps -x命令就不报错,可以正常输出。这样的例外选项一共有如下7个-S、-X、-h、-k、-r、-v和-x,本文第九节会给予说明。
这104个命令选项中的其余64个选项就构成了linux ps命令的庞大命令选项体系,接下的内容就对他们分别给予介绍。
要查看我们当前ps命令的版本,就用到V、-V这2个选项。
为了本文中意思表达更加准确,这里借用数据库中的几个概念。ps命令输出结果,约定为结果集。结果集中的每一行,我们约定为记录(record)。结果集或记录中的每一列,我们约定为字段(field)。
三、Linux ps命令的记录类选项
差不多每一个工程师使用ps命令时应该都有这样的疑问, 使用ps aux时输出结果中记录行数要远大于只使用ps命令时(如下所示)。这其实会让很多工程师在使用ps命令查找需要的进程时心里很忐忑,会不会由于命令的选项使用不当导致ps没有列出所需要的进程信息。正是这个原因,我们首先需要搞懂ps命令影响记录行数的那些选项。
3.1、all_processes选项
Linux ps命令的记录类选项大概有20几个之多。有些可以列出所有的进程信息,有些按某种规则筛选显示部分进程信息。如今操作系统中awk、sed和grep这些shell文本处理命令的功能都十分强大,我们重点还是掌握ps命令中那些显示所有进程信息记录的选项,其他ps命令过滤选项都可以通过shell文本处理命令(awk、sed和grep)间接实现。
Linux ps命令显示所有进程信息的选项只有2个,即SYSV风格的-e和-A。相比之下,-e更容易记忆和书写,请大家牢记这个-e选项。
大家知道,ps命令的所有信息都是linux kernel生成,并通过/proc/目录输出给用户空间的。在/proc/目录下,每一个以数字开头的目录,就对应一个进程信息。既然如此,通过如下命令便可一目了然。
参数-e和-A显示的进程记录数确实和proc目录下的所有进程目录数一致。
3.2、simple_select选项
Linux ps命令的simple_select选项一共5个,具体包括-a、-d、a、g和x。他们包括2个SYSV风格和3个BSD风格选项。2个SYSV风格和3个BSD风格的选项不能同时使用,否则会报错。2个SYSV风格或3个BSD风格内部可以组合使用,具体的组合可能性有-ad、ga、ax、gx、agx。这里值得注意的是这种字母组合选项绝对不是单字母选项筛选规则的简单组合,ps命令给这几种组合赋予了新的筛选规则。
Linux环境下的ps命令,会对BSD风格simple_select选项部分做2个特殊处理:
在原来的BSD风格simple_select情况下,再额外增加一个g选项;
如果已经有a、g和x三个选项都出现了,那么就直接替换为-e选项;
按照这2个特殊处理规则,ps aux选项组合等价于ps auxg,等价于ps agx u,等价于ps -e u。
总结下来,ps命令simple_select选项只有6种组合情况-a、-d、-ad、g、ga、gx。每一种选项都赋予一个位图值。ps命令通过位图计算来实现它的筛选规则。比如g选项的位图值select_bits是0x0a0a,下面以g选项为例说明。
关于8、4、2、1的含义(关于tty、session、tgid和euid字段含义参考第七节):
“8” tty值等于当前进程tty值的进程;
“4” tty值为空的进程;
“2” session值等于当前进程tgid(pid)值的进程;
“1” euid值等于当前进程euid值的进程;
很明显“4”这位都缺失,1这位都存在,那么g选项的含义就是:显示所有tty值存在的且euid值等于当前进程euid值的进程。对此持怀疑态度的同学可以通过如下2个命令进行验证。
同样的分析方法,其他几个选项和选项组合的含义:
- 选项-a含义:显示所有tty值存在的且session值不等于当前进程tgid(pid)值的进程;
- 选项-d含义:显示所有session值不等于当前进程tgid(pid)值的进程;
- 选项组合-ad含义:显示所有tty值存在的或session值不等于当前进程tgid(pid)值的进程。换句话说,被过滤掉的是所有tty值为空的且session值等于当前进程tgid(pid)值的进程;
- 选项组合ga(或选项a)含义:显示所有tty值存在的进程;
- 选项组合gx(或选项x)含义:显示所有euid值等于当前进程euid值的进程;
3.3、selection_list选项
这类选项比较容易理解,都是根据进程的某个属性值对进程进行筛选。他们大多需要一个选项的参数,而且也都有功能完全一样的GNU风格的长选项对应。此类选项一共13个,主要分为如下几组:
进程ID选项,查询PID值为一个或几个PID值范围的进程信息。
进程会话(session)ID选项,有关SessionID可以参见8.1小节。
用户ID选项,参见8.7小节。
用户组ID选项,参见8.7小节。
进程名称选项,显示符合当前进程名称参数的进程。这里需要注意,当进程名参数值字符串长度大于15时,只是用其前15位作为匹配条件,参见8.2小节
进程终端(tty)选项
这些选项不但可以单独使用,还可以组合使用(如下所示)。需要注意的是这些选项之间的组合是逻辑或的关系,即或者符合-u选项条件或者符合-p选项条件。
3.4、特殊选择选项
当不希望结果中出现标题页头这一行信息时,h选项可以隐藏ps输出结果中的标题栏。
如果我们只希望列出运行中的R状态和D状态的进程,r 选项选中时将只显示其他筛选条件过滤后的结果集中的R和D状态进程。帮助手册上写只筛选R状态是不正确的,这里也包括D状态进程的筛选。
有时候如果我们需要显示的进程记录不好筛选,但是他的补集却很容易筛选。那我们可以使用-N选项。选中此选项时,将以系统中所有进程(ps -e结果)为全集对前述条件筛选后的结果取补集,即只显示原来不显示的记录。下面的例子中,去掉标题栏一行信息后,472等于456加16,说明全集等于本集加补集(如下示例所示)。
3.5、记录类选项的作用顺序
本节前几部分全面的介绍了记录筛选类选项,本小节将对这些选项的综合作用顺序进行一个系统介绍。
记录类选项是对/proc/目录下的进程信息逐条筛选过滤的:
- 首先判断是否有all_processes选项。如果有all_processes选项,则本条记录被选择。如果没有all_processes选项,则继续下一规则。
- 其次判断是否有simple_select选项。如果有simple_select选项,则使用simple_select选项规则判断本条记录是否被选中。如果没有被simple_select选项选择,则继续下一条规则。
- 然后判断是否有selection_list选项。如果有selection_list选项,则使用selection_list选项判断本条记录是否被选中。如果没有selection_list选项,则继续下一条规则。
- 然后判断选项中是否有BSD风格的选项。如果有BSD风格选项,则使用simple_select类的选项g规则判断本条记录是否被选中。如果没有BSD风格选项,则进入下一条规则。
- 此时不论ps命令有SYSV风格选项还是仅无选项ps命令,都会使用一个新的simple_select选项位图进行筛选过滤,位图值为0xaa00。如果还没有被选中,则彻底失去被选中的机会。位图值0xaa00的意义是:所有tty值等于当前进程tty的且euid值等于当前进程euid值的进程。
- 接下来再看是否有r选项。如果有r选项则以上5个环节被选中的记录中,只有R和D状态的记录才能被继续选中。如果没有r选项,则以上5个环节中被选中的记录,都继续被选中。
- 最后看是否有-N选项,如果有-N选项则以上第6个环节选中的将不被选中,第6个环节未选中的将被选中。如果没有-N选项,则以上第6个环节中被选中的记录,还继续被选中。
- 选项h单独生效,如果有h选项则取消结果集的标题栏,如果没有h选项则标题栏保持输出。
四、Linux ps命令输出结果排序选项
上面一章介绍了记录类选项的使用,了解了如何筛选符合我们要求的记录集。如果我们需要对输出结果进行排序,那么ps命令也给我们提供了3个选项,分别是k、f和-H。
4.1、字段排序选项
选项k可以让我们以某个字段为条件对输出结果进行排序,并且还可以使用+-符号设置升序排序还是降序排序。
选项k还可以使用多个字段同时对结果集排序,从输出结果可以看到,先按ppid进行升序排序,ppid值相同时,再按rss值进行升序排序。
4.2、树形排序选项
每一个进程都有一个父进程,所有用户空间的进程的最终父进程都是1号进程,所有内核空间的线程的最终父线程都是2号线程。这样所有进程按照父子进程的关系就可以构成2个树形结构。选项f和-H就是实现这个树形排序功能的2个选项。
从上面的结果中不难看出,选项f是使用ACSII码对父子进程进行关联,选项-H是使用tab空格对父子进程进行关联。
五、Linux ps命令线程展开选项
前面章节主要是说明了如何筛选和显示进程的信息。同一个进程有时候还会起多个线程,同样内核也在/proc/目录下显示了进程的线程信息,如下所示。
Linux ps同样提供了一组选项可以将每个进程的线程信息详细展现,这组选项包括H、-L、-T、M、m和-m。在讲解这些选项之前,我们先看一个小测试。
同样为了统计的准确,用h选项去掉标题栏信息。其中最后一个486的值是ps -e h的记录数,说明当前系统有486个进程。非常巧的是486恰巧等于1217减去731的值。从这里我们可以了解到H、-L和-T这3个选项记录数都是731,M、m和-m三个选项记录数都是1217。找一个起了多线程的进程查看下具体输出内容。
选项-L的输出可以看到一共4行输出结果,第一行PID等于LWP(线程ID)的值,说明是线程组的主线程(即进程)。其余三个线程ID各不相同,但PID值都和主线程的PID值一样,说明是同一线程组的普通线程。
第二组三个选项单纯的显示不便识别,我们这里先引入一个后面讲解的O选项,额外增加一个输出值LWP。
可以看到一共5行输出结果,对照上面的输出我们可以判断出,第二组选项除了把线程组中的4个线程分别显示之外,又额外增加了一行内容专门用于显示这个线程组(即进程)的信息。我们再回头看前面的1271减去731等于486应该就很容易明白了。
在H、-L和-T之间,以及M、m和-m之间,输出信息也略有不同,不过这些都是数据项和格式的不同,后面有专门章节介绍。
六、Linux ps命令的字段选项
6.1、字段组合类通用选项
很多人在使用ps命令时都会注意到,在我们输入不同的命令组合时,ps命令输出结果中列的数据项并不统一。比如下面2个命令。
Linux ps命令的aux选项组合输出PID、%CPU、%MEM、RSS、TIME等数据项,ps命令的-el选项组合输出PID、PPID、WCHAN、TIME、CMD等数据项。首先一个问题就是,ps命令一共有多少数据项可以输出。这个问题很好回答,通过L选项很容易获取,一共有168个数据输出项。
其次的一个问题就是,是什么决定了ps aux命令输出结果中恰恰包含USER、PID、%CPU、%MEM、VSZ、RSS、TTY、STAT、START、TIME和COMMAND这11个数据项呢。原因是ps命令中有一些选项用来对数据字段进行固定组合的作用。其中aux中的u选项就固定包含了以上11个数据输出项,并且他们的显示顺序也已经固化在代码中。
Linux ps命令这种字段组合类选项一共15个。其中6个选项用途比较广泛,其余9个选项都主要适合在查询某一种问题时使用。
本小节先介绍6个通用选项:
面向用户角度来显示进程状况,其中的%CPU、%MEM、VSZ和RSS字段都是非常常用的信息。
采用详细格式显示进程状况,此类选项所显示字段主要为一些常用字段信息。
采用完整格式显示进程状况,此类选项所显示字段同样为一些常用字段信息。
6.2、字段组合类专用选项
本小节先介绍9个适合特殊用途的专用选项:
采用作业(job)控制的格式显示进程状况,字段PPID、PID、PGID、SID和TPGID都是此选项的关键信息。
采用旧式的linux i386寄存器格式显示进程状况,很明显此选项特点是STACKP、ESP和EIP这些寄存器信息。
Linux或sunos操作系统中会额外增加PSR字段的显示,PSR字段是指当前进程被调度到的CPU核序号。
采用程序信号的格式显示进程状况,此选项特色字段是PENDING、BLOCKED、IGNORED和CAUGHT字段。很显然,当我们进行linux信号编程时,使用此选项非常有用。
采用虚拟内存的角度显示进程状况,此选项特色字段包括MAJFL、TRS、DRS、RSS和%MEM。
打印操作系统强制访问控制(SELinux)的安全标签信息,此选项特点是LABEL字段信息。
也许有人已经观察到了,以上字段组合选项,不论哪个都会固定的有几个字段总是出现,比如PID、TTY、TIME和CMD等。下面请大家先看一下这几个例子。
从以上例子中,我们可以得出几个信息。参数为BSD风格时,默认都会显示PID、TTY、STAT、TIME和COMMAND这5个字段。参数为SYSV风格时,默认都会显示PID、TTY、TIME和CMD这4个字段。ps命令无参数时默认为SYSV风格。
6.3、自定义字段选项
上一小节字段组合选项是ps命令为了一些常用场景固化在代码中的固定数据项组合。但是如果以上所有组合都不满足我们的要求,或者我们为了提升ps命令运行效率仅仅需要个别的数据项输出。那么我们可以通过-o或o选项来实现自定义数据项的输出功能。比如我们对ps j这个命令字段组合的输出信息不满意,我们自定义他的输出。
前文提到过,ps命令一共可以输出168个字段,ps L命令可以显示这168个字段的详细情况。第一列小写字母是-o选项的参数,可以通过逗号隔开。第二列大写字母是ps命令输出的结果集标题栏名称。
尽管大部分情况下-o参数和标题栏都仅仅是大小写的转换关系,但也有不那么完美的时候,以进程的执行命令字段为例。
这个例子至少可以说明两点,不同的说明符(specifier,-o选项参数)可能输出同样的标题栏。尽管标题栏一样,但显示的内容可能是有区别的。
有些说明符还提供缩写,下表是ps命令有缩写的说明符和缩写的对应关系表,一共15个。
有了说明符的缩写之后,可以对自定义字段的输出字段之间添加自定义分隔符。区别于以往ps命令各输出字段都是使用空格作为分割,使用自定义分隔符之后将更方便使用shell数据处理命令进行解析。
前文提到所有字段组合选项都默认包含4个或5字段。如果想在自定义字段组合时也默认添加一些常用字段,而同时又省去-o选项参数的输入过程,那么可以使用O或-O选项。
这2个选项O或-O,会在自定义字段之前默认增加pid字段,在自定义字段之后默认增加state、tname、time和command字段。
七、Linux ps命令字段修饰选项
本节前面的选项都是决定输出结果中字段的数量和顺序,本小节将介绍几个只对输出结果中某个字段进行修饰的选项。首先来看-w和w选项。
这个实例说明,当屏幕不是很宽时,如果进程命令很长,默认情况下,会将命令超出屏幕的部分截取掉,这样势必会影响系统管理员调查问题,使用w或-w选项,就会将完整的进程命令信息显示,多出的部分换行显示。有的时候为了效果好一点,建议我们可以多使用几次w选项,比如ww、www或wwww。
接下来我们再来看一下c和e选项。
选项u的COMMAND字段,默认会输出进程路径和执行参数信息。从上面的例子可以看出,选项c可以使选项u的COMMAND字段更加精简,只保留进程名称。选项e可以使选项u的COMMAND字段更加丰富,增加进程环境变量的相关内容。
当选项S被选中,ps命令在显示如下6个字段信息时,会将已经死亡的子进程信息也包含计算在内,如果未选中S选项将不会计算这些已经死亡的子进程信息 。快速同时执行如下2个命令,即可看出这6个字段值,选中S值后比之前有明显增大。具体字段含义请参考8.5小节和8.6小节。
Linux ps命令的字段中有个wchan字段(wchan相关含义参考8.8小节)。默认情况下ps命令会输出wchan的符号信息,如果希望输出wchan的原始数值信息,可以使用n选项。请比较如下2个命令,添加n选项前后第11个字段的输出差别。除了wchan之外,选项n还可以将原本输出user name的地方转换为user id输出。
字段wchan的数值信息和符号信息的映射关系通过操作系统中一些System.map文件完成,如果用户需要使用自定义的System.map文件,可以通过选项N或-n完成,如下示例。
八、Linux ps命令常用字段
前文提到linux ps一共最多可以输出168个字段,通过ps L命令可获取详情。通过字段相关选项可以获取符合用途的字段组合。为了让大家对ps命令的理解更加深入,本节会深入介绍一些常用的输出字段的含义。
按照这些常用字段的内在关系,我们将分为以下八个小结介绍。
8.1、进程ID类字段
进程ID类字段是ps命令字段中最基础的一类。为了能更加形象的说明这些ID的关系和含义,请大家按照如下命令顺序操作。
对以上输出结果的字段逐条说明:
字段tid表示进程的线程ID,可以看出每个线程的tid都不相同。
字段nlwp表示当前线程组中的线程个数,以上命令都是单线程进程,因此此值均为1。
字段pid表示进程ID,也可以看出每个进程的PID都不相同。
字段pgid表示进程组ID,上面的例子中除了和setsid结合的vmstat命令,其余三组通过shell管道连接起来的命令的pgid都相同。比如tail、awk和nl命令的pgid都为1384,且pgid值为组内第一个命令tail的pid值;iostat、sed和fold命令的pgid都为1388,且pgid值为组内第一个命令iostat的pid值。
字段sid表示会话ID。上面的例子中最后一行是第三个登录终端的shell。第一个登录终端上的所有进程sid都相同,且为登录shell的pid值1351;除了和setsid结合的vmstat命令,第二个终端上的所有进程sid都相同,且为登录shell的pid值1394。
字段tpgid表示进程连接到的tty(终端)所在的前台进程组的ID。除了vmstat进程之外,第二个终端上的所有进程tpgid也都等于登录shell的pid值1394。但是第一个终端上的所有进程tpgid却都等于第一个终端上又启动的那个shell的进程id值1370。充分说明了tpgid值是链接着终端的前台进程组ID值。
字段ppid表示父进程ID。
最后我们再来解释和vmstat结合的setsid,setsid就是使和它结合的vmstat脱离原来的会话,脱离之后pgid和sid都等于了vmstat进程的pid,同时父进程也由1号进程托管。此时也没有了所依附的终端,tpgid统一等于-1。Linux上的所有守护进程的tpgid值都是-1。
进程ID类字段的别名情况:字段spid和字段lwp是字段tid的别名,字段tgid是字段pid的别名,字段pgrp是字段pgid的别名,字段sess和字段session是字段sid的别名。
8.2、命令名字段
命令名相关的字段一共有3组,如下所示。
命令名字段的别名情况:字段comm和字段ucomm是字段ucmd的别名,字段args和字段command是字段cmd的别名。
建议大家掌握ucmd和cmd这2个字段,cmd为长命令名字段,ucmd为短命令名字段,可以理解为unadorned cmd(未加修饰的命令名)。
前文提到过如果程序名称长度超过15位,ps命令的短命令名无法完整显示16位及以上的部分。下面看一个小例子来说明这个问题。
从上面的例子可以看出,当程序名称超过15位时,确实短命令名无法显示完整的程序名称,只显示了15位。进一步查看/proc/8040/目录,可以发现如下信息。
查询内核代码,可以发现comm值取自内核struct task_struct结构体的comm属性字段。
这就告诉我们通过ps命令短命令字段无论如何都无法输入超过15位的程序名称,原因是内核数据结构原生就只支持15个字符长度的程序名称。
除此之外上面的例子还给我们另外一个启示,如果通过使用SYSV风格的短命令名就可以满足使用要求(如ps -el),那就尽量不要使用BSD风格的长命令名(如ps -e u,即ps aux)。长命令名需要依赖内核中健康的文件系统,而当文件系统工作不正常时,往往短命令名却可以不受影响。所以我们在实际生产中偶尔会发现系统中有大量ps aux进程D住的情况。
8.3、进程状态字段
进程状态类字段一共有三个,分别是s、state和stat,如下所示。
字段s和state互为别名,值为单字节进程状态。这里重点介绍一下stat选项的多字节进程状态,查看一下ps命令关于这个多字节进程状态的c语言代码。
根据以上源代码,我们来逐条解释:
字符'<'表示nice值小于0,nice值最小为-20。进程特性nice值允许进程间接的影响内核调度算法。所谓nice就是指对其他进程的谦让程度,显然比0越小就越不谦让,比0越大就越谦让。顾名思义,越谦让的君子在CPU调度过程中占用CPU的时间会越少,反之不谦让的进程相比较可能占用更多的CPU时间。因此字符'<'表示此进程可能在调度过程中获得优势。
字符'N'表示nice值大于0,nice值最大为19。因此字符'N'表示此进程可能在调度过程中不能获得优势。
字符'L'表示进程vm_lock值为真,即此进程有内存页被锁在内存中,这些内存页不能通过换页换出。
字符's'表示进程的tgid(pid)值等于进程的session(sid)值,这说明当前进程是会话的leader,参考8.1小节。
字符'l'表示进程中的线程数量大于1,这说明当前进程是一个多线程程序。
字符'+'表示进程的pgrp(pgid)值等于进程的tpgid前台进程组ID,这表示当前进程在前台进程组中。
8.4、时钟(系统)时间类字段
时钟时间(系统时间)类字段,记录了进程开始时间点和执行的时长信息,这类字段一共有6组。其中4个记录进程开始时间点,2个记录进程执行时长信息,示例如下。
从自动化运维脚本的角度,lstart字段的输出信息格式更加规范便于解析,etimes字段作为一个正整数也可以直接使用。字段start_time是字段stime的别名。下面给一个使用的例子。
字段lstart的输出固定占用了2 2 6这5列信息,这样在其后的etimes字段也固定占用了第$7列。使用awk结合date命令很方便的就将进程开始时间转化为时间戳格式。
8.5、CPU时间和使用率字段
CPU时间和使用率类字段一共有5组,记录了进程所消耗的CPU时间片和CPU使用率信息,示例如下。
字段bsdtime的输出相比较cputime更加方便转换为正整数的秒数。字段cp的单位是千分比,不能超过999。字段c是百分比,不能超过99。
进程CPU时间类字段别名:字段atime和字段time是字段cputime的别名;字段util是字段c的别名;字段%cpu是字段pcpu的别名,但是%字符在crontab中使用并不友好,推荐使用pcpu。
下面来说明一下ps命令的CPU利用率的含义,先运行一个例子。
从这个命令运行的结果可以看出bsdtime除以etimes的值转换为百分比后,基本和pcpu的值相等。这就足以说明ps命令的CPU利用率字段指标是指从进程开始运行以来进程所耗费的CPU时间片占时钟时间的百分比。有时候这个值大于100%,那是因为进程启用了多线程,很多时候有多核在同时使用CPU时间片。
看过top命令源码可以知道,top命令默认是取最近3秒钟进程所耗费的CPU时间片除以3秒钟的百分比值。我们可以设想一种场景,如果一个进程已经运行了1年以上,平时都很稳定。但是刚刚就在十几分钟前突然运行大量线程,占用大量CPU资源。结果你在你刚刚登陆系统之前10秒钟这些运行的线程都结束了。那么你不论是通过top命令的CPU利用率指标,还是ps命令的CPU利用率指标都无法发现刚才作怪的这个线程的迹象。
8.6、进程内存相关字段
进程内存相关字段也ps命令字段中非常重要的一类,主要分为如下9组,示例如下。
对以上输出结果的字段逐条说明:
- 字段vsz(virtual memory size)表示进程所申请的虚拟地址空间的内存大小,单位KB。在64位系统中,每个进程都有128Tb大小的堆内存虚拟地址空间的内存空间大小。vsz值并不反映进程占用的真正物理内存大小。
- 字段rss(resident set size)表示进程真正占用了的物理内存大小,单位KB。
- 字段pmem表示进程占用的物理内存大小(rss)占本机总物理内存大小百分比。
- 字段trs(text resident set size)表示用于可执行代码的物理内存大小,约等于进程的程序尺寸大小。
- 字段drs(data resident set size)表示可执行代码之外的内存大小,实际基本等于vsz减去trs的值。
- 字段size表示如果进程交换到磁盘所需的交换空间大小。
- 字段sz表示进程在物理页面中的核心镜像的大小。
- 字段minflt表示此进程中发生的次缺页异常的数量,下面详细介绍。
- 字段majflt表示此进程中发生的主缺页异常的数量。
进程内存相关字段别名:字段m_drs和字段dsiz是字段drs的别名,字段vsize是字段vsz的别名,字段m_size是字段sz的别名,字段rssize、字段rsz和字段sgi_rss是字段rss的别名,字段m_trs、字段trss和字段tsiz是字段trs的别名,字段%mem是字段pmem的别名,字段min_flt是字段minflt的别名,字段maj_flt和字段pagein是字段majflt的别名。
下面通过一个例子来加深对缺页异常的理解。
可以看出字段rss和字段minflt的比值趋近于4。操作系统管理内存的基本单元是4096字节大小的页框,当进程访问尚未有物理内存建立页表映射关系的虚拟内存地址值时,会产生一次缺页异常。在缺页异常处理过程中会为虚拟内存页分配一个物理内存页并建立映射。所以每一次缺页异常就会分配4096(4kb)字节的物理内存,这样rss和minflt的比值当然就是4了。如果分配之后又有释放,后面再次分配,会使这个比值逐步小于4。如果这个比值过于小,那我们就有充足理由怀疑用户程序代码在内存管理上存在重大问题。
8.7、进程凭证类字段
进程凭证类字段一共有30多个,其中大部分可以汇总为如下表格。
其中有些凭证是另外凭证的别名,比如uid是euid的别名,svuid是suid的别名,fsuid是fuid的别名。因此这些凭证字段简单的可以归纳为如下4方面凭证:
- 实际凭证 (real user ID):一般表示进程的创建者,属于哪个用户创建。
- 有效凭证 (effective user ID):表示进程对于文件和资源的访问权限,具备等同于哪个用户的权限。
- 保护凭证 (saved set-user-ID):set-user-ID的保存ID。
- 文件系统凭证 (file-system user ID):已经基本废弃。
8.8、WCHAN字段
WCHAN类字段一共3个nwchan、wchan和wname。WCHAN就是waiting channel的意思,进程正在休眠的内核函数的函数符号名称。R状态进程此字段值为“-”。
字段wchan和wname都显示的是内核函数的函数符号名称信息,默认只显示6个字节。如果希望显示完整的函数名称,可以通过在字段名称后加冒号再加宽度数值的方式显示更丰富信息,即wchan:25。
字段nwchan显示的是内核函数符号的指针地址数值信息。一个完整的64位的内核函数指针地址是一个16位的十六进制值,前10位固定为'ffffffff81',因此ps命令的nwchan字段只显示出了后6位的十六进制值。比如指针地址是ffffffff8124bb7e,那么nwchan显示24bb7e。如果后6位的高位有0,则省略掉0的显示。
九、Linux ps命令选项容错机制
Linux ps命令所有的选项和大多数字段都解释过了,现在该说说文章开头那个报错的ps -axu了。
ps命令会提供一种选项容错机制。当用户输入的是一个SYSV风格参数组合后,如果参数解析失败,ps命令会继续尝试把同样的字母组合都转换为BSD风格再尝试进行一次解析。比如ps -aux解析失败后尝试按ps aux解析,ps -x解析失败后尝试按ps x解析。当然了,如果再次按照BSD风格尝试解析仍失败,那ps命令会最终失败报错。
事实上,能有机会被ps命令容错机制纠正的错误选项只有这7个,-S、-X、-h、-k、-v、-r和-x。因为这些字母虽然没有SYSV风格的选项,但是却都有BSD风格的选项。
最后说一下,没有将BSD风格到SYSV风格的容错机制,比如SYSV风格有-F选项,而BSD风格没有F选项。运行命令ps F还是会报错。
十、Linux ps命令综合解析
10.1、常用选项摘要
本文对linux ps命令的104个短选项都进行了说明,其中只有一部分选项比较常用,下面根据我的经验推荐给大家重点关注:
- 选项-e:显示所有进程的记录,记住这个参数就可以保证把当前系统的所有进程都输出。需要筛选进程时,可以结合grep等文本处理命令实现目的。
- 选项h:选中时可以隐藏输出结果的标题栏信息,在一些自动化脚本中使用此参数可以去除页头信息。
- 选项k:通过此选项可以实现对输出结果的排序。
- 选项-L:通过此选项可以把多线程的进程展开每个线程的细颗粒度。
- 选项-l:或选项l,此选项可以列出进程的最基本信息,包括s、pid、ppid、time和ucmd等字段信息。
- 选项u:此选项可以列出cpu使用率、mem使用率、rss内存等字段信息。
- 选项-o:或选项o,通过此选项可以自定义输出符合自己需求的字段信息。
Linux ps命令的168个输出字段,我们也对大部分进行了介绍。下面根据经验推荐给大家一些常用的关注。
结尾
Linux ps命令博大精深,以上内容可能不一定完整。如果大家觉得文章中有任何补充,请加群反馈信息。如果你对ps命令使用还有其他疑问,也欢迎加群答疑。