表面上区别
??表白上它们的区别是调用system函数输入命令后,system函数就能将结果显示出来,而popen函数并不会将直接返回出来,而是把结果保存在流中,输入从流中读取出结果然后将结果打印出来。
实际上
??实际上system中它屏蔽了 SIG_INT, SIG_QUIT, 然后阻塞wait 子进程,以此来保证子进程正确的回收。
??实际上而执行了popen , 还必须执行 pclose 将这个流关闭,否则就有僵尸进程。因为popen 中只是申请了管道,然后通过管道与生成的子进程进程数据交互,然后execl去替换命令,然后退出,并没有实际上的wait子进程,只有调用了 pclose 函数,执行命令的子进程才能被回收,否则就产生了僵尸进程。
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_SETMASK, &orig_mask, NULL);execl("/bin/sh", "sh", "-c", command, (char *) 0);exit(127);default:while(waitpid(pid, NULL, 0) == -1) //4. wait child process exit{if(errno != EINTR){break;}}}
}
return 0;
}-------------------------------------------------------------------
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) { /* child */ 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 (childpid[ 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);
}
vfork 不能直接返回
??因为,调用vfork 内核并没有给子进程分配虚拟地址空间,父子进程公用同一份虚拟地址空间,那么它们就公用同一份栈,那么子进程直接return 的话,势必会破坏原有父进程的栈帧,那么当父进程恢复调度的时候,必定会因为跳转到一个错误的地方导致段错误。
??另外vfork中,子进程也不能通过exit终止,exit会释放stdin 、stdout ,因为父子进程同属一个虚拟地址空间,这样当父进程回来的时候势必会发生未定义行为。所以,请使用execve 或者 _exit来替换子进程吧。