热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Linux调研popen/system,理解这两个函数和fork的区别.

自己的总结:1.popen是并行(最后子进程是由pclose回收),system是串行(会等待子进程做完事,然

 

自己的总结:

          1.popen是并行(最后子进程是由pclose回收),system是串行(会等待子进程做完事,然后收拾)。

          2.system() 在等待命令终止时将忽略SIGINT 和SIGQUIT 信号,同时阻塞SIGCHLD 信号,但是popen里面都没有涉及到信号

          3.system返回值比较复杂,里面有fork,exec,waitpid,返回值可能就是fork,exec,waitpid的回值,而popen会返回一个流,popen可以打开一个文件

          4.system只有一个参数,例如system("mkdir dir");创建一个目录,popen有两个参数,一个是文件的路径:路径,一个是选项

        

           5.popen() 的参数是指向以空字符结尾的字符串的指针,这些字符串分别包含一个shell 命令行和一个I/O 模式,此

模式可以是进行读取的r ,或进行写入的w 。

 

           6.在linux中我们可以通过system()来执行一个shell命令,popen()也是执行shell命令并且通过管道和shell命令进行通信。

system()、popen()给我们处理了fork、exec、waitpid等一系列的处理流程,让我们只需要关注最后的返回结果(函数的返回值)即可。

 

原文://blog.csdn.net/liuxingen/article/details/47057539?utm_source=copy 

linux popen()与system()的区别

popen() 可以在调用程序和POSIX shell /usr/bin/sh 要执行的命令之间创建一个管道(请参阅sh-posix(1) )。

popen() 的参数是指向以空字符结尾的字符串的指针,这些字符串分别包含一个shell 命令行和一个I/O 模式,此

模式可以是进行读取的r ,或进行写入的w 。

popen() 可返回一个流指针,这样,当I/O 模式为w 时,便可以通过写入文件stream 来写入到命令的标准输入;

当I/O 模式为r 时,通过从文件stream 读取数据,从命令的标准输出读取数据。

popen() 打开的流应由pclose() 关闭,这需要等待终止关联的进程,然后返回命令的退出状态。

因为打开的文件是共享的,所以类型为r 的命令可用作输入过滤器,类型为w 的命令可用作输出过滤器。

 

system() 可执行由command 指向的字符串指定的命令。已执行命令的环境就如同使用fork() (请参阅fork(2) )

创建了一个子进程,子进程按以下方式通过调用execl() (请参阅exec(2) )来调用sh-posix(1) 实用程序:

execl("/usr/bin/sh", "sh", "-c", command, 0);

system() 在等待命令终止时将忽略SIGINT 和SIGQUIT 信号,同时阻塞SIGCHLD 信号。如果这会导致应用程

序错过一个终止它的信号,则应用程序应检查system() 的返回值;如果由于收到某个信号而终止了命令,应用程

序应采取一切适当的措施。

system() 不影响除自己创建的一个或多个进程以外的调用进程的任何子进程的终止状态。

在子进程终止之前, system() 不会返回。

 

 

1. system()和popen()简介

 

在linux中我们可以通过system()来执行一个shell命令,popen()也是执行shell命令并且通过管道和shell命令进行通信。

system()、popen()给我们处理了fork、exec、waitpid等一系列的处理流程,让我们只需要关注最后的返回结果(函数的返回值)即可。

 

2. system()、popen()源码

 

首先我们来看一下这两个函数在源码(伪代码)上面的差异。

 

1. system()和popen()简介

在linux中我们可以通过system()来执行一个shell命令,popen()也是执行shell命令并且通过管道和shell命令进行通信。 
system()、popen()给我们处理了fork、exec、waitpid等一系列的处理流程,让我们只需要关注最后的返回结果(函数的返回值)即可。

2. system()、popen()源码

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;}}}
}

上面是一个不算完整的system函数源码,后面需要我们关注和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] !&#61; &#39;r&#39; && type[0] !&#61; &#39;w&#39;) || type[1] !&#61; 0) { errno &#61; EINVAL; /* required by POSIX.2 */ return(NULL); } if (childpid &#61;&#61; NULL) { /* first time through */ /* allocate zeroed out array for child pids */ maxfd &#61; open_max(); if ( (childpid &#61; calloc(maxfd, sizeof(pid_t))) &#61;&#61; NULL) return(NULL); } if (pipe(pfd) <0) return(NULL); /* errno set by pipe() */ if ( (pid &#61; fork()) <0) return(NULL); /* errno set by fork() */ else if (pid &#61;&#61; 0) { /* child */ if (*type &#61;&#61; &#39;r&#39;) { close(pfd[0]); if (pfd[1] !&#61; STDOUT_FILENO) { dup2(pfd[1], STDOUT_FILENO); close(pfd[1]); } } else { close(pfd[1]); if (pfd[0] !&#61; STDIN_FILENO) { dup2(pfd[0], STDIN_FILENO); close(pfd[0]); } } /* close all descriptors in childpid[] */ for (i &#61; 0; i 0) close(i); execl(SHELL, "sh", "-c", cmdstring, (char *) 0); _exit(127); } /* parent */ if (*type &#61;&#61; &#39;r&#39;) { close(pfd[1]); if ( (fp &#61; fdopen(pfd[0], type)) &#61;&#61; NULL) return(NULL); } else { close(pfd[0]); if ( (fp &#61; fdopen(pfd[1], type)) &#61;&#61; NULL) return(NULL); } childpid[fileno(fp)] &#61; pid; /* remember child pid for this fd */ return(fp);
}

上面是popen的源码。

3. 执行流程

从上面的源码可以看到system和popen都是执行了类似的运行流程&#xff0c;大致是fork->execl->return。但是我们看到system在执行期间调用进程会一直等待shell命令执行完成(waitpid等待子进程结束)才返回&#xff0c;但是popen无须等待shell命令执行完成就返回了。我们可以理解system为串行执行&#xff0c;在执行期间调用进程放弃了”控制权”&#xff0c;popen为并行执行。 
popen中的子进程没人给它”收尸”了啊&#xff1f;是的&#xff0c;如果你没有在调用popen后调用pclose那么这个子进程就可能变成”僵尸”。 
上面我们没有给出pclose的源码&#xff0c;其实我们根据system的源码差不多可以猜测出pclose的源码就是system中第4部分的内容。

4. 信号处理

我们看到system中对SIGCHLD、SIGINT、SIGQUIT都做了处理&#xff0c;但是在popen中没有对信号做任何的处理。 
SIGCHLD是子进程退出的时候发给父进程的一个信号&#xff0c;system()中为什么要屏蔽SIGCHLD信号可以参考&#xff1a;system函数的总结、waitpid(or wait)和SIGCHILD的关系&#xff0c;总结一句就是为了system()调用能够及时的退出并且能够正确的获取子进程的退出状态(成功回收子进程)。 
popen没有屏蔽SIGCHLD&#xff0c;主要的原因就是popen是”并行”的。如果我们在调用popen的时候屏蔽了SIGCHLD&#xff0c;那么如果在调用popen和pclose之间调用进程又创建了其它的子进程并且调用进程注册了SIGCHLD信号处理句柄来处理子进程的回收工作(waitpid)那么这个回收工作会一直阻塞到pclose调用。这也意味着如果调用进程在pclose之前执行了一个wait()操作的话就可能获取到popen创建的子进程的状态&#xff0c;这样在调用pclose的时候就会回收(waitpid)子进程失败&#xff0c;返回-1&#xff0c;同时设置errno为ECHLD&#xff0c;标示pclose无法获取子进程状态。 
system()中屏蔽SIGINT、SIGQUIT的原因可以继续参考上面提到的system函数的总结&#xff0c;popen()函数中没有屏蔽SIGINT、SIGQUIT的原因也还是因为popen是”并行的”&#xff0c;不能影响其它”并行”进程。

5. 功能

从上面的章节我们基本已经把这两个函数剖析的差不多了&#xff0c;这两个的功能上面的差异也比较明显了&#xff0c;system就是执行shell命令最后返回是否执行成功&#xff0c;popen执行命令并且通过管道和shell命令进行通信。

 


推荐阅读
  • 本文介绍了深入浅出Linux设备驱动编程的重要性,以及两种加载和删除Linux内核模块的方法。通过一个内核模块的例子,展示了模块的编译和加载过程,并讨论了模块对内核大小的控制。深入理解Linux设备驱动编程对于开发者来说非常重要。 ... [详细]
  • Go Cobra命令行工具入门教程
    本文介绍了Go语言实现的命令行工具Cobra的基本概念、安装方法和入门实践。Cobra被广泛应用于各种项目中,如Kubernetes、Hugo和Github CLI等。通过使用Cobra,我们可以快速创建命令行工具,适用于写测试脚本和各种服务的Admin CLI。文章还通过一个简单的demo演示了Cobra的使用方法。 ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • 本文介绍了设计师伊振华受邀参与沈阳市智慧城市运行管理中心项目的整体设计,并以数字赋能和创新驱动高质量发展的理念,建设了集成、智慧、高效的一体化城市综合管理平台,促进了城市的数字化转型。该中心被称为当代城市的智能心脏,为沈阳市的智慧城市建设做出了重要贡献。 ... [详细]
  • Java序列化对象传给PHP的方法及原理解析
    本文介绍了Java序列化对象传给PHP的方法及原理,包括Java对象传递的方式、序列化的方式、PHP中的序列化用法介绍、Java是否能反序列化PHP的数据、Java序列化的原理以及解决Java序列化中的问题。同时还解释了序列化的概念和作用,以及代码执行序列化所需要的权限。最后指出,序列化会将对象实例的所有字段都进行序列化,使得数据能够被表示为实例的序列化数据,但只有能够解释该格式的代码才能够确定数据的内容。 ... [详细]
  • 【Windows】实现微信双开或多开的方法及步骤详解
    本文介绍了在Windows系统下实现微信双开或多开的方法,通过安装微信电脑版、复制微信程序启动路径、修改文本文件为bat文件等步骤,实现同时登录两个或多个微信的效果。相比于使用虚拟机的方法,本方法更简单易行,适用于任何电脑,并且不会消耗过多系统资源。详细步骤和原理解释请参考本文内容。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • 本文介绍了三种方法来实现在Win7系统中显示桌面的快捷方式,包括使用任务栏快速启动栏、运行命令和自己创建快捷方式的方法。具体操作步骤详细说明,并提供了保存图标的路径,方便以后使用。 ... [详细]
  • Linux环境变量函数getenv、putenv、setenv和unsetenv详解
    本文详细解释了Linux中的环境变量函数getenv、putenv、setenv和unsetenv的用法和功能。通过使用这些函数,可以获取、设置和删除环境变量的值。同时给出了相应的函数原型、参数说明和返回值。通过示例代码演示了如何使用getenv函数获取环境变量的值,并打印出来。 ... [详细]
  • 前景:当UI一个查询条件为多项选择,或录入多个条件的时候,比如查询所有名称里面包含以下动态条件,需要模糊查询里面每一项时比如是这样一个数组条件:newstring[]{兴业银行, ... [详细]
  • 本文讨论了clone的fork与pthread_create创建线程的不同之处。进程是一个指令执行流及其执行环境,其执行环境是一个系统资源的集合。在调用系统调用fork创建一个进程时,子进程只是完全复制父进程的资源,这样得到的子进程独立于父进程,具有良好的并发性。但是二者之间的通讯需要通过专门的通讯机制,另外通过fork创建子进程系统开销很大。因此,在某些情况下,使用clone或pthread_create创建线程可能更加高效。 ... [详细]
  • 本文介绍了如何使用Express App提供静态文件,同时提到了一些不需要使用的文件,如package.json和/.ssh/known_hosts,并解释了为什么app.get('*')无法捕获所有请求以及为什么app.use(express.static(__dirname))可能会提供不需要的文件。 ... [详细]
  • 本文记录了在vue cli 3.x中移除console的一些采坑经验,通过使用uglifyjs-webpack-plugin插件,在vue.config.js中进行相关配置,包括设置minimizer、UglifyJsPlugin和compress等参数,最终成功移除了console。同时,还包括了一些可能出现的报错情况和解决方法。 ... [详细]
  • baresip android编译、运行教程1语音通话
    本文介绍了如何在安卓平台上编译和运行baresip android,包括下载相关的sdk和ndk,修改ndk路径和输出目录,以及创建一个c++的安卓工程并将目录考到cpp下。详细步骤可参考给出的链接和文档。 ... [详细]
  • 后台获取视图对应的字符串
    1.帮助类后台获取视图对应的字符串publicclassViewHelper{将View输出为字符串(注:不会执行对应的ac ... [详细]
author-avatar
zj
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有