1. 程式人生 > >system函式呼叫失敗

system函式呼叫失敗

背景

專案在一次升級版本後,發現在DSP發生異常後,沒有生成dspcrash檔案。該檔案通過system函式,直接呼叫可執行程式。後來增加定位資訊,發現system返回-1。而在串列埠上直接呼叫可執行程式,執行成功。在控制檯呼叫system函式,返回-1。system函式在該處理器上必現呼叫失敗。

定位

首先需要了解system的執行過程,實際上system執行了三步操作:
1. fork一個子程序;
2. 在子程序中呼叫exec函式執行command;
3. 在父程序中呼叫wait等待子程序結束。
對於fork失敗,system函式返回-1,;如果exec執行成功,則返回command通過exit或return返回的值;如果exec執行失敗,system返回127。
由於返回值是-1,因此比較懷疑fork執行失敗。在控制檯上輸入fork後,果然返回-1。fork返回-1主要由兩種原因:系統達到最大程序數上限或者系統記憶體不足。由於系統當前程序數並不多,因此懷疑記憶體不足。
0803版本中升級了由於CPUH記憶體空間不足,導致升級失敗的問題。這個問題是通過將CPUL的100M記憶體挪至CPUH解決的。至此,問題已基本明確,確實是由於挪動記憶體後,CPUL上的剩餘記憶體不足以fork。
其實這裡我有一個疑問,fork會複製父程序的資料空間、堆和棧的副本。因此,如果父程序的記憶體佔用超過系統總可用記憶體的50%,呼叫system或fork就會存在風險。而linux提供了另外一個函式:vfork。vfork並不會複製父程序的完整地址空間,這個函式主要應用的場景就是fork後,立即呼叫exec(exec會使用新程式替換掉當前程序的正文段、資料段、堆和棧)。按照上面提到的fork流程,fork時需要大量的記憶體來存放父程序的地址空間,而在exec時又會立即用新程式替換掉。對於system呼叫失敗,僅僅是由於記憶體不足以容納另一份父程序地址空間,未免有點冤了。而直接使用vfork,則空閒記憶體並不需要那麼大,system為何沒有使用更合適的vfork呢?

解決

有兩個解決方案,一個是重新調整CPUL以及CPUH的記憶體分配,另一個是調整核心引數。
其中方案1存在一定的風險,因為記憶體的使用是動態的,如果分回來的過多,可能導致CPUH再次出現偶爾升級失敗,如果分過來的不夠,則有可能出現CPUL呼叫system失敗,因此,這裡主要考慮使用方案2。
在前面分析的過程中,已經明確,是由於觸發了Linux的虛擬記憶體保護,導致fork失敗。可以通過修改/proc/sys/vm/overcommit_memory的值,來修改系統的虛擬記憶體保護策略。這個檔案可以取0、1、2三個值,含義如下:
 0-Heuristic overcommit handling
0是LINUX系統的預設值。允許程序申請的記憶體超出一個合理(reasonable)值,如果程序申請超出的過多,則記憶體分配會被拒絕。如果程序超出範圍在合理值內,則也有可能觸發omm-kill,導致程序被殺死。
 1-Always overcommit
接收程序申請的任何記憶體大小
 2-Don’t overcommit
不允許超出記憶體總量。記憶體總量受另外一個引數overcommit_ratio控制,系統總可用記憶體計算公式如下:
總可用記憶體 = (swap space + RAM size * overcommit_ratio)
可以使用該選項為系統預留一定量的記憶體。overcommit_ratio引數可通過修改/proc/sys/vm/overcommit_ratio設定。
考慮到我們的程式碼中出了system外,並沒有fork的需求,而且system在執行fork後會立即呼叫exec釋放掉申請的空間,因此我們將overcommit_memory設定為1。