關於linux system函數調用遇到的坑

關於linux c++守護進程調用調用system函數

我們的程序是守護進程,也就是說在最開始會設置一些信號處理,比如說

...
signal(SIGCHLD, SIG_IGN); //忽略子進程結束的信號
...

在這樣的場景之下,我想實現這樣的一個功能:

通過調用system函數來執行一些系統命令,並根據系統命令的返回值是不是0來判斷命令是否執行成功(正常情況下調用system函數,執行成功 return 0; 執行失敗 return 正數;)當我寫下

int ret = system("ls -a");
std::cout<<"call system ret="<<ret<<std::endl;

時發現始終 ret = -1 查看errno發現errno=10(errno.10 is: No child processes)

進一步分析:

system的實現大致是這樣的:

1)主進程fork一個子進程
2)子進程調用exec函數去執行命令
3)父進程調用waitpid來等來子進程結束

也就是說在調用waitpid的時候子進程退出,父進程收不到他的退出信號只發現這個進程不在了,所以有errno=10 ret = -1
具體原因:

當父進程未調用wait系列函數等待子進程結束且未顯示忽略SIGCHLD信號,則子進程將成爲殭屍進程。如果忽略SIGCHLD信號,子進程不會成爲殭屍進程,子進程結束後將會直接退出並釋放資源,等父進程調用waitpid時發現pid進程不存在了

解決辦法:

signal(SIGCHLD, SIG_DFL);
int ret = system(cmd);
signal(SIGCHLD, SIG_IGN);

在調用system函數的時候按照默認方式處理SIGCHLD信號。

深入探索

system函數的內部實現是要阻塞SIGCHLD信號,原因是怕system的調用者在調用system函數之前有註冊過SIGCHLD的信號處理函數,當產生SIGCHLD信號時system的調用者使用一種wait獲取到子進程的終止狀態,而system函數就無法獲取子進程的終止狀態(子進程的終止狀態只能被獲取一次)
注意一點:不管有沒有SIGCHLD信號通知,waitpid都可獲取子進程的終止狀態

忽略SIGCHLD信號調用system的結果是errno=10 ret=-1,忽略SIGCHLD信號然後阻塞SIGCHLD信號並沒有影響errno=10 ret=-1的結果,因爲阻塞是暫時不接受這個信號,忽略是接受到這個信號後的一種處理方式

關於TcpServer程序調用system函數的問題

場景:我有一個tcp服務器假設它叫agent,它會監聽一個端口(如8420)。它的工作是接受遠程發過來的命令,來啓動、停止一個或多個服務程序。這個處理命令的實現是通過system函數來實現的,比如說啓動:system(“cd /home/user/xxxsvr; ./xxxsvr”)

出現的問題:當agent收到命令並根據命令啓動多個程序之後把agent關閉,他監聽的端口8420並沒有關閉,而是被agent啓動的某個程序佔用

分析 : 還是system函數的實現。fork一個子進程會複製父進程的所有資源,包括文件描述符。

解決的辦法

fcntl(listen_fd, F_SETFD, FD_CLOEXEC);

FD_CLOEXEC標誌用來控制在執行 exec 後,是否關閉對應的文件描述符

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章