Linux进程照妖镜strace命令

strace是个功能强大的Linux调试分析诊断工具,可用于跟踪程序执行时进程系统调用(system call)和所接收的信号,尤其是针对源码不可读或源码无法再编译的程序。在Linux系统中,用户进程不能直接访问计算机硬件设备。当进程需要访问硬件设备(如读取磁盘文件或接收网络数据等)时,必须由用户态模式切换至内核态模式,通过系统调用访问硬件设备。strace可跟踪进程产生的系统调用,包括参数、返回值和执行所消耗的时间。若strace没有任何输出,并不代表此时进程发生阻塞;也可能程序进程正在执行某些不需要与系统其它部分发生通信的事情。strace从内核接收信息,且无需以任何特殊方式来构建内核。

一个简单例子来说明strace命令的作用
1.我们先写一个简单的测试程序,源码如下

#include <stdio.h>
#include <unistd.h>

int main(int argc, char** argv)
{
    char buffer[256];

    getcwd(buffer, 255);

    printf(buffer);

    return 0;
}

2.将上面源码保存为test.c文件,然后执行gcc test.c命令进行编译,编译后生成a.out可执行文件

gcc test.c

3.执行./a.out命令输出如下,测试程序仅仅获取当前路径进行输出

/home/devl

4.执行strace ./a.out命令输出的最后几行如下

getcwd("/home/devl", 255) = 11
fstat(1, {st_mode=S_IFCHR|0620,  ...})  = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, ...) = 0x7f7f0a8b2000
write(1, "/home/devl", 10/home/devl) = 10
exit_group(0) = ?
+++ exited with 0 +++

可以看到a.out调用了getcwd函数,传入可用长度为255的缓冲区,将11字节(包含字符串结束标志)的/home/devl内容复制到缓冲区中,然后调用write函数将10个字符的/home/devl写入编号为1的文件中(编号为文件实际上就是标准输出设备stdout)。从上面输出内容中可以看出C语言库函数printf实际上会调用以下三个系统调用:

  • fstat检查文件状态
  • mmap将文件地址映射到内存空间
  • write写入文件内容

我们将上面的代码最后多加几个printf函数,调整后的代码如下:

#include <stdio.h>
#include <unistd.h>

int main(int argc, char** argv)
{
    char buffer[256];

    getcwd(buffer, 255);

    printf(buffer);
    printf(buffer);
    printf(buffer);

    return 0;
}

保存调整后的代码,依次执行gcc test.c与strace ./a.out输出内容如下:

munmap(0x7f3dcf146000, 47893) = 0
getcwd("/home/devl", 255) = 11
fstat(1, {st_mode=S_IFCHR|0620, ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, ...) = 0x7f3dcf151000
write(1, "/home/devl/home/devl/home/devl", 30/home/devl/home/devl/home/devl) = 30
exit_group(0) = ?
+++ exited with 0 +++

很明显程序中系统调用没有任何变化,1个pirntf函数与3个printf函数的系统调用完全相同,说明printf函数在对连续打印进行了优化。这个优化是依赖mmap函数的,可以断定这种情况下调用printf函数只是将内存拷贝到输出缓冲区中,最后一次性调用write函数输出全部缓冲区的内容。

我们不防再做一个实验,在上面的buffer后加上一个换行符,调整后的代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

int main(int argc, char** argv)
{
    char buffer[256];

    getcwd(buffer, 255);

    strcat(buffer, "\n");
    printf(buffer);
    printf(buffer);
    printf(buffer);

    return 0;
}

保存调整后的代码,依次执行gcc test.c与strace ./a.out输出内容如下:

getcwd("/home/devl", 255) = 11
fstat(1, {st_mode=S_IFCHR|0620, ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, ...)= 0 x7f947d400000
write(1, "/home/devl\n", 11/home/devl
)            = 11
write(1, "/home/devl\n", 11/home/devl
)            = 11
write(1, "/home/devl\n", 11/home/devl
)            = 11
exit_group(0) = ?
+++ exited with 0 +++

结果非常有意思,这次竟然调用3次write函数。实际上printf函数在遇到换行符\n时会刷新输出缓冲区,我们可以大概猜到刷新输出缓冲区实际上就是执行write函数将缓冲区内容写入到具体文件或设备。
常用命令
strace command

执行名称为command的命令或程序并跟踪系统调用
strace -p procid

跟踪ID为的procid的进程系统调用情况
strace -c -p procid

统计ID为的procid的进程系统调用次数与用时,按CTRL+C结束统计,执行结果如下:

[work@xungen ~]$ strace -c -p 27637
strace: Process 27637 attached
strace: Process 27637 detached
% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
 99.26    0.091733       30578         3           accept
  0.73    0.000671         224         3           epoll_ctl
  0.01    0.000010           2         6           setsockopt
------ ----------- ----------- --------- --------- ----------------
100.00    0.092414                    12           total

https://zhuanlan.zhihu.com/p/69527356

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值