背景:

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()阻塞性问题验证的更多相关文章

  1. popen&pclose管道方式操作shell命令

    popen, pclose - pipe stream to or from a process FILE *popen( const char *command, const char *type) ...

  2. nodejs选择JavaScript作为开发语言,是因为一般的开发语言的标准库都是带有IO模块的,并且通常这个 模块是阻塞性的,所以nodejs选择了没有自带IO模块的Javascript

    Javascrip本身不带IO功能,nodejs选择JavaScript作为开发语言,是因为一般的开发语言的标准库都是带有IO模块的,并且通常这个 模块是阻塞性的,所以nodejs选择了没有自带IO模 ...

  3. [apue] popen/pclose 疑点解惑

    问题请看这里: [apue] 使用 popen/pclose 的一点疑问 当时怀疑是pclose关闭了使用完成的管道,因此在pclose之前加一个足够长的sleep,再次观察进程文件列表: 哈哈,这下 ...

  4. [apue] 使用 popen/pclose 的一点疑问

    当我们需要将输出作为标准输入传递给一个命令,或者将一个命令的输出作为标准输入来读取, 一般会想到使用pipe与fork相结合的方式,来重定向标准输入/输出给指定命令. popen/pclose 帮助我 ...

  5. netty5心跳与阻塞性业务消息分发实例

    继续之前的例子(netty5心跳与业务消息分发实例),我们在NettyClientHandler把业务消息改为阻塞性的: package com.wlf.netty.nettyclient.handl ...

  6. python subprocess.Popen 非阻塞

    1.非阻塞设置subprocess.Popen(args, stdout=subprocess.PIPE,stderr=subprocess.PIPE) def non_block_read(outp ...

  7. popen pclose 不等待命令执行完毕

    $handle = popen("start D:\\test.bat", "r"); //exec("start D:\\test.bat" ...

  8. Linux popen/pclose

    popen() 函数 #include <stdio.h>FILE * popen(const char *command , const char *type );int pclose( ...

  9. Python Subprocess Popen 管道阻塞问题分析解决

    http://ju.outofmemory.cn/entry/279026 场景:1>不断播放mp3文件: 2>使用订阅发布模式保持tcp长连接,从服务器接收信息 造成程序hang死,但是 ...

随机推荐

  1. SpringBoot初体验及原理解析

    一.前言 ​ 上篇文章,我们聊到了SpringBoot得以实现的幕后推手,这次我们来用SpringBoot开始HelloWorld之旅.SpringBoot是Spring框架对“约定大于配置(Conv ...

  2. vue-05-webpack安装-vue单文件启动

    1, webpack是什么 1), 是一个打包工具, 比gulp, grunt更先进 2), 额外功能 项目部署上线, 清空目录等 hot module reload, 页面刷新后, 数据不变化 3) ...

  3. 深入理解L1、L2正则化

    过节福利,我们来深入理解下L1与L2正则化. 1 正则化的概念 正则化(Regularization) 是机器学习中对原始损失函数引入额外信息,以便防止过拟合和提高模型泛化性能的一类方法的统称.也就是 ...

  4. Ubuntu14.04 编译 Android 5.1.1源码(采用国内镜像)

    欢迎转载,转载请注明出处: http://www.cnblogs.com/lanrenxinxin/p/5424554.html 之前就有编译android源码的想法,但是由于有GFW的存在,又没有梯 ...

  5. kubernetes系列(一)安装和配置

    谈到kubernetes(或者说k8s)不得不提到云计算.虚拟化以及容器技术,相关介绍网上一大堆,不再赘述.而kubernetes的出现就是为了高效的管理云端运行的docker容器. 环境 docke ...

  6. 非常完善的Log4net详细说明(转)

    最可能来源:https://blog.csdn.net/ydm19891101/article/details/50561638 其它转载者:http://www.cnblogs.com/zhangc ...

  7. Go开发之路 -- Go语言基本语法 - 作业

    1. 判断101 - 200之间有多少个素数,并输出所有素数. package main import ( "fmt" ) var count = 0 func prime(a, ...

  8. var声明变量

    var操作符定义变量将成为定义该变量作用域的局部变量 举例说明: 例子1: function test(){ var message = "hi"; //message是函数内部局 ...

  9. a标签禁止跳转或者不跳转的几种实现方式

    1.onclick事件中返回false <a href="http://www.baidu.com"  onclick="return false" &g ...

  10. JS中实现跨域的方法总结

    今天早上在地铁看了点基础知识的考察题,看到了一个JS跨域的问题,仔细想了想自己脑子里竟然只剩下jsonp跨域和用nginx反向代理进行跨域,想着还有别的几种方法,就是想不起来,这个人呢,一上岁数这个脑 ...