gdb调试器之测不准原则
生活随笔
收集整理的這篇文章主要介紹了
gdb调试器之测不准原则
小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
gdb調(diào)試器之"測不準(zhǔn)原則"??
2012-05-07 22:25:30|??分類: gdb源代碼分析 |字號?訂閱
一、測不準(zhǔn)原則我大學(xué)物理學(xué)的不太好,特別是高等物理,這個概念是在很多科普性的讀物中都可以見到,就像”羅素悖論“、哥德爾的”不完備理論“、愛因斯坦的”相對論“等,大家都是一知半解,然后根據(jù)這個概念大家自由發(fā)揮,所以就有千奇百怪的場景和理解了,最后以訛傳訛,倒也不清楚這個東西原始真正意義,這種現(xiàn)象在很多成語中也經(jīng)常出現(xiàn),例如經(jīng)典的、也是考試的時候出鏡率很高的”差強(qiáng)人意“。
作為無數(shù)民間科學(xué)家中的一員,我對這個”測不準(zhǔn)“的理解就是當(dāng)你真正觀察它的時候,它和它正常的行為是不同的。這一點可能在很多其他場合也是用,例如……(此處大家可以盡情發(fā)揮一下)。或者圍城中胖詩人曹元朗說的”當(dāng)你以為你理解了我的時候,你就誤解了我“。
二、調(diào)試器依賴的手段
當(dāng)調(diào)試器調(diào)試一個任務(wù)的時候,它同樣會對被調(diào)試的任務(wù)產(chǎn)生一些微妙的影響,這些影響在一些實時系統(tǒng)中表現(xiàn)的比較突出,特別是那些FIFO類型的實時調(diào)度任務(wù),因為當(dāng)任務(wù)被調(diào)試的時候,它的很多重要事件都要由內(nèi)核代勞首先通知給調(diào)試器,在調(diào)試器發(fā)出下一個指示之前,被調(diào)試任務(wù)(線程)不能繼續(xù)運行,這一點對于很多對初始化順序有嚴(yán)格要求的系統(tǒng)來說是不能容忍的,所以調(diào)試器在很多時候并不是完成的。
對于非實時的系統(tǒng),調(diào)試器同樣可能會影響調(diào)度,原因同上,但是現(xiàn)象可能不盡相同。因為非實時系統(tǒng)對于任務(wù)的調(diào)度順序沒有依賴和假設(shè),它本來就是可以以任意順序運行的,如果需要排序可能使用各種鎖來同步。
調(diào)試器主要是通過SIGSTOP來主動要求一個線程暫時冷靜下來,而內(nèi)核則通過ptrace_stop來強(qiáng)制,大家可以看一下內(nèi)核在哪些地方調(diào)用了這個函數(shù):
static void ptrace_stop(int exit_code, int nostop_code, siginfo_t *info)
{
??? /* Let the debugger run.? */
??? set_current_state(TASK_TRACED);該狀態(tài)不可運行,并且不接收信號。
……
if (may_ptrace_stop()) {
??? ??? do_notify_parent_cldstop(current, CLD_TRAPPED);通知父進(jìn)程,
??? ??? read_unlock(&tasklist_lock);
??? ??? schedule();讓出調(diào)度權(quán)。
??? } else {
??? ??? /*
??? ??? ?* By the time we got the lock, our tracer went away.
??? ??? ?* Don't stop here.
??? ??? ?*/
??? ??? read_unlock(&tasklist_lock);
??? ??? set_current_state(TASK_RUNNING);
??? ??? current->exit_code = nostop_code;
??? }
}
當(dāng)進(jìn)程附加的時候,內(nèi)核也不拿自己當(dāng)外人,也是毫不客氣的發(fā)送了一個SIGSTOP信號過去。我們知道,很多時候,線程都是信號敏感的,也就是系統(tǒng)調(diào)用是可以被信號喚醒的,這明顯會影響系統(tǒng)任務(wù)的執(zhí)行,我們看一下調(diào)試器附加的代碼:
sys_ptrace--->>>ptrace_attach
??? force_sig_specific(SIGSTOP, task);
這里向被附加任務(wù)發(fā)送了一個SIGSTOP信號,之后將會看到,這個調(diào)用將會對被調(diào)試進(jìn)程的運行產(chǎn)生影響。
三、附加被調(diào)試任務(wù)
1、測試代碼
之前的代碼已經(jīng)看到,它會發(fā)送SIGSTOP給被附加線程,我們測試一下最為簡單的read系統(tǒng)調(diào)用,測試程序為:
[tsecer@Harry TracerInter]$ cat tracersense.c
#include <fcntl.h>
#include <stdio.h>
int main()
{
??? char buf[10];
??? int readin = read(0,buf,sizeof buf);
??? printf("readin is %d\n",readin);
}
[tsecer@Harry TracerInter]$ gcc tracersense.c -g
[tsecer@Harry TracerInter]$ sleep 1000 | ./a.out
然后到另一個終端中使用調(diào)試器附加a.out對應(yīng)的進(jìn)程,看它是否會被從read系統(tǒng)調(diào)用喚醒。
[root@Harry ~]# cat /proc/18587/status
Name:??? a.out
State:??? S (sleeping)
……
voluntary_ctxt_switches:??? 3 注意這個調(diào)度次數(shù),在調(diào)試器附加之后,被調(diào)試線程的調(diào)度次數(shù)將會增加
nonvoluntary_ctxt_switches:??? 2
[root@Harry ~]# gdb -p 18587
……
(gdb) shell cat /proc/18587/status
Name:??? a.out
State:??? T (tracing stop)
……
voluntary_ctxt_switches:??? 4 調(diào)試器附加之后,被調(diào)試任務(wù)執(zhí)行次數(shù)加一,說明被調(diào)試線程從read系統(tǒng)調(diào)用返回了,但是程序沒有退出運行。
nonvoluntary_ctxt_switches:??? 2
(gdb) quit
A debugging session is active.
??? Inferior 1 [process 18587] will be detached.
Quit anyway? (y or n) y
Detaching from program: /home/tsecer/CodeTest/TracerInter/a.out, process 18587
[root@Harry ~]# cat /proc/18587/status
Name:??? a.out
State:??? S (sleeping)
Tgid:??? 18587
……
voluntary_ctxt_switches:??? 5調(diào)試器退出附加之后,被調(diào)試線程調(diào)度次數(shù)再次增加。但是奇怪的是被調(diào)試任務(wù)并沒有從read系統(tǒng)調(diào)用返回到用戶態(tài)空間(否則進(jìn)程會直接退出)。
nonvoluntary_ctxt_switches:??? 2
2、管道read被喚醒
linux-2.6.21\fs\pipe.c
pipe_read(struct kiocb *iocb, const struct iovec *_iov, unsigned long nr_segs, loff_t pos)
??? ??? if (signal_pending(current)) {
??? ??? ??? if (!ret)
??? ??? ??? ??? ret = -ERESTARTSYS;
??? ??? ??? break;
??? ??? }
注意這個返回錯誤碼。當(dāng)調(diào)試器收到一個子進(jìn)程上報的信號之后,如果是自己發(fā)送的SIGSTOP,那么會對被調(diào)試任務(wù)透明的取消這個信號,取消的方法就是通過ptrace的PTRACE_CONT請求實現(xiàn),看其實現(xiàn)非常簡單linux-2.6.21\arch\i386\kernel\ptrace.c
??? ??? child->exit_code = data;
然后子進(jìn)程繼續(xù)運行,執(zhí)行信號獲取函數(shù)
linux-2.6.21\kernel\signal.c:?? int get_signal_to_deliver(siginfo_t *info, struct k_sigaction *return_ka, struct pt_regs *regs, void *cookie)
??? ??? ??? /* Let the debugger run.? */
??? ??? ??? ptrace_stop(signr, signr, info);
??? ??? ??? /* We're back.? Did the debugger cancel the sig?? */
??? ??? ??? signr = current->exit_code;
??? ??? ??? if (signr == 0)
??? ??? ??? ??? continue;
對于我們測試的例子,它剛好滿足這個條件(調(diào)試器通過PTRACE_CONT清空了這個信號值),所以直接返回,然后進(jìn)入信號處理函數(shù)
linux-2.6.21\arch\i386\kernel\signal.c:static void fastcall do_signal(struct pt_regs *regs)
if (signr > 0) {不滿足該條件,執(zhí)行下面分支。
……
??? ??? return;
??? }
??? /* Did we come from a system call? */
??? if (regs->orig_eax >= 0) {
??? ??? /* Restart the system call - no handlers present */
??? ??? switch (regs->eax) {
??? ??? case -ERESTARTNOHAND:
??? ??? case -ERESTARTSYS:
??? ??? case -ERESTARTNOINTR:
??? ??? ??? regs->eax = regs->orig_eax;
??? ??? ??? regs->eip -= 2;
??? ??? ??? break;
??? ??? case -ERESTART_RESTARTBLOCK:
??? ??? ??? regs->eax = __NR_restart_syscall;
??? ??? ??? regs->eip -= 2;這是整個機(jī)制的實現(xiàn)核心:將用戶態(tài)指針減去兩個字節(jié),也就是386體系結(jié)構(gòu)中l(wèi)inux下系統(tǒng)調(diào)用int 0x80指令占用的兩個字節(jié),這樣被中斷的系統(tǒng)調(diào)用(read)就可以再次執(zhí)行而不真正對APP可見這次喚醒。
??? ??? ??? break;
??? ??? }
??? }
我看了一下另一個常用的可以測試的系統(tǒng)調(diào)用sys_pause,它被喚醒的時候也是設(shè)置
asmlinkage long
sys_pause(void)
{
??? current->state = TASK_INTERRUPTIBLE;
??? schedule();
??? return -ERESTARTNOHAND;
}
所以使用pause測試應(yīng)用程序可感知喚醒也不行、select也不行,所以這個現(xiàn)象只是作為一個理論存在,但是工程中應(yīng)該比價少出現(xiàn)的情況,暫且不說。
四、對于不可喚醒睡眠任務(wù)的附加失敗例子
[tsecer@Harry TracerInter]$ cat NonInt.c
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
int main()
{
??? pid_t foker;
??? if (0 == (foker = vfork())) 執(zhí)行vfork,從而使父進(jìn)程進(jìn)入不可喚醒休眠。
??? {
??? ??? sleep (1000);
??? }
??? printf("Father side This sentense should never been seen\n");
}
[tsecer@Harry TracerInter]$ gcc NonInt.c -o NonInt.c.exe -g
[tsecer@Harry TracerInter]$ ./NonInt.c.exe
另一個終端中調(diào)試器附加父進(jìn)程:
[root@Harry ~]# ps aux
……
tsecer?? 18841? 0.0? 0.0?? 1740?? 272 pts/0??? D+?? 22:17?? 0:00 ./NonInt.c.exe
tsecer?? 18842? 0.0? 0.0?? 1740?? 272 pts/0??? S+?? 22:17?? 0:00 ./NonInt.c.exe
root???? 18843? 1.0? 0.0?? 4688?? 992 pts/6??? R+?? 22:17?? 0:00 ps aux
[root@Harry ~]# cat /proc/18841/status
Name:??? NonInt.c.exe
State:??? D (disk sleep)
Tgid:??? 18841
Pid:??? 18841
PPid:??? 12127
……
[root@Harry ~]# gdb -p 18841 附加父進(jìn)程,
GNU gdb (GDB) Fedora (7.0-3.fc12)
Copyright (C) 2009 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.? Type "show copying"
and "show warranty" for details.
This GDB was configured as "i686-redhat-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Attaching to process 18841
這個顯示將會一直持續(xù),也就是說調(diào)試器將會一直無法從這里返回,這說明調(diào)試器沒有收到內(nèi)核通知的子進(jìn)程收到SIGSTOP的事件,調(diào)試器在此一直等待。
五、sum up
這里沒有分析gdb的實現(xiàn)代碼,只是結(jié)合了內(nèi)核的相關(guān)接口猜測和觀察了一下gdb的執(zhí)行原理,純粹是探討性內(nèi)容,可能實際意義不大。
總結(jié)
以上是生活随笔為你收集整理的gdb调试器之测不准原则的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python积木式编程_TurnipBi
- 下一篇: Blur Multiple Images