管道-阻塞与非阻塞

2023-11-19

非阻塞的管道和FIFO

  • 管道和FIFO都可以设置非阻塞。它们两者都可以在打开之后通过fcntl函数设置O_NONBLOCK标志来enable。一般而言,我们都是先使用F_GETFL来获取当前文件状态标志,将它与O_NONBLOCK按位或之后,再通过F_SETFL来设置新的文件状态标志。
int flags = fcntl(fd, F_GETFL, 0);
if (flags < 0) {
    ERR_EXIT("F_GETFL ERROR");
}
flags |= O_NONBLOCK;
if (fcntl(fd, F_SETFL, flags) < 0) {
    ERR_EXIT("F_SETFL ERROR");
}
  • 另外,FIFO还可以通过open函数设置O_NONBLOCK标志,由于管道不能使用open来打开,所以管道不能使用此方法设置非阻塞。
readfd = open(pathname, O_RDONLY|O_NONBLOCK, 0);

阻塞与非阻塞管道和FIFO属性

前面也有讲到,如果进程在打开FIFO读之前没有其他进程已打开该FIFO的写,那么该进程会一直阻塞到有其他进程打开FIFO的写为止。同样,如果进程在打开FIFO**写之前没有其他进程已打开该FIFO的读**,那么该进程会一直阻塞到有其他进程打开FIFO的读为止。

管道和FIFO在创建和打开的语法上有所不同,但一旦打开之后,它们的使用语法基本一致。而对于阻塞的管道和FIFO而言,使用read和write也很容易引起阻塞。
而当管道和FIFO设置成非阻塞之后,它们的读写行为就会有些改变。

非阻塞open FIFO

使用open来打开非阻塞的FIFO,如果该FIFO当前没有打开读,那么会返回ENXIO错误。

#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <cstdlib>
#include <cstdio> //perror
#include <iostream>

#define ERR_EIXT(msg) \
        do { \
            perror(msg); \
            exit(EXIT_FAILURE); \
        }while(0)

static const char FIFOPATH[] = "/tmp/fifo";
static const mode_t MODE = (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);

int main(int argc, char *argv[])
{
    if ((mkfifo(FIFOPATH, MODE) < 0) && (errno != EEXIST))
        ERR_EIXT("FIFO ERROR");

    int writefd = open(FIFOPATH, O_WRONLY | O_NONBLOCK, 0);
    if (writefd < 0)
        if (errno == ENXIO) 
            ERR_EIXT("NO READ");
        else 
            ERR_EIXT("OPEN FIFO FOR WRITE ERROR");

    std::cout << "OPEN FIFO FOR WRITE SUCCESSFULLY" << std::endl;
    close(writefd);
    unlink(FIFOPATH);
    exit(EXIT_SUCCESS);
}

运行结果:
这里写图片描述
上面的程序是使用open非阻塞的打开FIFO写,而在这之前没有打开任何该FIFO的读,所以该程序会接收到ENXIO。
如果我们再运行上面的程序之前,先运行打开与FIFOPATH关联的FIFO读(如代码),并且保持打开,那么在运行上面的程序就不会接收到ENXIO,而是成功打开FIFO写。

int main(int argc, char *argv[])
{
    if ((mkfifo(FIFOPATH, MODE) < 0) && (errno != EEXIST))
        ERR_EIXT("FIFO ERROR");

    int readfd = open(FIFOPATH, O_RDONLY | O_NONBLOCK, 0);
    if (readfd < 0)
        ERR_EIXT("OPEN FIFO FOR READ ERROR");

    std::cout << "OPEN FIFO FOR READ SUCCESSFULLY" << std::endl;
    sleep(5); //睡眠5秒,以免读在写之前关闭
    close(readfd);
    unlink(FIFOPATH);
    exit(EXIT_SUCCESS);
}

运行上面程序结果:
这里写图片描述
此时在运行打开FIFO写程序,结果如下:
这里写图片描述
FIFO写成功打开。

FIFO非阻塞write属性

PIPE_BUF

在讲这个write属性之前,需要先了解一下PIPE_BUF这常数。它表示管道或者FIFO写操作原子性的最大字节数。也就是说,向管道或者FIFO写数据时,只有写的字节数小于等于PIPE_BUF时,这个写操作才能保证原子性(当两个进程想同一个管道或者FIFO写数据时,两个进程所写数据不会交错,必须是先写一个进程数据,写完后再写另外一个进程的数据)。当进程写的字节数大于PIPE_BUF,那么写的数据就不能保证是原子性的,进程间写的数据可能是交错的。linux下默认是4096 bytes通过ulimit -p查看PIPE_BUF的的大小(其单位是512 bytes):
这里写图片描述
也可以通过以下代码输出:

#inlcude <climits>
std::cout << PIPE_BUF << std::endl;

这里写图片描述

还有一点需要注意的是,PIPE_BUF的值它不是管道或者FIFO的缓冲区大小,缓冲区的大小一般比PIPE_BUF大的多。

int main(int argc, char *argv[])
{
    if ((mkfifo(FIFOPATH, MODE) < 0) && (errno != EEXIST))
        ERR_EIXT("FIFO ERROR");

    int writefd = open(FIFOPATH, O_WRONLY | O_NONBLOCK, 0);
    if (writefd < 0)
        if (errno == ENXIO) 
            ERR_EIXT("NO READ");
        else 
            ERR_EIXT("OPEN FIFO FOR WRITE ERROR");

    std::cout << "OPEN FIFO FOR WRITE SUCCESSFULLY" << std::endl;
    for (int i = 1; ; ++i) {
        if (write(writefd, "1", 1) < 0) {
            if (errno == EAGAIN) 
                ERR_EIXT("FIFO BUFFER IS FULL");
            else
                ERR_EIXT("FIFO WRITE ERROR");
        }
        std::cout << i << " bytes writen" << std::endl; 
    }
    close(writefd);
    exit(EXIT_SUCCESS);
}

上面的程序主要是用来确定管道或者FIFO的缓冲区大小的。首先打开FIFO的写,然后一次写一个字节,知道满为止。当FIFO设置为nonblock时,如果其缓冲区已满,再向其写数据时会返回一个EAGAIN错误(当FIFO为阻塞情况是,向满的FIFO写数据会阻塞)。其输出结果为:
这里写图片描述
可以看到当缓冲区已经写入65536 bytes数据时,再写就会产生EAGAIN错误,从而确定缓冲区大小为65536 bytes。

write属性

使用write向管道或者FIFO写数据其结果是成功还是阻塞还是出错需要取决于该管道或者FIFO是否设置了nonblock,还取决于写入数据的字节数(假设是n)是否大于PIPE_BUF,具体讨论如下:

  • 阻塞且n<=PIPE_BUF: 这n个字节的数据的写入是原子写入的。如果当前没有足够的空间写入n字节,那么write会阻塞到有足够空间写入位置。
  • 非阻塞且n<=PIPE_BUF: 如果缓冲区中还有至少n个字节的空间,那么该n个字节会马上写入到管道或者FIFO中;否则会返回错误EAGAIN。
  • 阻塞且n>PIPE_BUF:该n个字节会交错的写入管道或者FIFO中,并且write会一直阻塞到这n个字节全部写入管道或FIFO中。
  • 非阻塞且n>PIPE_BUF:如果缓冲区满的话,会write会出错,并且errno设置为EAGAIN。如果缓冲区不满,那么write就会写入部分的数据,所以调用者需要检查write返回的实际写入字节数,并且这些数据可能会与其他进程的数据交错写入。

参考:
《UNP 卷2》
http://man7.org/linux/man-pages/man7/pipe.7.html

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

管道-阻塞与非阻塞 的相关文章

  • faster RCNN 的细节理解

    1 anchors不同的大小但是采用了ROI pooling一样的策略 都映射到3 3的卷积核上 最后通过1 1的卷积核 相当与全连接分成了18类 9个anchors的话 2 分类的时候 reshape 两次 第一次为了softmax分类
  • Go的并发的退出

    有时候我们需要通知goroutine停止它正在干的事情 比如一个正在执行计算的web服务 然而它的客户端已经断开了和服务端的连接 Go语言并没有提供在一个goroutine中终止另一个goroutine的方法 由于这样会导致goroutin
  • #452. 序列操作

    序列操作 题目 Daimayuan Online Judge 问题描述 思路 首先想的是第二次操作的y可以将前面所以操作进行抵消 只需要第二次操作的最大值即可 但是发现 对于第一个操作 它是单点修改 每修改一次对于第二次操作都是有影响的 导
  • 最简单的区块链实现,不到50行代码!(一)

    什么是区块链 Blockchain 一个电子记账本 以比特币和其他密码加密货币进行的交易公开地 按照日期顺序记录其中 总的来说 它是一个公开的数据库 新的数据存储在一个称为区块的容器中 并且附加到一个 不可变 的链条 即区块链 上 链条上还
  • 《基于Python的大数据分析基础及实战》第二章

    第二章 个人信息 kwd info kwd info kwd info ipynb等文件下载 https wwm lanzouf com iklXf023qeef 对数据进行分析首先得对数据进行处理 本章主要介绍P thon在数据处理方面的
  • 5.2 主机扫描:主机探测

    目录 一 预备知识 主机扫描方法 二 实验环境 三 实验步骤 一 预备知识 主机扫描方法 主机扫描 Host Scan 是指通过对目标网络 一般为一个或多个IP网段 中主机IP地址的扫描 以确定目标网络中有哪些主机处于运行状态 主机扫描的实
  • Ubuntu下,Java中利用JNI调用codeblocks c++生成的动态库的使用步骤

    1 打开新立得包管理器 搜索JDK 选择openjdk 6 jdk安装 2 打开Ubuntu软件中心 搜索Eclipse 选择Eclipse集成开发环境 安装 3 打开Eclipse File gt New gt Java Project
  • 剑指Offer 22. 链表中倒数第k个节点(Easy)/ 19. 删除链表的倒数第 N 个结点(Medium)/ ListNode调用!!!

    LeetCode 19 删除链表的倒数第 N 个结点 Medium 题目链接 题解 链表中倒数第 k 个节点 双指针 清晰图解 思路 代码 Definition for singly linked list class ListNode d
  • Unity 音频卡顿 静帧 等待等问题的解决方案

    是否遇到过在Unity中加载音频文件卡顿 也就是画面卡住 的现象 特别是加载外部音频文件时 虽然时间很短 但这终归不是什么好现象 尤其是打游戏的话 影响很大 但是一些有牌面的Boss也不能不配音乐 当然也可以通过其它方式解决 比如特定条件统
  • 《人工智能导论》 第7章 机器学习

    机器学习 机器学习的基本概念 机器学习 Machine learning 使计算机能模拟人的学习行为 自动地通过学习来获取知识和技能 不断改善性能 实现自我完善 机器学习主要研究以下三个问题 学习机理 人类获取知识 技能和抽象概念的天赋能力
  • win7安装PS2019CC启动时报d3dcompiler_47.dll的问题解决

    原先安装的PS2017太卡顿了 有些功能用的也不习惯 卸载准备重装一下PS2019 在安装时发现报错 具体如下图所示 因此在网上下载了D3DCOMPILER 47 dll程序 放到了指定文件夹还是不行 百度参考这个解决了 WIN7缺少D3D
  • 全局光照算法:reflective shadow maps

    1 技术理解 RSM的全称是reflective shadow maps 受到Instant Radiosity这个离线技术的启发 其思想和ShadowMap的思想近似 在正式介绍和了解这个技术之前 我需要确定RSM用处何在 我想 RTR4
  • Docker-swarm 介绍,集群,多服务部署实战

    一 什么是Docker Swarm Swarm是Docker公司推出的用来管理docker集群的平台 几乎全部用GO语言来完成的开发的 代码开源在https github com docker swarm 它是将一群Docker宿主机变成一
  • flutter doctor不识别已安装的android studio及授权报错

    不识别解决 flutter config android studio dir C Program Files Android Android Studio 授权报错解决 Open Android Studio Tools gt SDK M

随机推荐

  • Unity使用Remote直接在手机上调试游戏

    Unity Remote是一个调试小工具 方便在手机上直接显示运行效果 并不需要Unity打包 只要在手机上安装UnityRemote APK程序 然后Unity上点击运行就可以了 因为我一般很少在手机上测试 本来都想不起来这个Remote
  • 用Python帮忙找指定小说最新更新且网速最快的网站

    一 引言 这个五一假期自驾回老家乡下 家里没装宽带 用手机热点方式访问网络 这次回去感觉4G信号没有以前好 通过百度查找小说最新更新并打开小说网站很慢 有时要打开好多个网页才能找到可以正常打开的最新更新 为了躲懒 老猿决定利用Python爬
  • 基于rCore的试点班lab1 实现一个系统调用

    实现一个属于你的系统调用 并写一个用户进程来调用该系统调用 该系统调用的功能为 输出你的名字 拼音即可 和学号 相关知识 系统调用 是操作系统提供给应用程序使用的接口 可以理解为一种可供应用程序调用的特殊函数 应用程序可以发出系统调用请求来
  • Python 基础(一):入门必备知识

    目录 1 标识符 2 关键字 3 引号 4 编码 5 输入输出 6 缩进 7 多行 8 注释 9 数据类型 10 运算符 10 1 常用运算符 10 2 运算符优先级 基础 进阶 爬虫 自动化 数据分析 编写小游戏 趣味 Python 文档
  • ajax xhr参数无法接收到,AJAX XHR-Call会创建无效的参数异常

    所以我尝试从异步任务 在此处未显示 因为它不相关 我认为它工作 中的值通过Jquery xhr请求放入进度条中 服务器端的方法并不相关 因为它的工作方式和jquery xhr call都可以 仅限第一次 AJAX XHR Call会创建无效
  • Rasa中文聊天机器人开发指南(3):Core篇

    文章目录 1 对话管理1 1 多轮对话1 2 对话管理 2 Rasa Core 2 1 Stories 2 2 Domain 2 3 Responses 2 4 Actions 2 5 Policies 2 6 Slots 2 6 1 Sl
  • nvm-use成功,但是实际并没有切换到对应node版本

    nvm use命令行运行成功 但是nvm list显示并没有成功 解决方案 情况描述 说明 使用nvm安装完node版本成功之后 nvm list显示并没有切换成功 node v使用的不是use版本 原因 因为在安装nvm之前 独自安装了一
  • rtsp协议c语言,RTSP协议

    RTSP简介 RTSP Real Time Streaming Protocol 是由Real Network和Netscape共同提出的如何有效地在IP网络上传输流媒体数据的应用层协议 RTSP对流媒体提供了诸如暂停 快进等控制 而它本身
  • 浏览器无法加载本地文件

    问题描述 在Visual Studio Code 编写HTML文件时需要将 csv文件内容在浏览器控制台窗口输出 浏览器控制一直报错 如下图所示 原因 跨域资源共享问题 本地文件是放在file 这样的系统下 而非网络资源比如http 下 造
  • Matlab实现PID控制仿真(附上30个完整仿真源码+数据)

    本文介绍了如何使用Matlab实现PID控制器的仿真 首先 我们将简要介绍PID控制器的原理和控制算法 然后 我们将使用Matlab编写一个简单的PID控制器 并使用仿真环境来验证其性能 最后 我们将通过调整PID控制器的参数来优化控制系统
  • LVGL笔记7--lv_label标签控件

    LVGL笔记7 lv label标签控件 lv label标签控件是LVGL中使用最频繁的控件 主要是用来显示文本信息的 可在程序运行中动态修改文本内容 支持换行显示 图标字体 部分文本重绘色 长文本显示 6种显示模式等功能 lv labe
  • 八段数码管动态显示(输入数据为BCD编码)

    八段数码管动态显示 输入数据为BCD编码 一 数码管概述 图1 八段共阴数码管内部等效原理图 图2 八段共阳数码管内部等效原理图 上面两图分别是对应八段共阴 共阳的数码管内部等效图 共阴是将八个LED数码管的阴极连接在一起接低 阳极segm
  • Docker系列01—容器的发展历程---Docker的生态圈

    Docker 和容器技术的发展可谓是日新月异 本文试图以全局的视角来梳理一下 docker 目前的生态圈 既然是概览 所以不会涉及具体的技术细节 Docker 自从发布以来发生了很多的变化 并且有些方面的变化还非常大 对于技术爱好者来说 我
  • 【模电】0017 开关电源的原理及分析

    开关电源一般简称为DCDC 比我们前两节分析的线性电源复杂一些 它与线性电源最大的不同在于其调整管的工作状态 开关电源中的调整管工作在开关状态 即只工作在饱和区和截止区 1 典型开关电源的原理 一个典型的降压型开关电源原理如下图 首先 我们
  • 前端代理配置

    dev env require dev env port process env PORT 8080 autoOpenBrowser true assetsSubDirectory static assetsPublicPath proxy
  • 海量影像图元合并可以考虑用openmp

    影像合并图元耗时较长 又是重复的功能 可以考虑并行openmp
  • Linux测试比较语句

    测试和比较语句用于if或脚本命令中 if condition then commands else if conditon then commands fi 或 condition command 如果condition为真则执行comma
  • [ 数据结构-C语言 ] 二叉树--初阶 大总结~~

    今天要和大家一起步入一个新的数据结构 二叉树 在学习了解二叉树之前我们先来了解什么是树 以下是本篇的主要内容及目录 目录 1 树的概念及其结构 1 1树的概念 1 2树的相关概念 重点 1 3树的表示 2 二叉树概念及结构 2 1概念 2
  • 优化算法 - BGD、MBGD、SGD - 梯度下降

    优化算法 BGD MBGD SGD 梯度下降 BGD SGD MBGD BGD BGD Batch Gradient Descent 批量梯度下降 损失函数 L X
  • 管道-阻塞与非阻塞

    非阻塞的管道和FIFO 管道和FIFO都可以设置非阻塞 它们两者都可以在打开之后通过fcntl函数设置O NONBLOCK标志来enable 一般而言 我们都是先使用F GETFL来获取当前文件状态标志 将它与O NONBLOCK按位或之后