UNPV2 学习:Pipes and FIFOs 学习记录

news2024/12/26 11:18:20

命名管道 fifo 的特点

特点描述

  1. 打破了匿名管道只能在父子进程间使用的限制,可以在无亲缘关系的进程之间使用同一个 fifo
  2. 未指定 NONBLOCK 参数 open fifo 时可能会 block,不当的编码可能会导致进程死锁
  3. 当请求读超过 fifo 中现有数据大小的数据时,只会返回现有数据大小的内容
  4. wirte 数据的原子性由写入的大小与 PIPE_BUF 的关系确定与是否指定 O_NONBLOCK 标志无关,当 wirte 的大小小于或等于 PIPE_BUF 时,write 操作被保证是原子执行,不会产生数据交错,当大小超过 PIPE_BUF 时 write 操作不保证能原子执行
  5. 向一个未以读模式打开的 FIFO 写入数据时,内核会向此进程发送 SIGPIPE 信号

mkfifo 函数调用的系统调用

linux 中 mkfifo 函数并不是一个系统调用而是一个 C 库函数,mkfifo 函数最终会调用 mknode 系统调用创建 fifo。

未指定 NONBLOCK 参数 open fifo 时可能会 block

以只读方式打开一个 fifo

打开一个 fifo 来读时,内核会判断此 fifo 上是否有写者,如果没有则调用 wait_for_partner 函数挂起当前进程等待对端就绪。

相关代码如下:

switch (filp->f_mode & (FMODE_READ | FMODE_WRITE)) {
	case FMODE_READ:
	/*
	 *  O_RDONLY
	 *  POSIX.1 says that O_NONBLOCK means return with the FIFO
	 *  opened, even when there is no process writing the FIFO.
	 */
		pipe->r_counter++;
		if (pipe->readers++ == 0)
			wake_up_partner(pipe);

		if (!is_pipe && !pipe->writers) {
			if ((filp->f_flags & O_NONBLOCK)) {
				/* suppress EPOLLHUP until we have
				 * seen a writer */
				filp->f_version = pipe->w_counter;
			} else {
				if (wait_for_partner(pipe, &pipe->w_counter))
					goto err_rd;
			}
		}
		break;

上述代码首先递增 pipe 内部计数器 r_counter,当 readers 计数递增前值为 0 时调用 wake_up_partner 尝试唤醒阻塞在以 WRITE 模式调用 open 系统调用阻塞的进程,这些进程以 pipe 结构的 r_counter 变量为参数等待读者上线,r_counter 变量的值有更新表明成功,wait_for_partner 函数返回 0,否则继续等待。

wait_for_partner 函数代码如下:

static int wait_for_partner(struct pipe_inode_info *pipe, unsigned int *cnt)                                                                     
{
    DEFINE_WAIT(rdwait);
    int cur = *cnt;

    while (cur == *cnt) {
        prepare_to_wait(&pipe->rd_wait, &rdwait, TASK_INTERRUPTIBLE);
        pipe_unlock(pipe);
        schedule();
        finish_wait(&pipe->rd_wait, &rdwait);
        pipe_lock(pipe);
        if (signal_pending(current))
            break;
    }    
    return cur == *cnt ? -ERESTARTSYS : 0; 
}

while 循环终止的条件为计数值发生变化,这与上文的描述一致。

以只写方式打开一个 fifo

同理,以只写方式打开一个 fifo 时,内核会判断此 fifo 上是否有读者,如果没有则调用 wait_for_partner 函数挂起,直到读者调用 wake_up_partner 唤醒。

写操作 open 的实现代码如下:

case FMODE_WRITE:
	/*
	 *  O_WRONLY
	 *  POSIX.1 says that O_NONBLOCK means return -1 with
	 *  errno=ENXIO when there is no process reading the FIFO.
	 */
		ret = -ENXIO;
		if (!is_pipe && (filp->f_flags & O_NONBLOCK) && !pipe->readers)
			goto err;

		pipe->w_counter++;
		if (!pipe->writers++)
			wake_up_partner(pipe);

		if (!is_pipe && !pipe->readers) {
			if (wait_for_partner(pipe, &pipe->r_counter))
				goto err_wr;
		}
		break;

上述代码首先递增 pipe 内部计数器 w_counter,当 writers 计数递增前值为 0 时调用 wake_up_partner 尝试唤醒阻塞在以 READ 模式调用 open 系统调用阻塞的进程,这些进程以 pipe 结构的 w**_counter** 变量为参数等待写者上线,w_counter 变量的值有更新表明成功,wait_for_partner 函数返回 0,否则继续等待。

以读写模式打开 fifo

POSIX 标准并未定义以读写模式且设置 O_NONBLOCK 标志时的行为,linux 内核在这种情况下不会阻塞,会直接返回。

相关代码如下:

case FMODE_READ | FMODE_WRITE:
	/*
	 *  O_RDWR
	 *  POSIX.1 leaves this case "undefined" when O_NONBLOCK is set.
	 *  This implementation will NEVER block on a O_RDWR open, since
	 *  the process can at least talk to itself.
	 */

		pipe->readers++;
		pipe->writers++;
		pipe->r_counter++;
		pipe->w_counter++;
		if (pipe->readers == 1 || pipe->writers == 1)
			wake_up_partner(pipe);
		break;

此处代码同时递增读写相关计数器并尝试唤醒阻塞在以读、写 open fifo 的进程。

修改 pipe 共享数据时的锁保护

上述操作均在获取了 pipe 互斥锁的条件下进行以保证共享数据的一致性。获取锁的接口为 __pipe_lock,其实现如下:

static inline void __pipe_lock(struct pipe_inode_info *pipe)
{
	mutex_lock_nested(&pipe->mutex, I_MUTEX_PARENT);
}

从名称上看它是一个互斥锁,保证同一时刻只有一个用户占有,同时它支持嵌套调用,即如果一个已经获取了这把锁的进程再次获取时也能够成功,而释放时也需要释放相同次数。

为什么描述的是 fifo,内核代码中却用的是 pipe?

linux 内核中的 fifo 基于 pipe 实现,核心差别在于 open 操作。fifo 与 pipe 使用同一套 file_operations,相关代码如下:

const struct file_operations pipefifo_fops = {
	.open		= fifo_open,
	.llseek		= no_llseek,
	.read_iter	= pipe_read,
	.write_iter	= pipe_write,
	.poll		= pipe_poll,
	.unlocked_ioctl	= pipe_ioctl,
	.release	= pipe_release,
	.fasync		= pipe_fasync,
	.splice_write	= iter_file_splice_write,
};

pipe 通过 pipe 系统调用创建,内核创建两个 file 并绑定 file_operation 为 pipefifo_fops。

多进程同时操作两个 fifo 时潜在的锁死问题

UNPV2 Figure4.17 中有如下客户端与服务器端通信的操作:
在这里插入图片描述

上图中创建了两个 fifo,使用这两个 fifo 能够模拟全双工通信。然而当编码不当时,上述过程可能会触发进程死锁。

上图中 parent 进程创建 fifo1 与 fifo2 两个 fifo,然后将 fifo1 以只写方式打开,将 fifo2 以只读方式打开;child 进程使用相同路径将 fifo1 以只读方式打开,将 fifo2 以只写方式打开。parent 进程与 child 打开 fifo 的顺序一致而读写的模式刚好相反就能够唤醒阻塞的进程。

如果交换 parent 进程打开 fifo1 与 fifo2 的顺序,就会触发这两个进程死锁,parent 与 child 进程都阻塞在打开不同的 fifo 上,永远不会被唤醒。

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

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

相关文章

基于yolov5轻量级的学生上课姿势检测识别分析系统

在我之前的博文中已经做过关于人体姿势识别人体姿态检测的博文,都是比较早期的技术模型了,随机技术的迭代更新,很多更加出色的模型陆续出现,这里基于一些比较好用的轻量级模型开发的姿态检测模型。 原始博文如下: 《…

HTML+CSS+JS家乡主题网页设计 学生网页设计作品 dreamweaver作业静态HTML网页设计模板 旅游景点网页作业制作

家乡旅游景点网页作业制作 网页代码运用了DIV盒子的使用方法,如盒子的嵌套、浮动、margin、border、background等属性的使用,外部大盒子设定居中,内部左中右布局,下方横向浮动排列,大学学习的前端知识点和布局方式都有…

BERT模型的详细介绍

Bert模型的基本原理是什么? BERT 来自 Google 的论文Pre-training of Deep Bidirectional Transformers for Language Understanding,BERT 是“Bidirectional Encoder Representations from Transformers”的首字母缩写,整体是一个自编码语言模型&…

全文检索-Elasticsearch-入门

文章目录前言一、基本概念1.1 Index(索引)1.2 Type(类型)1.3 Document(文档)1.4 ES与关系型数据库对比1.5 倒排索引二、初步检索2.1 _cat2.2 索引一个文档(保存)2.3 查询文档前言 全…

[静态时序分析简明教程(八)]虚假路径

静态时序分析简明教程-虚假路径一、写在前面1.1 快速导航链接二、虚假路径2.1 set_false_path2.2 -from -to -through2.3 上升/下降沿 约束2.4 建立/保持 约束2.5 虚假路径示例三、总结一、写在前面 一个数字芯片工程师的核心竞争力是什么?不同的工程师可能给出不同…

数据结构 | 堆的向上调整和向下调整算法【奇妙的堆排序】

堆一、堆的概念及结构二、向上调整算法⭐⭐1、算法思路分析【孙子的谋权篡位之旅👑】2、代码详情解说三、向下调整算法⭐⭐⭐⭐⭐1、算法图解分析【高处不胜寒🆒趁早做打算】2、代码考究精析四、堆的数据结构各接口算法实现结构体的定义及声明1、堆的初始…

Windows-》CMD命令

CMD命令【1】Windows-》CMD命令1.mstsc:打开远程桌面连接。2.services.msc:打开本地服务设置。3.notepad:打开记事本。4.control:打开控制面板。5.regedit:打开注册列表编辑器。6.compmgmt.msc---设备管理器。&#xf…

[附源码]计算机毕业设计springboot医疗器械公司公告管理系统

项目运行 环境配置: Jdk1.8 Tomcat7.0 Mysql HBuilderX(Webstorm也行) Eclispe(IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持)。 项目技术: SSM mybatis Maven Vue 等等组成,B/S模式 M…

计算机图形学中的曲线问题——贝塞尔曲线的绘制

贝塞尔曲线的绘制 由于 CSDN 的博客修改字数的限制,我们不得不将这一部分放到一个新的博客中。原文详见: GGN_2015 计算机图形学中的曲线问题 贝塞尔曲线的几何作图法 在上面介绍儿时的回忆中,我们介绍了对于抛物线绘制的一种方法。如下图所…

Egg 1. 快速开始 Quick Start 1.3 一步步 Step by Step 1.3.5 创建服务

Egg Egg 本文仅用于学习记录,不存在任何商业用途,如侵删 文章目录Egg1. 快速开始 Quick Start1.3 一步步 Step by Step1.3.5 创建服务Hacker News API1. 快速开始 Quick Start 1.3 一步步 Step by Step 1.3.5 创建服务 在实际开发中,控制器…

[附源码]计算机毕业设计学生综合数据分析系统Springboot程序

项目运行 环境配置: Jdk1.8 Tomcat7.0 Mysql HBuilderX(Webstorm也行) Eclispe(IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持)。 项目技术: SSM mybatis Maven Vue 等等组成,B/S模式 M…

Linux根文件系统说明和常用命令

Linux根文件系统说明【1】Linux内核由哪几个子系统组成?【2】Linux下软连接和硬链接的区别?【3】sync【4】shutdown –h now【5】shutdown –h 20:25【6】shutdown –r now【7】shutdown –r 10【8】reboot【9】halt【10】Linux根文件系统说明【11】此主…

机器学习 分类、回归、聚类、特征工程区别

一、分类和回归的区别 简单理解分类和回归的区别在于输出变量的类型不同。 定量输出称为回归,或者说是连续变量预测;定性输出称为分类,或者说是离散变量预测。 举个例子: 预测明天的气温是多少度,这是一个回归任务&…

AD入门学习—元件库(原理图库)的创建

目录 1.1 电阻类、电容类、电感类元件创建 1.2 LED灯、按键类元件创建 1.3 IC芯片类元件创建 学习目录 创建一个PCB工程,原理图库,原理图。 1.1 电阻类、电容类、电感类元件创建 左侧的菜单栏不见了:视图,panels,projects。 按…

JavaWeb(三)

前言:JavaWeb的三大组件Servlet,Filter,Listener。是Java EE Web服务规定的服务器动态组件,由开发者编写,由web容器创建,加载顺序为Listener->Filter->Servlet。 当来了一个application请求 呢么web三…

一文读懂Java中的String类之助力Java进阶之路

🔝🔝🔝🔝🔝🔝🔝🔝🔝🔝🔝🔝🔝🔝🔝 🥇博主昵称:Jovy. 🍟博客主页…

以太网 TCP协议(三次握手、四次挥手)

2.7.0 以太网 TCP协议(三次握手、四次挥手) 传输控制协议(TCP,Transmission Control Protocol)是一种面向连接的、可靠的传输层通信协议。 面向连接:指使用TCP协议的程序在使用的时候,需要预先进…

PLC远程维护解决方案

一、方案背景:随着工业4.0大力推动智能制造,完善的售后维护和技术服务逐步被各设备厂商所重视,服务将成为企业新的利益增长点,传统的售后服务已经远远不能满足客户需求,甚至给公司带来一定的经营成本和维护成本的压力&…

volatile与Java内存模型

volatile与Java内存模型 被volatile修改的变量有2大特点 特点 可以保证 1.可见性 2.有序性 还是那张图,volatile只能保证可见性和有序性 那为什么volatile可以实现这些功能呢 volatile的内存语义 当写一个volatile变量时,JMM会把该线程对应的本地…

# 智慧社区管理系统-基础信息管理-05车位管理

一后端 1:entuty package com.woniu.community.entity;import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor;Data AllArgsConstructor NoArgsConstructor public class Parking {private int id;private String numbers;//车位号private…