vmstat

vmstat命令的含义为显示虚拟内存状态(“Viryual Memor Statics”),但是它可以报告关于进程、内存、I/O等系统整体运行状态。

语法

vmstat(选项)(参数)

选项

-a:显示活动内页;
-f:显示启动后创建的进程总数;
-m:显示slab信息;
-n:头信息仅显示一次;
-s:以表格方式显示事件计数器和内存状态;
-d:报告磁盘状态;
-p:显示指定的硬盘分区状态;
-S:输出信息的单位。

参数

  • 事件间隔:状态信息刷新的时间间隔;
  • 次数:显示报告的次数。

实例

vmstat 3
procs -----------memory---------- ---swap-- -----io---- --system-- -----cpu------
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
 0  0    320  42188 167332 1534368    0    0     4     7    1    0  0  0 99  0  0
 0  0    320  42188 167332 1534392    0    0     0     0 1002   39  0  0 100  0  0
 0  0    320  42188 167336 1534392    0    0     0    19 1002   44  0  0 100  0  0
 0  0    320  42188 167336 1534392    0    0     0     0 1002   41  0  0 100  0  0
 0  0    320  42188 167336 1534392    0    0     0     0 1002   41  0  0 100  0  0

字段说明:

Procs(进程)

  • r: 运行队列中进程数量,这个值也可以判断是否需要增加CPU。(长期大于1)
  • b: 等待IO的进程数量。

Memory(内存)

  • swpd: 使用虚拟内存大小,如果swpd的值不为0,但是SI,SO的值长期为0,这种情况不会影响系统性能。
  • free: 空闲物理内存大小。
  • buff: 用作缓冲的内存大小。
  • cache: 用作缓存的内存大小,如果cache的值大的时候,说明cache处的文件数多,如果频繁访问到的文件都能被cache处,那么磁盘的读IO bi会非常小。

Swap

  • si: 每秒从交换区写到内存的大小,由磁盘调入内存。
  • so: 每秒写入交换区的内存大小,由内存调入磁盘。

注意:内存够用的时候,这2个值都是0,如果这2个值长期大于0时,系统性能会受到影响,磁盘IO和CPU资源都会被消耗。有些朋友看到空闲内存(free)很少的或接近于0时,就认为内存不够用了,不能光看这一点,还要结合si和so,如果free很少,但是si和so也很少(大多时候是0),那么不用担心,系统性能这时不会受到影响的。

IO(现在的Linux版本块的大小为1kb)

  • bi: 每秒读取的块数
  • bo: 每秒写入的块数

注意:随机磁盘读写的时候,这2个值越大(如超出1024k),能看到CPU在IO等待的值也会越大。

system(系统)

  • in: 每秒中断数,包括时钟中断。
  • cs: 每秒上下文切换数。

注意:上面2个值越大,会看到由内核消耗的CPU时间会越大。

CPU(以百分比表示)

  • us: 用户进程执行时间百分比(user time)

us的值比较高时,说明用户进程消耗的CPU时间多,但是如果长期超50%的使用,那么我们就该考虑优化程序算法或者进行加速。

  • sy: 内核系统进程执行时间百分比(system time)

sy的值高时,说明系统内核消耗的CPU资源多,这并不是良性表现,我们应该检查原因。

  • wa: IO等待时间百分比

wa的值高时,说明IO等待比较严重,这可能由于磁盘大量作随机访问造成,也有可能磁盘出现瓶颈(块操作)。

  • id: 空闲时间百分比

time

time命令用于统计给定命令所花费的总时间。

语法

time(参数)

参数

指令:指定需要运行的额指令及其参数。

实例

当测试一个程序或比较不同算法时,执行时间是非常重要的,一个好的算法应该是用时最短的。所有类UNIX系统都包含time命令,使用这个命令可以统计时间消耗。例如:

[root@localhost ~]# time ls
anaconda-ks.cfg  install.log  install.log.syslog  satools  text

real    0m0.009s
user    0m0.002s
sys     0m0.007s

输出的信息分别显示了该命令所花费的real时间、user时间和sys时间。

  • real时间是指挂钟时间,也就是命令开始执行到结束的时间。这个短时间包括其他进程所占用的时间片,和进程被阻塞时所花费的时间。
  • user时间是指进程花费在用户模式中的CPU时间,这是唯一真正用于执行进程所花费的时间,其他进程和花费阻塞状态中的时间没有计算在内。
  • sys时间是指花费在内核模式中的CPU时间,代表在内核中执系统调用所花费的时间,这也是真正由进程使用的CPU时间。

shell内建也有一个time命令,当运行time时候是调用的系统内建命令,应为系统内建的功能有限,所以需要时间其他功能需要使用time命令可执行二进制文件/usr/bin/time

使用-o选项将执行时间写入到文件中:

/usr/bin/time -o outfile.txt ls

使用-a选项追加信息:

/usr/bin/time -a -o outfile.txt ls

使用-f选项格式化时间输出:

/usr/bin/time -f "time: %U" ls

-f选项后的参数:

参数 描述
%E real时间,显示格式为[小时:]分钟:秒
%U user时间。
%S sys时间。
%C 进行计时的命令名称和命令行参数。
%D 进程非共享数据区域,以KB为单位。
%x 命令退出状态。
%k 进程接收到的信号数量。
%w 进程被交换出主存的次数。
%Z 系统的页面大小,这是一个系统常量,不用系统中常量值也不同。
%P 进程所获取的CPU时间百分百,这个值等于user+system时间除以总共的运行时间。
%K 进程的平均总内存使用量(data+stack+text),单位是KB。
%w 进程主动进行上下文切换的次数,例如等待I/O操作完成。
%c 进程被迫进行上下文切换的次数(由于时间片到期)。

tload

tload命令以图形化的方式输出当前系统的平均负载到指定的终端。假设不给予终端机编号,则会在执行tload指令的终端机显示负载情形。

语法

tload(选项)(参数)

选项

-s:指定闲时的刻度;
-d:指定间隔的时间(秒)。

参数

终端:指定显示信息的终端设备文件。

实例

使用tload命令查看系统负载情况:

tload -d 1
0.08, 0.02,0.01
0.04, 0.01, 0.00
0.04, 0.01, 0.00
0.04, 0.01,0.00
0.06, 0.02, 0.00

lsof

lsof命令用于查看你进程开打的文件,打开文件的进程,进程打开的端口(TCP、UDP)。找回/恢复删除的文件。是十分方便的系统监视工具,因为lsof命令需要访问核心内存和各种文件,所以需要root用户执行。

在linux环境下,任何事物都以文件的形式存在,通过文件不仅仅可以访问常规数据,还可以访问网络连接和硬件。所以如传输控制协议 (TCP) 和用户数据报协议 (UDP) 套接字等,系统在后台都为该应用程序分配了一个文件描述符,无论这个文件的本质如何,该文件描述符为应用程序与基础操作系统之间的交互提供了通用接口。因为应用程序打开文件的描述符列表提供了大量关于这个应用程序本身的信息,因此通过lsof工具能够查看这个列表对系统监测以及排错将是很有帮助的。

语法

lsof(选项)

选项

-a:列出打开文件存在的进程;
-c<进程名>:列出指定进程所打开的文件;
-g:列出GID号进程详情;
-d<文件号>:列出占用该文件号的进程;
+d<目录>:列出目录下被打开的文件;
+D<目录>:递归列出目录下被打开的文件;
-n<目录>:列出使用NFS的文件;
-i<条件>:列出符合条件的进程。(4、6、协议、:端口、 @ip )
-p<进程号>:列出指定进程号所打开的文件;
-u:列出UID号进程详情;
-h:显示帮助信息;
-v:显示版本信息。

实例

lsof
command     PID USER   FD      type             DEVICE     SIZE       NODE NAME
init          1 root  cwd       DIR                8,2     4096          2 /
init          1 root  rtd       DIR                8,2     4096          2 /
init          1 root  txt       REG                8,2    43496    6121706 /sbin/init
init          1 root  mem       REG                8,2   143600    7823908 /lib64/ld-2.5.so
init          1 root  mem       REG                8,2  1722304    7823915 /lib64/libc-2.5.so
init          1 root  mem       REG                8,2    23360    7823919 /lib64/libdl-2.5.so
init          1 root  mem       REG                8,2    95464    7824116 /lib64/libselinux.so.1
init          1 root  mem       REG                8,2   247496    7823947 /lib64/libsepol.so.1
init          1 root   10u     FIFO               0,17                1233 /dev/initctl
migration     2 root  cwd       DIR                8,2     4096          2 /
migration     2 root  rtd       DIR                8,2     4096          2 /
migration     2 root  txt   unknown                                        /proc/2/exe
ksoftirqd     3 root  cwd       DIR                8,2     4096          2 /
ksoftirqd     3 root  rtd       DIR                8,2     4096          2 /
ksoftirqd     3 root  txt   unknown                                        /proc/3/exe
migration     4 root  cwd       DIR                8,2     4096          2 /
migration     4 root  rtd       DIR                8,2     4096          2 /
migration     4 root  txt   unknown                                        /proc/4/exe
ksoftirqd     5 root  cwd       DIR                8,2     4096          2 /
ksoftirqd     5 root  rtd       DIR                8,2     4096          2 /
ksoftirqd     5 root  txt   unknown                                        /proc/5/exe
events/0      6 root  cwd       DIR                8,2     4096          2 /
events/0      6 root  rtd       DIR                8,2     4096          2 /
events/0      6 root  txt   unknown                                        /proc/6/exe
events/1      7 root  cwd       DIR                8,2     4096          2 /

lsof输出各列信息的意义如下:

  • COMMAND:进程的名称
  • PID:进程标识符
  • PPID:父进程标识符(需要指定-R参数)
  • USER:进程所有者
  • PGID:进程所属组
  • FD:文件描述符,应用程序通过文件描述符识别该文件。

文件描述符列表:

  1. cwd:表示current work dirctory,即:应用程序的当前工作目录,这是该应用程序启动的目录,除非它本身对这个目录进行更改
  2. txt:该类型的文件是程序代码,如应用程序二进制文件本身或共享库,如上列表中显示的 /sbin/init 程序
  3. lnn:library references (AIX);
  4. er:FD information error (see NAME column);
  5. jld:jail directory (FreeBSD);
  6. ltx:shared library text (code and data);
  7. mxx :hex memory-mapped type number xx.
  8. m86:DOS Merge mapped file;
  9. mem:memory-mapped file;
  10. mmap:memory-mapped device;
  11. pd:parent directory;
  12. rtd:root directory;
  13. tr:kernel trace file (OpenBSD);
  14. v86  VP/ix mapped file;
  15. 0:表示标准输出
  16. 1:表示标准输入
  17. 2:表示标准错误

一般在标准输出、标准错误、标准输入后还跟着文件状态模式:

  1. u:表示该文件被打开并处于读取/写入模式。
  2. r:表示该文件被打开并处于只读模式。
  3. w:表示该文件被打开并处于。
  4. 空格:表示该文件的状态模式为unknow,且没有锁定。
  5. -:表示该文件的状态模式为unknow,且被锁定。

同时在文件状态模式后面,还跟着相关的锁:

  1. N:for a Solaris NFS lock of unknown type;
  2. r:for read lock on part of the file;
  3. R:for a read lock on the entire file;
  4. w:for a write lock on part of the file;(文件的部分写锁)
  5. W:for a write lock on the entire file;(整个文件的写锁)
  6. u:for a read and write lock of any length;
  7. U:for a lock of unknown type;
  8. x:for an SCO OpenServer Xenix lock on part      of the file;
  9. X:for an SCO OpenServer Xenix lock on the      entire file;
  10. space:if there is no lock.

文件类型:

  1. DIR:表示目录。
  2. CHR:表示字符类型。
  3. BLK:块设备类型。
  4. UNIX: UNIX 域套接字。
  5. FIFO:先进先出 (FIFO) 队列。
  6. IPv4:网际协议 (IP) 套接字。
  7. DEVICE:指定磁盘的名称
  8. SIZE:文件的大小
  9. NODE:索引节点(文件在磁盘上的标识)
  10. NAME:打开文件的确切名称

fuser

fuser命令用于报告进程使用的文件和网络套接字。fuser命令列出了本地进程的进程号,那些本地进程使用file,参数指定的本地或远程文件。对于阻塞特别设备,此命令列出了使用该设备上任何文件的进程。

每个进程号后面都跟随一个字母,该字母指示进程如何使用文件。

  • c:指示进程的工作目录。
  • e:指示该文件为进程的可执行文件(即进程由该文件拉起)。
  • f:指示该文件被进程打开,默认情况下f字符不显示。
  • F:指示该文件被进程打开进行写入,默认情况下F字符不显示。
  • r:指示该目录为进程的根目录。
  • m:指示进程使用该文件进行内存映射,抑或该文件为共享库文件,被进程映射进内存。

语法

fuser(选项)(参数)

选项

-a:显示命令行中指定的所有文件;
-k:杀死访问指定文件的所有进程;
-i:杀死进程前需要用户进行确认;
-l:列出所有已知信号名;
-m:指定一个被加载的文件系统或一个被加载的块设备;
-n:选择不同的名称空间;
-u:在每个进程后显示所属的用户名。

参数

文件:可以是文件名或者TCP、UDP端口号。

实例

要列出使用/etc/passwd文件的本地进程的进程号,请输入:

fuser /etc/passwd

要列出使用/etc/filesystems文件的进程的进程号和用户登录名,请输入:

fuser -u /etc/filesystems

要终止使用给定文件系统的所有进程,请输入:

fuser -k -x -u -c /dev/hd1  或者  fuser -kxuc /home

任一命令都列出了进程号和用户名,然后终止每个正在使用/dev/hd1 (/home)文件系统的进程。仅有root用户能终止属于另一用户的进程。如果您正在试图卸下/dev/hd1文件系统,而一个正在访问/dev/hd1文件系统的进程不允许这样,您可能希望使用此命令。

要列出正在使用已从给定文件系统删除的文件的全部进程,请输入:

fuser -d /usr文件

/dev/kmem 用于系统映像。
/dev/mem  也用于系统映像。

sysctl

sysctl命令被用于在内核运行时动态地修改内核的运行参数,可用的内核参数在目录/proc/sys中。它包含一些TCP/ip堆栈和虚拟内存系统的高级选项, 这可以让有经验的管理员提高引人注目的系统性能。用sysctl可以读取设置超过五百个系统变量。

语法

sysctl(选项)(参数)

选项

-n:打印值时不打印关键字;
-e:忽略未知关键字错误;
-N:仅打印名称;
-w:当改变sysctl设置时使用此项;
-p:从配置文件“/etc/sysctl.conf”加载内核参数设置;
-a:打印当前所有可用的内核参数变量和值;
-A:以表格方式打印当前所有可用的内核参数变量和值。

参数

变量=值:设置内核参数对应的变量值。

实例

查看所有可读变量:

sysctl -a

读一个指定的变量,例如kern.maxproc

sysctl kern.maxproc kern.maxproc: 1044

要设置一个指定的变量,直接用variable=value这样的语法:

sysctl kern.maxfiles=5000
kern.maxfiles: 2088 -> 5000

您可以使用sysctl修改系统变量,也可以通过编辑sysctl.conf文件来修改系统变量。sysctl.conf看起来很像rc.conf。它用variable=value的形式来设定值。指定的值在系统进入多用户模式之后被设定。并不是所有的变量都可以在这个模式下设定。

sysctl变量的设置通常是字符串、数字或者布尔型。(布尔型用 1 来表示'yes',用 0 来表示'no')。

sysctl -w kernel.sysrq=0
sysctl -w kernel.core_uses_pid=1
sysctl -w net.ipv4.conf.default.accept_redirects=0
sysctl -w net.ipv4.conf.default.accept_source_route=0
sysctl -w net.ipv4.conf.default.rp_filter=1
sysctl -w net.ipv4.tcp_syncookies=1
sysctl -w net.ipv4.tcp_max_syn_backlog=2048
sysctl -w net.ipv4.tcp_fin_timeout=30
sysctl -w net.ipv4.tcp_synack_retries=2
sysctl -w net.ipv4.tcp_keepalive_time=3600
sysctl -w net.ipv4.tcp_window_scaling=1
sysctl -w net.ipv4.tcp_sack=1

配置sysctl

编辑此文件:/etc/sysctl.conf

如果该文件为空,则输入以下内容,否则请根据情况自己做调整:

# Controls source route verification
# Default should work for all interfaces
net.ipv4.conf.default.rp_filter = 1
# net.ipv4.conf.all.rp_filter = 1
# net.ipv4.conf.lo.rp_filter = 1
# net.ipv4.conf.eth0.rp_filter = 1

# Disables IP source routing
# Default should work for all interfaces
net.ipv4.conf.default.accept_source_route = 0
# net.ipv4.conf.all.accept_source_route = 0
# net.ipv4.conf.lo.accept_source_route = 0
# net.ipv4.conf.eth0.accept_source_route = 0

# Controls the System Request debugging functionality of the kernel
kernel.sysrq = 0

# Controls whether core dumps will append the PID to the core filename.
# Useful for debugging multi-threaded applications.
kernel.core_uses_pid = 1

# Increase maximum amount of memory allocated to shm
# Only uncomment if needed!
# kernel.shmmax = 67108864

# Disable ICMP Redirect Acceptance
# Default should work for all interfaces
net.ipv4.conf.default.accept_redirects = 0
# net.ipv4.conf.all.accept_redirects = 0
# net.ipv4.conf.lo.accept_redirects = 0
# net.ipv4.conf.eth0.accept_redirects = 0

# enable Log Spoofed Packets, Source Routed Packets, Redirect Packets
# Default should work for all interfaces
net.ipv4.conf.default.log_martians = 1
# net.ipv4.conf.all.log_martians = 1
# net.ipv4.conf.lo.log_martians = 1
# net.ipv4.conf.eth0.log_martians = 1

# Decrease the time default value for tcp_fin_timeout connection
net.ipv4.tcp_fin_timeout = 25

# Decrease the time default value for tcp_keepalive_time connection
net.ipv4.tcp_keepalive_time = 1200

# Turn on the tcp_window_scaling
net.ipv4.tcp_window_scaling = 1

# Turn on the tcp_sack
net.ipv4.tcp_sack = 1

# tcp_fack should be on because of sack
net.ipv4.tcp_fack = 1

# Turn on the tcp_timestamps
net.ipv4.tcp_timestamps = 1

# Enable TCP SYN Cookie Protection
net.ipv4.tcp_syncookies = 1

# Enable ignoring broadcasts request
net.ipv4.icmp_echo_ignore_broadcasts = 1

# Enable bad error message Protection
net.ipv4.icmp_ignore_bogus_error_responses = 1

# make more local ports available
# net.ipv4.ip_local_port_range = 1024 65000

# set TCP Re-Ordering value in kernel to ‘5′
net.ipv4.tcp_reordering = 5

# Lower syn retry rates
net.ipv4.tcp_synack_retries = 2
net.ipv4.tcp_syn_retries = 3

# Set Max SYN Backlog to ‘2048′
net.ipv4.tcp_max_syn_backlog = 2048

# Various Settings
net.core.netdev_max_backlog = 1024

# Increase the maximum number of skb-heads to be cached
net.core.hot_list_length = 256

# Increase the tcp-time-wait buckets pool size
net.ipv4.tcp_max_tw_buckets = 360000

# This will increase the amount of memory available for socket input/output queues
net.core.rmem_default = 65535
net.core.rmem_max = 8388608
net.ipv4.tcp_rmem = 4096 87380 8388608
net.core.wmem_default = 65535
net.core.wmem_max = 8388608
net.ipv4.tcp_wmem = 4096 65535 8388608
net.ipv4.tcp_mem = 8388608 8388608 8388608
net.core.optmem_max = 40960

如果希望屏蔽别人 ping 你的主机,则加入以下代码:

# Disable ping requests
net.ipv4.icmp_echo_ignore_all = 1

编辑完成后,请执行以下命令使变动立即生效:

/sbin/sysctl -p
/sbin/sysctl -w net.ipv4.route.flush=1

vgremove

vgremove命令用于用户删除LVM卷组。当要删除的卷组上已经创建了逻辑卷时,vgremove命令需要进行确认删除,防止误删除数据。

语法

vgremove(选项)(参数)

选项

-f:强制删除。

参数

卷组:指定要删除的卷组名称。

实例

使用vgremove命令删除LVM卷组"vg1000"。在命令行中输入下面的命令:

[root@localhost ~]# vgremove vg1000    #删除卷组"vg1000"
Volume group "vg1000" successfully removed

set

set命令作用主要是显示系统中已经存在的shell变量,以及设置shell变量的新变量值。使用set更改shell特性时,符号"+"和"-"的作用分别是打开和关闭指定的模式。set命令不能够定义新的shell变量。如果要定义新的变量,可以使用declare命令以变量名=值的格式进行定义即可。

语法

set(选项)(参数)

选项

-a:标示已修改的变量,以供输出至环境变量。
-b:使被中止的后台程序立刻回报执行状态。
-C:转向所产生的文件无法覆盖已存在的文件。
-d:Shell预设会用杂凑表记忆使用过的指令,以加速指令的执行。使用-d参数可取消。
-e:若指令传回值不等于0,则立即退出shell。
-f:取消使用通配符。
-h:自动记录函数的所在位置。
-H Shell:可利用"!"加<指令编号>的方式来执行history中记录的指令。
-k:指令所给的参数都会被视为此指令的环境变量。
-l:记录for循环的变量名称。
-m:使用监视模式。
-n:只读取指令,而不实际执行。
-p:启动优先顺序模式。
-P:启动-P参数后,执行指令时,会以实际的文件或目录来取代符号连接。
-t:执行完随后的指令,即退出shell。
-u:当执行时使用到未定义过的变量,则显示错误信息。
-v:显示shell所读取的输入值。
-x:执行指令后,会先显示该指令及所下的参数。

参数

取消某个set曾启动的参数。

实例

使用declare命令定义一个新的环境变量"mylove",并且将其值设置为"Visual C++",输入如下命令:

declare mylove='Visual C++'   #定义新环境变量

再使用set命令将新定义的变量输出为环境变量,输入如下命令:

set -a mylove                 #设置为环境变量

执行该命令后,将会新添加对应的环境变量。用户可以使用env命令和grep命令分别显示和搜索环境变量"mylove",输入命令如下:

env | grep mylove             #显示环境变量值

此时,该命令执行后,将输出查询到的环境变量值。

apropos

apropos命令在一些特定的包含系统命令的简短描述的数据库文件里查找关键字,然后把结果送到标准输出。 

如果你不知道完成某个特定任务所需要命令的名称,可以使用一个关键字通过Linux apropos实用程序来搜索它。该实用程序可以搜索关键字并且显示所有包含匹配项的man页面的简短描述。另外,使用man实用程序和-k(关键字)选项,可以得到和用Linux apropos实用程序相同的结果(实际上是相同的命令)。

语法

apropos [-dalhvV] [-e|-w|-r] [-s section] [-m system[,...]] [-M path] [-L locale] [-C file] keyword ...

选项

-d, --debug:输出调试信息。
-v, --verbose:输出详细的警告信息。
-r, -- regex:将每个keyword作为正则表达式解释。这是默认行为。每个keyword将匹配手册页和描述。
-w, --wildcard:将每个keyword作为shell样式的通配符解释。
-e, --exact:每个keyword将精确匹配手册页名字和描述。
-a, --and:只显示匹配所有keyword的手册页和描述。默认显示匹配任何keyword的项。
-l, --long:不根据终端宽度缩减输出。
-s section, --section section:只查找指定的手册section。
-m system[,...], --systems=system[,...]:用于查找其它操作系统的手册页。
-M path, --manpath=path:指定从其它以冒号分隔的手册页层次查找。默认使用$MANPATH环境变量。这个选项覆盖$MANPATH的内容。
-L locale, --locale=locale:apropos调用C函数setlocale来得到当前本地化信息,包括$LC_MESSAGE和$LANG。使用该选项提供一个locale字符串来临时更改本地化信息。
-C file, --config-file=file:使用这个用户配置文件而不是默认的~/.manpath。
-h, --help:打印帮助信息并退出。
-V, --version:打印版本信息并退出。

返回值

返回0表示成功,1表示用法、语法或配置文件错误,2表示操作错误,16表示没有找到匹配的内容。

实例

[root@localhost ~]# man -k who
at.allow [at]        (5)  - determine who can submit jobs via at or batch
at.deny [at]         (5)  - determine who can submit jobs via at or batch
jwhois               (1)  - client for the whois service
jwhois              (rpm) - Internet whois/nicname client.
Net::LDAP::Extension::whoami (3pm)  - LDAP Who am I? Operation
w                    (1)  - Show who is logged on and what they are doing
who                  (1p)  - display who is on the system
who                  (1)  - show who is logged on
whoami               (1)  - print effective userid

[root@localhost ~]# apropos who
at.allow [at]        (5)  - determine who can submit jobs via at or batch
at.deny [at]         (5)  - determine who can submit jobs via at or batch
jwhois               (1)  - client for the whois service
jwhois              (rpm) - Internet whois/nicname client.
Net::LDAP::Extension::WhoAmI (3pm)  - LDAP Who am I? Operation
w                    (1)  - Show who is logged on and what they are doing
who                  (1p)  - display who is on the system
who                  (1)  - show who is logged on
whoami               (1)  - print effective userid

查找手册页名字和描述中包含emacsvi的手册页:

apropos -a emacs vi

strace

strace命令是一个集诊断、调试、统计与一体的工具,我们可以使用strace对应用的系统调用和信号传递的跟踪结果来对应用进行分析,以达到解决问题或者是了解应用工作过程的目的。当然strace与专业的调试工具比如说gdb之类的是没法相比的,因为它不是一个专业的调试器。

strace的最简单的用法就是执行一个指定的命令,在指定的命令结束之后它也就退出了。在命令执行的过程中,strace会记录和解析命令进程的所有系统调用以及这个进程所接收到的所有的信号值。

语法

strace  [  -dffhiqrtttTvxx  ] [ -acolumn ] [ -eexpr ] ...
    [ -ofile ] [-ppid ] ...  [ -sstrsize ] [ -uusername ]
    [ -Evar=val ] ...  [ -Evar  ]...
    [ command [ arg ...  ] ]

strace  -c  [ -eexpr ] ...  [ -Ooverhead ] [ -Ssortby ]
    [ command [ arg...  ] ]

选项

-c 统计每一系统调用的所执行的时间,次数和出错的次数等.
-d 输出strace关于标准错误的调试信息.
-f 跟踪由fork调用所产生的子进程.
-ff 如果提供-o filename,则所有进程的跟踪结果输出到相应的filename.pid中,pid是各进程的进程号.
-F 尝试跟踪vfork调用.在-f时,vfork不被跟踪.
-h 输出简要的帮助信息.
-i 输出系统调用的入口指针.
-q 禁止输出关于脱离的消息.
-r 打印出相对时间关于,,每一个系统调用.
-t 在输出中的每一行前加上时间信息.
-tt 在输出中的每一行前加上时间信息,微秒级.
-ttt 微秒级输出,以秒了表示时间.
-T 显示每一调用所耗的时间.
-v 输出所有的系统调用.一些调用关于环境变量,状态,输入输出等调用由于使用频繁,默认不输出.
-V 输出strace的版本信息.
-x 以十六进制形式输出非标准字符串
-xx 所有字符串以十六进制形式输出.
-a column 设置返回值的输出位置.默认 为40.
-e expr 指定一个表达式,用来控制如何跟踪.格式:[qualifier=][!]value1[,value2]...
qualifier只能是 trace,abbrev,verbose,raw,signal,read,write其中之一.value是用来限定的符号或数字.默认的 qualifier是 trace.感叹号是否定符号.例如:-eopen等价于 -e trace=open,表示只跟踪open调用.而-etrace!=open 表示跟踪除了open以外的其他调用.有两个特殊的符号 all 和 none. 注意有些shell使用!来执行历史记录里的命令,所以要使用\\.
-e trace=set 只跟踪指定的系统 调用.例如:-e trace=open,close,rean,write表示只跟踪这四个系统调用.默认的为set=all.
-e trace=file 只跟踪有关文件操作的系统调用.
-e trace=process 只跟踪有关进程控制的系统调用.
-e trace=network 跟踪与网络有关的所有系统调用.
-e strace=signal 跟踪所有与系统信号有关的 系统调用
-e trace=ipc 跟踪所有与进程通讯有关的系统调用
-e abbrev=set 设定strace输出的系统调用的结果集.-v 等与 abbrev=none.默认为abbrev=all.
-e raw=set 将指定的系统调用的参数以十六进制显示.
-e signal=set 指定跟踪的系统信号.默认为all.如 signal=!SIGIO(或者signal=!io),表示不跟踪SIGIO信号.
-e read=set 输出从指定文件中读出 的数据.例如: -e read=3,5
-e write=set 输出写入到指定文件中的数据.
-o filename 将strace的输出写入文件filename
-p pid 跟踪指定的进程pid.
-s strsize 指定输出的字符串的最大长度.默认为32.文件名一直全部输出.
-u username 以username的UID和GID执行被跟踪的命令

实例

追踪系统调用

现在我们做一个很简单的程序来演示strace的基本用法。这个程序的C语言代码如下:

# filename test.c
#include <stdio.h>
 
int main()
{
    int a;
    scanf("%d", &a);
    printf("%09d\n", a);
    return 0;
}

然后我们用gcc -o test test.c编译一下,得到一个可执行的文件test。然后用strace调用执行:

strace ./test

执行期间会要求你输入一个整数,我们输入99,最后得到如下的结果:

// 直接执行test的结果
oracle@orainst[orcl]:~ $./test

// 执行的结果
99
000000099

// 通过strace执行test的结果
oracle@orainst[orcl]:~ $strace ./test

// strace的trace结果
execve("./test", ["./test"], [/* 41 vars */]) = 0
uname({sys="Linux", node="orainst.desktop.mycompany.com", ...}) = 0
brk(0)                                  = 0x8078000
fstat64(3, {st_mode=S_IFREG|0644, st_size=65900, ...}) = 0
old_mmap(NULL, 65900, PROT_READ, MAP_PRIVATE, 3, 0) = 0xbf5ef000
close(3)                                = 0
open("/lib/tls/libc.so.6", O_RDONLY)    = 3
read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0\200X\1"..., 512) = 512
fstat64(3, {st_mode=S_IFREG|0755, st_size=1571692, ...}) = 0
old_mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xbf5ee000
old_mmap(NULL, 1275340, PROT_READ|PROT_EXEC, MAP_PRIVATE, 3, 0) = 0xa02000
old_mmap(0xb34000, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED, 3, 0x132000) = 0xb34000
old_mmap(0xb37000, 9676, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0xb37000
close(3)                                = 0
set_thread_area({entry_number:-1 -> 6, base_addr:0xbf5ee740, limit:1048575, seg_32bit:1, contents:0, read_exec_only:0, limit_in_pages:1, seg_not_present:0, useable:1}) = 0
munmap(0xbf5ef000, 65900)               = 0
fstat64(0, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 0), ...}) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xbf5ff000
read(0, 99
"99\n", 1024)                   = 3
fstat64(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 0), ...}) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xbf5fe000
write(1, "000000099\n", 10000000099
)             = 10
munmap(0xbf5fe000, 4096)                = 0
exit_group(0)                           = ?

从trace结构可以看到,系统首先调用execve开始一个新的进行,接着进行些环境的初始化操作,最后停顿在”read(0,”上面,这也就是执行到了我们的scanf函数,等待我们输入数字呢,在输入完99之后,在调用write函数将格式化后的数值”000000099″输出到屏幕,最后调用exit_group退出进行,完成整个程序的执行过程。

跟踪信号传递

我们还是使用上面的那个test程序,来观察进程接收信号的情况。还是先strace ./test,等到等待输入的画面的时候不要输入任何东西,然后打开另外一个窗口,输入如下的命令

killall test

这时候就能看到我们的程序推出了,最后的trace结果如下:

oracle@orainst[orcl]:~
$strace ./test

execve("./test", ["./test"], [/* 41 vars */]) = 0
uname({sys="Linux", node="orainst.desktop.mycompany.com", ...}) = 0
brk(0)                                  = 0x9ae2000
old_mmap(NULL, 65900, PROT_READ, MAP_PRIVATE, 3, 0) = 0xbf5ef000
close(3)                                = 0
open("/lib/tls/libc.so.6", O_RDONLY)    = 3
read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0\200X\1"..., 512) = 512
fstat64(3, {st_mode=S_IFREG|0755, st_size=1571692, ...}) = 0
old_mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xbf5ee000
old_mmap(NULL, 1275340, PROT_READ|PROT_EXEC, MAP_PRIVATE, 3, 0) = 0x2e9000
old_mmap(0x41b000, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED, 3, 0x132000) = 0x41b000
old_mmap(0x41e000, 9676, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x41e000
close(3)                                = 0
set_thread_area({entry_number:-1 -> 6, base_addr:0xbf5ee740, limit:1048575, seg_32bit:1, contents:0, read_exec_only:0, limit_in_pages:1, seg_not_present:0, useable:1}) = 0
munmap(0xbf5ef000, 65900)               = 0
fstat64(0, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 0), ...}) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xbf5ff000
read(0, 0xbf5ff000, 1024)               = ? ERESTARTSYS (To be restarted)
--- SIGTERM (Terminated) @ 0 (0) ---
+++ killed by SIGTERM +++

trace中很清楚的告诉你test进程”+++ killed by SIGTERM +++”。

系统调用统计

strace不光能追踪系统调用,通过使用参数-c,它还能将进程所有的系统调用做一个统计分析给你,下面就来看看strace的统计,这次我们执行带-c参数的strace:

strace -c ./test

最后能得到这样的trace结果:

oracle@orainst[orcl]:~
$strace -c ./test
execve("./test", ["./test"], [/* 41 vars */]) = 0
% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
 45.90    0.000140           5        27        25 open
 34.43    0.000105           4        24        21 stat64
  7.54    0.000023           5         5           old_mmap
  2.62    0.000008           8         1           munmap
  1.97    0.000006           6         1           uname
  1.97    0.000006           2         3           fstat64
  1.64    0.000005           3         2         1 read
  1.31    0.000004           2         2           close
  0.98    0.000003           3         1           brk
  0.98    0.000003           3         1           mmap2
  0.66    0.000002           2         1           set_thread_area
------ ----------- ----------- --------- --------- ----------------
100.00    0.000305                    68        47 total

这里很清楚的告诉你调用了那些系统函数,调用次数多少,消耗了多少时间等等这些信息,这个对我们分析一个程序来说是非常有用的。

常用参数说明

除了-c参数之外,strace还提供了其他有用的参数给我们,让我们能很方便的得到自己想要的信息,下面就对那些常用的参数一一做个介绍。

重定向输出

参数-o用在将strace的结果输出到文件中,如果不指定-o参数的话,默认的输出设备是STDERR,也就是说使用”-o filename”和” 2>filename”的结果是一样的。

# 这两个命令都是将strace结果输出到文件test.txt中
strace -c -o test.txt ./test
strace -c ./test  2>test.txt

对系统调用进行计时

strace可以使用参数-T将每个系统调用所花费的时间打印出来,每个调用的时间花销现在在调用行最右边的尖括号里面。

oracle@orainst[orcl]:~
$strace -T ./test

// 这里只摘录部分结果
read(0, 1
"1\n", 1024)                    = 2 <2.673455>
fstat64(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 0), ...}) = 0 <0.000014>
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xbf5fe000 <0.000017>
write(1, "000000001\n", 10000000001
)             = 10 <0.000016>
munmap(0xbf5fe000, 4096)                = 0 <0.000020>
exit_group(0)                           = ?

系统调用的时间

这是一个很有用的功能,strace会将每次系统调用的发生时间记录下来,只要使用-t/tt/ttt三个参数就可以看到效果了,具体的例子可以自己去尝试。

参数名 输出样式 说明
-t 10:33:04 exit_group(0) 输出结果精确到秒
-tt 10:33:48.159682 exit_group(0) 输出结果精确到微妙
-ttt 1262169244.788478 exit_group(0) 精确到微妙,而且时间表示为unix时间戳

截断输出

-s参数用于指定trace结果的每一行输出的字符串的长度,下面看看test程序中-s参数对结果有什么影响,现指定-s为20,然后在read的是是很我们输入一个超过20个字符的数字串

strace -s 20 ./test

read(0, 2222222222222222222222222      // 我们输入的2一共有25个
"22222222222222222222"..., 1024) = 26  // 而我们看到的结果中2只有20个

trace一个现有的进程

strace不光能自己初始化一个进程进行trace,还能追踪现有的进程,参数-p就是取这个作用的,用法也很简单,具体如下。

strace -p pid

综合例子

说了那么多的功能和参数,现在我们来一个实用点的,就是研究下Oracle的lgwr进程,看看这个进程是不是像文档所说的那样没3s钟写一次log文件,考虑到lgwr写日志的触发条件比较多,我们需要找一个空闲的Oracle实例做这个实验。

我们先要得到lgwr进程的pid,运行下面的命令

ps -ef|grep lgwr

oracle    5912     1  0 Nov12 ?        00:14:56 ora_lgwr_orcl

得到lgwr的pid是5912,现在启动strace,然后将trace的几个输出到lgwr.txt文件中,执行下面的命令

strace -tt -s 10 -o lgwr.txt -p 5912

过一会之后停止strace,然后查看结果。由于输出的结果比较多,为了方便我们只看Oracle写入log文件时用的pwrite函数的调用

grep pwrite\(20 lgwr.txt

等等,为什么grep的时候用的是”pwrite(2″呢?,因为我知道我这个机器打开的当前的log文件的句柄编号都是2开始的。具体查找方法是先使用下面的语句找出当前活动的日志文件都有哪些:

select member, v$log.status from v$log, v$logfile
where v$log.group#=v$logfile.group#;

得到

MEMBER                                             STATUS
-------------------------------------------------- ----------------
/db/databases/orcl/redo-01-a/redo-t01-g03-m1.log    INACTIVE
/db/databases/orcl/redo-03-a/redo-t01-g03-m2.log    INACTIVE
/db/databases/orcl/redo-02-a/redo-t01-g02-m1.log    CURRENT
/db/databases/orcl/redo-04-a/redo-t01-g02-m2.log    CURRENT
/db/databases/orcl/redo-01-a/redo-t01-g01-m1.log    INACTIVE
/db/databases/orcl/redo-03-a/redo-t01-g01-m2.log    INACTIVE
/db/databases/orcl/redo-02-a/redo-t01-g04-m1.log    INACTIVE
/db/databases/orcl/redo-04-a/redo-t01-g04-m2.log    INACTIVE

然后到/proc中去找打开文件的句柄:

ll /proc/.5912/fd/

得到

lrwx------    1 oracle   dba            64 Dec 30 10:55 18 -> /db/databases/orcl/redo-01-a/redo-t01-g01-m1.log
lrwx------    1 oracle   dba            64 Dec 30 10:55 19 -> /db/databases/orcl/redo-03-a/redo-t01-g01-m2.log
lrwx------    1 oracle   dba            64 Dec 30 10:55 20 -> /db/databases/orcl/redo-02-a/redo-t01-g02-m1.log
lrwx------    1 oracle   dba            64 Dec 30 10:55 21 -> /db/databases/orcl/redo-04-a/redo-t01-g02-m2.log
lrwx------    1 oracle   dba            64 Dec 30 10:55 22 -> /db/databases/orcl/redo-01-a/redo-t01-g03-m1.log
lrwx------    1 oracle   dba            64 Dec 30 10:55 23 -> /db/databases/orcl/redo-03-a/redo-t01-g03-m2.log
lrwx------    1 oracle   dba            64 Dec 30 10:55 24 -> /db/databases/orcl/redo-02-a/redo-t01-g04-m1.log
lrwx------    1 oracle   dba            64 Dec 30 10:55 25 -> /db/databases/orcl/redo-04-a/redo-t01-g04-m2.log

现在能看到我机器当前日志文件的句柄分别是20和21。

现在我们得到如下结果

11:13:55.603245 pwrite(20, "\1\"\0\0J!"..., 1536, 4363264) = 1536
11:13:55.603569 pwrite(21, "\1\"\0\0J!"..., 1536, 4363264) = 1536
11:13:55.606888 pwrite(20, "\1\"\0\0M!"..., 1536, 4364800) = 1536
11:13:55.607172 pwrite(21, "\1\"\0\0M!"..., 1536, 4364800) = 1536
11:13:55.607934 pwrite(20, "\1\"\0\0P!"..., 1536, 4366336) = 1536
11:13:55.608199 pwrite(21, "\1\"\0\0P!"..., 1536, 4366336) = 1536
11:13:55.610260 pwrite(20, "\1\"\0\0S!"..., 1536, 4367872) = 1536
11:13:55.610530 pwrite(21, "\1\"\0\0S!"..., 1536, 4367872) = 1536
11:14:00.602446 pwrite(20, "\1\"\0\0V!"..., 1536, 4369408) = 1536
11:14:00.602750 pwrite(21, "\1\"\0\0V!"..., 1536, 4369408) = 1536
11:14:00.606386 pwrite(20, "\1\"\0\0Y!"..., 1536, 4370944) = 1536
11:14:00.606676 pwrite(21, "\1\"\0\0Y!"..., 1536, 4370944) = 1536
11:14:00.607900 pwrite(20, "\1\"\0\0\\"..., 1024, 4372480) = 1024
11:14:00.608161 pwrite(21, "\1\"\0\0\\"..., 1024, 4372480) = 1024
11:14:00.608816 pwrite(20, "\1\"\0\0^!"..., 1024, 4373504) = 1024
11:14:00.609071 pwrite(21, "\1\"\0\0^!"..., 1024, 4373504) = 1024
11:14:00.611142 pwrite(20, "\1\"\0\0`!"..., 1536, 4374528) = 1536
11:14:00.611454 pwrite(21, "\1\"\0\0`!"..., 1536, 4374528) = 1536
11:14:05.602804 pwrite(20, "\1\"\0\0c!"..., 1024, 4376064) = 1024
11:14:05.603119 pwrite(21, "\1\"\0\0c!"..., 1024, 4376064) = 1024
11:14:05.607731 pwrite(20, "\1\"\0\0e!"..., 1024, 4377088) = 1024
11:14:05.608020 pwrite(21, "\1\"\0\0e!"..., 1024, 4377088) = 1024
11:14:05.608690 pwrite(20, "\1\"\0\0g!"..., 1024, 4378112) = 1024
11:14:05.608962 pwrite(21, "\1\"\0\0g!"..., 1024, 4378112) = 1024
11:14:05.611022 pwrite(20, "\1\"\0\0i!"..., 1536, 4379136) = 1536
11:14:05.611283 pwrite(21, "\1\"\0\0i!"..., 1536, 4379136) = 1536