#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
/* 參考 glibc sysdeps/posix/system.c: __libc_system/do_system */
int test_system(char* cmd)
{
int status;
pid_t pid;
struct sigaction sa;
struct sigaction intr, quit;
sigset_t omask;
if (NULL == cmd) {/* glibc中當cmd為空時, 將cmd賦值為 'exit 0' */
return 0;
}
sa.sa_handler = SIG_IGN; /* 對捕獲的信號采取忽略操作 */
sa.sa_flags = 0;
sigemptyset(&sa.sa_mask); /* 清空信號集, 不包含任何信號 */
/* 忽略 SIGINT 和 SIGQUIT 信號, 為什么?
有誰知道嗎?
參考:https://www.cons.org/cracauer/sigint.html */
sigaction(SIGINT, &sa, &intr); /* 原有 SIGINT 處理存儲到 intr 中, 用於恢復 */
sigaction(SIGQUIT, &sa, &quit); /* 原有 SIGQUIT 處理存儲到 quit 中,用於恢復 */
/* 阻塞SIGCHLD信號,為什么?
子進程結束后,內核會給父進程發送SIGCHLD信號, 如果你注冊了該信號的處理函數,並且
在其中也用waipid獲取了子進程結束的狀態, 當信號處理函數先於system中waitpid執行,
隨后system函數中的waipid就會返回 No child processes 錯誤,無法獲取到shell命令執行的結果, 只能返回 -1.
阻塞SIGCHLD可以確保system中waitpid先執行, 以獲取子進程結束狀態,
阻塞SIGCHLD期間, 如果還有其他子進程退出, 那么他們產生的SIGCHLD信號也會阻塞,
但是阻塞解除后你只會收到一個SIGCHLD通知,如果你需要使用waitpid獲取所以子進程狀態,那么需要循環調用waitpid */
sigaddset(&sa.sa_mask, SIGCHLD); /* 復用面前的信號集(空的), 將SIGCHLD信號加入信號集 */
sigprocmask(SIG_BLOCK, &sa.sa_mask, &omask); /* 阻塞信號集中的信號(其實信號集中只有SIGCHLD信號) */
pid = fork();
if (pid == (pid_t)0) {/* 子進程 */
const char *new_argv[4];
new_argv[0] = "sh";
new_argv[1] = "-c";
new_argv[2] = cmd;
new_argv[3] = NULL;
/* 子進程繼承父進程的信號掩碼, 恢復SIGINT和SIGQUIT的信號處理操作. */
sigaction(SIGINT, &intr, (struct sigaction *)NULL);
sigaction(SIGQUIT, &quit, (struct sigaction *)NULL);
sigprocmask(SIG_SETMASK, &omask, (sigset_t *)NULL);
/* Exec the shell. */
(void)execve("/bin/sh", (char *const *) new_argv, __environ);
/* execve通常不會返回, 返回就說明發生錯誤 */
_exit(127); /* 子進程返回狀態碼127 */
}
else if (pid < (pid_t) 0) {
/* fork()失敗返回 -1 */
status = -1; /* 錯誤查看 errno */
}
else { /* 父進程 */
/* Note the system() is a cancellation point. But since we call
waitpid() which itself is a cancellation point we do not have to do anything here. */
if (waitpid(pid, &status, 0) != pid) {/* 子進程回收 */
status = -1; /* 錯誤查看 errno */
}
}
/* 父進程信號處理恢復 */
sigaction(SIGINT, &intr, (struct sigaction *)NULL);
sigaction(SIGQUIT, &quit, (struct sigaction *)NULL);
sigprocmask(SIG_SETMASK, &omask, (sigset_t *)NULL);
return status;
}