当前位置: 代码迷 >> 综合 >> popen / system的区别/为什么vfork 不能直接return 返回
  详细解决方案

popen / system的区别/为什么vfork 不能直接return 返回

热度:59   发布时间:2024-01-14 08:38:28.0

表面上区别

??表白上它们的区别是调用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来替换子进程吧。

  相关解决方案