什么是管道通信?Linux下匿名管道和命名管道的区别与用法

 更新时间:2026年07月23日 09:06:29   作者:qualitymanmd  
这篇文章主要介绍了什么是管道通信?Linux下匿名管道和命名管道的区别与用法,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教

介绍

进程间通信目的

首先我们要了解进程间通信的目的,一共有4点

1:数据传输:一个进程需要将它的数据传输给另一个进程

2:资源共享:多个进程之间共享同样的资源

2:通知事件:一个进程给另一个进程发送信息,通知发生了某种事件

4:进程控制:某些进程希望完全控制另一个进程的执行,此时进程希望另一个进程能进行拦截另一个进程的一场,并能够及时知道它的状态改变

进程间通信发展

管道,SystemV进程间通信,POSIX进程间通信

其中这三个都有各自的分支

  • 管道:匿名管道pipe,命名管道
  • SystemV IPC:System V 消息队列/共享内存/信号量
  • POSIX IPC:消息队列,共享内存,信号量,互斥量,条件变量,读写锁

管道

什么是管道:管道是UNIX系统最古老的进程间通信方式

一个进程链接另一个进程的另一个数据流成为一个管道

匿名管道

这里是示例代码

#include<unistd.h>
//功能是创建无名管道
int pipe(int fd[2]);
//参数解释:fd为文件描述符数组,fd[0]是读端,fd[1]为写端

用fork来共享管道原理

站在文件描述符角度-深度理解管道

站在内核角度

测试管道读写代码示例

#include<unistd.h>
#include<stdlib.h>
#include<stdio.h>
#include<errno.h>
#include<string.h>
#define ERR_EXIT(m)
do {
	perror(m);
	exit(EXIT_FAILURE);
} while (0)

int main(int argc, char* argv[])
{
	int pipefd[2];
	if (pipe(pipefd) == -1)
		ERR_EXIT("pipe error");
	pid_t pid;
	pid = fork();
	if (pid == -1)
	{
		ERR_EXIT("fork error");
	}
	if (pid == 0)
	{
		close(pipefd[0]);
		write(pipefd[1], "hello", 9);
		close(pipefd[1]);
		exit(EXIT_SUCCESS);
	}
	close(pipefd[1]);
	char buf[10] = { 0 };
	read(pipefd[0], buf, 10);
	printf("buf=%s\n", buf);
	return 0;
}

管道读写规则

当没有数据可读时

  • O_NONBLOCK disable:read 调用阻塞,进程暂停执行,一直等到数据来到为止
  • O_NONBLOCK enable :read调用返回-1,errno值为EAGAIN

当管道满的时候

  • O_NONBLOCK disable:write调用阻塞,直到有进程读走数据
  • O_NONBLOCK enable:调用返回-1,errno值为EAGAIN
  • 如果所有管道读写对应文件描述符被关闭,errno为EAGAIN
  • 如果所有管道读端量不大于PIPE_BUF时,linux将保证写入的原子性
  • 当要写⼊的数据量⼤于PIPE_BUF时,linux将不再保证写⼊的原⼦性。

管道特点

只能用于具有共同祖先的进程之间进行通信,通常一个管道由一个进程创建,然后进程调用fork,此后父子进程之间可用该管道

管道提供流式服务

  • 一般而言,进程退出管道释放
  • 所以管道的生命周期随进程变化而变化
  • 管道是半双工的,数据只能向一个方向流动,需要双方通信时,需要建立两个管道

3-8验证管道通信的4种情况

  • • 读正常&&写满
  • • 写正常&&读空
  • • 写关闭&&读正常
  • • 读关闭&&写正常

命名管道

管道应用的一个限制就是只能在具有共同祖先的进程间通信

如果我们在不相关的进程间进行通信,可以用FIFO文件进行此工作,它通常被命名为管道

命名管道是一种特殊类型文件

创建命名管道

$ mkfifo filename

匿名管道和命名管道的区别

  • 匿名管道是pipe函数创建并打开
  • 命名管道由mkfifo函数创建,打开用open
  • fifo和pipe之间唯一的区别就是他们创建和打开的方式不同,一旦这些工作完成之后,他们具有相同的语义

命名管道的打开规则

打开操作/未读/打开fifo

  • O_NONBLOCKdisable:阻塞直到有相应进程为写⽽打开该FIF
  • O_NONBLOCKenable:⽴刻返回成功

如果当前打开操作是为写⽽打开FIFO时

  • O_NONBLOCK disable:阻塞直到有相应进程为读⽽打开该FIFO
  • O_NONBLOCK enable:⽴刻返回失败,错误码为ENXIO

System V 共享内存

共享内存是最快的IPC形式,一旦这样的内存映射搭配共享它的进程的地址空间,这些进程间数据传递不再涉及到内核,换句话说是进程不在通过执行进入内核的操作系统调用来传递彼此的数据

5-3 共享内存相关函数

一、shmget ():创建 / 获取共享内存

功能

创建一块新共享内存段,或获取已存在的共享内存段标识。

函数原型

int shmget(key_t key, size_t size, int shmflg);

参数说明

  • key:共享内存键值,标识共享内存段的唯一名字,不同进程通过相同 key 访问同一块共享内存。
  • size:共享内存段字节大小,创建时指定,获取已有段时填 0 即可。
  • shmflg:权限 + 控制标志,用法同文件 open 的 mode 权限,常用组合:
  • IPC_CREAT:不存在则创建;已存在则直接获取该段。
  • IPC_CREAT | IPC_EXCL:不存在则创建;已存在函数报错返回 - 1,保证新建。

返回值

  • 成功:返回非负整数 shmid(共享内存操作标识)
  • 失败:返回 -1

二、shmat ():挂载共享内存到进程虚拟地址空间

功能

将指定共享内存段映射、连接到当前进程的地址空间,挂载后进程可直接读写这块内存。

函数原型

void *shmat(int shmid, const void *shmaddr, int shmflg);

参数说明

  • shmid:shmget 返回的共享内存标识符。
  • shmaddr:手动指定挂载地址
  • 传 NULL:由操作系统内核自动分配合适地址(最常用);
  • 非 NULL:用户手动指定挂载起始地址。
  • shmflg:挂载属性标志,两个常用取值:
  • SHM_RDONLY:只读挂载,进程只能读共享内存,不可写;
  • SHM_RND:地址对齐标志。

地址对齐规则说明

  • shmaddr == NULL:内核自动分配地址,无需处理对齐;
  • shmaddr != NULL 且无 SHM_RND:直接使用 shmaddr 作为挂载地址;
  • shmaddr != NULL 且带 SHM_RND:地址向下对齐到 SHMLBA 整数倍,计算公式:
实际挂载地址 = shmaddr - (shmaddr % SHMLBA)

返回值

  • 成功:返回共享内存段首地址指针;
  • 失败:返回 (void *)-1

三、shmdt ():卸载共享内存(脱离映射)

功能

断开当前进程与共享内存段的地址映射,进程无法再访问该共享内存。

注意:仅脱离进程映射,不会删除操作系统中的共享内存实体。

函数原型

int shmdt(const void *shmaddr);

参数说明

shmaddr:shmat 成功返回的共享内存首地址指针。

返回值

  • 成功:返回 0
  • 失败:返回 -1

四、shmctl ():共享内存控制(删除 / 修改属性 / 获取状态)

功能

对共享内存执行控制操作:获取状态、修改权限、销毁共享内存段。

函数原型

int shmctl(int shmid, int cmd, struct shmid_ds *buf);

参数说明

  • shmid:shmget 返回的共享内存标识;
  • cmd:控制动作指令(三种核心取值);
  • buf:struct shmid_ds 结构体指针,存储共享内存权限、状态信息。

返回值

  • 成功:返回 0
  • 失败:返回 -1

system V 消息队列

消息队列提供一个从一个进程向另外一个进程发送一块数据的方法

每个数据块都被认为是有一个类型,接收者进程接收的数据块可以有不用的类型值

特性方面

IPC资源必须删除,否则不会自动删除,除非自启,所以System V ipc资源的生命周期随内核变化而变化

system V 信号量

多个执行流程,能看到的同一份访问资源,共享资源

被保护起来的共享资源:临界资源

保护的方式常见为互斥和同步

任何时刻只允许一个执行流访问资源,叫做互斥

多个执行流,访问临界资源的时候,具有一定顺序性,叫做同步

系统中某些资源一次只允许一个进程实用,这样的称谓临界资源或互斥资源

在进程中涉及到互斥资源的程序段叫临界区。你写的代码=访问临界资源的代码(临界区)+不访问

临界资源的代码(⾮临界区)

•所谓的对共享资源进⾏保护,本质是对访问共享资源的代码进⾏保护

信号量

ipc资源必须删除,否则不会自动清除,除非重启,所以system v ipc 资源的生命周期随内核变化而变化

信号量是一个计数器

  • 作用方面:临界保护区
  • 本质方面:资源的预定机制

操作⽅⾯

  • 申请资源,计数器–,P操作
  • 释放资源,计数器++,V操作

内核是怎么组织管理IPC资源的

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • Linux lseek函数的使用详解

    Linux lseek函数的使用详解

    这篇文章主要介绍了Linux lseek函数的使用详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-12-12
  • Centos7 Mysql 5.6 多主一从 解决方案与详细配置

    Centos7 Mysql 5.6 多主一从 解决方案与详细配置

    这篇文章主要介绍了Centos7 Mysql 5.6 多主一从 解决方案与详细配置,需要的朋友可以参考下
    2016-04-04
  • 详解centos7使用无线wifi连接的方法

    详解centos7使用无线wifi连接的方法

    本篇文章主要介绍了centos7使用无线wifi连接的方法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下。
    2016-12-12
  • Linux卸载自带jdk并安装新jdk版本的图文教程

    Linux卸载自带jdk并安装新jdk版本的图文教程

    在Linux系统中,有时需要卸载预装的OpenJDK并安装特定版本的JDK,例如JDK 1.8,所以本文给大家详细介绍了Linux卸载自带jdk并安装新jdk版本的图文教程,对大家的学习或工作有一定的帮助,需要的朋友可以参考下
    2025-04-04
  • 详解Linux环境下NodeJS的安装配置(HelloWorld)

    详解Linux环境下NodeJS的安装配置(HelloWorld)

    本篇文章主要介绍了Linux环境下NodeJS的安装配置,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-01-01
  • ubuntu lighttpd+webpy (fastcgi)配置方法

    ubuntu lighttpd+webpy (fastcgi)配置方法

    首先安装 lighttpd 和 webpy,因为用 kpackagekit 做软件管理,在安装 webpy 的时候会自动安装 flup
    2009-07-07
  • Ubuntu 20.04 apt 更换国内源的实现方法

    Ubuntu 20.04 apt 更换国内源的实现方法

    这篇文章主要介绍了Ubuntu 20.04 apt 更换国内源的实现方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-05-05
  • linux网络配置工具的使用

    linux网络配置工具的使用

    这篇文章主要介绍了linux网络配置工具的使用,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-01-01
  • Linux进程地址空间详解

    Linux进程地址空间详解

    在C语言编程中,内存管理是一个重要的环节,本文详细解析了C语言中的内存区域划分,包括栈区、堆区、全局区等,并探讨了fork函数的遗留问题,即如何出现一个变量同时等于0又大于0的情况,此外,文章还深入讨论了进程地址空间的概念
    2024-10-10
  • VMware下CentOS 6.7安装图文教程

    VMware下CentOS 6.7安装图文教程

    这篇文章主要为大家详细介绍了VMware下CentOS 6.7安装图文教程,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-03-03

最新评论