Linux上程式除錯的基石 1 ptrace

2021-06-18 02:50:20 字數 3499 閱讀 3967

1.在linux系統中,程序狀態除了我們所熟知的task_running,task_interruptible,task_stopped等,還有乙個task_traced。這表明這個程序處於什麼狀態? 

2.strace可以方便的幫助我們

記錄程序所執行的系統呼叫

,它是如何跟蹤到程序執行的? 

3.gdb是我們除錯程式的利器,可以設定斷點,單步跟蹤程式。它的實現原理又是什麼? 

所有這一切的背後都隱藏著linux所提供的乙個強大的

系統呼叫ptrace()

. 1.ptrace系統呼叫 

ptrace系統調從名字上看是用於程序跟蹤的,它提供了

父程序可以觀察和控制其子程序執行的能力

,並允許父程序檢查和替換子程序的核心映象(包 括暫存器)的值。其基本原理是: 當使用了ptrace跟蹤後,

所有傳送給被跟蹤的子程序的訊號(除了sigkill),都會被**給父程序,而子程序則會被阻塞,這時子程序的狀態就會被 系統標註為task_traced

。而父程序收到訊號後,就可以對停止下來的子程序進行檢查和修改,然後讓子程序繼續執行。 

其原型為:

[cpp]view plain

copy

print?

#include 

long

ptrace(

enum

__ptrace_request request, pid_t pid, 

void

*addr, 

void

*data);   

ptrace有四個引數: 

1). enum __ptrace_request request:指示了ptrace要執行的命令。 

2). pid_t pid: 指示ptrace要跟蹤的程序。 

3). void *addr: 指示要監控的記憶體位址。 

4). void *data: 存放讀取出的或者要寫入的資料。 

ptrace是如此的強大,以至於有很多大家所常用的工具都基於ptrace來實現,如strace和gdb。接下來,我們借由對strace和gdb的實現,來看看ptrace是如何使用的。 

2. strace的實現 

strace常常被用來攔截和記錄程序所執行的系統呼叫,以及程序所收到的訊號。如有這麼一段程式: 

helloworld.c:

[cpp]view plain

copy

print?

#include 

intmain()   

編譯後,用strace跟蹤: strace ./helloworld 

可以看到形如: 

execve("./helloworld", ["./helloworld"], [/* 67 vars */]) = 0 

brk(0)                                  = 0x804a000 

mmap2(null, 4096, prot_read|prot_write, map_private|map_anonymous, -1, 0) = 0xb7f18000 

access("/etc/ld.so.preload", r_ok)      = -1 enoent (no such file or directory) 

open("/home/supperman/workspace/lib/tls/i686/sse2/libc.so.6", o_rdonly) = -1 enoent (no such file or directory) 

... 

的一段輸出,這就是在執行helloworld中,系統所執行的系統呼叫,以及他們的返回值。 

下面我們用ptrace來研究一下它是怎麼實現的。 

...

[cpp]view plain

copy

print?

switch

(pid = fork())   

else

//第二次(退出系統呼叫),獲取系統呼叫的返回值 

ptrace(ptrace_syscall,pid,null,null);   

}   

}   

...   

在上面的程式中,fork出的子程序先呼叫了ptrace(ptrace_traceme)表示子程序讓父程序跟蹤自己。然後子程序呼叫 execl載入執行了helloworld。而在父程序中則使用wait系統呼叫等待子程序的狀態改變。子程序因為設定了ptrace_traceme而 在執行系統呼叫被系統停止(設定為task_traced),這時父程序被喚醒,使用ptrace(ptrace_peekuser,pid,...)分 別去讀取子程序執行的系統呼叫id(放在orig_eax中)以及系統呼叫返回時的值(放在eax中)。然後使用ptrace (ptrace_syscall,pid,...)指示子程序執行到下一次執行系統呼叫的時候(進入或者退出),直到子程序退出為止。 

程式的執行結果如下: 

process executed system call id = 11 

process executed system call id = 45 with return value= 134520832 

process executed system call id = 192 with return value= -1208934400 

process executed system call id = 33 with return value= -2 

process executed system call id = 5 with return value= -2 

... 

其中,11號系統呼叫就是execve,45號是brk,192是mmap2,33是access,5是open...經過比對可以發現,和 strace的輸出結果一樣。當然strace進行了更詳盡和完善的處理,我們這裡只是揭示其原理,感興趣的同學可以去研究一下strace的實現。 

ps: 

1). 在系統呼叫執行的時候,會執行pushl %eax # 儲存系統呼叫號orig_eax在程式使用者棧中。 

2). 在系統呼叫返回的時候,會執行movl %eax,eax(%esp)將系統呼叫的返回值放入暫存器%eax中。 

3). wifexited()巨集用來判斷子程序是否為正常退出的,如果是,它會返回乙個非零值。 

4). 被跟蹤的程式在進入或者退出某次系統呼叫的時候都會觸發乙個sigtrap訊號,而被父程序捕獲。 

5). execve()系統呼叫執行成功的時候並沒有返回值,因為它開始執行一段新的程式,並沒有"返回"的概念。失敗的時候會返回-1。 

6). 在父程序進行進行操作的時候,用ps檢視,可以看到子程序的狀態為t,表示子程序處於task_traced狀態。當然為了更具操作性,你可以在父程序中加入sleep()。

除錯PPC裝置上的網路程式

最近在除錯ppc裝置上的網路程式時,總是發現有時可以連線成功,有時則無法連線,多次試驗後發現 當ppc裝置連線到電腦後,原來通過gprs或cdma 1x建立的網路連線會自動中斷,ppc裝置會改為使用pc機的網路進行網路連線,在這種情況下自然是不能測試gprs或cdma 1x網路程式。解決辦法 在pp...

遊承超 手機鋼化玻璃膜平邊與弧邊的區別(1P)

無可否認,現今智慧型手機市場是觸控螢幕的時代,螢幕越來越大,隨之而來的是螢幕容易摔壞的問題。試想如果您的愛機不小心掉地上了,螢幕摔壞了,手機就暫時不能用了。換個觸控螢幕又要很大的費用而且要等很長的一段時間。而貼一張小小的鋼化膜,可以給您的手機帶來多一層保護,如果掉地上,損壞掉的只是一層鋼化膜,而你的...

Linux上的C C 偵錯程式GDB

gnu 的偵錯程式稱為 gdb,該程式是乙個互動式工具,工作在字元模式。在 x window 系統中,有乙個 gdb 的前端圖形工具,稱為 gdb。gdb 是功能強大的除錯程式,可完成如下的除錯任務 設定斷點 監視程式變數的值 程式的單步執行 修改變數的值。在可以使用 gdb 除錯程式之前,必須使用...