文/俞理超 胡益群 袁昌權(quán) 許光
當前,由于Linux資源完全公開,使得Linux的發(fā)展日益廣泛快速?;贚inux的各種應用已逐漸深入日常生活的方方面面,尤其是在嵌入式領(lǐng)域,由于內(nèi)核可裁減定制,因此可隨意地根據(jù)用戶需求進行整個系統(tǒng)的定制與重構(gòu)。Linux由于其內(nèi)核特性,在網(wǎng)絡通信的使用上可能會存在令人棘手的問題,針對不同的用戶場景模型,需要進行不同的問題分析,可能涉及操作系統(tǒng)內(nèi)核、任務調(diào)度、硬件驅(qū)動等原因。
現(xiàn)象描述:主控板運行Linux系統(tǒng),與DSP(信號處理機)進行tcp通信。在通信中,DSP時不時進行復位操作,主控中有監(jiān)測線程,監(jiān)測tcp中斷后,關(guān)閉當前tcp連接,并重新創(chuàng)建線程,與dsp建立tcp連接,應對通信錯誤。正常情況下,dsp復位后,主控仍能與DSP建立連接。在反復復位幾次后,出現(xiàn)主控與dsp無法正常通信的情況。
問題分析與復現(xiàn):
在問題分析初期,我們懷疑現(xiàn)象與用戶程序有關(guān),根據(jù)用戶描述,開始嘗試復現(xiàn)目標現(xiàn)象。根據(jù)用戶描述,linux先起一個TCP客戶端任務,去連DSP板起的TCP服務端,數(shù)據(jù)由linux發(fā)送給DSP服務端,節(jié)拍約為800ms;在linux中起另一個監(jiān)控線程,觀察TCP的connect狀態(tài),若觀察到TCP斷開,則釋放資源,并重啟線程,另啟一個TCP服務端。在發(fā)送過程中,若DSP板復位,監(jiān)控線程在正常重啟線程若干次后,會出現(xiàn)通訊/線程異常的情況。
為了復現(xiàn)現(xiàn)象,使用了單TCP線程加上監(jiān)控線程的模式,在反復重啟DSP的過程中,出現(xiàn)了類似用戶描述的現(xiàn)象,linux在發(fā)送過程中,若DSP復位(TCP客戶端異常結(jié)束,TCP異常斷開),linux進程自動結(jié)束的現(xiàn)象。根據(jù)此現(xiàn)象,查閱相關(guān)資料,得到以下的第一個結(jié)論。
現(xiàn)象機理:當Linux服務器監(jiān)聽并接受一個客戶端鏈接的時候,可以不斷向客戶端發(fā)送數(shù)據(jù),這時如果客戶端斷開socket鏈接,服務器繼續(xù)向一個關(guān)閉的socket發(fā)送數(shù)據(jù)(send,write)的時候,系統(tǒng)會默認對服務器進程發(fā)送一個SIGPIPE信號,系統(tǒng)會出BrokePipe,關(guān)閉當前進程。經(jīng)測試,Linux作為客戶端發(fā)送數(shù)據(jù)時,也會有同樣的問題。
系統(tǒng)里邊定義了三種處理方法:
(1)SIG_DFL /* Default action */信號專用的默認動作:
(a)如果默認動作是暫停線程,則該線程的執(zhí)行被暫時掛起。當線程暫停期間,發(fā)送給線程的任何附加信號都不交付,直到該線程開始執(zhí)行,但是SIGKILL除外。
(b)把掛起信號的信號動作設置成SIG_DFL,且其默認動作是忽略信號(SIGCHLD)。
(2)SIG_IGN /* Ignore action */忽略信號
(a)該信號的交付對線程沒有影響
(3)系統(tǒng)不允許把SIGKILL或SIGTOP信號的動作設置為SIG_DFL
(4)SIG_ERR /* Error return */
而TCP通信中send()函數(shù)的定義如下:
ssize_t send(int sockfd,const void *buff,size_t nbytes,int flags);
其中,send()函數(shù)的最后一個參數(shù)flag可以設MSG_NOSIGNAL,,禁止send()函數(shù)向系統(tǒng)發(fā)送異常消息,這樣在發(fā)送數(shù)據(jù)異常時,系統(tǒng)就不會異常退出。通過與項目人員溝通,協(xié)助其修改程序。
經(jīng)修改后,線程退出的現(xiàn)象有所好轉(zhuǎn),但依然存在,并且上述三種方法都無法徹底解決目標問題。進一步與項目人員交流發(fā)現(xiàn),他們的linux程序比我們的測試程序更復雜,線程更多。在出現(xiàn)問題時,也發(fā)現(xiàn)了另一個bond1(外口)在ifconfig中查看,存在dropped包的現(xiàn)象。現(xiàn)象如下:
程序共有10個子線程,分別是:
子線程1:tcp_recv接收DSP程序剛起來時的握手信號(1個字節(jié)),獲取DSP的IP地址以及端口號,子線程1在完成上述工作后,自動退出線程;
子線程2:multi_recv通過bond1雙冗余的外口接收陣元數(shù)據(jù),累計一定數(shù)據(jù)量后轉(zhuǎn)發(fā)給子線程3、4、5,由這3個線程分別處理陣元數(shù)據(jù);
子線程3:work_dp按格式轉(zhuǎn)換處理低頻數(shù)據(jù),累積到1024*4096 float后通過信號量通知子線程6轉(zhuǎn)發(fā);
子線程4:work_hp按格式轉(zhuǎn)換處理高頻數(shù)據(jù),累計到768*4096*3 float后通過信號量通知子線程7轉(zhuǎn)發(fā);
子線程5:work_senor按格式轉(zhuǎn)換處理處理傳感器數(shù)據(jù),累積到512float,和低頻數(shù)據(jù)拼接在一起,由子線程6一起轉(zhuǎn)發(fā);
子線程6:tcp_dp_send,收到子線程3的信號量后,通過bond0內(nèi)網(wǎng)卡雙網(wǎng)冗余轉(zhuǎn)發(fā)給DSP程序,當返回值sendNum<0時,清空資源,退出程序;
子線程7:tcp_hp_send,收到子線程4的信號量后,通過bond0內(nèi)網(wǎng)卡雙網(wǎng)冗余轉(zhuǎn)發(fā)給DSP程序,當返回值sendNum<0時,清空資源,退出程序;
子線程8:監(jiān)控子線程6和子線程7的TCP的Connect State,發(fā)現(xiàn)有TCP連接斷開后重啟線程1、線程6、線程7、等待DSP重連;
子線程9:通過bond1定時上報心跳程序;
子線程10:給數(shù)據(jù)庫上報丟包信息;
系統(tǒng)工作流程:主線程運行后,將10個子線程創(chuàng)建并起動;其中可能涉及內(nèi)存與調(diào)度/雙網(wǎng)網(wǎng)卡狀態(tài)等問題。
進一步測試發(fā)現(xiàn),當DSP復位時,其0核和1核網(wǎng)絡同時復位,但線程6,7中判定其TCP斷開并非同時,當問題復現(xiàn)時,線程6的TCP狀態(tài)為斷開,監(jiān)控線程重啟線程1,6,但現(xiàn)場7的TCP未被判定斷開,最終導致了通信錯誤。
監(jiān)控線程未正常工作可能的原因是:
1、線程的切換優(yōu)先級不恰當,導致監(jiān)控線程中判斷線程7中的標志位值未改變;
2、DSP復位的網(wǎng)絡初始化問題導致。
針對上述兩種可能,選擇了修改程序優(yōu)先級以及信號量觸發(fā)監(jiān)控線程的模式來測試;在測試結(jié)果中,監(jiān)控線程正常的起到了kill線程并重啟的功能;在另一種解決方式中,采取了如下的方式:當一路TCP斷開時,直接kill線程6與線程7,并重啟線程1,6,7,這是基于DSP(TCP客戶端)兩路會同時重啟或斷開的情景。
關(guān)于存在dropped包現(xiàn)象:
在SUSE的kb中可以發(fā)現(xiàn)如下內(nèi)容:
Beginning with kernel 2.6.37,it has been changed the meaning of dropped packet count.Before,dropped packets was most likely due to an error.Now,the rx_dropped counter shows statistics for dropped frames because of:
從2.6.37內(nèi)核以后,改變了dropped包的統(tǒng)計方式,其不再是以錯誤包的方式統(tǒng)計,以下情況也會計入dropped包。其值只是做為一種狀態(tài)統(tǒng)計了。
最后一句中說明,在bond主備模式中,備用網(wǎng)卡接到的所有包都會計入dropped。
而redhat和其他發(fā)行對應的發(fā)行版如SUSE、ubuntu等來比,kernel和包都相對版本要低一些,這和其策略有關(guān),未確認穩(wěn)定的,不會加入到當前的發(fā)行版本中。在未單獨升級過kernel的情況下,其只在rhel7中才會有該情況發(fā)生,而且其kb給出了前后統(tǒng)計方式不同的源碼,具體變化在rt_kernel core/dev.c文件中。
因此dropped包原因可能是TCP中斷導致的雙網(wǎng)切換有關(guān)。
針對linux的TCP通訊,linux不是一個完全照顧吞吐的系統(tǒng),也不是一個完全照顧響應的系統(tǒng),它是兩者的兼容,是一個軟實時系統(tǒng)。優(yōu)先級和調(diào)度策略均會影響Linux的工作狀態(tài),并且在內(nèi)核在處理錯誤信息時,部分信號可能會直接殺死進程,導致程序異常。根據(jù)不同的應用場景與網(wǎng)絡問題,應同時考慮硬件、內(nèi)核、任務調(diào)度等方面來考慮問題。在TCP通信過程中,任務調(diào)度會影響TCP的速率和響應,TCP通信異常的信號會導致內(nèi)核直接殺死進程。