Linux的system和popen的差异
1.system()和popen()簡(jiǎn)介
在linux中我們可以通過(guò)system()來(lái)執(zhí)行一個(gè)shell命令,popen()也是執(zhí)行shell命令并且通過(guò)管道和shell命令進(jìn)行通信。
system()、popen()給我們處理了fork、exec、waitpid等一系列的處理流程,讓我們只需要關(guān)注最后的返回結(jié)果(函數(shù)的返回值)即可。
2.system()、popen()源碼
首先我們來(lái)看一個(gè)這兩個(gè)函數(shù)在源碼(偽代碼)上面的差異。
int system(const char *command) {struct sigaction sa_ignore, sa_intr, sa_quit;sigset_t block_mask, orig_mask;pid_t pid;sigemptyset(&block_mask);sigaddset(&block_mask, SIGCHLD);sigprocmask(SIG_BLOCK, &block_mask, &orig_mask); //1.block SIGCHLDsa_ignore.sa_handler = SIG_IGN;sa_ignore.sa_flags = 0;sigemptyset(&sa_ignore.sa_mask);sigaction(SIGINT, &sa_ignore, &sa_intr); //2.ignore SIGINT signalsigaction(SIGQUIT, &sa_ignore, &sa_quit); //3.ignore SIGQUIT signalswitch((pid = fork())) {case -1:return -1;case 0:sigaction(SIGINT, &sa_intr, NULL);sigaction(SIGQUIT, &sa_quit, NULL);sigprocmask(SIG_SETMASAK, &orig_mask, NULL);execl("/bin/sh", "sh", "-c", command, (char *)0);exit(127);default:while(waitpid(pid, NULL, 0) == -1) { //4.wait child process exitif (errno != EINTR) {break;}}}return 0; }上面是一個(gè)不算完整的system函數(shù)源碼,后面需要我們關(guān)注和popen差異的部分已經(jīng)用數(shù)字標(biāo)識(shí)出來(lái)了。
static pid_t *childpid = NULL; /* ptr to array allocated at run-time */static int maxfd; /* from our open_max(), {Prog openmax} */#define SHELL "/bin/sh"FILE * popen(const char *cmdstring, const char *type) {int i, pfd[2];pid_t pid;FILE *fp;/* only allow "r" or "w" */if ((type[0] != 'r' && type[0] != 'w') || type[1] != 0) {errno = EINVAL; /* required by POSIX.2 */return (NULL);}if (childpid == NULL) { /*first time through *//*allocate zeroed out array for child pids */maxfd = open_max();if (( childpid = calloc(maxfd, sizeof(pid_t))) == NULL)return (NULL);}if (pipe(pfd) < 0)return (NULL); /*errno set by pipe() */if ((pid = fork()) < 0) return (NULL); /*errno set by fork() */else if (pid == 0) {if (*type == 'r') {close(pfd[0]);if (pfd[1] != STDOUT_FILENO) {dup2(pfd[1], STDOUT_FILENO);close(pfd[1]);}} else {close(pfd[1]);if (pfd[0] != STDIN_FILENO) {dup2(pfd[0], STDIN_FILENO);close(pfd[0]);}}/*close all descriptors in childpid[]*/for (i=0; i<maxfd; i++)if (childpfd[i] > 0) close(i);execl(SHELL, "sh", "-c", cmdstring, (char *)0);_exit(127);}/*parent*/if (*type == 'r') {close(pfd[1]);if ((fp = fdopen(pfd[0], type)) == NULL) return (NULL);} else {close(pfd[0]);if ((fp = fdopen(pfd[1], type)) == NULL)return (NULL);}childpid[fileno(fp)] = pid; /*remember child pid for this fd*/return (fp); }上面是popen的源碼。
3.執(zhí)行流程
? ? ? ?從上面的源碼可以看到system和popen都是執(zhí)行了類(lèi)似的運(yùn)行流程,大致是fork->execl->return。但是我們看到system在執(zhí)行期間調(diào)用進(jìn)程會(huì)一直等待shell命令執(zhí)行完成(waitpid等待子進(jìn)程結(jié)束)才返回,但是popen無(wú)須等待shell命令執(zhí)行完成就返回了。我們可以理解system為串行執(zhí)行,在執(zhí)行期間調(diào)用進(jìn)程放棄了"控制權(quán)",popen為并行執(zhí)行。
? ? ? ?popen中的子進(jìn)程沒(méi)人給它“收尸”了啊?是的,如果你沒(méi)有在調(diào)用popen后調(diào)用pclose那么這個(gè)子進(jìn)程就可能變成"僵尸"。
? ? ? ?上面我們沒(méi)有給出pclose的源碼,其實(shí)我們根據(jù)system的源碼差不多可以猜測(cè)出pclose的源碼就是system中第4部分的內(nèi)容。
4.信號(hào)處理
我們看到system中對(duì)SIGCHLD、SIGINT、SIGQUIT都做了處理,但是在popen中沒(méi)有對(duì)信號(hào)做任何的處理。
SIGCHLD是子進(jìn)程退出的時(shí)候發(fā)給父進(jìn)程的一個(gè)信號(hào),system()中為什么要屏蔽SIGCHLD信號(hào)可以參考:https://blog.csdn.net/astrotycoon/article/details/40626355、waitpid(or wait)和SIGCHILD的關(guān)系,總結(jié)一句就是為了system()調(diào)用能夠及時(shí)的退出并且能夠正確的獲取子進(jìn)程的退出狀態(tài)(成功回收子進(jìn)程)。
popen沒(méi)有屏蔽SIGCHLD,主要的原因就是popen是"并行"的。如果我們?cè)谡{(diào)用popen的時(shí)候屏蔽了SIGCHLD,那么如果在調(diào)用popen和pclose之間調(diào)用進(jìn)程又創(chuàng)建了其他的子進(jìn)程并且調(diào)用進(jìn)程注冊(cè)了SIGCHLD信號(hào)處理句柄來(lái)處理子進(jìn)程的回收工作(waitpid),那么這個(gè)回收工作就會(huì)一直阻塞到pclose調(diào)用。這也意味著如果調(diào)用進(jìn)程在pclose之前執(zhí)行了一個(gè)wait()操作的話就可能獲取到popen創(chuàng)建的子進(jìn)程的狀態(tài),這樣在調(diào)用pclose的時(shí)候就會(huì)回收(waitpid)子進(jìn)程失敗,返回-1,同時(shí)設(shè)置errno為ECHLD,標(biāo)識(shí)pclose無(wú)法獲取子進(jìn)程狀態(tài)。
system()中屏蔽SIGINT、SIGQUIT的原因可以繼續(xù)參考上面提到的https://blog.csdn.net/astrotycoon/article/details/40626355,popen()函數(shù)中沒(méi)有屏蔽SIGINT、SIGQUIT的原因也還是因?yàn)閜open是"并行的",不能影響它"并行"進(jìn)程。
5.功能
從上面的章節(jié)我們基本已經(jīng)把這兩個(gè)函數(shù)剖析的差不多了,這兩個(gè)的功能上面的差異也比較明顯了,system就是執(zhí)行shell命令最后返回是否執(zhí)行成功,popen執(zhí)行并且通過(guò)管道和shell命令進(jìn)行通信。
NOTE
在特權(quán)(setuid、setgid)進(jìn)程中千萬(wàn)注意不要使用system和popen。
?
轉(zhuǎn)自:https://blog.csdn.net/liuxingen/article/details/47057539
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎(jiǎng)勵(lì)來(lái)咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎(jiǎng)總結(jié)
以上是生活随笔為你收集整理的Linux的system和popen的差异的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: [shell]C语言调用shell脚本接
- 下一篇: 带超时的system