strace :监控程序的执行状况
在linux 空间下,运行一个程序时,操作系统会将应用程序封装一个进程的形式,参与操作系统的调度,可以使用strace 跟踪程序运行的情况。
基本功能
监控用户进程与内核进程的交互
追踪进程的系统调用、信号传递、状态变化
一、系统调用
分类
文件和设备访问:open、close、read、write、ioctl等
进程管理:fork、clone、execve、exit 等
信号:signal、kill等
内存管理:brk、mmap、mlock等
进程间通信:semget、信号量、消息队列
网络通信:socket、connect等
常用参数
-c 统计每一系统调用的所执行的时间,次数和出错的次数等
-d 输出strace关于标准错误的调试信息
-f 跟踪由目标进程及调用所产生的子进程
-F 尝试跟踪目标调用 在-f时,vfork不被跟踪
-a 设置返回值的输出位置.默认 为40
-r 打印出相对时间关于每一个系统调用
-t 在每行输出的后面,显示调用花费时间
-tt 在每行输出的前面,显示调用花费毫秒级别的时间
-T 每次系统调用所花费的时间
-v 对某些相关调用,把完整的环境变量、文件stat结构打印出来
-p pid 指定要跟踪的进程pid,同时跟踪的多个pid,重复多次-p选项即可
-o filename: 将跟踪输出写入文件名
-s 当系统调用的某个参数是字符串时,最多输出制定长度的内容,默认时32个字节
-e set: 仅跟踪某些系统调用
-e open,close: 仅跟踪打开/关闭系统调用
-e file: 仅跟踪文件系统调用/文件操作相关的
-e process: 跟踪所有涉及流程管理的系统调用
-e network: 跟踪所有与网络相关的系统调用
-e signal:跟踪所有与信号相关的系统调用
-e ipc: 跟踪所有与ipc相关的系统调用
-e desc: 跟踪所有与文件描述符相关的系统调用
-e memory: 跟踪所有与内存映射相关的系统调用
-e set: 仅跟踪指定的信号子集
示例《一》
查看一个程序所有的open、close系统调用
查看每个程序调用消耗的时间
统计系统调用次数、错误次数统计
打印系统调用的时间戳
将追踪日志保存到log文件中
注释:
查看所有程序运行 strace python server.py
查看所有程序运行 strace ./aa.sh
查看打开系统调用情况 strace -e open ./aa.sh
查看打开关闭系统调用情况 strace -e open,close ./aa.sh
查看打开关闭系统调用时间 strace -T -e open,close python3 main.py (T 时间在后面显示)
查看打开关闭系统调用次数 strace -c -T -e open,close ./aa.sh (如程序一直在运行,停止后才会统计)
查看打开关闭系统调用时间 strace -t -T -e open,close ./aa.sh
查看打开关闭系统调用时间 strace -tt -T -e open,close ./aa.sh (tt
查看打开系统调用输入log strace -tt -T -e open -o trace.log ./aa.sh (-o 输入trace.log文件)
strace -tt -T -V -e trace=file -o /data/log/trace.log -s 1024 -p 23489
二、系统调用函数说明
进程管理
函数名 说明
fork 创建一个新进程
clone 按制定条件创建子进程
execve 运行可执行文件
pause 进程将处于阻塞状态
wait 等待子进程终止
waitpid 等待制定子进程终止
文件和设备访问
函数名 说明
open 打开文件
create 创建新文件
close 关闭文件描述符
read 读文件
write 写文件
pread 对文件随机读
pwrite 对文件随机写
poll I/o多路转换
truncate 截断文件
文件系统操作
函数名 说明
access 确定文件的可存取性
chmod 确定文件的可存取性
chown 改变文件的属主或用户组
chroot 改变文件状态信息
stat 获取文件的状态信息
lstat 获取文件的状态信息
getdents 读取目录项
mkdir 创建目录
link 建立链接
内存管理
函数名 说明
mmap 映射虚拟内存页(分配内存)
sync 将内存缓冲区数据写回硬盘
socket 控制
函数名 说明
socketcall socket系统调用
socket 建立socket
bind 绑定socket到端口
connect 链接远程主机
send 通过socket发送信息
sendto 发送UDP信息
sendmsg 参见send
listen 监听socket端口
select 对多路同步I/O进行轮询
示例《二》
ping baidu系统调用过程
ping www.baidu.com
strace ping www.baidu.com
strace -e poll,select,connect,sendto ping www.baidu.com
cat /etc/passwd 系统调用过程
strace cat /etc/passwd
[root@VM-0-14-centos ~]# strace cat /etc/passwd
运行命令
execve("/usr/bin/cat", ["cat", "/etc/passwd"], 0x7ffeee8bfe78 /* 28 vars */) = 0
brk(NULL) = 0x1d9f000
为运行命名分配内存空间
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f6081756000
确定文件的可存取性 -1 表示文件不可读
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (没有那个文件或目录)
打开文件
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
文件状态:作用于已打开的文件的文件描述符而不是文件名
fstat(3, {st_mode=S_IFREG|0644, st_size=37645, ...}) = 0
mmap(NULL, 37645, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f608174c000
close(3) = 0
open("/lib64/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
读取文件
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0`&\2\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=2156352, ...}) = 0
mmap(NULL, 3985920, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f6081168000
参见下文mprotect 命令
mprotect(0x7f608132c000, 2093056, PROT_NONE) = 0
mmap(0x7f608152b000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1c3000) = 0x7f608152b000
mmap(0x7f6081531000, 16896, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f6081531000
close(3) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f608174b000
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f6081749000
arch_prctl(ARCH_SET_FS, 0x7f6081749740) = 0
mprotect(0x7f608152b000, 16384, PROT_READ) = 0
mprotect(0x60b000, 4096, PROT_READ) = 0
mprotect(0x7f6081757000, 4096, PROT_READ) = 0
munmap(0x7f608174c000, 37645) = 0
brk(NULL) = 0x1d9f000
brk(0x1dc0000) = 0x1dc0000
brk(NULL) = 0x1dc0000
open("/usr/lib/locale/locale-archive", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=106176928, ...}) = 0
mmap(NULL, 106176928, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f607ac25000
close(3) = 0
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 0), ...}) = 0
open("/etc/passwd", O_RDONLY) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=1419, ...}) = 0
fadvise64(3, 0, 0, POSIX_FADV_SEQUENTIAL) = 0
read(3, "root:x:0:0:root:/root:/bin/bash\n"..., 65536) = 1419
write(1, "root:x:0:0:root:/root:/bin/bash\n"..., 1419root:x:0:0:root:
在Linux中,mprotect()函数可以用来修改一段指定内存区域的保护属性。
函数原型如下:
#include <unistd.h>
#include <sys/mmap.h>
int mprotect(const void *start, size_t len, int prot);
mprotect()
函数把自start开始的、长度为len的内存区的保护属性修改为prot指定的值。
prot可以取以下几个值,并且可以用“|”将几个属性合起来使用:
1)PROT_READ:表示内存段内的内容可写;
2)PROT_WRITE:表示内存段内的内容可读;
3)PROT_EXEC:表示内存段中的内容可执行;
4)PROT_NONE:表示内存段中的内容根本没法访问。
其他:
需要指出的是,指定的内存区间必须包含整个内存页(4K)。区间开始的地址start必须是一个内存页的起始地址,并且区间长度len必须是页大小的整数倍。