python进程间通信——命名管道(Named Pipe、FIFO)

文章目录

  • Python中的命名管道:深入理解进程间通信
    • 1. 命名管道简介
    • 2. 创建和删除命名管道
    • 3. 写入命名管道
    • 4. 读取命名管道
    • 5. 示例:进程间通信
      • write_to_pipe.py
      • read_from_pipe.py
      • 测试运行
    • 6. 注意事项和限制
      • 命名管道的半双工机制
      • 命名管道读写任意一方未打开,另一方默认阻塞(可以尝试使用非阻塞方式打开`os.O_NONBLOCK`)
      • 命名管道能被读写多方同时打开,但数据只能从某个写方到某个读方,不会产生数据复制现象(这属于非常规操作,不要这样使用)
      • 命名管道权限问题
    • 7. 命名管道非阻塞打开示例
      • 说明
      • 情况1:读取进程以阻塞方式打开命名管道,写入进程以非阻塞方式打开命名管道
        • write_to_pipe_nonblock.py
        • read_from_pipe_block.py
        • 测试运行
        • 额外功能:申请缓存帧
      • 情况2:读取进程以非阻塞方式打开命名管道,写入进程以阻塞方式打开命名管道
        • write_to_pipe_block.py
        • read_from_pipe_nonblock.py
        • 测试运行
        • 注意事项
          • 读进程报`BlockingIOError`错误
    • 结论

Python中的命名管道:深入理解进程间通信

命名管道(Named Pipe),也被称为FIFO,是一种在UNIX、Linux和类Unix系统中用于实现进程间通信(IPC)的机制。在Python中,我们可以使用os模块来创建和操作命名管道。

1. 命名管道简介

命名管道与普通管道类似,都是基于字节流进行通信的,但不同的是命名管道有路径名与之关联,并且其生命周期超过了引发创建它的进程。这使得不相关的进程可以通过命名管道进行通信。

2. 创建和删除命名管道

在Python中,我们可以使用os.mkfifo()函数创建一个命名管道。该函数接受两个参数,第一个参数是要创建的命名管道的路径,第二个参数是管道的权限设置,它是可选的,默认值为0o666

import os

pipe_name = "my_pipe"

# 创建命名管道
os.mkfifo(pipe_name)

当你不再需要这个命名管道时,可以使用os.unlink()os.remove()函数删除它。

# 删除命名管道
os.unlink(pipe_name)

3. 写入命名管道

要向命名管道写入数据,我们首先需要使用os.open()函数以写入模式打开它。然后,可以使用os.write()函数写入数据。

pipeout = os.open(pipe_name, os.O_WRONLY)

message = "Hello, World!"

# 写入数据
os.write(pipeout, message.encode())

# 关闭管道
os.close(pipeout)

注意,我们需要将要写入的字符串编码为字节对象,因为os.write()函数期望其参数是字节对象。

4. 读取命名管道

读取命名管道的数据与写入类似。我们首先使用os.open()函数以读取模式打开管道,然后使用os.read()函数读取数据。

pipein = os.open(pipe_name, os.O_RDONLY)

# 读取数据
data = os.read(pipein, 100)  # 读取前100个字节

# 解码数据并打印
print(data.decode())

# 关闭管道
os.close(pipein)

这里,我们使用了os.read()函数的两个参数版本,其中第二个参数指定要读取的最大字节数。如果不提供这个参数,os.read()函数将读取所有可用的数据。

5. 示例:进程间通信

现在让我们来看一个使用命名管道进行进程间通信的示例。假设我们有两个Python脚本,一个脚本负责向管道写入数据,另一个脚本负责从管道读取数据。

write_to_pipe.py

import os

pipe_name = "my_pipe"

# 创建命名管道
if not os.path.exists(pipe_name):
    os.mkfifo(pipe_name)

pipeout = os.open(pipe_name, os.O_WRONLY)

message = "Hello, World!"

# 写入数据
os.write(pipeout, message.encode())

# 关闭管道
os.close(pipeout)

read_from_pipe.py

import os

pipe_name = "my_pipe"

if os.path.exists(pipe_name):
    pipein = os.open(pipe_name, os.O_RDONLY)

    # 读取数据
    data = os.read(pipein, 100)  # 读取前100个字节

    # 解码数据并打印
    print(data.decode())

    # 关闭管道
    os.close(pipein)

测试运行

在这个示例中,write_to_pipe.py脚本向命名管道写入一条消息,然后read_from_pipe.py脚本从同一个命名管道读取并打印这条消息。

无论先运行发送方,还是先运行接收方,另一方都会阻塞(等待):

(先运行发送方)

在这里插入图片描述
(先运行接收方)

在这里插入图片描述

当双方都开启,接收方才会收到消息,发送方也才会结束:

在这里插入图片描述

在这里插入图片描述

6. 注意事项和限制

虽然命名管道是一种非常有用的IPC机制,但在使用它时还需要注意一些事项。

命名管道的半双工机制

首先,命名管道是半双工的,这意味着数据只能(同一时间)在一个方向上流动。如果你需要实现全双工通信(即两个进程可以互相发送和接收数据),那么你需要创建两个命名管道。

注意:只用一个命名管道,两个进程交替进行读/写是可行的,但这需要非常谨慎的同步管理来确保正确的操作顺序和数据完整性,通常不建议这么使用。

命名管道读写任意一方未打开,另一方默认阻塞(可以尝试使用非阻塞方式打开os.O_NONBLOCK

其次,当你尝试读取或写入一个没有打开的管道时,你的进程将被阻塞,直到管道的另一端被打开。为了避免这种情况,你可以在调用os.open()函数时使用os.O_NONBLOCK标志。

命名管道能被读写多方同时打开,但数据只能从某个写方到某个读方,不会产生数据复制现象(这属于非常规操作,不要这样使用)

命名管道权限问题

最后,当你使用命名管道时,需要考虑到权限和安全问题。任何有访问权限的进程都可以读写命名管道。因此,如果你的程序处理敏感信息,那么你应该小心地设置管道的权限,以防止未授权的访问。

7. 命名管道非阻塞打开示例

说明

对于大多数应用来说,通常只有一个进程(读或写)会以非阻塞方式打开命名管道,而另一个进程则以正常(阻塞)方式打开。这样可以确保当一方尝试读取或写入数据时,如果数据不可用或管道已满,那么该进程就会被阻塞,直到条件满足为止。

然而,在某些情况下,可能需要两个进程都以非阻塞方式打开命名管道。比如在某些实时系统或高性能计算中,进程不能接受任何形式的阻塞,即使是等待IPC操作也不行。在这种情况下,进程会以非阻塞方式打开命名管道,并使用轮询、事件通知或其他机制来检查是否可以进行读/写操作。

此外,还有一些特殊的情况,比如网络编程或者并发编程中,程序可能需要同时处理多个I/O操作(包括文件操作、网络操作和IPC操作等),并且不能因为任何一个操作的阻塞而停止处理其他操作。在这种情况下,程序可能会选择以非阻塞方式打开所有的I/O资源,包括命名管道,并使用select、poll、epoll等机制来高效地管理这些I/O操作。

但是,总体来说,除非有特殊的需求,否则通常不会让两个进程都以非阻塞方式打开命名管道。

情况1:读取进程以阻塞方式打开命名管道,写入进程以非阻塞方式打开命名管道

write_to_pipe_nonblock.py
import os
import time

pipe_name = "my_pipe"

# 创建命名管道
if not os.path.exists(pipe_name):
    os.mkfifo(pipe_name)

pipeout = os.open(pipe_name, os.O_WRONLY | os.O_NONBLOCK)

message = "Hello, World!"

try:
    # 写入数据
    os.write(pipeout, message.encode())
except BlockingIOError:
    print("Pipe is full. Waiting...")

# 关闭管道
os.close(pipeout)
read_from_pipe_block.py
import os

pipe_name = "my_pipe"

if os.path.exists(pipe_name):
    pipein = os.open(pipe_name, os.O_RDONLY)  # 阻塞模式

    # 读取数据
    data = os.read(pipein, 100)  # 读取前100个字节

    # 解码数据并打印
    print(data.decode())

    # 关闭管道
    os.close(pipein)
测试运行

在这里插入图片描述

write_to_pipe_nonblock.py脚本以非阻塞方式打开命名管道,并尝试写入一条消息。如果此时管道没有进程在读,os.open()函数将引发OSError;如果管道已满,os.write()函数将引发BlockingIOError异常。所以非阻塞写要求读进程先打开。

read_from_pipe_block.py脚本以阻塞方式打开同一个命名管道,并从中读取将要写入管道的消息。如果管道为空,那么os.read()函数将阻塞,直到有数据可读为止。

额外功能:申请缓存帧

客户端向缓存帧编码服务申请缓存帧的时候,可以先客户端先开启阻塞读,然后用http接口告知缓存帧编码服务自身已开启读监听,同时告知其监听的命名管道名和申请帧所属摄像头,然后缓存帧编码服务器取对应摄像头最新帧,用非阻塞写方式写到对应命名管道中,完成缓存帧交付。

情况2:读取进程以非阻塞方式打开命名管道,写入进程以阻塞方式打开命名管道

write_to_pipe_block.py
import os
import time

pipe_name = "my_pipe"

# 创建命名管道
if not os.path.exists(pipe_name):
    os.mkfifo(pipe_name)

pipeout =  os.open(pipe_name, os.O_WRONLY)

message = "Hello, World!"

try:
    # 写入数据
    os.write(pipeout, message.encode())
except BlockingIOError:
    print("Pipe is full. Waiting...")

# time.sleep(5)  # 确保读取进程有足够的时间来读取数据

# 关闭管道
os.close(pipeout)

read_from_pipe_nonblock.py
import os

pipe_name = "my_pipe"

if os.path.exists(pipe_name):
    pipein = os.open(pipe_name, os.O_RDONLY | os.O_NONBLOCK)  # 阻塞模式

    # 读取数据
    #data = os.read(pipein, 100)  # 读取前100个字节
	while True:
	    try:
	        data = os.read(pipein, 100)
	        break
	    except BlockingIOError:
	        continue

    # 解码数据并打印
    print(data.decode())

    # 关闭管道
    os.close(pipein)
测试运行

当没有阻塞写时,非阻塞读不会报错,会读到空内容:
在这里插入图片描述

当有阻塞写时,非阻塞读能正常读到内容:

在这里插入图片描述

注意事项
读进程报BlockingIOError错误
Traceback (most recent call last):
  File "read_from_pipe_nonblock.py", line 10, in <module>
    data = os.read(pipein, 100)  # 读取前100个字节
BlockingIOError: [Errno 11] Resource temporarily unavailable

当你的读进程运行os.read()方法时,由于你使用了O_NONBLOCK标志打开了管道,如果此时管道中没有任何可供读取的数据,那么程序会立即返回一个BlockingIOError。

要解决这个问题,你可以尝试以下两种方法:

  1. 让你的读进程在尝试读取数据之前先等待一段时间,以确保写进程有足够的时间把数据写入管道。你可以使用time.sleep()函数来实现这个等待操作。

    if os.path.exists(pipe_name):
        pipein = os.open(pipe_name, os.O_RDONLY | os.O_NONBLOCK)  # 阻塞模式
        time.sleep(1)  # 等待1秒
    
        # 然后进行读取操作...
    
  2. 或者你可以捕获BlockingIOError异常,并在发生这种异常时再次尝试读取数据,直到成功为止。

    while True:
        try:
            data = os.read(pipein, 100)
            break
        except BlockingIOError:
            continue
    
    # 然后进行解码和打印操作...
    

结论

命名管道是UNIX和Linux系统中一种非常强大的IPC机制,它使得不相关的进程能够方便地进行通信。Python通过os模块提供了对命名管道的支持,使得我们可以很容易地在Python程序中使用这种机制。然而,尽管命名管道很有用,但在使用它时还需要注意其半双工性质、可能出现的阻塞情况,以及权限和安全问题。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

防范水坑攻击:了解原理、类型与措施

水坑攻击是一种常见的网络攻击方式&#xff0c;它利用了人类在互联网上的行为习惯&#xff0c;诱导用户访问恶意网站或下载恶意软件&#xff0c;从而获取用户的个人信息或控制用户的计算机系统。本文将介绍水坑攻击的原理、类型和防范措施。 一、水坑攻击的原理 水坑攻击&…

Pytest 结合 Allure 生成测试报告

测试报告在项目中是至关重要的角色&#xff0c;一个好的测试报告&#xff1a; 可以体现测试人员的工作量&#xff1b; 开发人员可以从测试报告中了解缺陷的情况&#xff1b; 测试经理可以从测试报告中看到测试人员的执行情况及测试用例的覆盖率&#xff1b; 项目负责人可以通过…

2023年前前同事12个月薪水未发,来聊聊普通的开发人如何避开丛丛杀机的新一年

转眼又一年&#xff0c;2024已经快走完了十二分之一。 回顾2023&#xff0c;整个程序员行业关键词总结为&#xff1a;跳槽&#xff0c;加班&#xff0c;裁员&#xff0c;失业&#xff0c;欠薪。 前几年互联网的火爆随着这几年疫情&#xff0c;国际社会动荡&#xff0c;国内外经…

Java设计模式详解-更新中

收藏和关注的同时&#xff0c;请也关注 公众号 “IT技术馆” 各位大家好&#xff0c;从今天开始&#xff0c;作者开始整理 《JAVA软件设计模式&#xff08;GOF&#xff09;》 专栏。请各位多多关注&#xff01; 该专栏是根据作者的技术经验和设计模式的了解&#xff0c;进行详…

Docker-Confluence部署记录

启动 docker container run -v $(pwd):/var/atlassian/application-data/confluence/ --nethost -d --nameconfluence_720_20240120 confluence/confluence:7.2.0新建mysql数据库 导入破解包 atlassian-agent 参考-Confluence 破解方式&#xff08;Linux&#xff09; 按流程破…

idea消除代码区黄色警告

文章目录 前言一、修改配置总结 前言 idea的检查项较多&#xff0c;导致警告比较多看上去很不雅观。 一、修改配置 在idea中打开Settings 取消Warning和Weak Warning的勾选 总结 回到顶部

【Python学习】Python学习21- 正则表达式(1)

目录 【Python学习】Python学习21- 正则表达式&#xff08;1&#xff09; 前言re.match函数实例 re.search方法re.match与re.search的区别参考 文章所属专区 Python学习 前言 本章节主要说明Python的正则表达式。 正则表达式是一个特殊的字符序列&#xff0c;它能帮助你方便的…

uniCloud uni-id体系的使用

目录 简介 uni-id导入和配置 用户表与文章表关联foreignKey 字段级权限控制 指定数据集权限控制 权限规则的变量和运算符 简介 uni-id已完成的功能&#xff1a; 注册、登录、发送短信验证码、密码加密保存、修改密码、忘记密码、头像管理、token管理、rbac权限角色体系、…

04 单链表

目录 链表的概念和结构单链表OJ练习 1. 链表的概念和结构 1.1 链表的概念 链表是一种物理存储结构上非连续、非顺序的存储结构&#xff0c;数据元素的逻辑顺序是通过链表中的指针链接次序实现的 1.从上图可以看出链式结构在逻辑上是连续的&#xff0c;物理上不一定连续 2.现…

提升网站关键词排名的工具

随着互联网的蓬勃发展&#xff0c;网站的关键词排名成为衡量网站流量和曝光度的重要指标。在这个竞争激烈的数字时代&#xff0c;站在搜索引擎结果的前列变得至关重要。为了实现这一目标&#xff0c;合理利用关键词排名优化工具是必不可少的。本文将重点介绍147SEO软件&#xf…

数据库入门:快速了解数据库

目录 序言 一、什么是数据库&#xff1f; 二、DBMS 数据库管理系统&#xff08;DataBase Management System&#xff09; 三、数据库与文件系统的区别 四、数据库的发展和规划 五、常见数据库 5.1 关系型数据库 5.2 非关系型数据库 六、DBMS支持的数据模型 6.1 层次模…

2024年【河北省安全员B证】最新解析及河北省安全员B证试题及解析

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 河北省安全员B证最新解析是安全生产模拟考试一点通生成的&#xff0c;河北省安全员B证证模拟考试题库是根据河北省安全员B证最新版教材汇编出河北省安全员B证仿真模拟考试。2024年【河北省安全员B证】最新解析及河北省…

C++入门学习(八)sizeof关键字

sizeof 是 C 和 C 中的一个运算符&#xff0c;用于确定特定类型或对象的内存大小&#xff08;以字节为单位&#xff09;。 1、查看数据类型占据内存大小 #include <iostream> using namespace std; int main() {short a 1;int b 1;long c 1;long long d 1;cout<…

跨部门算法迭代需求,从提出到上线的全流程实践

文章目录 引言需求评审技术方案评审模块开发系统联调QA测试产品验收经验教训 引言 最近工作中有一个算法迭代的需求&#xff0c;我在其中作为技术侧负责人&#xff08;技术主R&#xff09;推动需求完成上线。 需求涉及多个部门&#xff0c;前后耗时接近1个月。 我第一次在这…

Spring-简介

在向读者描述Spring时&#xff0c;笔者不打算从某处粘贴一段常见的概念性文字糊弄完本专栏的第一篇文章&#xff0c;而是用易于理解的话向读者指出几个重点。 &#xff08;1&#xff09;是框架。何谓框架&#xff1f;就像搭房子一样&#xff0c;框架就如同是墙体结构&#xff…

【已解决】Qt Creator设计模式被禁用不能点的原因及解决方案

Qt Creator 下载地址&#xff08;含历史版本&#xff09;&#xff1a;https://download.qt.io/official_releases/qtcreator/ 症状 Qt Creator 目前最新版为12.0.1&#xff0c;安装后打开.qml文件发现设计工具图标为禁用状态。 原因及解决方案 根据官网材料&#xff08;Qt C…

苹果笔记本 macbook 在 office word 中使用 mathtype 的方法

前言 想在 MacBook 中使用 mathtype&#xff0c;去搜索&#xff0c;去 Apple Store 下载也发现没有 解决方法 打开 office Word 的「插入」中的「获取加载项」、「我的加载项」。 在应用商店中下载&#xff0c;需要登录自己的微软账号。 加载成功后就可以使用了。 注意 和…

Python 类变量和实例变量详解

更多资料获取 &#x1f4da; 个人网站&#xff1a;ipengtao.com 在Python中&#xff0c;变量分为类变量和实例变量两种类型&#xff0c;它们有着不同的作用范围和生命周期。理解这两种变量类型的区别对于面向对象编程非常重要。本文将详细介绍Python中的类变量和实例变量&…

webpack 核心武器:loader 和 plugin 的使用指南(上)

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…

docker使用http_proxy配置代理

钢铁知识库&#xff0c;一个学习python爬虫、数据分析的知识库。人生苦短&#xff0c;快用python。 在内网服务器中&#xff0c;docker经常需要下载拉取镜像&#xff0c;但由于没有网络要么只能手动导入镜像包&#xff0c;又或者通过http_proxy代理到其它服务器下载。 解决方法…