system函數
system函數用友善在一個程序中執行指令行(一行shell指令)。 用法如下:
#include <stdio.h>
#include <stdlib.h>
int main()
{
printf("Hello\n");
system("sleep 5");
return 0;
}
在程式中通過system調用了指令行 sleep 5。(這裡知識舉一個例子,當然可以執行一個類似“ bash test.sh”之類的腳本 在這個小程式的運作時,可以通過ps -aux 看到新增加了三個程序。 一個是我們程式本身a.out 一個是shell程序:sh -c *** 一個是我們執行的指令行程序:sleep程序
system函數的實作:
《APUE》中給出了system的一中實作:
#include <sys/wait.h>
#include <errno.h>
#include <signal.h>
#include <unistd.h>
int
system(const char *cmdstring) /* with appropriate signal handling */
{
pid_t pid;
int status;
struct sigaction ignore, saveintr, savequit;
sigset_t chldmask, savemask;
if (cmdstring == NULL)
return(1); /* always a command processor with UNIX */
ignore.sa_handler = SIG_IGN; /* ignore SIGINT and SIGQUIT */
sigemptyset(&ignore.sa_mask);
ignore.sa_flags = 0;
if (sigaction(SIGINT, &ignore, &saveintr) < 0)
return(-1);
if (sigaction(SIGQUIT, &ignore, &savequit) < 0)
return(-1);
sigemptyset(&chldmask); /* now block SIGCHLD */
sigaddset(&chldmask, SIGCHLD);
if (sigprocmask(SIG_BLOCK, &chldmask, &savemask) < 0)
return(-1);
if ((pid = fork()) < 0) {
status = -1; /* probably out of processes */
} else if (pid == 0) { /* child */
/* restore previous signal actions & reset signal mask */
sigaction(SIGINT, &saveintr, NULL);
sigaction(SIGQUIT, &savequit, NULL);
sigprocmask(SIG_SETMASK, &savemask, NULL);
execl("/bin/sh", "sh", "-c", cmdstring, (char *)0);
_exit(127); /* exec error */
} else { /* parent */
while (waitpid(pid, &status, 0) < 0)
if (errno != EINTR) {
status = -1; /* error other than EINTR from waitpid() */
break;
}
}
/* restore previous signal actions & reset signal mask */
if (sigaction(SIGINT, &saveintr, NULL) < 0)
return(-1);
if (sigaction(SIGQUIT, &savequit, NULL) < 0)
return(-1);
if (sigprocmask(SIG_SETMASK, &savemask, NULL) < 0)
return(-1);
return(status);
}
很多人對這個實作有不少的疑問。疑問點主要在下面幾個方面: 首先:為什麼要忽略中斷信号SIGINT和停止信号SIGQUIT。 其次:為什麼要鎖住子程序結束信号SIGCHLD 另外:為什麼鎖住了子程序結束信号SIGCHLD後,waitpid還能正确傳回。
1. 為什麼要忽略中斷信号SIGINT和停止信号SIGQUIT。 前面已經介紹,./a.out執行時,産生了三個程序:a.out、(shell程序)sh -c、system調用的指令行産生的程序(如sleep 5, 在apue中是ed程序)。 如果不忽略SIGINT和SIGQUIT會出現上面情況呢,《APUE》中通過自己設計的不屏蔽SIGINT和SIGQUIT信号的實作進行了測試,發現SIGINT和SIGQUIT信号會向./a.out産生的三個程序發送,即會發送給a.out、sh -c、ed。(其中sh -c預設忽略此信号),這樣會捕獲信号的有a.out、ed。這樣在實際運作中會出現上面情況呢:在我們要通過Ctl+c關閉ed(這種互動式程式經常會用ctl+c來關閉)時,信号也被發送給了a.out,也就是a.out和ed都被關閉了,這樣可能a.out還有其他工作也不能完成了。 出于以上的原因,我們要求system屏蔽SIGINT和SIGQUIT。
2.為什麼要鎖住子程序結束信号SIGCHLD 首先明白,如果不鎖住SIGCHLD,那麼在system執行的指令行建立的子程序(如ed)結束時,會向./a.out也發送SIGCHLD信号。這會産生什麼問題呢? 加入我們在調用system之前,./a.out建立了一個子程序(子程序A),并且是通過wait函數等待他結束。但是我們的ed程序結束時就像a.out發送了SIGCHLD,導緻wait直接傳回,由此a.out就無法等到子程序A結束了。
3.為什麼鎖住了子程序結束信号SIGCHLD後,waitpid還能正确傳回。 這一部分我還沒找到确切的證據,但描述的也都是一些合理的推斷: 我認為wait和waitpid并不是通過捕獲SIGCHLD來傳回子程序結束狀态的。理由如下: a.Linux系統對SIGCHLD的預設處理方式就是ignore b.經常看到一些code裡,捕獲SIGCHLD,然後再SIGCHLD的信号處理函數中調用wait,來使得父程序不阻塞。 c.waitpid中的option選項為WNOHANG時,waitpid可以立即傳回。也就是可以循環調用waitpid來判斷子程序是否退出。 基于以上三點,基本上可以退出waitpid并不是通過捕獲SIGCHLD來判斷子程序狀态的。而應該是通過阻塞讀取核心某個狀态值