管道的概述

管道也叫无名管道,它是是 UNIX 系统 IPC(进程间通信) 的最古老形式,所有的 UNIX 系统都支持这种通信机制。

无名管道有如下特点:

1、半双工,数据在同一时刻只能在一个方向上流动。

2、数据只能从管道的一端写入,从另一端读出。

3、写入管道中的数据遵循先入先出的规则。

4、管道所传送的数据是无格式的,这要求管道的读出方与写入方必须事先约定好数据的格式,如多少字节算一个消息等。

5、管道不是普通的文件,不属于某个文件系统,其只存在于内存中。

6、管道在内存中对应一个缓冲区。不同的系统其大小不一定相同。

7、从管道读数据是一次性操作,数据一旦被读走,它就从管道中被抛弃,释放空间以便写更多的数据。

8、管道没有名字,只能在具有公共祖先的进程(父进程与子进程,或者两个兄弟进程,具有亲缘关系)之间使用。

对于无名管道特点的理解,我们可以类比现实生活中管子,管子的一端塞东西,管子的另一端取东西。

无名管道是一种特殊类型的文件,在应用层体现为两个打开的文件描述符。

管道的操作

所需头文件:

#include <unistd.h>

int pipe(int filedes[2]);

功能:

创建无名管道。

参数:

filedes: 为 int 型数组的首地址,其存放了管道的文件描述符 filedes[0]、filedes[1]。

当一个管道建立时,它会创建两个文件描述符 fd[0] 和 fd[1]。其中 fd[0] 固定用于读管道,而 fd[1] 固定用于写管道。一般文件 I/O 的函数都可以用来操作管道( lseek() 除外)。

返回值:

成功:0

失败:-1

下面我们写这个一个例子,子进程通过无名管道给父进程传递一个字符串数据:

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>int main(int argc, char *argv[])
{int fd_pipe[2] = {0};pid_t pid;if( pipe(fd_pipe) < 0 ){// 创建无名管道perror("pipe");}pid = fork(); // 创建进程if( pid < 0 ){ // 出错perror("fork");exit(-1);}if( pid == 0 ){ // 子进程char buf[] = "I am mike";// 往管道写端写数据write(fd_pipe[1], buf, strlen(buf));_exit(0);}else if( pid > 0){// 父进程wait(NULL);  // 等待子进程结束,回收其资源char str[50] = {0};// 从管道里读数据read(fd_pipe[0], str, sizeof(str));printf("str=[%s]\n", str); // 打印数据}return 0;
}

运行结果如下:

管道的特点

每个管道只有一个页面作为缓冲区,该页面是按照环形缓冲区的方式来使用的。这种访问方式是典型的“生产者——消费者”模型。当“生产者”进程有大量的数据需要写时,而且每当写满一个页面就需要进行睡眠等待,等待“消费者”从管道中读走一些数据,为其腾出一些空间。相应的,如果管道中没有可读数据,“消费者” 进程就要睡眠等待,具体过程如下图所示:

默认的情况下,从管道中读写数据,最主要的特点就是阻塞问题(这一特点应该记住),当管道里没有数据,另一个进程默认用 read() 函数从管道中读数据是阻塞的。

测试代码如下:

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>int main(int argc, char *argv[])
{int fd_pipe[2] = {0};pid_t pid;if( pipe(fd_pipe) < 0 ){// 创建无名管道perror("pipe");}pid = fork(); // 创建进程if( pid < 0 ){ // 出错perror("fork");exit(-1);}if( pid == 0 ){ // 子进程_exit(0);}else if( pid > 0){// 父进程wait(NULL); // 等待子进程结束,回收其资源char str[50] = {0};printf("before read\n");// 从管道里读数据,如果管道没有数据, read()会阻塞read(fd_pipe[0], str, sizeof(str));printf("after read\n");printf("str=[%s]\n", str); // 打印数据}return 0;
}

运行结果如下:

当然,我们编程时可通过 fcntl() 函数设置文件的阻塞特性。

设置为阻塞:fcntl(fd, F_SETFL, 0);

设置为非阻塞:fcntl(fd, F_SETFL, O_NONBLOCK);

测试代码如下:

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <fcntl.h>int main(int argc, char *argv[])
{int fd_pipe[2] = {0};pid_t pid;if( pipe(fd_pipe) < 0 ){// 创建无名管道perror("pipe");}pid = fork(); // 创建进程if( pid < 0 ){ // 出错perror("fork");exit(-1);}if( pid == 0 ){ // 子进程sleep(3);char buf[] = "hello, mike";write(fd_pipe[1], buf, strlen(buf)); // 写数据_exit(0);}else if( pid > 0){// 父进程fcntl(fd_pipe[0], F_SETFL, O_NONBLOCK); // 非阻塞//fcntl(fd_pipe[0], F_SETFL, 0); // 阻塞while(1){char str[50] = {0};read( fd_pipe[0], str, sizeof(str) );//读数据printf("str=[%s]\n", str);sleep(1);}}return 0;
}

运行结果如下:

默认的情况下,从管道中读写数据,还有如下特点(知道有这么回事就够了,不用刻意去记这些特点):

1)调用 write() 函数向管道里写数据,当缓冲区已满时 write() 也会阻塞。

测试代码如下:;

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>int main(int argc, char *argv[])
{int fd_pipe[2] = {0};pid_t pid;char buf[1024] = {0};memset(buf, 'a', sizeof(buf)); // 往管道写的内容int i = 0;if( pipe(fd_pipe) < 0 ){// 创建无名管道perror("pipe");}pid = fork(); // 创建进程if( pid < 0 ){ // 出错perror("fork");exit(-1);}if( pid == 0 ){ // 子进程while(1){write(fd_pipe[1], buf, sizeof(buf));i++;printf("i ======== %d\n", i);}_exit(0);}else if( pid > 0){// 父进程wait(NULL);  // 等待子进程结束,回收其资源}return 0;
}

运行结果如下:

2)通信过程中,别的进程先结束后,当前进程读端口关闭后,向管道内写数据时,write() 所在进程会(收到 SIGPIPE 信号)退出,收到 SIGPIPE 默认动作为中断当前进程。

测试代码如下:

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>int main(int argc, char *argv[])
{int fd_pipe[2] = {0};pid_t pid;if( pipe(fd_pipe) < 0 ){// 创建无名管道perror("pipe");}pid = fork(); // 创建进程if( pid < 0 ){ // 出错perror("fork");exit(-1);}if( pid == 0 ){ // 子进程//close(fd_pipe[0]);_exit(0);}else if( pid > 0 ){// 父进程wait(NULL);    // 等待子进程结束,回收其资源close(fd_pipe[0]); // 当前进程读端口关闭char buf[50] = "12345";// 当前进程读端口关闭// write()会收到 SIGPIPE 信号,默认动作为中断当前进程write(fd_pipe[1], buf, strlen(buf));while(1);    // 阻塞}return 0;
}

运行结果如下:

【Linux系统编程】进程间通信--无名管道(pipe)相关推荐

  1. Linux系统编程——进程间通信:命名管道(FIFO)

    命名管道的概述 无名管道,由于没有名字,只能用于亲缘关系的进程间通信(更多详情,请看<无名管道>).为了克服这个缺点,提出了命名管道(FIFO),也叫有名管道.FIFO 文件. 命名管道( ...

  2. Linux系统编程阶段:管道和信号相关知识及部分函数介绍。

    进程间的通讯方式: 管道.信号.共享内存.消息队列.信号量.套接字 1.管道 1)无名管道 特点: 不会在进程空间产生一个实实在在的管道文件: 只有用于亲缘关系的进程: 数据读出后就不存在管道: 创建 ...

  3. Linux系统编程—进程间通信—信号量

    信号量 信号量是在多线程环境下使用的一种设施,是可以用来保证两个或多个关键代码段不被并发调用.在进入一个关键代码段之前,线程必须获取一个信号量:一旦该关键代码段完成了,那么该线程必须释放信号量.其它想 ...

  4. linux pipe 文件,Linux系统常用指令、管道(pipe)、文件查找(find)

    //在3~4个目录层次中间查找passwd文件 find / -mindepth 3 -maxdepth 4 -name passwd //查找文件名中含有"an"的文件 find ...

  5. Linux系统编程—进程间通信—共享内存

    Linux共享内存 共享内存就是允许两个或多个进程共享一定的存储区.就如同 malloc() 函数向不同进程返回了指向同一个物理内存区域的指针.当一个进程改变了这块地址中的内容的时候,其它进程都会察觉 ...

  6. 入门Linux系统编程--网络编程

    文章目录 一.网络编程 1.socket服务端代码实现(无连接客户端) 6.socket服务端代码实现(连接客户端) 7.socket客户端代码实现 8.实现双方聊天 9.多方消息收发 二.往期文章 ...

  7. 【Linux系统编程】进程间通信之无名管道

    00. 目录 文章目录 00. 目录 01. 管道概述 02. 管道创建函数 03. 管道的特性 04. 管道设置非阻塞 05. 附录 01. 管道概述 管道也叫无名管道,它是是 UNIX 系统 IP ...

  8. 【Linux系统编程学习】匿名管道pipe与有名管道fifo

    此为牛客Linux C++和黑马Linux系统编程课程笔记. 0. 关于进程通信 Linux环境下,进程地址空间相互独立,每个进程各自有不同的用户地址空间.任何一个进程的全局变量在另一个进程中都看不到 ...

  9. Linux系统编程- 无名管道(匿名管道)

    无名管道作为Linux进程间通讯,我们这里把理论和实际结合起来说明. 1.什么是管道 管道,英文位pipe,在学习linux系统编程一个重要概念.它的发明人是道格拉斯.麦克罗伊,这位也是UNIX上早期 ...

  10. linux进程间通信:无名管道 pipe

    文章目录 内核层实现 结构 通信原理 特点 使用 函数声明 使用实例 单向通信 双向通信 编程注意事项 管道中无数据时读操作会阻塞 将管道的写端句柄关闭,不会影响读端数据读取 管道中没有数据,写操作关 ...

最新文章

  1. CocosCreator游戏开发---菜鸟学习之路(三)如何在CocosCreator中使用Pomelo
  2. oracle 如何数组变成表,Oracle从零开始19——表的管理09——嵌套表和可变数组
  3. ORACLE和MYSQL函数
  4. [原]变参函数原理详解
  5. cassandra 数据库_使用Apache Cassandra构建分布式NoSQL数据库
  6. 【Django 2021年最新版教程5】前端传递数据到后端处理 GET 方法
  7. RM2016视觉开源OpenCv2代码
  8. JDBC 连接mysql数据库出现 client does not support authen…… update mysql client
  9. TRUNCATE和DETELE的区别
  10. 使用python来完成数据的线性拟合
  11. int / int(double); java保留小数
  12. word2vec理解及pytorch实现
  13. 网络营销中的动态定价策略
  14. Future异步回调详解
  15. python:大球吃小球
  16. 利用ajax实现织梦dedecms瀑布流无限加载功能
  17. 合肥工业大学宣城校区Java技术实验一 Java开发环境的安装与配置
  18. Mac OS X下删除或添加鼠标右键菜单项目及清除Mac OS右键菜单重复项
  19. FANUC机器人如何查看诊断画面状态?
  20. 微记账软件—站立会议05

热门文章

  1. 创建型模式(一):FactoryMethod ( 工厂方法 )
  2. 统计代码行数_推荐一波代码量、行数、提交量、作者等全维度统计神器
  3. c语言浮点乘法 溢出,浮点加减乘除运算各在什么情况下会发生溢出?
  4. 表单如何添加大的文本框_在 Flutter 中进行文本框的创建和设定
  5. python工作环境_CentOS7下python工作环境管理
  6. Java黑皮书课后题第5章:*5.20(打印2到1000之间的素数)修改程序清单5-15,打印2到1000之间(包括2和1000)的所有素数。每1行显示8个素数,数字之间用一个空格字符隔开
  7. 任意长度的高精度大整数加法
  8. Flutter 中的基本路由
  9. linux安装报错之:ifconfig command not found解决
  10. sql server总结二