浅聊一下system()函数与popen()函数
1.system()函数
system()函数先fork一个子进程,在这个子进程中调用/bin/sh -c来执行command指定的命令。/bin/sh在系统中一般是个软链接,指向dash或者bash等常用的shell,-c选项是告诉shell从字符串command中读取要执行的命令。父进程则调用waitpid()函数来为变成僵尸的子进程收尸,获得其结束状态,然后将这个结束状态返回给system()函数的调用者。
system()源码:
int system(const char * cmdstring) {
pid_t pid;
int status;
if(cmdstring == NULL) {
return (1); //如果cmdstring为空,返回非零值,一般为1
}
if((pid = fork())<0) {
status = -1; //fork失败,返回-1
} else if (pid == 0) {
execl("/bin/sh", "sh", "-c", cmdstring, (char *)0);
_exit(127); // exec执行失败返回127,注意exec只在失败时才返回现在的进程,成功的话现在的进程就不存在
} else { //父进程
while(waitpid(pid, &status, 0) < 0) {
if(errno != EINTR) {
status = -1; //如果waitpid被信号中断,则返回-1
break;
}
}
}
return status; //如果waitpid成功,则返回子进程的返回状态
}
(1)当参数command是NULL的时候
返回1,表明系统的命令处理程序,即/bin/sh是可用的。
返回0,命令处理程序不可用。
(2)当参数command不是NULL的时候
1)如果fork等系统调用失败,或者waitpid函数发生除EINTR外的错误时,system返回-1
2)一切致使execl失败的情况下,system返回127
3)除此之外,system返回/bin/sh的终止状态
2.popen()函数
popen总是和pclose一起出现被使用的。popen() 创建一个匿名管道,通过fork或者invoke一个子进程,然后执行command。返回值在标准IO流中,由于是在管道之中,因此数据流是单向的,command只能产生stdout或者读取stdin,因此type只有两个值:‘w’或‘r’。r表示command从管道中读取数据流,而w表示command的stdout输出到管道中。command无法同时读取和输出。popen返回该FIFO数据流的指针。并行执行,可以在程序中读取命令的标准输出,也可在程序中写入命令的标准输入。
pclose函数关闭标准IO流,等待命令的终止,然后返回shell的终止状态。如果你没有在调用popen后调用pclose那么这个子进程就可能变成”僵尸”。popen()函数源码:
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() */
/* child */
else if (pid == 0) {
if (*type == 'r') {
close(pfd[0]);
if (pfd[1] != STDOUT_FILENO) {
dup2(pfd[1], STDOUT_FILENO); // 子进程的标准输出写到pfd[1]
close(pfd[1]);
}
} else {
close(pfd[1]);
if (pfd[0] != STDIN_FILENO) {
dup2(pfd[0], STDIN_FILENO); // 子进程从的标准输入从pfd[0]读入
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);
}
若成功则返回文件指针,否则返回NULL,错误原因存于errno中。
3.区别
1.功能:
-
popen()
函数:popen()
函数可以用来创建一个管道,通过管道与子进程进行双向通信。它可以执行一个外部命令,并返回一个文件指针,可以用于读取子进程的输出或向子进程发送输入。 -
system()
函数:system()
函数用于执行一个外部命令,但它只能执行命令,无法直接获取子进程的输入或输出。
2.使用方式:
-
popen()
函数:popen()
函数需要传入两个参数,第一个参数是要执行的命令字符串,第二个参数是打开模式("r"用于读取子进程输出,"w"用于向子进程发送输入)。它返回一个标准I/O文件指针,可以用于读取子进程的输出或向子进程发送输入。使用完毕后,需要通过pclose()
函数关闭文件指针。 -
system()
函数:system()
函数只需要传入要执行的命令字符串作为参数即可。它会在执行命令完成后返回,并返回命令的退出状态。
3.安全性:
-
popen()
函数:popen()
函数对于执行外部命令的参数需要进行适当的过滤和验证,以防止命令注入等安全漏洞。 -
system()
函数:system()
函数对于参数的安全性验证较弱,如果参数字符串来自用户输入等不可信源,存在命令注入的风险。
4.信号处理文章来源:https://www.toymoban.com/news/detail-495732.html
system中对SIGCHLD、SIGINT、SIGQUIT都做了处理,但是在popen中没有对信号做任何的处理。
SIGCHLD是子进程退出的时候发给父进程的一个信号,总结为了system()调用能够及时的退出并且能够正确的获取子进程的退出状态(成功回收子进程)。
popen没有屏蔽SIGCHLD,主要的原因就是popen是”并行”的。如果我们在调用popen的时候屏蔽了SIGCHLD,那么如果在调用popen和pclose之间调用进程又创建了其它的子进程并且调用进程注册了SIGCHLD信号处理句柄来处理子进程的回收工作(waitpid)那么这个回收工作会一直阻塞到pclose调用。这也意味着如果调用进程在pclose之前执行了一个wait()操作的话就可能获取到popen创建的子进程的状态,这样在调用pclose的时候就会回收(waitpid)子进程失败,返回-1,同时设置errno为ECHLD,标示pclose无法获取子进程状态。
popen()函数中没有屏蔽SIGINT、SIGQUIT的原因也还是因为popen是”并行的”,不能影响其它”并行”进程。文章来源地址https://www.toymoban.com/news/detail-495732.html
总结:
-
popen()
函数适合在需要与子进程进行双向通信的情况下使用,可以方便地读取子进程的输出或向子进程发送输入。 -
system()
函数适合简单的命令执行,不需要与子进程进行交互的情况下使用。 - 在使用这些函数时,需要注意对命令参数的验证和过滤,以避免安全漏洞。
到了这里,关于浅聊一下system()函数与popen()函数的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!