Merge pull request #16184 from wxy/20191025-Understanding-system-calls-on-Linux-with-strace

PRF&PUB:20191025 Understanding system calls on Linux with strace
This commit is contained in:
Xingyu.Wang 2019-11-06 13:31:29 +08:00 committed by GitHub
commit 131208ab11
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -1,8 +1,8 @@
[#]: collector: (lujun9972)
[#]: translator: (wxy)
[#]: reviewer: ( )
[#]: publisher: ( )
[#]: url: ( )
[#]: reviewer: (wxy)
[#]: publisher: (wxy)
[#]: url: (https://linux.cn/article-11545-1.html)
[#]: subject: (Understanding system calls on Linux with strace)
[#]: via: (https://opensource.com/article/19/10/strace)
[#]: author: (Gaurav Kamathe https://opensource.com/users/gkamathe)
@ -10,13 +10,13 @@
在 Linux 上用 strace 来理解系统调用
======
> 使用 strace 跟踪用户进程和 Linux 内核之间的薄层
> 使用 strace 跟踪用户进程和 Linux 内核之间的交互
![Hand putting a Linux file folder into a drawer][1]
<ruby>系统调用<rt>system call</rt></ruby>是程序从内核请求服务的一种编程方式,而 `strace` 是一个功能强大的工具,可让你跟踪用户进程与 Linux 内核之间的薄层
<ruby>系统调用<rt>system call</rt></ruby>是程序从内核请求服务的一种编程方式,而 `strace` 是一个功能强大的工具,可让你跟踪用户进程与 Linux 内核之间的交互
要了解操作系统的工作原理,首先需要了解系统调用的工作原理。操作系统的主要功能之一是为用户程序提供抽象。
要了解操作系统的工作原理,首先需要了解系统调用的工作原理。操作系统的主要功能之一是为用户程序提供抽象机制
操作系统可以大致分为两种模式:
@ -25,9 +25,9 @@
  
用户大多使用命令行实用程序和图形用户界面GUI来执行日常任务。系统调用在后台静默运行与内核交互以完成工作。
系统调用与函数调用非常相似,这意味着它们接受并处理参数然后返回值。唯一的区别是系统调用进入内核,而函数调用不进入。从用户空间切换到内核空间是使用特殊的 [trap][2] 机制完成的。
系统调用与函数调用非常相似,这意味着它们接受并处理参数然后返回值。唯一的区别是系统调用进入内核,而函数调用不进入。从用户空间切换到内核空间是使用特殊的 [trap][2] 机制完成的。
通过使用系统库(在 Linux 系统上又称为 glibc系统调用大部分对用户隐藏了。尽管系统调用本质上是通用的,但是发出系统调用的机制在很大程度上取决于机器。
通过使用系统库(在 Linux 系统上又称为 glibc大部分系统调用对用户隐藏了。尽管系统调用本质上是通用的,但是发出系统调用的机制在很大程度上取决于机器(架构)
本文通过使用一些常规命令并使用 `strace` 分析每个命令进行的系统调用来探索一些实际示例。这些示例使用 Red Hat Enterprise Linux但是这些命令运行在其他 Linux 发行版上应该也是相同的:
@ -71,7 +71,7 @@ yum install strace
(我使用 `/tmp` 目录是因为每个人都可以访问它,但是你可以根据需要选择另一个目录。)
`testdir` 目录下使用 `ls` 命令验证文件已经创建:
`testdir` 目录下使用 `ls` 命令验证文件已经创建:
```
[root@sandbox tmp]# ls testdir/
@ -79,13 +79,11 @@ file1  file2
[root@sandbox tmp]#
```
你可能每天都使用`ls`命令,而没有意识到系统调用在其下面发生的作用。这里有抽象作用。该命令的工作方式如下:
你可能每天都使用 `ls` 命令,而没有意识到系统调用在其下面发挥的作用。抽象地来说,该命令的工作方式如下:
```
Command-line utility -> Invokes functions from system libraries (glibc) -> Invokes system calls
```
> 命令行工具 -> 从系统库glibc调用函数 -> 调用系统调用
`ls` 命令在 Linux 上从系统库(即 glibc内部调用函数。这些库调用完成大部分工作的系统调用。
`ls` 命令内部从 Linux 上的系统库(即 glibc调用函数。这些库去调用完成大部分工作的系统调用。
如果你想知道从 glibc 库中调用了哪些函数,请使用 `ltrace` 命令,然后跟上常规的 `ls testdir/`命令:
@ -99,8 +97,7 @@ ltrace ls testdir/
yum install ltrace
```
一堆输出会被显示到屏幕上;不必担心,只需继续就行。`ltrace` 命令输出中与该示例有关的一些重要库函数包括:
大量的输出会被堆到屏幕上;不必担心,只需继续就行。`ltrace` 命令输出中与该示例有关的一些重要库函数包括:
```
opendir("testdir/") = { 3 }
@ -116,11 +113,11 @@ readdir({ 3 }) = nil
closedir({ 3 })                    
```
通过查看上面的输出,你或许可以了解正在发生的事情。`opendir` 库函数打开一个名为 `testdir` 的目录,然后调用 `readdir` 函数,该函数读取目录的内容。最后,有一个对 `closedir` 函数的调用,该函数将关闭先前打开的目录。现在先忽略其他 `strlen``memcpy` 功能。
通过查看上面的输出,你或许可以了解正在发生的事情。`opendir` 库函数打开一个名为 `testdir` 的目录,然后调用 `readdir` 函数,该函数读取目录的内容。最后,有一个对 `closedir` 函数的调用,该函数将关闭先前打开的目录。现在先忽略其他 `strlen``memcpy` 功能。
你可以看到正在调用哪些库函数,但是本文将重点介绍由系统库函数调用的系统调用。
与上述类似,要了解调用了哪些系统调用,只需将 `strace` 放在 `ls testdir` 命令之前,如下所示。 再次,一堆乱码丢到了你的屏幕上,你可以按照以下步骤进行操作:
与上述类似,要了解调用了哪些系统调用,只需将 `strace` 放在 `ls testdir` 命令之前,如下所示。 再次,一堆乱码丢到了你的屏幕上,你可以按照以下步骤进行操作:
```
[root@sandbox tmp]# strace ls testdir/
@ -137,14 +134,14 @@ exit_group(0) = ?
[root@sandbox tmp]#
```
运行 `strace` 命令后屏幕上的输出是运行 `ls` 命令的系统调用。每个系统调用都为操作系统提供特定的用途,可以将它们大致分为以下几个部分:
运行 `strace` 命令后屏幕上的输出是运行 `ls` 命令的系统调用。每个系统调用都为操作系统提供特定的用途,可以将它们大致分为以下几个部分:
* 进程管理系统调用
* 文件管理系统调用
* 目录和文件系统管理系统调用
* 其他系统调用
分析显示到屏幕上的信息的一种更简单的方法是使用 `strace` 方便使用`-o` 标志将输出记录到文件中。在 `-o` 标志后添加一个合适的文件名,然后再次运行命令:
分析显示到屏幕上的信息的一种更简单的方法是使用 `strace` 方便的 `-o` 标志将输出记录到文件中。在 `-o` 标志后添加一个合适的文件名,然后再次运行命令:
```
[root@sandbox tmp]# strace -o trace.log ls testdir/
@ -173,7 +170,7 @@ execve("/usr/bin/ls", ["ls", "testdir/"], [/* 40 vars */]) = 0
* 括号内的文本是提供给该系统调用的参数。
* 符号 `=` 后的数字(在这种情况下为 `0`)是 `execve` 系统调用的返回值。
现在的输出似乎还不太吓人,不是吗?你可以应用相同的逻辑来理解其他行。
现在的输出似乎还不太吓人,对吧。你可以应用相同的逻辑来理解其他行。
现在,将关注点集中在你调用的单个命令上,即 `ls testdir`。你知道命令 `ls` 使用的目录名称,那么为什么不在 `trace.log` 文件中使用 `grep` 查找 `testdir` 并查看得到的结果呢?让我们详细查看一下结果的每一行:
@ -262,19 +259,19 @@ write(1, "file1  file2\n", 13)          = 13
* `1`:标准输出
* `2`:标准错误
因此,`write` 系统调用将在标准显示(就是终端,由 `1` 所标识的)上显示 `file1``file2`
因此,`write` 系统调用将在标准显示(就是这个终端,由 `1` 所标识的)上显示 `file1``file2`
现在你知道哪个系统调用完成了 `ls testdir/` 命令的大部分工作。但是在 `trace.log` 文件中其它的 100 多个系统调用呢?操作系统必须做很多内务处理才能运行一个进程,因此,你在该日志文件中看到的很多内容都是进程初始化和清理。阅读整个 `trace.log` 文件,并尝试了解什么使 `ls` 命令可以工作
现在你知道哪个系统调用完成了 `ls testdir/` 命令的大部分工作。但是在 `trace.log` 文件中其它的 100 多个系统调用呢?操作系统必须做很多内务处理才能运行一个进程,因此,你在该日志文件中看到的很多内容都是进程初始化和清理。阅读整个 `trace.log` 文件,并尝试了解 `ls` 命令是怎么工作起来的
既然你知道了如何分析给定命令的系统调用,那么就可以将该知识用于其他命令来了解正在执行哪些系统调用。`strace` 提供了许多有用的命令行标志,使你更容易使用,下面将对其中一些进行描述。
默认情况下,`strace` 并不包含所有系统调用信息。但是,它有一个方便的 `-v verbose` 选项,可以在每个系统调用中提供附加信息:
默认情况下,`strace` 并不包含所有系统调用信息。但是,它有一个方便的 `-v` 冗余选项,可以在每个系统调用中提供附加信息:
```
strace -v ls testdir
```
在运行 `strace` 命令时始终使用 `-f` 选项是一种好的作法。它允许 `strace` 跟踪由当前正在跟踪的进程创建的任何子进程:
在运行 `strace` 命令时始终使用 `-f` 选项是一种好的作法。它允许 `strace` 当前正在跟踪的进程创建的任何子进程进行跟踪
```
strace -f ls testdir
@ -286,7 +283,7 @@ strace -f ls testdir
strace -c ls testdir/
```
假设你想专注于特定的系统调用,例如专注于 `open` 系统调用,而忽略其余部分。你可以使用`-e`标志跟上系统调用的名称:
假设你想专注于特定的系统调用,例如专注于 `open` 系统调用,而忽略其余部分。你可以使用`-e` 标志跟上系统调用的名称:
```
[root@sandbox tmp]# strace -e open ls testdir
@ -305,7 +302,7 @@ file1  file2
[root@sandbox tmp]#
```
如果你想关注多个系统调用怎么办?不用担心,你同样可以使用 `-e` 命令行标志,并用逗号分隔开两个系统调用。例如,要查看 `write``getdents` 系统调用:
如果你想关注多个系统调用怎么办?不用担心,你同样可以使用 `-e` 命令行标志,并用逗号分隔开两个系统调用的名称。例如,要查看 `write``getdents` 系统调用:
```
[root@sandbox tmp]# strace -e write,getdents ls testdir
@ -317,11 +314,11 @@ write(1, "file1  file2\n", 13file1  file2
[root@sandbox tmp]#
```
到目前为止,这些示例已明确跟踪了运行的命令。但是,要跟踪已经运行并正在执行的命令又怎么办呢?例如,如果要跟踪只是长时间运行的进程的守护程序,该怎么办?为此,`strace` 提供了一个特殊的 `-p` 标志,你可以向其提供进程 ID。
到目前为止,这些示例是明确地运行的命令进行了跟踪。但是,要跟踪已经运行并正在执行的命令又怎么办呢?例如,如果要跟踪用来长时间运行进程的守护程序,该怎么办?为此,`strace` 提供了一个特殊的 `-p` 标志,你可以向其提供进程 ID。
在守护程序上运行 `strace`,而是以 `cat` 命令为例,如果你将文件名作为参数,通常会显示文件的内容。如果没有给出参数,`cat` 命令会在终端上等待用户输入文本。输入文本后,它将重复给定的文本,直到用户按下 `Ctrl + C` 退出为止。
我们的示例不在守护程序上运行 `strace`,而是以 `cat` 命令为例,如果你将文件名作为参数,通常 `cat` 会显示文件的内容。如果没有给出参数,`cat` 命令会在终端上等待用户输入文本。输入文本后,它将重复给定的文本,直到用户按下 `Ctrl + C` 退出为止。
从一个终端运行 `cat` 命令;它会向你显示一个提示,等待在那里(记住 `cat` 仍在运行且尚未退出):
从一个终端运行 `cat` 命令;它会向你显示一个提示,等待在那里(记住 `cat` 仍在运行且尚未退出):
```
[root@sandbox tmp]# cat
@ -344,7 +341,7 @@ strace: Process 22443 attached
read(0,
```
现在,返回到你使 `cat` 命令运行的终端,并输入一些文本。我出于演示目的输入了 `x0x0`。注意 `cat` 是如何简单地重复我输入的内容。因此,`x0x0` 出现了两次。我输入了第一个,第二个是 `cat` 命令重复的输出:
现在,返回到你运行 `cat` 命令的终端,并输入一些文本。我出于演示目的输入了 `x0x0`。注意 `cat` 是如何简单地重复我输入的内容。因此,`x0x0` 出现了两次。我输入了第一个,第二个是 `cat` 命令重复的输出:
```
[root@sandbox tmp]# cat
@ -352,7 +349,7 @@ x0x0
x0x0
```
返回到将 `strace` 接驳到 `cat` 进程的终端。现在你会看到两个额外的系统调用:较早的 `read` 系统调用,现在在终端中读取 `x0x0`,另一个为 `write`,将 `x0x0` 写回到终端,然后是再一个新的 `read`,正在等待从终端读取。请注意,标准输入(`0`)和标准输出(`1`)都在同一终端中:
返回到将 `strace` 接驳到 `cat` 进程的终端。现在你会看到两个额外的系统调用:较早的 `read` 系统调用,现在在终端中读取 `x0x0`,另一个为 `write``x0x0` 写回到终端,然后是再一个新的 `read`,正在等待从终端读取。请注意,标准输入(`0`)和标准输出(`1`)都在同一终端中:
```
[root@sandbox ~]# strace -p 22443
@ -399,7 +396,7 @@ via: https://opensource.com/article/19/10/strace
作者:[Gaurav Kamathe][a]
选题:[lujun9972][b]
译者:[wxy](https://github.com/wxy)
校对:[校对者ID](https://github.com/校对者ID)
校对:[wxy](https://github.com/wxy)
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出