在linux 空间下,运行一个程序时,操作系统会将应用程序封装一个进程的形式,参与操作系统的调度,可以使用strace 跟踪程序运行的情况。
-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: 仅跟踪指定的信号子集
注释:
查看所有程序运行 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:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
operator:x:11:0:operator:/root:/sbin/nologin
games:x:12:100:games:/usr/games:/sbin/nologin
ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin
nobody:x:99:99:Nobody:/:/sbin/nologin
systemd-network:x:192:192:systemd Network Management:/:/sbin/nologin
dbus:x:81:81:System message bus:/:/sbin/nologin
polkitd:x:999:998:User for polkitd:/:/sbin/nologin
libstoragemgmt:x:998:997:daemon account for libstoragemgmt:/var/run/lsm:/sbin/nologin
rpc:x:32:32:Rpcbind Daemon:/var/lib/rpcbind:/sbin/nologin
ntp:x:38:38::/etc/ntp:/sbin/nologin
abrt:x:173:173::/etc/abrt:/sbin/nologin
sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin
postfix:x:89:89::/var/spool/postfix:/sbin/nologin
chrony:x:997:995::/var/lib/chrony:/sbin/nologin
tcpdump:x:72:72::/:/sbin/nologin
syslog:x:996:994::/home/syslog:/bin/false
mysql:x:27:27:MySQL Server:/var/lib/mysql:/bin/false
tss:x:59:59:Account used by the trousers package to sandbox the tcsd daemon:/dev/null:/sbin/nologin
nginx:x:995:991:nginx user:/var/cache/nginx:/sbin/nologin
redis:x:994:990:Redis Database Server:/var/lib/redis:/sbin/nologin
) = 1419
read(3, "", 65536) = 0
close(3) = 0
close(1) = 0
close(2) = 0
exit_group(0) = ?
+++ exited with 0 +++
在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必须是页大小的整数倍。