system 調用 Qt 程序的問題
在公司做了一個嵌入式系統的引導程序,進程在系統開始後自啓動(這裏使用的是 rc.local 添加啓動項的方法),隨後按照相應的順序依次啓動相應的程序,之前一直沒有出現什麼問題,直到增加了一個啓動 Qt 程序,並監控程序狀態的需求後,出現了一些無法解釋的問題。
編寫代碼的過程
- 第一個需求:進程中調用一個 Qt 程序。
這個代碼很簡單,爲了省事,簡單使用了 system()
函數。
system("QtApplication -qws");
- 第二個需求:需要獲取 QtApplication 的返回值。
system 函數的執行過程分爲三個步驟:
1. 創建一個子進程,主要是 fork()
等過程。
2. 調用 /bin/sh
拉起 shell 腳本。
3. 執行相應的 shell 腳本,waitpid()。
因此,system 的返回值(status)囊括了以上三個步驟的結果:
1. 如果調用子進程失敗,或者 waitpid() 返回除了 EINTR
之外的錯誤,system 返回 -1;
2. 如果 shell 拉起失敗或未正常執行結束(只要能夠調用到 /bin/sh
,並且執行shell過程中沒有被其他信號異常中斷,都算正常結束),原因值被寫入到status的低8~15比特位中。
系統提供了宏:WIFEXITED(status)
來判斷 shell 的執行結果。如果 WIFEXITED(status)
爲真,則說明正常結束。
3. 如果 shell 腳本正常執行結束,將 shell 返回值填到 status 的低8~15比特位中。
同樣系統提供了宏:WEXITSTATUS(status)
來獲取相應腳本的執行結果。
一個簡單的執行代碼如下, 因爲程序需要執行一段時間,所以添加了一個輪詢:
int system_call()
{
int status = 9;
status = system("QtApplication -qws");
for(int i = 0; i < 6000; i++)
{
if(status != 9)
{
if( -1 == status) //status == -1, 子進程創建失敗
{
printf("system error!");
return -1;
}
else
{
if(WIFEXITED(status)) //WIFEXITED(status) 爲真,QtApplication 成功執行
{
if(0 == WEXITSTATUS(status))
{
printf("QtApplication successfully.\n"); //Qt 程序執行成功
return 0;
}
else
{
printf("QtApplication failed, exit code: [%d]\n", WEXITSTATUS(status));//Qt 程序執行失敗,獲取失敗返回值。
return -1;
}
}
else
{
printf("exit status = [%d]\n", WEXITSTATUS(status)); //WIFEXITED(status) 不爲真,shell 調用失敗。
return -1;
}
}
}
usleep(100);
}
return -1;
}
出現的問題
編寫完這個代碼後簡單的執行,調試,沒有發現什麼問題,所以開始進行測試,結果出現一個很奇怪的現象:
當 Qt 程序沒有執行完成的時候,system 返回了程序的返回值,並打印了以下的語句:exit status = [0]
更爲奇怪的是同樣的問題,當手動執行 system_call 的時候,並不會出現這樣的問題。
爲什麼會出現這樣的問題?
經過對系統代碼的分析,發現,在系統啓動時啓動一個撥號程序,而這個程序在系統啓動大概200ms的時候殺死了所有的 sh 程序。
問題的解決
因爲撥號程序不能更改,所以只能先通過 exec 的方法調用程序,畢竟system()函數用起來很容易出錯!!!
於是,嘗試了使用 exec 直接執行的方式來調用子程序,代碼如下:
int system_call()
{
pid_t pid, ret;
int status;
if((pid = fork()) < 0)
{
printf("Fork Failed!");
return -1;
}
else if(pid == 0)
{
if((status = execlp("QtApplication", "QtApplication", "-qws", NULL)) < 0)
{
printf("EXEC Failed!");
}
}
else
{
for(int i = 0; i < 6000; i++)
{
ret = waitpid(pid, &status, WNOHANG);
if(ret == pid)
{
if(WIFEXITED(status))
{
if(0 == WEXITSTATUS(status))
{
printf("QtApplication successful", 3);
return 0;
}
else
{
printf("tcu_update failed with CODE[%d]", WEXITSTATUS(status));
return -1;
}
}
else
{
printf("tcu_update start failed with CODE[%d]", WEXITSTATUS(status));
return -2;
}
}
usleep(100000);
}
return -2;
}
}