背景:

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. java对文件的检索

    先说下思路吧. 1.先把xls等文件转换为静态html文件:可以用很多插件,类似我的博客地址: http://www.cnblogs.com/Alandre/p/3221006.html 2.再对转的 ...

  2. java ee期末项目相关

    1.项目简介 本项目是对纸杯生产进行管理的的一个系统,从前端接收到订单,然后根据订单内容进行纸杯的生产.如下为该系统的总流程图: 1.项目系统架构图 3.系统用例图 4.ER图 主要的代码和相关文件见 ...

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

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

  4. Hibernate学习(四)———— 双向多对多映射关系

    一.小疑问的解答 问题一:到这里,有很多学习者会感到困惑,因为他不知道使用hibernate是不是需要自己去创建表,还是hibernate全自动,如果需要自己创建表,那么主外键这种设置也是自己设置吗? ...

  5. NABCD校园生活

    N(Need,需求) 在校师生得到的信息分散,极大的影响了师生的学习效率,所以就会有快速得到全面信息的需求,而我们的APP正是在解决这个问题. A(Approach,做法) 我们会在主页进行信息分类, ...

  6. [转]Angular2-组件间数据传递的两种方式

    本文转自:https://www.cnblogs.com/longhx/p/6960288.html Angular2组件间数据传递有多种方式,其中最常用的有两种,一种是配置元数据(或者标签装饰),一 ...

  7. 分部类,分部方法 - 修饰符partial

    一.分部类 什么是部分类呢?简单来说就是将一个类型或方法拆分到两个或多个源文件中,每个源文件只包含类型定义的一部分. 当使用自动生成的源时,无须重新创建源文件便可将代码添加到类中.Visual Stu ...

  8. Android目录结构及作用

    1.add-ons-->Google API .比如GoogleMaps 2.build-tools-->各版本SDK编译工具. 3.docs-->离线开发者文档Android SD ...

  9. 【协议】5、gossip 协议

    Gossip是一种去中心化.容错并保证最终一致性的协议. Background:分布式环境 Gossip是为了解决分布式遇到的问题而设计的.由于服务和数据分布在不同的机器上,节点之间的每次交互都伴随着 ...

  10. 【协议】3、HTTP 协议入门

    HTTP 协议是互联网的基础协议,也是网页开发的必备知识,最新版本 HTTP/2 更是让它成为技术热点. 本文介绍 HTTP 协议的历史演变和设计思路. 一.HTTP/0.9 HTTP 是基于 TCP ...