APUE3e
簡介
APUE - 全稱 Advanced Programming in the UNIX Environment(Unix 環(huán)境高級編程),是 Unix 系統(tǒng)編程領(lǐng)域的必讀經(jīng)典之一
相較于其他偏理論的操作系統(tǒng)相關(guān)書籍,本書從 Unix 操作系統(tǒng)的整體架構(gòu)出發(fā),以 Unix 系統(tǒng)應(yīng)用程序開發(fā)者的視角,描述了 Unix 操作系統(tǒng)的作用以及它所提供的主要功能,同時(shí)作者以深厚的學(xué)識(shí)和豐富的經(jīng)驗(yàn)將本書的知識(shí)點(diǎn)融會(huì)貫通,最終構(gòu)成了一張非常龐大的 Unix 系統(tǒng)編程知識(shí)脈絡(luò),而非僅僅只是系統(tǒng)調(diào)用函數(shù)和庫函數(shù)的簡單羅列。因此本書的實(shí)踐性較強(qiáng),不僅提供了豐富的案例幫助讀者親自動(dòng)手實(shí)踐相關(guān)系統(tǒng)調(diào)用函數(shù)和庫函數(shù)的功能,也在相應(yīng)案例的背后深入剖析了相關(guān)系統(tǒng)調(diào)用函數(shù)的起源和發(fā)展
知識(shí)結(jié)構(gòu)
代碼倉庫
https://github.com/silenaker/apue
該倉庫重新整理了書中示例小程序和練習(xí)題(與書中并不完全一致),同時(shí)參考 man 命令,補(bǔ)充了一些其他書中沒有的示例,拆分了貫穿全書的 apue 庫,使其更好理解,倉庫中的所有示例均已在 macOS Catalina 上全部編譯運(yùn)行通過
DevOps
調(diào)試
子進(jìn)程調(diào)試
子進(jìn)程調(diào)試主要是指在 fork 之后 exec 之前的代碼調(diào)試,其他代碼可通過調(diào)試器(lldb/gdb)啟動(dòng)獨(dú)立進(jìn)程進(jìn)行調(diào)試,父進(jìn)程在調(diào)用 fork 后,由于競態(tài)條件的影響(參考 Chapter 8 - Process Control/Race Conditions 章節(jié)),調(diào)試子進(jìn)程可能會(huì)遇到一些麻煩,下面以調(diào)試 daemonize 函數(shù)為例來說明如何才能正確調(diào)試子進(jìn)程
在編譯 daemon.c (chapters/ch13/daemon.c) 主程序后,我們先直接運(yùn)行一下,然后通過 ps 命令查看程序的運(yùn)行情況,發(fā)現(xiàn)沒有啟動(dòng)成功,由于這個(gè)程序是以守護(hù)進(jìn)程的方式運(yùn)行的,它的日志被輸出到了 syslog 系統(tǒng)日志組件,在 Mac 平臺(tái),我們可以通過 Mac 的控制臺(tái)應(yīng)用來查看系統(tǒng)日志,在左側(cè)的 system.log 欄目里面,我們發(fā)現(xiàn)了 daemon 程序的輸出結(jié)果
通過這個(gè)結(jié)果我們可以知道進(jìn)程終止和系統(tǒng)調(diào)用失敗的原因,但我們并不知道具體在程序的哪一行代碼發(fā)生了問題,因此這個(gè)時(shí)候我們就需要利用調(diào)試器(lldb/gdb)來對我們的代碼進(jìn)行 line-by-line 的精準(zhǔn)調(diào)試
首先我們啟動(dòng) lldb,載入需要調(diào)試的程序(編譯時(shí)已設(shè)置調(diào)試選項(xiàng)),因?yàn)槲覀冃枰獙ψ舆M(jìn)程進(jìn)行調(diào)試,所以我們在 fork 系統(tǒng)調(diào)用函數(shù)這里設(shè)置一個(gè)斷點(diǎn),然后啟動(dòng)程序,如下圖
此時(shí)子進(jìn)程還沒有啟動(dòng),由于我們不能在同一個(gè) lldb 會(huì)話中同時(shí)調(diào)試多個(gè)進(jìn)程(process detach 命令會(huì)使進(jìn)程忽略調(diào)試信號(hào),正常運(yùn)行),因此我們還需要啟動(dòng)另一個(gè) lldb 會(huì)話并加載同一個(gè)程序,然后執(zhí)行 process attach -w 等待子進(jìn)程的創(chuàng)建,由于父子進(jìn)程的競態(tài)條件,attach 命令可能會(huì)以失敗告終(lldb 檢測到新的進(jìn)程后馬上就嘗試 attach,但此時(shí)子進(jìn)程還沒有從 fork 系統(tǒng)調(diào)用中返回,因此 attach 失敗)
與此同時(shí),父進(jìn)程已經(jīng)從 fork 中返回了,如果此時(shí)繼續(xù)調(diào)試父進(jìn)程或執(zhí)行 process detach 使父進(jìn)程正常運(yùn)行直至終止,子進(jìn)程可能永遠(yuǎn)也得不到執(zhí)行機(jī)會(huì),而在父進(jìn)程終止時(shí),由于父子進(jìn)程同屬于以父進(jìn)程為首的進(jìn)程組,該進(jìn)程組自然就變成了孤兒進(jìn)程組,那么子進(jìn)程也會(huì)被 init 進(jìn)程所發(fā)送的 SIGHUP 信號(hào)所終止,因此在這種情況下,我們是沒辦法調(diào)試子進(jìn)程的
那么,有什么解決辦法嗎
這個(gè)問題的本質(zhì)是由于父子進(jìn)程的競態(tài)條件,子進(jìn)程無法得到執(zhí)行機(jī)會(huì),所以只要我們想辦法在 fork 后讓系統(tǒng)調(diào)度運(yùn)行子進(jìn)程,并在子進(jìn)程內(nèi)預(yù)留一些操作時(shí)機(jī),比如通過 pause() 或 sleep(10) 阻塞子進(jìn)程,使我們能夠在子進(jìn)程退出前 attach,那么這個(gè)問題就迎刃而解了,需要注意的是我們同時(shí)也要保證在調(diào)試完成之前父進(jìn)程不能被終止,否則子進(jìn)程也會(huì)被終止,除非我們在子進(jìn)程中主動(dòng)忽略 SIGHUP 信號(hào)
總結(jié)
- 上一篇: python下载bt文件_使用libto
- 下一篇: 初探VBScript