system 函数的实现

2014-09-18 21:37:22   最后更新: 2014-09-20 17:43:19   访问数量:663




system 函数是由 fork 和 exec 函数实现的

写了一个基本的实现:

/* * file: system.c * author: 龙泉居士 * date: 2014-09-08 */ #include <stdio.h> #include <sys/wait.h> #include <unistd.h> int my_system(const char *cmd) { pid_t pid = fork(), son_pid; if (pid < 0) return -1; if (pid == 0) { son_pid = fork(); if (son_pid < 0 || (son_pid>0 && execl("/bin/sh", "sh", "-c", cmd, (char *)0) < 0)) return 127; } return pid; } int main() { pid_t pid = my_system("date"); printf("pid: %d\n", pid); return 0; }

 

参考了一下 APUE,发现有以下问题:

  1. 没有判断参数为空的情况
  2. fork两次,虽然避免了僵尸进程的产生,但却使得 system 函数返回了两次(可以通过在子进程中exit使最终只返回主进程的值)
  3. 没有对子进程的退出情况作出处理
  4. 孙子进程的执行可能会比主进程先执行,往往我们不希望如此

 

下面是改进的 my_system

/* * file: system.c * author: 龙泉居士 * date: 2014-09-08 */ #include <stdio.h> #include <sys/wait.h> #include <errno.h> #include <unistd.h> int my_system(const char *cmd) { pid_t pid; int status; if (cmd == NULL) return 1; if ((pid = fork()) < 0) return -1; else if (pid == 0) { execl("/bin/sh", "sh", "-c", cmd, (char *)0); _exit(127); } else { while (waitpid(pid, &status, 0) < 0) { if (errno != EINTR) { status = -1; break; } } } return status; } int main() { int status = my_system("date"); printf("status: %d\n", status); return 0; }

 

 

考虑通过 system 函数调用 ed 编辑器,此时,终端按下 CTRL+C 产生 SIGINT 信号

由父进程和子进程所构成的进程组接到这一信号,由于父进程没有捕获该信号,于是执行默认操作,父进程结束,随之关闭了标准输入输出,子进程由于无法从标准输入读入数据,于是也相继退出

另外,无论如何,在 system 执行过程中,不应该让调用进程去获得此时产生的一些信号,比如 SIGCHLD,调用进程会误以为自己的子进程结束,而因此作出处理

 

system 与信号处理

/* * file: system.c * author: 龙泉居士 * date: 2014-09-20 */ #include <stdio.h> #include <sys/wait.h> #include <errno.h> #include <unistd.h> int my_system(const char *cmd) { struct sigaction sig_action, old_int_action, old_quit_action, old_chld_action; pid_t pid; int status, save_errno = errno; if (cmd == NULL) return 1; if ((pid = fork()) < 0) return -1; else if (pid == 0) { execl("/bin/sh", "sh", "-c", cmd, (char *)0); _exit(127); } else { sig_action.sa_handler = SIG_IGN; sigemptyset(&sig_action.sa_mask); sig_action.sa_flags = 0; #ifdef SA_RESTART sig_action.sa_flags |= SA_RESTART; #endif if (sigaction(SIGINT, &sig_action, &old_int_action) < 0 || sigaction(SIGQUIT, &sig_action, &old_quit_action) < 0 || sigaction(SIGCHLD, &sig_action, &old_chld_action) < 0) { printf("sigaction error\n"); _exit(-2); } while (waitpid(pid, &status, 0) < 0) { if (sigaction(SIGINT, &old_int_action, NULL) < 0 || sigaction(SIGQUIT, &old_quit_action, NULL) < 0 || sigaction(SIGCHLD, &old_chld_action, NULL) < 0) { printf("sigaction backup error\n"); _exit(-1); } errno = save_errno; if (errno != EINTR && errno != 0) { printf("errno: %d\n", errno); status = -1; break; } } } return status; } int main() { int status = my_system("ed"); printf("status: %d\n", status); return 0; }

 

 

这样在程序运行过程中,父进程忽略了 SIGINT、SIGQUIT、SIGCHLD,不会因为终端产生的信号而退出

但是上述程序有下列问题:

  1. 由于 fork 后父子进程执行顺序的不确定性,所以不能依赖于父进程先运行,如果是子进程先运行,并马上产生了 SIGINT 信号,父进程还是会退出
  2. 不应简单的忽略 SIGCHLD 信号,有可能调用进程的子进程此时产生了这一信号,所以只应该阻塞该信号而不是忽略

 

system 的 POSIX 实现

/* * file: system.c * author: 龙泉居士 * date: 2014-09-20 */ #include <stdio.h> #include <sys/wait.h> #include <errno.h> #include <unistd.h> int my_system(const char *cmd) { int status; pid_t pid; struct sigaction sig_action, old_int_action, old_quit_action; sigset_t chldmask, savemask; if (cmd == NULL) return 1; sig_action.sa_handler = SIG_IGN; sigemptyset(&sig_action.sa_mask); sig_action.sa_flags = 0; if (sigaction(SIGINT, &sig_action, &old_int_action) < 0 || sigaction(SIGQUIT, &sig_action, &old_quit_action) < 0) { printf("sigaction error\n"); _exit(-2); } sigemptyset(&chldmask); sigaddset(&chldmask, SIGCHLD); if (sigprocmask(SIG_BLOCK, &chldmask, &savemask) < 0) { printf("sigprocmask error\n"); _exit(-2); } if ((pid = fork()) < 0) return -1; else if (pid == 0) { if (sigaction(SIGINT, &old_int_action, NULL) < 0 || sigaction(SIGQUIT, &old_quit_action, NULL) < 0) { printf("sigaction backup error\n"); _exit(-1); } if (sigprocmask(SIG_SETMASK, &savemask, NULL) < 0) { printf("sigprocmask error\n"); _exit(-2); } execl("/bin/sh", "sh", "-c", cmd, (char *)0); _exit(127); } else { while (waitpid(pid, &status, 0) < 0) { if (errno != EINTR) { printf("errno: %d\n", errno); status = -1; break; } } } if (sigaction(SIGINT, &old_int_action, NULL) < 0 || sigaction(SIGQUIT, &old_quit_action, NULL) < 0) { printf("sigaction backup error\n"); _exit(-1); } if (sigprocmask(SIG_SETMASK, &savemask, NULL) < 0) { printf("sigprocmask error\n"); _exit(-2); } return status; } int main() { int status = my_system("php"); printf("status: %d\n", status); return 0; }

 

 






读书笔记      技术帖      linux      unix      c语言      龙潭书斋      apue      unix环境高级编程      fork      exec      signal      信号      system     


京ICP备15018585号