进程间通信之管道通信

两个程序之间传递数据的一种简单方法是使用popen和pclose。

#include <stdio.h>
FILE *popen(const char *command, const char *type);
int pclose(FILE *stream);
    popen函数允许一个程序将另一个程序作为新进程来启动,并可以传递数据给它或者通过它接收数据。command字符串是要运行的程序名和相应的参数。type必须是"r"或"w"。如果type是"r",被调程序的输出就可以被调用程序使用,调用程序利用popen函数返回的FILE *文件流指针,可以读取被调程序的输出;如果type是"w",调用程序就可以向被调程序发送数据,而被调程序可以在自己的标准输入上读取这些数据。pclose函数只在popen启动的进程结束后才返回。如果调用pclose时它仍在运行,pclose将等待该进程的结束。
#include <stdio.h>#define SIZE 1024*100int main()
{FILE *fp = popen("ps -ef", "r");if (fp == NULL){perror ("popen");return -1;}char buf[SIZE] = {0};int ret = fread(buf, sizeof(char), SIZE-1, fp);// printf ("读到的数据:\n %s\n", buf);FILE *fp2 = popen("grep a.out", "w");if (fp2 == NULL){perror ("popen");return -1;}fwrite (buf, sizeof(char), ret, fp2);printf ("写入完成\n");pclose (fp);pclose (fp2);return 0;
}

管道是单向的、先进先出的,它把一个进程的输出和另一个进程的输入连接在一起。一个进程(写进程)在管道尾部写入数据,另一个进程(读进程)从管道的头部读出数据。管道包括无名管道和有名管道两种,前者用于父进程和子进程间的通信,后者可用于运行于同一系统中的任意两个进程间的通信。
无名管道由pipe( )函数创建:

int pipe(int filedis[2]);

当一个管道建立时,它会创建两个文件描述符:filedis[0]fi用于读管道,ledis[1]
用于写管道。
这里写图片描述
管道用于不同进程间通信。通常先创建一个管道,在通过fork函数创建一个子进程,该子进程会继承父进程所创建的管道描述符。必须在系统调用fork()前调用pipe(),否则子进程将不会继承文件描述符。

1、单个进程中的管道

#include <stdio.h>
#include <unistd.h>#define SIZE 1024*100int main()
{int fd[2];int ret = pipe(fd);if (ret == -1){perror ("pipe");return -1;}ret = write (fd[1], "hello", 5);printf ("写入 %d 个字节\n", ret);char ch;while (1){// 如果管道里面没有数据可读,read会阻塞ret = read (fd[0], &ch, 1);if (ret == -1){perror ("read");break;}printf ("读到 %d 字节: %c\n", ret, ch);}close (fd[0]);close (fd[1]);return 0;
}

2、父子进程通过管道进行通信

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <string.h>#define SIZE 1024// 子进程通过管道从父进程接收数据
void child_do(int *fd)
{// 将管道的写端关闭close (fd[1]);char buf [SIZE];while (1){// 从父进程读取数据int ret = read (fd[0], buf, SIZE-1);if (ret == -1){perror ("read");break;}buf[ret] = '\0';printf ("子进程读到 %d 字节数据: %s\n", ret, buf);}// 关闭读端close (fd[0]);
}// 父进程通过管道向子进程发送数据
void father_do(int *fd)
{// 将管道读端关闭close (fd[0]);char buf[SIZE];while (1){fgets (buf, SIZE, stdin);// 向子进程发送数据int ret = write (fd[1], buf, strlen(buf));printf ("父进程发送了 %d 字节数据\n", ret);}// 关闭写端close (fd[1]);
}int main()
{int fd[2];// 创建管道int ret = pipe(fd);if (ret == -1){perror ("pipe");return -1;}// 创建子进程pid_t pid = fork();switch (pid){case -1:perror ("fork");break;case 0:   // 子进程child_do(fd);break;default:father_do(fd);break;}return 0;
}

3、父子进程通过管道实现文件复制

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>#define SIZE 1024// 子进程通过管道从父进程接收数据
void child_do(int *fd)
{// 将管道的写端关闭close (fd[1]);int fd_write = open ("2.mmap", O_WRONLY|O_CREAT, 0777);if (fd_write == -1){perror ("open");return;}int ret;char buf [SIZE];// read 从管道读数据,如果管道没有数据可读,read 会阻塞// 如果 管道的写端 被关闭, read 返回 0while (ret = read (fd[0], buf, SIZE)){if (ret == -1){perror ("read");break;}// 把从父进程接收的数据写入到新文件中write (fd_write, buf, ret);}printf ("文件复制完成\n");// 关闭读端close (fd[0]);close (fd_write);
}// 父进程通过管道向子进程发送数据
void father_do(int *fd)
{// 将管道读端关闭close (fd[0]);int fd_read = open ("1.mmap", O_RDONLY);if (fd_read == -1){perror ("open");return;}int ret;char buf[SIZE];while (ret = read (fd_read, buf, SIZE)){if (ret == -1){perror ("read");break;}// 把读到的内容发送给子进程write (fd[1], buf, ret);}// 关闭写端close (fd[1]);close (fd_read);
}int main()
{int fd[2];// 创建管道int ret = pipe(fd);if (ret == -1){perror ("pipe");return -1;}// 创建子进程pid_t pid = fork();switch (pid){case -1:perror ("fork");break;case 0:   // 子进程child_do(fd);break;default:father_do(fd);break;}return 0;
}

4、管道读端关闭,写端继续写数据

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <string.h>#define SIZE 1024// 子进程通过管道从父进程接收数据
void child_do(int *fd)
{close (fd[1]);close (fd[0]);
}void father_do(int *fd)
{// 将管道读端关闭close (fd[0]);printf ("等待子进程关闭读端\n");sleep(2);// 所有读端都关闭了,写端继续往管道写入数据// 如果管道所有的读端都被关闭,继续写数据系统默认的操作是使程序退出write (fd[1], "hello", 5);printf ("11111111111111111111111111111111\n");// 关闭写端close (fd[1]);
}int main()
{int fd[2];// 创建管道int ret = pipe(fd);if (ret == -1){perror ("pipe");return -1;}// 创建子进程pid_t pid = fork();switch (pid){case -1:perror ("fork");break;case 0:   // 子进程child_do(fd);break;default:father_do(fd);break;}return 0;
}

以上是无名管道常用的一些操作。

命名管道(FIFO)和无名管道基本相同,但也有不同点:无名管道只能由父子进程使用;但是通过命名管道,不相关的进程也能交换数据。

命名管道具有很好的使用灵活性,表现在:

1) 既可用于本地,又可用于网络。

2) 可以通过它的名称而被引用。

3) 支持多客户机连接。

4) 支持双向通信。

5) 支持异步重叠I/O操作。

1、创建命名管道

#include <sys/types.h>
#include <sys/stat.h>int mkfifo(const char *pathname, mode_t mode);

pathname: FIFO文件名
mode:属性(同文件操作)

一旦创建了一个FIFO,就可用open打开它,一般的文件访问函数(close、read、write等)都可用于FIFO。

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>int main()
{int ret = mkfifo("/home/mkfifo", 0777);if (ret == -1){perror ("mkfifo");return -1;}return 0;
}

2、命名管道的传输
当打开FIFO时,非阻塞标识(O_NONBLOCK)将对以后的读写产生影响:
1、没有使用O_NONBLOCK:访问要求无法满足时进程将阻塞。如果试图读取空的FIFO,将导致进程阻塞。
2、使用O_NONBLOCK:访问要求无法满足时不阻塞,立刻出错返回。errno是ENXIO。

写入:

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <fcntl.h>#define SIZE 1024int main()
{int fd = open("/home/mkfifo", O_WRONLY);if (fd== -1){perror ("mkfifo");return -1;}char buf[SIZE];while (1){fgets (buf, SIZE, stdin);write (fd, buf, strlen(buf));}return 0;
}

读取:

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <fcntl.h>
#define SIZE 1024int main()
{int fd = open("/home/mkfifo", O_RDWR);if (fd == -1){perror ("mkfifo");return -1;}char buf[SIZE];while (1){int ret = read (fd, buf, SIZE);buf[ret] = '\0';printf ("读到 %d 字节: %s\n", ret, buf);}return 0;
}

管道作为进程间通信的4种方式之一,他并不会保存数据,区别与共享内存。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/448457.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

vue 各组件 使用 Demo

环境搭建 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到教程。 假设你已经通读vue官方文档&#xff08;文档都没读一遍&#xff08;至少&#xff09;&#xff0c;那不建议动手撸码&#xff…

Dropbox推独立应用,公司估值已达100亿美元

摘要&#xff1a;Dropbox刚获得5亿美元的新一轮融资&#xff0c;其估值已高达100亿美元&#xff0c;现在又推出针对云服务的独立应用Carousel&#xff0c;不久之后&#xff0c;还会有应用陆续推出&#xff0c;随着美国前国务卿赖斯的加盟&#xff0c;Dropbox在云市场的表现异常…

SQL经典面试题(二)

有3个表S&#xff0c;C&#xff0c;SCS&#xff08;SNO&#xff0c;SNAME&#xff09;代表&#xff08;学号&#xff0c;姓名&#xff09; //主键&#xff1a;SNO //多个人&#xff0c;多门课 3张表 &#xff0c;SC 关系表C&#xff08;CNO&#xff0c;CNAME&#xff0c;…

开源当自强:我们不是“便宜货”

之前人们争相使用开源软件&#xff0c;无非是因为它便宜、好用、易得&#xff0c;不过根据最新的开源软件调查显示&#xff0c;人们使用开源软件最主要的原因还是看重它的高质量。 成本低是很过公司参与开源项目的最主要原因之一&#xff0c;而现在他们视开源是一条通往创新、省…

python内建函数和工厂函数的整理

内建函数参阅&#xff1a; https://www.cnblogs.com/pyyu/p/6702896.html 工厂函数&#xff1a; 本篇博文比较粗糙&#xff0c;后续会深入整理转载于:https://www.cnblogs.com/qiang-upc/p/11155786.html

显卡天梯图:2014最新显卡性能天梯图

随着电脑游戏的推广&#xff0c;很多用户都喜欢上了电脑网络游戏&#xff0c;所以组装电脑用户在装机的时候&#xff0c;会考虑电脑配置的游戏性能&#xff0c;要提高电脑配置游戏性能首要条件就是显卡性能要强&#xff0c;如果显卡性能不佳&#xff0c;那么其它方面性能再强&a…

【显卡天梯图】2014年最新显卡天梯图 – 【迄今最全系列显卡】

【显卡天梯图】2014年最新显卡天梯图 – 【迄今最全系列显卡】 随着电脑游戏的推广&#xff0c;很多用户都喜欢上了电脑网络游戏&#xff0c;所以组装电脑用户在装机的时候&#xff0c;会考虑电脑配置的游戏性能&#xff0c;要提高电脑配置游戏性能首要条件就是显卡性能要强&am…

原理系列:Spark1.x 生态圈一览

Spark生态圈&#xff0c;也就是BDAS&#xff08;伯克利数据分析栈&#xff09;&#xff0c;是伯克利APMLab实验室精心打造的&#xff0c;力图在算法&#xff08;Algorithms&#xff09;、机器&#xff08;Machines&#xff09;、人&#xff08;People&#xff09;之间通过大规模…

SpringMVC 注解 : @ModelAttribute

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到教程。 SpringMVC 注解 &#xff1a; ModelAttribute 的用法如上。 转自&#xff1a;https://blog.csdn.net/lovesomnus/article/details/78873…

解决: idea 修改 jsp 后,页面刷新无效

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到教程。 idea 修改 jsp 后浏览器访问无效。 解决&#xff1a;进入 idea 配置 修改部署方式&#xff1a; 修改 更新文件方式&#xff1a; OK了。

Python中的getpass模块

getpass模块用于输入密码时&#xff0c;隐藏密码字符 代码 import getpass name input("请输入你的名字&#xff1a;") passwd getpass.getpass("请输入你的密码&#xff1a;")print (name,passwd)   首先我们要import引入getpass模块&#xff0c;然后…

如何做到每天写代码?

摘要&#xff1a;总有一大堆事情没有做完&#xff0c;没有时间和精力为业余项目写代码&#xff1f;不要着急&#xff0c;看看可汗学院计算机科学院院长John Resig怎么说。本文将教你如何保证在每天都能有时间给业余项目写代码。 你是否曾为业余项目没有进展而惆怅过&#xff1f…

Vue 实现前后端分离项目

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到教程。 Vue实现前后端分离项目的初体验 经过之前学习的Vue的知识&#xff1a; vue基本指令vue组件vue-resourcevue路由 其实我们已经可以开始…

优秀Unix管理员的七个习惯

摘要&#xff1a;Unix系统管理员可能会很懒或喜欢优雅的解决方法&#xff0c;这就是他们的存在之美。一位优秀的Unix系统管理员有着自己的习惯&#xff1a;不会等到问题来找你、精通所使用的工具和系统、确定事情优先次序和喜欢优雅的解决方案但不迷失等。 优秀的Unix系统管理员…

Vue.js 极简小例:读值、样式调用、if判断、a 标签、点击事件、管道

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到教程。 1. 写法&#xff1a; <template><div id"app"><!-- 三目运算符使用 ‘ok’ 的值定义在 data 中-->{{ ok …

C++与C中const的比较以及const和define的比较

C与C中const的比较&#xff1a; C语言中 const修饰的变量是一个 常变量&#xff0c;本质还是变量&#xff0c;有自己的地址空间C编译器对const常量的处理 当碰见常量声明时&#xff0c;在符号表中放入常量 > 问题&#xff1a;那又如何解释取地址编译过程中若发现对const使…

Vue.js 极简小例:数值计算、千米换算为米、九九乘法表、循环

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到教程。 代码&#xff1a; <template><div id"app"><!-- 千米和米换算 --><div id "computed_props&quo…

C++之内联函数

内联函数是一种特殊的函数&#xff0c;具有普通函数的特征&#xff08;参数检查&#xff0c;返回类型等&#xff09; 内联函数是对编译器的一种请求&#xff0c;因此编译器可能拒绝这种请求 内联函数由 编译器处理&#xff0c;直接将编译后的函数体插入调用的地方 宏代码片段…

Vue.js 极简小例: 4 种方式样式绑定、style 的多种方式实现

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到教程。 代码&#xff1a; <template><div id"app"><!-- JSON方式使用样式 --><div v-bind:style"{ colo…

50 Python - 装饰器 类定义装饰器

04 类定义装饰器 上节通过函数定义装饰器&#xff0c;本节通过类定义装饰器 001 定义类装饰器 定义一个类&#xff0c;类里面两个函数&#xff0c;一个构造函数init()&#xff0c;一个调用函数call() 构造函数init时候&#xff0c;传递一个函数func()进来 调用函数call()&#…