一个博客

strace

Posted at — Sep 20, 2021

首先贴出strace命令的维基百科解释

strace是Linux环境下的一款程序调试工具,用来监察一个应用程序所使用的系统调用及它所接收的系统信息。

关于strace命令的详细使用方式可以参考这篇文章

众所周知,Linux将内存分为内核态和用户态,大部分操作通常都需要调用系统完成相应调用,用户态和内核态就需要进行通信。接下来给出本人结合strace命令学习操作系统,了解Linux的系统调用的过程。

一些常用命令的系统调用

$ strace echo "hi"

本条命令分析echo hi即在命令行中打印出hi的系统调用过程

给出执行结果如下:

execve("/usr/bin/echo", ["echo", "hi"], 0x7ffc19e5d5e8 /* 65 vars */) = 0
brk(NULL)                               = 0x5624eb957000
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (没有那个文件或目录)
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=112173, ...}) = 0
mmap(NULL, 112173, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7fe3e041b000
close(3)                                = 0
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/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\260A\2\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=1824496, ...}) = 0
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fe3e0419000
mmap(NULL, 1837056, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fe3e0258000
mprotect(0x7fe3e027a000, 1658880, PROT_NONE) = 0
mmap(0x7fe3e027a000, 1343488, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x22000) = 0x7fe3e027a000
mmap(0x7fe3e03c2000, 311296, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x16a000) = 0x7fe3e03c2000
mmap(0x7fe3e040f000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1b6000) = 0x7fe3e040f000
mmap(0x7fe3e0415000, 14336, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7fe3e0415000
close(3)                                = 0
arch_prctl(ARCH_SET_FS, 0x7fe3e041a540) = 0
mprotect(0x7fe3e040f000, 16384, PROT_READ) = 0
mprotect(0x5624eafd1000, 4096, PROT_READ) = 0
mprotect(0x7fe3e045e000, 4096, PROT_READ) = 0
munmap(0x7fe3e041b000, 112173)          = 0
brk(NULL)                               = 0x5624eb957000
brk(0x5624eb978000)                     = 0x5624eb978000
openat(AT_FDCWD, "/usr/lib/locale/locale-archive", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=3246832, ...}) = 0
mmap(NULL, 3246832, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7fe3dff3f000
close(3)                                = 0
fstat(1, {st_mode=S_IFCHR|0600, st_rdev=makedev(0x88, 0x3), ...}) = 0
write(1, "hi\n", 3hi
)                     = 3
close(1)                                = 0
close(2)                                = 0
exit_group(0)                           = ?
+++ exited with 0 +++

可以看到第一个执行的系统调用是execve,其函数原型为

int execve(const char *filename, char *const argv[], char *const envp[]);

在命令输出中也能看见一些蛛丝马迹,即echo命令的位置为/usr/bin目录,hi作为一个参数传入。调用brk分配内存空间,acess检查权限,了解零拷贝的同学可以知道,mmap通常可用于减少read的系统调用次数,从而加快IO速度,mmap将内核缓冲区的数据映射到用户空间,这样可以操作系统和用户空间就不需要进行任何数据拷贝的操作。还有诸如read,open,close,write的基本调用,在许多命令中都可以见到他们的身影,大家可以去试试。

一个简单c程序的系统调用

写一个简单的读取数字再输出的c程序

#include<stdio.h>
void main(){
        int i;
        scanf("%d",&i);
        printf("%d",i);
}

使用gcc编译后执行命令$ strace ./a输出如下

其中在27行的位置调用read进行一次暂停等待用户输入数字,输入后打印完整调用过程。我们可以看到其中mprotect,munmap,lseek等调用就是Linux为了寻址,内存映射等进行的一系列操作。才学疏浅,欢迎指出错误。