系统调用主要分为几类:
- 文件和设备访问类 比如open/close/read/write/chmod等
- 进程管理类 fork/clone/execve/exit/getpid等
- 信号类 signal/sigaction/kill 等
- 内存管理 brk/mmap/mlock等
- 进程间通信IPC shmget/semget * 信号量,共享内存,消息队列等
网络通信 socket/connect/sendto/sendmsg
命令详解
strace -tt -T -v -f -e trace=file -o /data/log/strace.log -s 1024 -p 23489
strace -tt -T -f -e trace=file -o /data/log/strace.log -s 1024 ./nginx-tt 在每行输出的前面,显示毫秒级别的时间
- -T 显示每次系统调用所花费的时间
- -v 对于某些相关调用,把完整的环境变量,文件stat结构等打出来。
- -f 跟踪目标进程,以及目标进程创建的所有子进程
- -e 控制要跟踪的事件和跟踪行为,比如指定要跟踪的系统调用名称
- -o 把strace的输出单独写到指定的文件
- -s 当系统调用的某个参数是字符串时,最多输出指定长度的内容,默认是32个字节
- -p 指定要跟踪的进程pid, 要同时跟踪多个pid, 重复多次-p选项即可。
- -c 统计每一系统调用的所执行的时间,次数和出错的次数等.
strace的-e trace选项
要跟踪某个具体的系统调用,-e trace=xxx即可。但有时候我们要跟踪一类系统调用,比如所有和文件名有关的调用、所有和内存分配有关的调用。
如果人工输入每一个具体的系统调用名称,可能容易遗漏。于是strace提供了几类常用的系统调用组合名字。 ```cppinclude
include
int main(int argc, char **argv) { exit(1); }
strace -tt -e trace=process -f ./test_exit<br />说明: -e trace=process 表示只跟踪和进程管理相关的系统调用。
```cpp
23:07:24.672849 execve("./test_exit", ["./test_exit"], [/* 35 vars */]) = 0
23:07:24.674665 arch_prctl(ARCH_SET_FS, 0x7f1c0eca7740) = 0
23:07:24.675108 exit_group(1) = ?
23:07:24.675259 +++ exited with 1 +++
- -e trace=file 跟踪和文件访问相关的调用(参数中有文件名)
- -e trace=process 和进程管理相关的调用,比如fork/exec/exit_group
- -e trace=network 和网络通信相关的调用,比如socket/sendto/connect
- -e trace=signal 信号发送和处理相关,比如kill/sigaction
- -e trace=desc 和文件描述符相关,比如write/read/select/epoll等
- -e trace=ipc 进程间通信相关,比如shmget等
-e trace=clone 知道创建进程使用的是fork系统调用
定位共享内存异常
有个服务启动时报错:
shmget 267264 30097568: Invalid argument Can not get shm...exit!
错误日志大概告诉我们是获取共享内存出错,通过strace看下:
strace -tt -f -e trace=ipc ./a_mon_svr ../conf/a_mon_svr.conf
输出:22:46:36.351798 shmget(0x5feb, 12000, 0666) = 0 22:46:36.351939 shmat(0, 0, 0) = ? Process 21406 attached 22:46:36.355439 shmget(0x41400, 30097568, 0666) = -1 EINVAL (Invalid argument) shmget 267264 30097568: Invalid argument Can not get shm...exit!
这里,我们通过-e trace=ipc 选项,让strace只跟踪和进程通信相关的系统调用。
从strace输出,我们知道是shmget系统调用出错了,errno是EINVAL。同样, 查询下shmget手册页,搜索EINVAL的错误码的说明:EINVAL A new segment was to be created and size < SHMMIN or size > SHMMAX, or no new segment was to be created, a segment with given key existed, but size is greater than the size of that segment
翻译下,shmget设置EINVAL错误码的原因为下列之一:
要创建的共享内存段比 SHMMIN小 (一般是1个字节)
- 要创建的共享内存段比 SHMMAX 大 (内核参数kernel.shmmax配置)
- 指定key的共享内存段已存在,其大小和调用shmget时传递的值不同。
从strace输出看,我们要连的共享内存key 0x41400, 指定的大小是30097568字节,明显与第1、2种情况不匹配。那只剩下第三种情况。使用ipcs -m看下是否真的是大小不匹配:
ipcs -m | grep 41400
key shmid owner perms bytes nattch status
0x00041400 1015822 root 666 30095516 1
可以看到,已经0x41400这个key已经存在,并且其大小为30095516字节,和我们调用参数中的30097568不匹配,于是产生了这个错误。
在我们这个案例里面,导致共享内存大小不一致的原因,是一组程序中,其中一个编译为32位,另外一个编译为64位,代码里面使用了long这个变长int数据类型。
把两个程序都编译为64解决了这个问题。
某个进程现在在做什么?
某个进程突然占用了很多CPU? 或者某个进程看起来像hanging了?
找到对应的pid,然后
hang:
悬挂,挂起的意思
就是一个进程被暂时停止执行.
hang
root@dev:~# strace -p 15427
Process 15427 attached - interrupt to quit
futex(0x402f4900, FUTEX_WAIT, 2, NULL
Process 15427 detached
嗯,这个例子里面,它在调用futex()的时候挂起了
完整参数理解
-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=
只跟踪指定的系统 调用.例如:-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=
设定 strace输出的系统调用的结果集.-v 等与 abbrev=none.默认为abbrev=all.
-e raw=
将指 定的系统调用的参数以十六进制显示.
-e signal=
指定跟踪的系统信号.默认为all.如 signal=!SIGIO(或者signal=!io),表示不跟踪SIGIO信号.
-e read=
输出从指定文件中读出 的数据.例如:
-e read=,
-e write=
输出写入到指定文件中的数据.
-o filename
将strace的输出写入文件filename
-p pid
跟踪指定的进程pid.
-s strsize
指定输出的字符串的最大长度.默认为32.文件名一直全部输出.
-u username
以username 的UID和GID执行被跟踪的命令