shell调试手段三则
1. time
用途说明
time命令常用于测量一个命令的运行时间,注意不是用来显示和修改系统时间的(这是date命令干的事情)。常用参数
time命令最常用的使用方式就是在其后面直接跟上命令和参数:
time <command> [<arguments...>]
在命令执行完成之后就会打印出CPU的使用情况
real 0m5.064s <== 实际使用时间(real time)
user 0m0.020s <== 用户态使用时间(the process spent in user mode)
sys 0m0.040s <== 内核态使用时间(the process spent in kernel mode)
time命令跟上-p参数可以只打印时间数值(秒数),不打印单位。
使用示例
使用示例示例一 统计运行时间::
[root@web186 root]# time find . -name "mysql.sh"
./work186/sms/bin/mysql.sh
./work186/sms/src/scripts/mysql.sh
real 0m14.837s
user 0m0.030s
sys 0m0.120s
[root@web186 root]#
注: **real远大于user加上sys,因为find需要遍历各个目录,需要大量的I/O操作,
而磁盘I/O通常是最慢的环节,因此大部分时间find进程都在等待磁盘I/O完成。**
其实系统中有两个time
[root@web186 root]# type -a time
time is a shell keyword
time is /usr/bin/time
注:通过这条命令我们可以发现我们常用的time其实是一个Shell关键字,
还有一个外部命令/usr/bin/time,它有何不同呢?::
[root@web186 root]# /usr/bin/time
Usage: /usr/bin/time [-apvV] [-f format] [-o file] [--append] [--verbose]
[--portability] [--format=format] [--output=file] [--version]
[--help] command [arg...]
注:外部命令/usr/bin/time功能更强大。
解决time命令输出信息的重定向问题
time命令的输出信息是打印在标准错误输出上的, 我们通过一个简单的尝试来验证一下。:: [root@web186 root]# time find . -name "mysql.sh" >1.txt
real 0m0.081s
user 0m0.060s
sys 0m0.020s
*通过上面的尝试,发现无法将time的输出信息重定向到文件里面,为什么?
因为time是shell的关键字,shell做了特殊处理,它会把time命令后面的命令行作为一个
整体来进行处理,在重定向时,实际上是针对后面的命令来的,
time命令本身的输出并不会被重定向的。那现在怎么办呢?*
第一种解决方法,就是将time命令和将要执行的命令行放到一个shell代码块中,
也就是一对大括号中,要注意空格和分号的使用。::
[root@web186 root]# { time find . -name "mysql.sh"; } 2>2.txt
./work186/sms/bin/mysql.sh
./work186/sms/src/scripts/mysql.sh
[root@web186 root]# cat 2.txt
real 0m0.068s
user 0m0.030s
sys 0m0.040s
另外一种方式就是使用子Shell的方式,如下所示::
[root@web186 root]# (time find . -name "mysql.sh") 2>2.txt
./work186/sms/bin/mysql.sh
./work186/sms/src/scripts/mysql.sh
[root@web186 root]# cat 2.txt
real 0m0.083s
user 0m0.040s
sys 0m0.020s
[root@web186 root]#
2. lsof
lsof是遵从Unix哲学的典范,它只做一件事情,并且做的相当完美——它可以列出某个进程打开的所有文件信息。打开的文件可能是普通的文件,目录,NFS文件,块文件,
字符文件,共享库,常规管道,明明管道,符号链接,Socket流,网络Socket,
UNIX域Socket,以及其它更多。因为Unix系统中几乎所有东西都是文件,你可以想象lsof该有多有用。
找出谁在使用某个文件
# lsof /path/to/file
只需要执行文件的路径,lsof就会列出所有使用这个文件的进程,你也可以列出多个文件,lsof会列出所有使用这些文件的进程。
递归查找某个目录中所有打开的文件
# lsof +D /usr/lib
加上+D参数,lsof会对指定目录进行递归查找,注意这个参数要比grep版本慢::
# lsof | grep '/usr/lib'
之所以慢是因为+D首先查找所有的文件,然后一次性输出。
列出某个用户打开的所有文件
# lsof -u rms,root
这条命令会列出所有rms和root用户打开的文件。
查找某个程序打开的所有文件
# lsof -c apache
-c选项限定只列出以apache开头的进程打开的文件:
列出所有由某个PID对应的进程打开的文件
# lsof -p 450,980,333
-p选项让你可以使用进程id来过滤输出。
列出所有网络连接
# lsof -i
lsof的-i选项可以列出所有打开了网络套接字(TCP和UDP)的进程。
列出所有TCP网络连接
# lsof -i tcp
也可以为-i选项加上参数,比如tcp,tcp选项会强制lsof只列出打开TCP sockets的进程。
找到使用某个端口的进程
# lsof -i :25
:25和-i选项组合可以让lsof列出占用TCP或UDP的25端口的进程。
你也可以使用/etc/services中制定的端口名称来代替端口号,比如
# lsof -i :smtp
找到使用某个udp端口号的进程::
# lsof -i udp:53
3. strace
[root@localhost ~]# ps -ef|grep rb; netstat -anp | grep rb
root 1613 1 0 13:36 ? 00:00:00 ./rbtunnel lijuanping@itcchina.com 1028
root 4809 4720 0 14:21 pts/3 00:00:00 grep rb
root 13236 1 0 Dec10 ? 00:00:00 ./rbserver
...
tcp 0 0 0.0.0.0:13651 0.0.0.0:* LISTEN 13236/./rbserver
tcp 20 0 124.232.156.156:15056 114.255.102.198:57256 CLOSE_WAIT 1613/./rbtunnel
tcp 136458 0 124.232.156.156:15057 221.219.116.77:1182 ESTABLISHED 1613/./rbtunnel
tcp 1117 26064 124.232.156.156:15056 114.255.102.198:57667 ESTABLISHED 1613/./rbtunnel
发现进程1613的数据转发有问题,使用strace来看一下。
[root@localhost ~]# strace -p 1613
Process 1613 attached - interrupt to quit
gettimeofday({1355293347, 691650}, NULL) = 0
write(3, "I 0 14:20:25.792080 action.tcp_r"..., 97) = 97
select(10, [4 6 8 9], NULL, NULL, {30, 0}) = 3 (in [4 8 9], left {30, 0})
recv(9, "\36\3\317\324\n_\305\344\1\236|\317\31\\\361\r`\310U5*\266\2155\320R\3158\347\342\307\310"..., 4095, 0) = 4095
gettimeofday({1355293347, 713394}, NULL) = 0
send(8, "\36\3\317\324\n_\305\344\1\236|\317\31\\\361\r`\310U5*\266\2155\320R\3158\347\342\307\310"..., 4095, 0
Ctrl+C
<unfinished ...>
Process 1613 detached
[root@localhost ~]# )
参考资料:
Linux命令之time - 测定一个命令的资源使用情况Unix调试的瑞士军刀:lsof
使用strace找出程序在哪个地方吊死