加入星计划,您可以享受以下权益:

  • 创作内容快速变现
  • 行业影响力扩散
  • 作品版权保护
  • 300W+ 专业用户
  • 1.5W+ 优质创作者
  • 5000+ 长期合作伙伴
立即加入
  • 正文
    • 相关要点
    • 优缺点
    • 内部实现
    • 应用案例
    • 相关参考
    • 思考技术,也思考人生
  • 推荐器件
  • 相关推荐
  • 电子产业图谱
申请入驻 产业图谱

Linux-C编程 进程通信 以文件读写的方式和进程通讯

2020/12/15
222
阅读需 12 分钟
加入交流群
扫码加入
获取工程师必备礼包
参与热点资讯讨论

快速上手popen()

 

该函数用于运行指定命令,并且让刚启动的程序看起来像文件一样可以被读写。

2 个 demo

1) 从外部程序中读数据:

int main(int argc, char **argv)
{
    FILE *fp;
    char buf[100];
    int i = 0;

    fp = popen("ls -1X", "r");

    if (fp != NULL) {
        while(fgets(buf, 100, fp) != NULL) {
            printf("%d: %s", i++, buf);
        }
        pclose(fp);
        return 0;
    }
    return 1;
}

运行效果:

$ ./001_popen_r 
0: 001_popen_r
1: 002_popen_w
2: 001_popen_r.c
3: 002_popen_w.c
4: 004_popen_intern.c

2) 写数据到外部程序:

int main(int argc, char *argv)
{
    FILE *fp = NULL;
    char buffer[BUFSIZE];

    sprintf(buffer, "hello worldn");

    fp = popen("od -tcx1", "w");
    if (fp != NULL) {
        fwrite(buffer, sizeof(char), strlen(buffer), fp);
        pclose(fp);
        return 0;
    }
    return 1;
}

运行效果:

0000000   h   e   l   l   o       w   o   r   l   d  n
         68  65  6c  6c  6f  20  77  6f  72  6c  64  0a
0000014

相关要点

函数原型

FILE *popen(const char *command, const char *type);

popen() 会先执行 fork,然后调用 exec 执行 command,并且返回一个标准 I/O 文件指针。

type = "r":

  • 文件指针连接到 command 的标准输出。

type = "w":

  • 文件指针连接到 command 的标准输入。

点击查看大图

优缺点

优点:

  • 由于调用了 shell,所以可以支持通配符 (例如*.c) 等各种 shell 扩展特性;减少了代码量;

缺点:

  • 要启动 2 个程序:shell 和 目标程序,调用成本略高,比起直接 exec 某个程序来说要慢一些;

内部实现

popen() 的内部实现思路如下:

FILE *_popen(const char *command, const char *type)
{
    pipe()
    fork();
    if (pid > 0)
        close() child's fd
        return fdopen() parent's fd
    else
        close(parent's fd)
        dup2() child's data fd to stdin or stdout
        close() child's fd
        exec("/bin/sh -c") command
}
  1. 创建一个管道,用于父子进程间的通讯;父进程:
    • 关闭未使用的管道端;返回父进程数据管道端的 FILE *, 它可能连接父进程的 stdin / stdout;

子进程:

  • 关闭未使用的管道端;重定位子进程的数据管道端到 stdin / stdout;执行目标命令;

初步的代码实现:

FILE *_popen(const char *command, const char *type)
{
    int pfp[2];
    int parent_end, child_end;
    int pid;

    if (*type == 'r') {
        parent_end = READ;
        child_end = WRITE;
    } else if (*type == 'w') {
        parent_end = WRITE;
        child_end = READ;
    } else {
        return NULL;
    }

    pipe(pfp);
    pid = fork();
    if (pid > 0 ) {
        close(pfp[child_end]);
        return fdopen(pfp[parent_end], type);
    } else {
        close(pfp[parent_end]);
        dup2(pfp[child_end], child_end);
        close(pfp[child_end]);
        execl("/bin/sh", "sh", "-c", command, NULL);
        exit(0);
    }
    return NULL;
}

这里的实现有一些不足的地方,例如:

为了便于阅读,省略了错误检查;

没有保存子进程的 pid,后续无法使用 wait() 进行收尸;

一个进程可能调用 popen() 多次,需要用数组 / 链表来存储所有子进程的 pid;

更完善的实现可以参考:

https://android.googlesource.com/platform/bionic/+/3884bfe9661955543ce203c60f9225bbdf33f6bb/libc/unistd/popen.c

应用案例

开源软件 MJPG-steamer 为例。

MJPG-streamer 是什么?

简单地说,就是一个开源的流媒体服务器

https://github.com/jacksonliam/mjpg-streamer

通过 mjpg-streamer,你可以通过 PC 浏览器访问到板子上的摄像头图像。

MJPG-streamer 就是通过 popen() 来支持 CGI 功能的:

CGI 是早期出现的一种简单、流行的服务端应用程序执行接口,http server 通过运行 CGI 程序来完成更复杂的处理工作,在 MJPG-streamer . 里的相关代码如下:

plugins/output_http/httpd.c

void execute_cgi(int id, int fd, char *parameter, char *query_string)
{
    // prepare

    // 执行浏览器指定的 CGI 程序
    f = popen(buffer, "r");

    // 获得 CGI 程序的输出
    while((i = fread(buffer, 1, sizeof(buffer), f)) > 0) {
        if (write(fd, buffer, i) < 0) {
            fclose(f);
            free(buffer);
            close(lfd);
            return;
        }
    }

}

这里只是简单地了解一下 MJPG-Streamer,有兴趣的小伙伴们自行阅读更多的代码吧。

相关参考

Unix-Linux 编程实践教程 / 11.4 popen: 让进程看似文件

Linux 程序设计(第 4 版) / 13.3 将输出送往 popen

Unix 环境高级编程第 3 版 / 15.3 函数 popen 和 pclose

HTTP 权威指南

思考技术,也思考人生

要学习技术,更要学习如何生活。

你和我各有一个苹果,如果我们交换苹果的话,我们还是只有一个苹果。但当你和我各有一个想法,我们交换想法的话,我们就都有两个想法了。

嵌入式系统 (Linux、RTOS、OpenWrt、Android) 和 开源软件 感兴趣,关注公众号:嵌入式 Hacker。

觉得文章对你有价值,还请多多 转发。

推荐器件

更多器件
器件型号 数量 器件厂商 器件描述 数据手册 ECAD模型 风险等级 参考价格 更多信息
EP4CE15F23I8LN 1 Intel Corporation Field Programmable Gate Array, 963 CLBs, 362MHz, 15408-Cell, PBGA484, 23 X 23 MM, 1 MM PITCH, LEAD FREE, FBGA-484
暂无数据 查看
ATF22V10C-15PU 1 Microchip Technology Inc IC PLD 10MC 15NS 24DIP

ECAD模型

下载ECAD模型
$1.85 查看
XC6SLX25-2FG484I 1 AMD Xilinx Field Programmable Gate Array, 1879 CLBs, 667MHz, 24051-Cell, CMOS, PBGA484, 23 X 23 MM, 1 MM PITCH, FBGA-484
$656.54 查看

相关推荐

电子产业图谱