系统调用主要分为几类:

  • 文件和设备访问类 比如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提供了几类常用的系统调用组合名字。 ```cpp

    include

    include

int main(int argc, char **argv) { exit(1); }

  1. strace -tt -e trace=process -f ./test_exit<br />说明: -e trace=process 表示只跟踪和进程管理相关的系统调用。
  2. ```cpp
  3. 23:07:24.672849 execve("./test_exit", ["./test_exit"], [/* 35 vars */]) = 0
  4. 23:07:24.674665 arch_prctl(ARCH_SET_FS, 0x7f1c0eca7740) = 0
  5. 23:07:24.675108 exit_group(1) = ?
  6. 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执行被跟踪的命令