popen和system函数的区别 以及 popen打开的FILE指针能否用close替代fclose关闭

popen和system函数的区别

在c/cpp程序中执行shell命令,通常有两种方式,一种是使用popen函数,一种是使用system函数;两者会调用fork函数从父进程中fork出一个子进程,然后在子进程中执行shell命令,其主要的区别如下:

  • popen
    popen会先fork一个子进程,然后子进程去执行shell命令,函数同时返回一个FILE指针给调用者,调用者可以根据FILE指针来获取函数的执行结果。popen是非阻塞的,即执行后可立即返回。
  • system
    system会fork一个子进程,然后父进程等待子进程结束,也就是执行system函数是阻塞的,如果调用 system("sleep 1"),将会延迟1s再执行system后面的代码。

popen打开的FILE句柄能否用close替代fclose关闭?

急的话直接看答案就行:不行,会有一些副作用

实际中遇到的问题

最近遇到一个这样的问题:
场景:为了完成相关功能,需要在程序调用shell命令,用到了popen;为了支持多个命令并发,
在获取shell命令的结果时使用reactor模式(借助了epoll的多路复用能力,epoll监听对应的fd是否有事件过来),以提升处理效率。其伪代码大概如下:

//注册信号处理函数,防止产生僵死进程
signal(SIGCHLD, SIG_IGN);
//将popen打开的文件描述符加入epoll
FILE * file = popen("shell cmd", "r"); //这边shell cmd为具体的shell脚本
//将FILE转int,然后加入到epoll中
int fd = fileno(file);
//将描述符设置为非阻塞模式
set_no_block(fd);
struct epoll_event event;
event.events = EPOLLIN | EPOLL UT;
event.data = ...; //epoll保存的data
epoll_ctl(epfd, EPOLL_CTL_ADD, fd,&event);

在epoll主循环中,

int num = epoll_wait(timeout);
for(int i = 0; i < num; i++)
{
	//处理epoll事件
	processEvent(...)
}

其中processEvent的大体逻辑如下:

void processEvent(struct epoll_event* event)
{
	//事件处理,比如读取结果等

    //收尾工作,如删除epoll监听的fd,关闭文件描述符
    ...
    close(fd); //这边fd为第一部分添加尽量的描述符fd,可以从event中获取
}

功能开发完成后,自测ok,然后就放着让他跑,看看稳定性。这时程序的cpu使用率有时会飙升,外部程序调用该服务存在间断性的失败。这时用top或者ps看一下进程,会发现有时候出现多个此进程的子进程;且从失败的时间点看,周期和popen执行的周期基本能对上。由于子进程是fork出来的,popen内部调用了fork,就怀疑是popen带来的问题。
排查程序发现可能是使用close替代pclose来关闭用popen打开的描述符导致的问题,然后改了下代码后,发现该问题消失。

demo测试分析

为了对比使用close和fclose关闭popen打开的描述符的差别,这边进行了下面的实验,测试代码如下:

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <iostream>
using namespace std;

int main()
{
        signal(SIGCHLD, SIG_IGN);
        while(1)
        {
        char szCommand[128] = {0};
        snprintf(szCommand, sizeof(szCommand) - 1, "cat testfile"); //这边testfile为一个文本文件
        FILE* fp = popen(szCommand, "r");
        if(NULL == fp)
        {
                std::cout << "popen failed" << endl;
        }
        usleep(1000);
        
        //1
        int fd = fileno(fp);
        close(fd);
        
        //2
        //fclose(fp);
        }
        return 0;

}

分别使用close和fclose,编译出两个testpopen文件,然后运行,过一阵后,使用close关闭的程序表现如下:
使用close关闭的效果

使用pclose关闭的效果如下:
使用pclose关闭的效果
通过对比,看出了差异,如果通过close来关闭popen打开的FILE描述符的话,有些负面的作用【cpu高了,且有部分内存泄露】。

查了下pclose和close的差异,pclose除了会关闭文件描述符之外,还具有pclose会调用waitpid为popen时fork的子进程收尸,而fclose不会;另外pclose还具有在关闭文件时冲刷缓冲区的功能。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值