在 Linux 上用 strace 來理解系統呼叫
使用 strace 跟蹤使用者程式和 Linux 核心之間的互動。
系統呼叫是程式從核心請求服務的一種程式設計方式,而 strace
是一個功能強大的工具,可讓你跟蹤使用者程式與 Linux 核心之間的互動。
要了解作業系統的工作原理,首先需要了解系統呼叫的工作原理。作業系統的主要功能之一是為使用者程式提供抽象機制。
作業系統可以大致分為兩種模式:
- 核心模式:作業系統核心使用的一種強大的特權模式
- 使用者模式:大多數使用者應用程式執行的地方 使用者大多使用命令列實用程式和圖形使用者介面(GUI)來執行日常任務。系統呼叫在後臺靜默執行,與核心互動以完成工作。
系統呼叫與函式呼叫非常相似,這意味著它們都接受並處理引數然後返回值。唯一的區別是系統呼叫進入核心,而函式呼叫不進入。從使用者空間切換到核心空間是使用特殊的 trap 機制完成的。
通過使用系統庫(在 Linux 系統上又稱為 glibc),大部分系統呼叫對使用者隱藏了。儘管系統呼叫本質上是通用的,但是發出系統呼叫的機制在很大程度上取決於機器(架構)。
本文通過使用一些常規命令並使用 strace
分析每個命令進行的系統呼叫來探索一些實際示例。這些示例使用 Red Hat Enterprise Linux,但是這些命令執行在其他 Linux 發行版上應該也是相同的:
[root@sandbox ~]# cat /etc/redhat-release
Red Hat Enterprise Linux Server release 7.7 (Maipo)
[root@sandbox ~]#
[root@sandbox ~]# uname -r
3.10.0-1062.el7.x86_64
[root@sandbox ~]#
首先,確保在系統上安裝了必需的工具。你可以使用下面的 rpm
命令來驗證是否安裝了 strace
。如果安裝了,則可以使用 -V
選項檢查 strace
實用程式的版本號:
[root@sandbox ~]# rpm -qa | grep -i strace
strace-4.12-9.el7.x86_64
[root@sandbox ~]#
[root@sandbox ~]# strace -V
strace -- version 4.12
[root@sandbox ~]#
如果沒有安裝,執行命令安裝:
yum install strace
出於本示例的目的,在 /tmp
中建立一個測試目錄,並使用 touch
命令建立兩個檔案:
[root@sandbox ~]# cd /tmp/
[root@sandbox tmp]#
[root@sandbox tmp]# mkdir testdir
[root@sandbox tmp]#
[root@sandbox tmp]# touch testdir/file1
[root@sandbox tmp]# touch testdir/file2
[root@sandbox tmp]#
(我使用 /tmp
目錄是因為每個人都可以訪問它,但是你可以根據需要選擇另一個目錄。)
在 testdir
目錄下使用 ls
命令驗證該檔案已經建立:
[root@sandbox tmp]# ls testdir/
file1 file2
[root@sandbox tmp]#
你可能每天都在使用 ls
命令,而沒有意識到系統呼叫在其下面發揮的作用。抽象地來說,該命令的工作方式如下:
命令列工具 -> 從系統庫(glibc)呼叫函式 -> 呼叫系統呼叫
ls
命令內部從 Linux 上的系統庫(即 glibc)呼叫函式。這些庫去呼叫完成大部分工作的系統呼叫。
如果你想知道從 glibc 庫中呼叫了哪些函式,請使用 ltrace
命令,然後跟上常規的 ls testdir/
命令:
ltrace ls testdir/
如果沒有安裝 ltrace
,鍵入如下命令安裝:
yum install ltrace
大量的輸出會被堆到螢幕上;不必擔心,只需繼續就行。ltrace
命令輸出中與該示例有關的一些重要庫函式包括:
opendir("testdir/") = { 3 }
readdir({ 3 }) = { 101879119, "." }
readdir({ 3 }) = { 134, ".." }
readdir({ 3 }) = { 101879120, "file1" }
strlen("file1") = 5
memcpy(0x1665be0, "file1\0", 6) = 0x1665be0
readdir({ 3 }) = { 101879122, "file2" }
strlen("file2") = 5
memcpy(0x166dcb0, "file2\0", 6) = 0x166dcb0
readdir({ 3 }) = nil
closedir({ 3 })
通過檢視上面的輸出,你或許可以瞭解正在發生的事情。opendir
庫函式開啟一個名為 testdir
的目錄,然後呼叫 readdir
函式,該函式讀取目錄的內容。最後,有一個對 closedir
函式的呼叫,該函式將關閉先前開啟的目錄。現在請先忽略其他 strlen
和 memcpy
功能。
你可以看到正在呼叫哪些庫函式,但是本文將重點介紹由系統庫函式呼叫的系統呼叫。
與上述類似,要了解呼叫了哪些系統呼叫,只需將 strace
放在 ls testdir
命令之前,如下所示。 再次,一堆亂碼丟到了你的螢幕上,你可以按照以下步驟進行操作:
[root@sandbox tmp]# strace ls testdir/
execve("/usr/bin/ls", ["ls", "testdir/"], [/* 40 vars */]) = 0
brk(NULL) = 0x1f12000
<<< truncated strace output >>>
write(1, "file1 file2\n", 13file1 file2
) = 13
close(1) = 0
munmap(0x7fd002c8d000, 4096) = 0
close(2) = 0
exit_group(0) = ?
+++ exited with 0 +++
[root@sandbox tmp]#
執行 strace
命令後螢幕上的輸出就是執行 ls
命令的系統呼叫。每個系統呼叫都為作業系統提供了特定的用途,可以將它們大致分為以下幾個部分:
- 程式管理系統呼叫
- 檔案管理系統呼叫
- 目錄和檔案系統管理系統呼叫
- 其他系統呼叫
分析顯示到螢幕上的資訊的一種更簡單的方法是使用 strace
方便的 -o
標誌將輸出記錄到檔案中。在 -o
標誌後新增一個合適的檔名,然後再次執行命令:
[root@sandbox tmp]# strace -o trace.log ls testdir/
file1 file2
[root@sandbox tmp]#
這次,沒有任何輸出干擾螢幕顯示,ls
命令如預期般工作,顯示了檔名並將所有輸出記錄到檔案 trace.log
中。僅僅是一個簡單的 ls
命令,該檔案就有近 100 行內容:
[root@sandbox tmp]# ls -l trace.log
-rw-r--r--. 1 root root 7809 Oct 12 13:52 trace.log
[root@sandbox tmp]#
[root@sandbox tmp]# wc -l trace.log
114 trace.log
[root@sandbox tmp]#
讓我們看一下這個示例的 trace.log
檔案的第一行:
execve("/usr/bin/ls", ["ls", "testdir/"], [/* 40 vars */]) = 0
- 該行的第一個單詞
execve
是正在執行的系統呼叫的名稱。 - 括號內的文字是提供給該系統呼叫的引數。
- 符號
=
後的數字(在這種情況下為0
)是execve
系統呼叫的返回值。
現在的輸出似乎還不太嚇人,對吧。你可以應用相同的邏輯來理解其他行。
現在,將關注點集中在你呼叫的單個命令上,即 ls testdir
。你知道命令 ls
使用的目錄名稱,那麼為什麼不在 trace.log
檔案中使用 grep
查詢 testdir
並檢視得到的結果呢?讓我們詳細檢視一下結果的每一行:
[root@sandbox tmp]# grep testdir trace.log
execve("/usr/bin/ls", ["ls", "testdir/"], [/* 40 vars */]) = 0
stat("testdir/", {st_mode=S_IFDIR|0755, st_size=32, ...}) = 0
openat(AT_FDCWD, "testdir/", O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC) = 3
[root@sandbox tmp]#
回顧一下上面對 execve
的分析,你能說一下這個系統呼叫的作用嗎?
execve("/usr/bin/ls", ["ls", "testdir/"], [/* 40 vars */]) = 0
你無需記住所有系統呼叫或它們所做的事情,因為你可以在需要時參考文件。手冊頁可以解救你!在執行 man
命令之前,請確保已安裝以下軟體包:
[root@sandbox tmp]# rpm -qa | grep -i man-pages
man-pages-3.53-5.el7.noarch
[root@sandbox tmp]#
請記住,你需要在 man
命令和系統呼叫名稱之間新增 2
。如果使用 man man
閱讀 man
命令的手冊頁,你會看到第 2 節是為系統呼叫保留的。同樣,如果你需要有關庫函式的資訊,則需要在 man
和庫函式名稱之間新增一個 3
。
以下是手冊的章節編號及其包含的頁面型別:
1
:可執行的程式或 shell 命令2
:系統呼叫(由核心提供的函式)3
:庫呼叫(在程式的庫內的函式)4
:特殊檔案(通常出現在/dev
)
使用系統呼叫名稱執行以下 man
命令以檢視該系統呼叫的文件:
man 2 execve
按照 execve
手冊頁,這將執行在引數中傳遞的程式(在本例中為 ls
)。可以為 ls
提供其他引數,例如本例中的 testdir
。因此,此係統呼叫僅以 testdir
作為引數執行 ls
:
execve - execute program
DESCRIPTION
execve() executes the program pointed to by filename
下一個系統呼叫,名為 stat
,它使用 testdir
引數:
stat("testdir/", {st_mode=S_IFDIR|0755, st_size=32, ...}) = 0
使用 man 2 stat
訪問該文件。stat
是獲取檔案狀態的系統呼叫,請記住,Linux 中的一切都是檔案,包括目錄。
接下來,openat
系統呼叫將開啟 testdir
。密切注意返回的 3
。這是一個檔案描述符,將在以後的系統呼叫中使用:
openat(AT_FDCWD, "testdir/", O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC) = 3
到現在為止一切都挺好。現在,開啟 trace.log
檔案,並轉到 openat
系統呼叫之後的行。你會看到 getdents
系統呼叫被呼叫,該呼叫完成了執行 ls testdir
命令所需的大部分操作。現在,從 trace.log
檔案中用 grep
獲取 getdents
:
[root@sandbox tmp]# grep getdents trace.log
getdents(3, /* 4 entries */, 32768) = 112
getdents(3, /* 0 entries */, 32768) = 0
[root@sandbox tmp]#
getdents
的手冊頁將其描述為 “獲取目錄項”,這就是你要執行的操作。注意,getdents
的引數是 3
,這是來自上面 openat
系統呼叫的檔案描述符。
現在有了目錄列表,你需要一種在終端中顯示它的方法。因此,在日誌中用 grep
搜尋另一個用於寫入終端的系統呼叫 write
:
[root@sandbox tmp]# grep write trace.log
write(1, "file1 file2\n", 13) = 13
[root@sandbox tmp]#
在這些引數中,你可以看到將要顯示的檔名:file1
和 file2
。關於第一個引數(1
),請記住在 Linux 中,當執行任何程式時,預設情況下會為其開啟三個檔案描述符。以下是預設的檔案描述符:
0
:標準輸入1
:標準輸出2
:標準錯誤
因此,write
系統呼叫將在標準顯示(就是這個終端,由 1
所標識的)上顯示 file1
和 file2
。
現在你知道哪個系統呼叫完成了 ls testdir/
命令的大部分工作。但是在 trace.log
檔案中其它的 100 多個系統呼叫呢?作業系統必須做很多內務處理才能執行一個程式,因此,你在該日誌檔案中看到的很多內容都是程式初始化和清理。閱讀整個 trace.log
檔案,並嘗試瞭解 ls
命令是怎麼工作起來的。
既然你知道了如何分析給定命令的系統呼叫,那麼就可以將該知識用於其他命令來了解正在執行哪些系統呼叫。strace
提供了許多有用的命令列標誌,使你更容易使用,下面將對其中一些進行描述。
預設情況下,strace
並不包含所有系統呼叫資訊。但是,它有一個方便的 -v
冗餘選項,可以在每個系統呼叫中提供附加資訊:
strace -v ls testdir
在執行 strace
命令時始終使用 -f
選項是一種好的作法。它允許 strace
對當前正在跟蹤的程式建立的任何子程式進行跟蹤:
strace -f ls testdir
假設你只需要系統呼叫的名稱、執行的次數以及每個系統呼叫花費的時間百分比。你可以使用 -c
標誌來獲取這些統計資訊:
strace -c ls testdir/
假設你想專注於特定的系統呼叫,例如專注於 open
系統呼叫,而忽略其餘部分。你可以使用-e
標誌跟上系統呼叫的名稱:
[root@sandbox tmp]# strace -e open ls testdir
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
open("/lib64/libselinux.so.1", O_RDONLY|O_CLOEXEC) = 3
open("/lib64/libcap.so.2", O_RDONLY|O_CLOEXEC) = 3
open("/lib64/libacl.so.1", O_RDONLY|O_CLOEXEC) = 3
open("/lib64/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
open("/lib64/libpcre.so.1", O_RDONLY|O_CLOEXEC) = 3
open("/lib64/libdl.so.2", O_RDONLY|O_CLOEXEC) = 3
open("/lib64/libattr.so.1", O_RDONLY|O_CLOEXEC) = 3
open("/lib64/libpthread.so.0", O_RDONLY|O_CLOEXEC) = 3
open("/usr/lib/locale/locale-archive", O_RDONLY|O_CLOEXEC) = 3
file1 file2
+++ exited with 0 +++
[root@sandbox tmp]#
如果你想關注多個系統呼叫怎麼辦?不用擔心,你同樣可以使用 -e
命令列標誌,並用逗號分隔開兩個系統呼叫的名稱。例如,要檢視 write
和 getdents
系統呼叫:
[root@sandbox tmp]# strace -e write,getdents ls testdir
getdents(3, /* 4 entries */, 32768) = 112
getdents(3, /* 0 entries */, 32768) = 0
write(1, "file1 file2\n", 13file1 file2
) = 13
+++ exited with 0 +++
[root@sandbox tmp]#
到目前為止,這些示例是明確地執行的命令進行了跟蹤。但是,要跟蹤已經執行並正在執行的命令又怎麼辦呢?例如,如果要跟蹤用來長時間執行程式的守護程式,該怎麼辦?為此,strace
提供了一個特殊的 -p
標誌,你可以向其提供程式 ID。
我們的示例不在守護程式上執行 strace
,而是以 cat
命令為例,如果你將檔名作為引數,通常 cat
會顯示檔案的內容。如果沒有給出引數,cat
命令會在終端上等待使用者輸入文字。輸入文字後,它將重複給定的文字,直到使用者按下 Ctrl + C
退出為止。
從一個終端執行 cat
命令;它會向你顯示一個提示,並等待在那裡(記住 cat
仍在執行且尚未退出):
[root@sandbox tmp]# cat
在另一個終端上,使用 ps
命令找到程式識別符號(PID):
[root@sandbox ~]# ps -ef | grep cat
root 22443 20164 0 14:19 pts/0 00:00:00 cat
root 22482 20300 0 14:20 pts/1 00:00:00 grep --color=auto cat
[root@sandbox ~]#
現在,使用 -p
標誌和 PID(在上面使用 ps
找到)對執行中的程式執行 strace
。執行 strace
之後,其輸出說明了所接駁的程式的內容及其 PID。現在,strace
正在跟蹤 cat
命令進行的系統呼叫。看到的第一個系統呼叫是 read
,它正在等待檔案描述符 0
(標準輸入,這是執行 cat
命令的終端)的輸入:
[root@sandbox ~]# strace -p 22443
strace: Process 22443 attached
read(0,
現在,返回到你執行 cat
命令的終端,並輸入一些文字。我出於演示目的輸入了 x0x0
。注意 cat
是如何簡單地重複我輸入的內容的。因此,x0x0
出現了兩次。我輸入了第一個,第二個是 cat
命令重複的輸出:
[root@sandbox tmp]# cat
x0x0
x0x0
返回到將 strace
接駁到 cat
程式的終端。現在你會看到兩個額外的系統呼叫:較早的 read
系統呼叫,現在在終端中讀取 x0x0
,另一個為 write
,它將 x0x0
寫回到終端,然後是再一個新的 read
,正在等待從終端讀取。請注意,標準輸入(0
)和標準輸出(1
)都在同一終端中:
[root@sandbox ~]# strace -p 22443
strace: Process 22443 attached
read(0, "x0x0\n", 65536) = 5
write(1, "x0x0\n", 5) = 5
read(0,
想象一下,對守護程式執行 strace
以檢視其在後臺執行的所有操作時這有多大幫助。按下 Ctrl + C
殺死 cat
命令;由於該程式不再執行,因此這也會終止你的 strace
會話。
如果要檢視所有的系統呼叫的時間戳,只需將 -t
選項與 strace
一起使用:
[root@sandbox ~]#strace -t ls testdir/
14:24:47 execve("/usr/bin/ls", ["ls", "testdir/"], [/* 40 vars */]) = 0
14:24:47 brk(NULL) = 0x1f07000
14:24:47 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f2530bc8000
14:24:47 access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
14:24:47 open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
如果你想知道兩次系統呼叫之間所花費的時間怎麼辦?strace
有一個方便的 -r
命令,該命令顯示執行每個系統呼叫所花費的時間。非常有用,不是嗎?
[root@sandbox ~]#strace -r ls testdir/
0.000000 execve("/usr/bin/ls", ["ls", "testdir/"], [/* 40 vars */]) = 0
0.000368 brk(NULL) = 0x1966000
0.000073 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fb6b1155000
0.000047 access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
0.000119 open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
總結
strace
實用程式非常有助於理解 Linux 上的系統呼叫。要了解它的其它命令列標誌,請參考手冊頁和線上文件。
via: https://opensource.com/article/19/10/strace
作者:Gaurav Kamathe 選題:lujun9972 譯者:wxy 校對:wxy
訂閱“Linux 中國”官方小程式來檢視
相關文章
- Linux命令strace跟蹤程式的系統呼叫-linux學習用什麼書Linux
- Linux作業系統分析 | 深入理解系統呼叫Linux作業系統
- 在Linux中新增新的系統呼叫(轉)Linux
- ORACLE在LINUX上的系統配置OracleLinux
- oracle在linux系統上的部署OracleLinux
- Linux系統呼叫原理Linux
- linux系統呼叫getoptLinux
- Linux系統呼叫列表Linux
- 在Linux作業系統上執行Windows應用程式(轉)Linux作業系統Windows
- Dubbo 泛化呼叫在vivo統一配置系統的應用
- 在 Linux 上如何清理垃圾系統管理員Linux
- 系統呼叫篇——0環層面呼叫過程(上)
- Linux系統呼叫過程分析Linux
- Linux系統呼叫講義(轉)Linux
- 如何判斷Linux系統安裝在VMware上?Linux
- 在普通PC上建立Linux作業系統(轉)Linux作業系統
- 理解Linux系統負荷Linux
- Linux系統呼叫機制淺析Linux
- 在 Linux 和 Mac OS X 系統上執行 .NETLinuxMac
- 對 Linux 系統休眠的理解Linux
- SCSI裝置在Linux上跑起來(轉)Linux
- 在Linux作業系統上使用光碟搭建yum源Linux作業系統
- 在虛擬機器VMware上安裝Linux系統教程虛擬機Linux
- 反思一個問題(在應用系統上修改表)
- 線上環境 Linux 系統呼叫追蹤Linux
- Linux 下系統呼叫的三種方法Linux
- linux系統呼叫第一篇Linux
- Linux 系統管理(上)(轉)Linux
- 用strace除錯程式(zt)除錯
- 【Linux】手把手教你用Strace診斷問題Linux
- 將 Linux 應用程式移植到 64 位系統上Linux
- 使用strace來查詢php的坑PHP
- 在Linux中,如何理解系統管理工具,如Webmin和Ajenti。LinuxWeb
- 理解Linux檔案系統之 inodeLinux
- 在linux上通過ssh直接登入到windows系統上,不用密碼LinuxWindows密碼
- 小乾貨~ NFS在Linux系統中的應用NFSLinux
- 在Linux系統下FTP的配置與應用(轉)LinuxFTP
- 在 Linux 上安裝安卓 8.1 Oreo 來執行應用程式和遊戲Linux安卓遊戲