popen()/pclose()阻塞性问题验证
背景:
popen()函数通过创建一个管道,调用fork()产生一个子进程,执行一个shell以运行命令来开启一个进程。这个管道必须由pclose()函数关闭,而不是fclose()函数。
pclose()函数关闭标准I/O流,等待命令执行结束,然后返回shell的终止状态。如果shell不能被执行,则pclose()返回的终止状态与shell已执行exit一样。
而子进程的退出状态,常用以下几个宏进行获取。
1、 WIFEXITED(status) 若此值为非0 表明进程正常结束。
若上宏为真,此时可通过WEXITSTATUS(status)获取进程退出状态(exit时参数)
示例:
if(WIFEXITED(status)){
printf("退出值为 %d\n", WEXITSTATUS(status));
}
2、 WIFSIGNALED(status)为非0 表明进程异常终止。
若上宏为真,此时可通过WTERMSIG(status)获取使得进程退出的信号编号
示例:
if(WIFSIGNALED(status)){
printf("使得进程终止的信号编号: %d\n",WTERMSIG(status));
}
验证内容:
主要确认以下几点:
1, WEXITSTATUS等宏,能否正确取得shell退出状态?
2, popen之后直接调用pclose是否会等待命令执行结束?
3, 如果没有pclose,会如何?
验证代码:
测试用代码如下:
#include <stdio.h>
#include <sys/wait.h> int main(void)
{
int iRet = ;
FILE *fp = NULL;
char buff[] = {'\0'}; fp = popen("./test.sh", "r");
if (NULL == fp)
{
printf("popen failed.\n");
return ;
}
/*
while(fgets(buff, sizeof(buff), fp) != NULL)
{
printf("%s", buff);
}
*/
iRet = pclose(fp);
printf("iRet = %d\n", iRet);
printf("wifexited : %d\n", WIFEXITED(iRet));
printf("wifsignaled : %d\n", WIFSIGNALED(iRet));
printf("wifstopped : %d\n", WIFSTOPPED(iRet));
//if (WIFEXITED(iRet))
printf("exit :%d\n", WEXITSTATUS(iRet));
//if (WIFSIGNALED(iRet))
printf("signal :%d\n", WTERMSIG(iRet)); return ;
}
被调用的脚本如下:
#!/bin/sh #echo "before..." #注意,echo被注释掉,即,不会输出。
sleep #echo "after..." exit
结果:
1, WIFEXITED()等宏,可以正确获取test.sh的执行结果。
如下三个实验可以验证:
① test.sh没有执行权限时,WEXITSTATUS()的结果与直接执行test.sh的返回值是一致的。
zsy@ubuntu:~/work/popen$ ./test
sh: : ./test.sh: Permission denied
iRet =
wifexited :
wifsignaled :
wifstopped :
exit :
signal : zsy@ubuntu:~/work/popen$ ./test.sh
bash: ./test.sh: Permission denied
zsy@ubuntu:~/work/popen$ echo $?
② 给test.sh增加权限后,WEXITSTATUS()获取的正是test.sh中的exit 1的结果。
zsy@ubuntu:~/work/popen$ ./test
iRet =
wifexited :
wifsignaled :
wifstopped :
exit :
signal :
③ popen执行过程中,将shell子进程kill掉,WTERMSIG()获取的是SIGTERM=15。
zsy@ubuntu:~/work/popen$ ps -ef|grep test
zsy : pts/ :: ./test
zsy : pts/ :: sh -c ./test.sh
zsy : pts/ :: /bin/sh ./test.sh zsy@ubuntu:~/work/popen$ kill # 注意kill的pid zsy@ubuntu:~/work/popen$ ./test
iRet =
wifexited :
wifsignaled :
wifstopped :
exit :
signal :
注意:
③的例子中,可以看到popen实际上在fork之后,是执行了“sh –c ./test.sh”命令,然后由shell再启动test.sh。所以test.sh实际上是孙子进程。
如果kill的是孙子进程,结果会如何呢?
zsy@ubuntu:~/work/popen$ ps -ef|grep test
zsy : pts/ :: ./test
zsy : pts/ :: sh -c ./test.sh
zsy : pts/ :: /bin/sh ./test.sh
zsy@ubuntu:~/work/popen$ kill zsy@ubuntu:~/work/popen$ ./test
Terminated
iRet =
wifexited :
wifsignaled :
wifstopped :
exit :
signal :
也就是说,pclose返回的结果认为子进程shell是正常结束了,终了code为143(143=128+15,实际上就是test.sh收到了SIGTERM的值)。
2,pclose()调用时,确实会阻塞,等待test.sh中的sleep结束,才会返回。
但是,如果把sleep前的echo打开,则pclose()并不会阻塞,而是直接返回。如下:
zsy@ubuntu:~/work/popen$ ./test
iRet =
wifexited :
wifsignaled :
wifstopped :
exit :
signal :
原因何在呢?其实答案就在WEXITSTATUS()的结果141中。类似于上面kill 孙子进程时的返回值,141=128+13,说明test.sh(孙子进程)实际上接收到了信号SIGPIPE退出,导致shell子进程立刻返回了。
而test.sh收到SIGPIPE的原因,则是因为pclose()的时候,关闭了popen创建的管道,而test.sh的echo命令,想向管道写数据,就会产生SIGPIPE信号。
※因此,可以考虑两种解决方案。一种就是shell里面不要输出;另一种就是在pclose()前调用fgets,保证shell输出都读取出来后,再关闭。
3,在ubuntu 14.04x64的虚拟机上测试,即使没有pclose(),似乎也没有特别的问题。
但是,在ARM板上子跑的时候,会出现僵尸进程。
popen()/pclose()阻塞性问题验证的更多相关文章
- popen&pclose管道方式操作shell命令
popen, pclose - pipe stream to or from a process FILE *popen( const char *command, const char *type) ...
- nodejs选择JavaScript作为开发语言,是因为一般的开发语言的标准库都是带有IO模块的,并且通常这个 模块是阻塞性的,所以nodejs选择了没有自带IO模块的Javascript
Javascrip本身不带IO功能,nodejs选择JavaScript作为开发语言,是因为一般的开发语言的标准库都是带有IO模块的,并且通常这个 模块是阻塞性的,所以nodejs选择了没有自带IO模 ...
- [apue] popen/pclose 疑点解惑
问题请看这里: [apue] 使用 popen/pclose 的一点疑问 当时怀疑是pclose关闭了使用完成的管道,因此在pclose之前加一个足够长的sleep,再次观察进程文件列表: 哈哈,这下 ...
- [apue] 使用 popen/pclose 的一点疑问
当我们需要将输出作为标准输入传递给一个命令,或者将一个命令的输出作为标准输入来读取, 一般会想到使用pipe与fork相结合的方式,来重定向标准输入/输出给指定命令. popen/pclose 帮助我 ...
- netty5心跳与阻塞性业务消息分发实例
继续之前的例子(netty5心跳与业务消息分发实例),我们在NettyClientHandler把业务消息改为阻塞性的: package com.wlf.netty.nettyclient.handl ...
- python subprocess.Popen 非阻塞
1.非阻塞设置subprocess.Popen(args, stdout=subprocess.PIPE,stderr=subprocess.PIPE) def non_block_read(outp ...
- popen pclose 不等待命令执行完毕
$handle = popen("start D:\\test.bat", "r"); //exec("start D:\\test.bat" ...
- Linux popen/pclose
popen() 函数 #include <stdio.h>FILE * popen(const char *command , const char *type );int pclose( ...
- Python Subprocess Popen 管道阻塞问题分析解决
http://ju.outofmemory.cn/entry/279026 场景:1>不断播放mp3文件: 2>使用订阅发布模式保持tcp长连接,从服务器接收信息 造成程序hang死,但是 ...
随机推荐
- aliyun阿里云Maven仓库配置
maven仓库用过的人都知道,国内有多么的悲催.还好有比较好用的镜像可以使用,尽快记录下来.速度提升100倍. http://maven.aliyun.com/nexus/#view-reposito ...
- 详解C#的协变和逆变
一.使用协变(Covariance)和逆变(Contravariance )能够实现数组之间.委托实例和方法之间.泛型委托实例之间.泛型接口的变量和泛型类型的对象之间.泛型接口的变量之间的隐式转换:使 ...
- ARP的通信过程
在当今的以太网络通信中,在IP数据包中有两个必不可少的地址,那就是IP地址和网卡地址(即MAC地址),在数据包中,无论是IP地址还是MAC地址,都有源地址和目标地址,因为通信是双方的,所以就必须同时拥 ...
- shiro教程1(HelloWorld)
shiro简介 官网 Apache Shiro是一个强大且易用的Java安全框架,执行身份验证.授权.密码和会话管理.使用Shiro的易于理解的API,您可以快速.轻松地获得任何应用程序,从最小的移动 ...
- Java 使用 happen-before 规则实现共享变量的同步操作
前言 熟悉 Java 并发编程的都知道,JMM(Java 内存模型) 中的 happen-before(简称 hb)规则,该规则定义了 Java 多线程操作的有序性和可见性,防止了编译器重排序对程序结 ...
- Repeater 控件的嵌套使用
Repeater 控件的嵌套使用 ItemDataBound:数据绑定的时候(正在进行时)发生,多用在Repeater控件嵌套,对子Repeater控件进行数据绑定及模板列中统计列的计算处理等 ...
- 【Java】模拟Sping,实现其IOC和AOP核心(二)
接着上一篇,在上一篇完成了有关IOC的注解实现,这一篇用XML的方式实现IOC,并且完成AOP. 简易的IOC框图 注解的方式实现了左边的分支,那么就剩下右边的XML分支: XmlContext:这个 ...
- Java-关于类
java-关于类-成员初始化问题 成员初始化出现错误“ - Syntax error on token ";", , expected” java类不允许对成员进行操作,但可 ...
- 前端学习 之 Bootstrap (一)
中文文档 一.前言 1.简介 Bootsrtap作为一个前端框架,开箱即用,封装了前段开发的大量底层细节,开放灵活,对响应式设计网页很好支持,组件丰富,社区活跃. 特点: 可重用性 一致性 灵活的栅栏 ...
- CentOS 通过yum来升级php到php5.6,yum upgrade php 没有更新包
在文章中,我们将展示在centOS系统下如何将php升级到5.6,之前通过yum来安装lamp环境,直接升级的话,提示没有更新包,也就是说默认情况下php5.3.3是最新 1.查看已经安装的php版本 ...