popen函数:
#include <stdio.h>
FILE *popen(const char *command, const char *type);
int pclose(FILE *stream);
函数说明:
popen()函数:首先,调用fork()函数创建一个子进程,执行一个shell命令,其次,调用pipe()函数创建一个管道,与子进程进行通讯。这个子进程必须由pclose()函数关闭,pclose()函数用于关闭标准I/O流,等待命令执行结束,然后返回shell的终止状态,如果shell不能被执行,则pclose()函数返回的终止状态与shell已执行exit一样。
command:是一个指向将要在shell中执行的命令的C风格字符串的指针,这个命令将被传到bin/sh并使用-c标志执行。
type:只能是读或写的一种,得到的返回值(标准I/O流)也具有和type相应的类型,如果type为”r”,那么返回的文件指针连接到command的标准输出,如果type为”w”,那么返回的文件指针连接到command的标准输入。
返回值:如果调用fork()或pipe()失败,或者不能分配内存函数将返回NULL,否则返回标准I/O流。这个返回的流是基于pipe()创建的管道实现的,是单向的,所以,当这个流是输入流时,向流中写内容相当于写入该命令的标准输入,此时,命令的标准输出和调用popen的进程相同。当这个流是输出流时,从流中读出数据相当于读取命令的标准输出,此时,命令的标准输入与调用popen的进程相同。
返回错误: popen没有为内存分配失败设置errno值。如果调用fork或pipe失败,errno将被设置成相应的错误类型。如果type参数不合法,errno将返回EINVAL。
system函数:
#include <stdlib.h>
int system(const char *command);
函数说明:
system()会调用fork()产生子进程,由子进程调用exec()执行shell命令,此命令执行完后随即返回原调用的进程,在调用system()期间,SIGCHLD会被暂时屏蔽,SIGINT、SIGQUIT信号则会被忽略。
command:是一个指向将要在shell中执行的命令的C风格字符串的指针,这个命令将被传到bin/sh并使用-c标志执行。
返回值:如果fork()失败,返回-1,出现错误。如果exec()失败,表示不能执行shell,返回值相当于shell执行了exit,返回127。如果执行成功则返回子shell的终止状态。
附加说明:在编写具有SUID/SGID权限的程序时请勿使用system(),system()会继承环境变量,通过环境变量可能会造成系统安全的问题。
fork函数:
fork()函数读者可参考我的另一篇博客,链接如下:
Linux下fork()&vfork()的区别、getenv()&setenv()函数以及僵尸进程、孤儿进程讲解
区别:
执行流程:
system调用进程会一直等待shell命令执行完成拿到shell返回状态才继续执行,popen调用进程不用等待shell执行完成拿到shell返回状态便可执行,即system函数内部含有wait操作,而popen函数内部不含有wait操作,wait操作在pclose函数中执行。可以理解为system为串行执行、popen为并行执行。
信号处理:
system中会屏蔽SIGCHLD信号,到底为什么会屏蔽SIGCHLD信号呢???那是因为在system函数中内置有wait操作,当我们不屏蔽SIGCHLD信号时,system创建的子进程执行完成后,会激活信号处理函数,若此时在信号处理函数中调用wait函数,就会将子进程的资源释放掉,system中的wait函数便会调用失败,而规定system函数中需要返回shell的执行状态,所以应该屏蔽SIGCHLD信号。
popen中不用屏蔽SIGCHLD信号,是因为如果屏蔽SIGCHLD信号,若在popen和pclose中创建新进程的话,进程结束不会调用信号处理函数,此进程的回收工作会在pclose的wait操作中进行,而popen中的创建的进程将不会被回收,而不屏蔽的话,popen和pclose中创建的进程会在信号处理函数中回收,popen中创建的进程会在pclose中回收。所以不应该屏蔽SIGCHLD信号。