當fgets在FIFO中遇見 0

2021-05-26 05:55:23 字數 1287 閱讀 5282

今天一直在尋找乙個關於程式間fifo通訊丟失的bug,經過苦苦追尋和挖掘,功夫不負有心人,終於讓我挖到了根。

系統概述:

a,b 量程式通過管道通訊方式,以文字形式傳送和接收命令,並執行命令內容。

a程式所有傳送的命令都以'/n'結尾,使用write命令寫入管道

b程式輪詢使用fgets命令讀管道,根據返回值和strlen的長度來決定是否執行該命令

bug現象:ab兩程式間通過有名管道通訊,內容是以協議格式為ascii字元,偶爾會出現a程式以通過write返回值確認了管道資訊寫入管道,但是b程式卻沒有執行相應的命令。

a 程式當初由於對系統不夠熟悉,想當然的認為寫入管道的內容必須把'/0'也寫入(寫的時候位元組刻意設定為strlen() + 1),這樣的話就造成了管道裡有'/0'字元;b程式因為根據協議採用fgets獲取一行命令。但對於fgets的使用不夠了解以及管道的特殊情況,造成了bug的現象。首先,fgets是對乙個stream讀,如果stream沒有可讀內容就返回null, 如果有,則一直讀到換行符'/n'或者eof為止,這裡我的潛意識裡一直認為fgets遇到'/0'也會停止,這種錯誤的意思讓我沒有能夠意思到,在管道中,乙個游離『/0』放在管道中,就像乙個黑帽子,當這個游離的'/0'被讀走之前,有新的資訊寫入管道,這時候再去用fgets讀管道,就會連同'/0'這頂黑帽子一起被fgets讀出來(這裡強調下,fgets只認識'/n'和eof,其他無視,包括'/0',這個下面會考證)。程式設計師一般都喜歡fgets讀出來後,然後strlen一把讀出來的資料,因為帶了這個黑帽子,於是strlen直接返回0。該命令就這樣被忽略掉了,而且由於是fifo,導致讀出來後,就再也不會有機會重新讀取了。

對於a, 理應根據管道協議,沒有必要多寫個'/0'。這裡a多寫'/0'是通過指定位元組數,還有其他比較隱蔽的寫入函式,比如vfprintf等,也會自動在後加個'/0'

對於b, 可以自己重新定義個fgets,讓fgets遇到'/0'也會立即返回;如果使用glibc的fgets的話,通過strlen來判斷是否合法就需要考慮帶了黑帽子的命令的特殊情況,需要對讀到的字串進行整改,去除前端的『/0』

對於fgets的驗證,glibc中的原始碼如下

從**中可以看到,只有遇見eof和指定的delim引數才會返回。

如果還不死心,自己可以動手寫個fgets管道的小測試程式,然後用echo -n -e "/000/0102/n"這種方式來傳送ascii資料來驗證

總結:寫程式期間,最忌諱「想當然」,這種潛意識的評感覺判斷是否正確,往往後果很嚴重。當然有時候不是想當然,而是確實不了解。某位大牛不是說麼,「我不是比你聰明,而是我比你犯的錯要多」

Junit在多執行緒中遇見的坑

junit使用注意事項 junit單元測試不支援多執行緒。當新建執行緒,junit單元測試在主線程執行結束後就關閉,不會等子執行緒執行結束。main方法則不會存在這樣的問題。方法1 使用junit做多執行緒測試,發現程式並不會卡在讀的執行緒中,理論上到讀執行緒獲取到鎖後,由於沒有外界條件操作,是卡死...

return 0在程式中的影響

今天遇到這樣一道題 在借鑑了別人的做法後終於寫出了 其實是抄出了 這是第一版 include intmain switch gh x1 sum printf d x1 return0 執行時發現,輸入非法字元和分母為0時,除錯出現問題 這時對比正確的寫法,發現少了return 0 當分母為0時,缺少...

do while 0 在巨集定義中的作用

如果你是一名c程式設計師,你肯定熟悉巨集,它們非常的強大,如果正確使用可以讓你的工作事半功倍。然後,如果你在定義巨集時,很隨意沒有認真檢查,那麼它們可能使得你發狂,浪費n多時間。在很多c程式中,你可能看到許多看起來不是那麼直接的較為特殊的巨集定義。下面就是乙個例子 1 define set taks...