转载地址:http://www.cnblogs.com/ggjucheng/archive/2012/01/08/2316692.html
Strace介绍
strace常用来跟踪进程执行时的系统调用和所接收的信号。 在Linux世界,进程不能直接访问硬件设备,当进程需要访问硬件设备(比如读取磁盘文件,接收网络数据等等)时,必须由用户态模式切换至内核态模式,通 过系统调用访问硬件设备。strace可以跟踪到一个进程产生的系统调用,包括参数,返回值,执行消耗的时间。
输出含义
每一行都是一条系统调用,等号左边是系统调用的函数名及其参数,右边是该调用的返回值。
strace 显示这些调用的参数并返回符号形式的值。strace 从内核接收信息,而且不需要以任何特殊的方式来构建内核。
[root@node1 ~]# strace cat /dev/null
execve("/bin/cat", ["cat", "/dev/null"], [/* 21 vars */]) = 0
brk(0) = 0x21a7000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f0d90019000
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=27376, ...}) = 0
mmap(NULL, 27376, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f0d90012000
close(3) = 0
open("/lib64/libc.so.6", O_RDONLY) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0000\356\1\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=1923352, ...}) = 0
mmap(NULL, 3750184, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f0d8fa67000
mprotect(0x7f0d8fbf1000, 2097152, PROT_NONE) = 0
mmap(0x7f0d8fdf1000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x18a000) = 0x7f0d8fdf1000
mmap(0x7f0d8fdf7000, 14632, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f0d8fdf7000
close(3) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f0d90011000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f0d90010000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f0d9000f000
arch_prctl(ARCH_SET_FS, 0x7f0d90010700) = 0
mprotect(0x7f0d8fdf1000, 16384, PROT_READ) = 0
mprotect(0x7f0d9001a000, 4096, PROT_READ) = 0
munmap(0x7f0d90012000, 27376) = 0
brk(0) = 0x21a7000
brk(0x21c8000) = 0x21c8000
open("/usr/lib/locale/locale-archive", O_RDONLY) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=99164480, ...}) = 0
mmap(NULL, 99164480, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f0d89bd4000
close(3) = 0
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 0), ...}) = 0
open("/dev/null", O_RDONLY) = 3
fstat(3, {st_mode=S_IFCHR|0666, st_rdev=makedev(1, 3), ...}) = 0
read(3, "", 32768) = 0
close(3) = 0
close(1) = 0
close(2) = 0
exit_group(0) = ?
+++ exited with 0 +++
[root@node1 ~]#
strace参数
-c 统计每一系统调用的所执行的时间,次数和出错的次数等.
-d 输出strace关于标准错误的调试信息.
-f 跟踪由fork调用所产生的子进程.
-ff 如果提供-o filename,则所有进程的跟踪结果输出到相应的filename.pid中,pid是个进程的进程号
-F 尝试跟踪vfork调用,-f时,vfork不被跟踪
-h 输出简要的帮助信息
-i 输出系统调用的入口指针,[每行首输出代码地址,利于GDB设置断点调试]
-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,感叹号是否定符号,
例如:-e open 等价于 -e trace=open 表示只跟踪open调用,而-e trace != open 表示除open以外的其他调用都跟踪,有两个特殊符号all 和 none
有些shell使用!来执行历史记录的命令所以要用\\.
-e trace=set
只跟踪指定的系统调用,例如:-e trace=open,close,read,write表示只跟踪这四个系统调用,默认的为set=all.
-e trace=file
只跟踪有关文件操作的系统调用
-e trace=process
只跟踪有关进程控制的系统调用
-e trace=network
跟踪与网络有关的所有系统调用
-e trace=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 -o output.txt -T -tt -e trace=all -p 28979
上面的含义是 跟踪28979进程的所有系统调用(-e trace=all),
并统计系统调用的花费时间,以及开始时间(并以可视化的时分秒格式显示),最后将记录结果存在output.txt文件里面。
strace 调试程序
strace 解决动态依赖库
限制strace只跟踪特定的系统调用
fork() 与 vfork()系统调用的区别:
1.vfork保证子进程先运行,在它调用exec或exit之后父进程才可能被调度运行。如果在调用这两个函数之前子进程依赖于父进程的进一步动作,则会导致死锁。
2.fork要拷贝父进程的进程环境;而vfork则不需要完全拷贝父进程的进程环境,在子进程没有调用exec和exit之前,子进程与父进程共享进程环境,相当于线程的概念,此时父进程阻塞等待。
为什么会有vfork呢?
因为以前的fork当它创建一个子进程时,将会创建一个新的地址空间,并且拷贝父进程的资源,然后将会有两种行为:
1.执行从父进程那里拷贝过来的代码段
2.调用一个exec执行一个新的代码段
当进程调用exec函数时,一个新程序替换了当前进程的正文,数据,堆和栈段。这样,前面的拷贝工作就是白费力气了,这种情况下,聪明的人就想出了vfork。vfork并不复制父进程的进程环境,子进程在父进程的地址空间中运行,所以子进程不能进行写操作,并且在儿子“霸占”着老子的房子时候,要委屈老子一下了,让他在外面歇着(阻塞),一旦儿子执行了exec或者exit后,相当于儿子买了自己的房子了,这时候就相当于分家了。
因此,如果创建子进程是为了调用exec执行一个新的程序的时候,就应该使用vfork