当前位置: 首页 > news >正文

linux系统编程(七)管道和FIFO

1、管道

使用系统调用pipe可以创建一个新管道:

#include <unistd.h>
int pipe(int filedes[2]);

成功的pipe调用会在数组filedes中返回两个打开的文件描述符,读取端为filedes[0],写入端为filedes[1]。我们可以使用read/write系统调用在管道上执行IO。管道上的read调用会读取的数据量为请求的字节数与管道中当前存在的字计数两者之间较小的那个。

在单个进程中管道的用途不多,一般来说是使用管道让两个进程进行通信。为了让两个进程通过管道进行连接,调用完pipe调用之后可以调用fork。fork之后一般会让其中一个进程立即关闭管道的写入段文件描述符,另一端关闭读取的文件描述符。

关闭pipe需要先关闭其写入端,这样读取端就可以读取到eos,这时候读取端就可以正常关闭了。

以下是一个简单示例:

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/wait.h>#define MAX_BUF 1024int main(int argc, char **argv) {int fds[2];size_t numRead = -1;size_t numWritten = -1;char buf[MAX_BUF];int ret = pipe(fds);if(ret != 0) {printf("create pipe fail, err:%s", strerror(errno));exit(1);}switch(fork()) {case 0:{close(fds[0]);while((numRead = read(STDIN_FILENO, buf, MAX_BUF-1)) > 0) {buf[numRead] = '\0';printf("child pid:%d write %s", getpid(), buf);write(fds[1], buf, numRead);}printf("child pid:%d close pipe of write\n", getpid());close(fds[1]);exit(0);}default:{close(fds[1]);while((numRead = read(fds[0], buf, MAX_BUF - 1)) > 0) {buf[numRead] = '\0';printf("parent pid:%d read %s", getpid(), buf);}printf("parent pid:%d close pipe of read\n", getpid());close(fds[0]);pid_t pid = wait(NULL);printf("parent recyle child:%d OK\n", pid);exit(0);}}return 0;
}/*
hello world
child pid:5353 write hello world
parent pid:5352 read hello world
child pid:5353 close pipe of write
parent pid:5352 close pipe of read
parent recyle child:5353 OK
*/

管道还可以用来进程同步,做法是多个进程继承同一管道,其中一个进程关闭写,其他所有进程关闭读,当其他进程做完工作后关闭管道的写端口,剩余唯一一个读端口就可以关闭了,这时候就实现了进程同步。

接下来是一个使用管道连接ls和wc的示例:

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/wait.h>#define MAX_BUF 1024int main(int argc, char **argv) {int fds[2];size_t numRead = -1;size_t numWritten = -1;char buf[MAX_BUF];int ret = pipe(fds);if(ret != 0) {printf("create pipe fail, err:%s", strerror(errno));exit(1);}switch(fork()) {case 0:{close(fds[0]);dup2(fds[1], STDOUT_FILENO);	// redirect pipe write to stdoutclose(fds[1]);execlp("ls", "ls", (char *)NULL);       // ls会把结果输出到标准输出,实际上是输出到管道的写端了exit(0);}default:break;}switch(fork()) {case 0:{close(fds[1]);dup2(fds[0], STDIN_FILENO);close(fds[0]);execlp("wc", "wc", "-l", (char *)NULL);         // wc 是从标准输入读数据,所以实际上是从管道的读端读取的数据exit(0);}default:break;}close(fds[0]);close(fds[1]);wait(NULL);wait(NULL);return 0;
}

管道常见的用途是执行shell命令并读取输出,popen简化了这个任务:

#include <stdio.h>FILE *popen(const char *command, const char *mode);
int pclose(FILE *stream);

对于管道的使用我们要注意,如果管道的读取端都被关闭,那么写入端写入数据的时候会发送SIGPIPE信号,默认会中止进程。如果堆SIGPIPE信号做特殊处理,那么write系统会返回-1,表示写入操作出现错误,errno会被设定为EPIPE。

2、FIFO

FIFO和管道类似,它们之间最大的区别在于FIFO在文件系统中拥有一个名称,并且打开方式和打开一个普通文件是一样的,这样就可以将FIFO用于非相关进程之间的通信。

使用mkfifo命令可以在shell中创建一个FIFO:

$ mkfifo [-m mode] pathname
// prw-r--r-- 1 may may     0 Dec 26 07:32 myfifo

mkfifo函数可以创建一个名为pathname的全新FIFO

#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode);

打开FIFO时应该一个进程设定O_RDONLY,另一个设置O_WRONLY。要避免设置O_RDWR,设置之后另一个写入端关闭后,读取的进程会无法看到eos。

接下来我们基于FIFO来实现一个简单的迭代式服务器。

首先有几个问题:
FIFO/PIPE中的数据是字节流,消息之间没有边界,这意味这多条消息被送到一个进程中时,发送者和接收者需要约定一种规则来分隔消息。有几种做法:

  1. 每条消息可以使用换行符之类的分隔字符结束;
  2. 每条消息包含一个固定大小的头,头中包含一个表示消息长度的字段,该字段表示消息中剩余部分的长度;
  3. 使用固定长度的消息,让服务器总是读取这个大小固定的消息。

接下来的示例将使用第三种。

什么是迭代式服务器?迭代表示服务器会在读取并处理完当前客户端之后才去处理下一个客户端。另一种设计方法是并发式服务器,使用单独的子线程/进程来处理客户端请求。

// fifo_demo.h
#ifndef FIFO_DEMO_H
#define FIFO_DEMO_H#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>#define SERVER_FIFO "./seqnum_sv"
#define CLIENT_FIFO_TEMPLATE "./seqnum_cl.%d"
#define CLIENT_FIFO_NAME_LEN (sizeof(CLIENT_FIFO_TEMPLATE) + 20)struct request {pid_t pid;int seqLen;
};struct response {int seqNum;
};#endif// server.c
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include "03_fifo_demo.h"
#include <signal.h>int main(int argc, char **argv) {int ret = mkfifo(SERVER_FIFO, S_IRUSR | S_IWUSR | S_IWGRP);if(ret != 0 && errno != EEXIST) {printf("server create fifo fail, err:%s\n", strerror(errno));exit(1);}int serverFd = open(SERVER_FIFO, O_RDONLY);int seqNum = 0;if(serverFd == -1) {printf("server open fifo fail, err:%s\n", strerror(errno));exit(1);} else {printf("server open server fifo %s success\n", SERVER_FIFO);}int dummyFd = open(SERVER_FIFO, O_WRONLY);signal(SIGPIPE, SIG_IGN);struct request req;struct response resp;char clientFIFO[CLIENT_FIFO_NAME_LEN];int clientFd;while(1) {if(read(serverFd, &req, sizeof(struct request)) != sizeof(struct request)) {continue;}snprintf(clientFIFO, CLIENT_FIFO_NAME_LEN, CLIENT_FIFO_TEMPLATE, req.pid);printf("server get request from client:%s, seqLen:%d\n", clientFIFO, req. seqLen);clientFd = open(clientFIFO, O_WRONLY);printf("server open client fifo %s success\n", clientFIFO);resp.seqNum = seqNum;if(write(clientFd, &resp, sizeof(struct response)) != sizeof(struct response)) {printf("error write to client fifo: %s\n", clientFIFO);}close(clientFd);seqNum++;}return 0;
}// client.c#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include "03_fifo_demo.h"int main(int argc, char **argv) {int serverFd, clientFd;struct request req;struct response resp;char clientFIFO[CLIENT_FIFO_NAME_LEN];snprintf(clientFIFO, CLIENT_FIFO_NAME_LEN, CLIENT_FIFO_TEMPLATE, getpid());mkfifo(clientFIFO, S_IRUSR | S_IWUSR | S_IWGRP);req.pid = getpid();req.seqLen = atoi(argv[1]);serverFd = open(SERVER_FIFO, O_WRONLY);printf("client:%d open server OK!\n", getpid());write(serverFd, &req, sizeof(struct request));clientFd = open(clientFIFO, O_RDONLY);printf("client:%d open client fifo:%s OK!\n", getpid(), clientFIFO);read(clientFd, &resp, sizeof(struct response));printf("get response from server, seqNum:%d\n", resp.seqNum);close(clientFd);unlink(clientFIFO);return 0;
}

关于FIFO还有一些注意事项,当进程打开FIFO的一端时,如果FIFO的另一端还没有被打开,那么该进程会被阻塞。

以上面的代码为例,服务打开之后会有阻塞的情况出现,等到第一个client进行连接之后open才能结束阻塞。这不是我们预期的行为,我们可以在open时添加O_NONBLOCK来标记非阻塞打开。

添加O_NONBLOCK之后,会有以下情况:

  • 如果FIFO是为了读取,当前FIFO的写入端已经被打开,那么open会立即成功。
  • 如果FIFO是为了写入,FIFO的读取端还没有打开,那么open会调用失败,errno被设置为ENXIO

O_NONBLOCK还会影响到read和write的操作:当管道中没有数据时,read将会返回EAGAIN。当写入失败时,write会返回EAGAIN。

相关文章:

linux系统编程(七)管道和FIFO

1、管道 使用系统调用pipe可以创建一个新管道&#xff1a; #include <unistd.h> int pipe(int filedes[2]);成功的pipe调用会在数组filedes中返回两个打开的文件描述符&#xff0c;读取端为filedes[0]&#xff0c;写入端为filedes[1]。我们可以使用read/write系统调用在…...

【vLLM大模型TPS测试三部曲】

安装 pip install vllm模型自行下载 例如: https://modelscope.cn/models/jackle/Qwen2.5-Coder-32B-GPTQ-Int4/ 部署测试 export VLLM_MODELQwen2.5-Coder-32B-GPTQ-Int4 # 启动 python3 -m vllm.entrypoints.openai.api_server --model $VLLM_MODEL --deviceauto --enf…...

Elasticsearch:使用 Ollama 和 Go 开发 RAG 应用程序

作者&#xff1a;来自 Elastic Gustavo Llermaly 使用 Ollama 通过 Go 创建 RAG 应用程序来利用本地模型。 关于各种开放模型&#xff0c;有很多话要说。其中一些被称为 Mixtral 系列&#xff0c;各种规模都有&#xff0c;而一种可能不太为人所知的是 openbiollm&#xff0c;这…...

Windows平台ROBOT安装

Windows环境下ROBOT的安装,按照下文进行部署ROBOT的前提是你的python已安装并且环境变量已设置好. 一、安装setuptools 1、下载后安装 https://pypi.python.org/pypi/setuptools/ 下载你需要的包 setuptools-75.6.0.tar.gz 解压下载的包在命令行中进入该包,敲击如下命令后…...

【动态规划篇】穿越算法迷雾:约瑟夫环问题的奇幻密码

欢迎拜访&#xff1a;羑悻的小杀马特.-CSDN博客 本篇主题&#xff1a;带你众人皆知的约瑟夫环问题 制作日期&#xff1a;2024.12.29 隶属专栏&#xff1a;C/C题海汇总 目录 引言&#xff1a; 一约瑟夫环问题介绍&#xff1a; 11问题介绍&#xff1a; 1.2起源与历史背景&…...

代码随想录算法训练营第51期第32天 | 理论基础、509. 斐波那契数、70. 爬楼梯、746. 使用最小花费爬楼梯

理论基础 动态规划&#xff1a;dp&#xff0c;每一个状态都是由上个状态推导出来的&#xff0c;因为我是先写完三道题再看理论的&#xff0c;所以有点感概&#xff1b; 确定dp数组&#xff08;dp table&#xff09;以及下标的含义确定递推公式dp数组如何初始化确定遍历顺序举…...

爱思唯尔word模板

爱思唯尔word模板 有时候并不一定非得latex https://download.csdn.net/download/qq_38998213/90199214 参考文献书签链接...

每日一题 354. 俄罗斯套娃信封问题

354. 俄罗斯套娃信封问题 需要对信封排序 ,重点是再宽度相同时&#xff0c;逐步减少其高度 class Solution { public:int maxEnvelopes(vector<vector<int>>& envelopes) {sort(envelopes.begin(),envelopes.end(),[](const vector<int>&a,const v…...

ASP.net网站的注册、登录和密码修改的操作详解

一、进入注册、登录和密码修改操作详解 ASP.net网站为用户提供不同权限状态下的操作界面。根据用户登录状态&#xff0c;页面会显示不同的选项。 已登录用户的操作 图1 登录后操作界面 当用户已登录系统时&#xff0c;会显示以下内容和功能&#xff1a; 1. 欢迎信息 页面顶部…...

2024.12.29(进程线程实现并发服务器)

作业 多进程多线程并发服务器实现一遍提交。 服务器 #include <myhead.h> #define PORT 12345 #define IP "192.168.124.123"void *fun(void *fd) {int newfd *(int *)fd;char buff[1024];while(1){int res recv(newfd,buff,sizeof(buff),0);if(res 0){p…...

如何在 Ubuntu 上安装 PyTorch

简介 PyTorch 因其易用性、动态计算图和高效性而日益流行&#xff0c;成为实现深度学习模型的首选。如果你想探索这个工具并学习如何在 Ubuntu 上安装 PyTorch&#xff0c;本指南将对你有所帮助&#xff01; 在本教程中&#xff0c;我们将引导你完成在 Ubuntu 系统上使用 Pip…...

8-Gin 中间件 --[Gin 框架入门精讲与实战案例] 【文末有测试代码】

路由中间件 Gin 是一个用 Go (Golang) 编写的 HTTP web 框架。它以性能好、中间件支持灵活著称&#xff0c;非常适合用来构建微服务或 RESTful API 服务。下面我将提供三个使用 Gin 的路由中间件的完整示例。 示例 1: 简单的日志记录中间件 这个中间件会在每个请求处理前后打…...

【潜意识Java】深入详细理解分析Java中的toString()方法重写完整笔记总结,超级详细。

目录 一、toString() 方法是啥&#xff1f; &#xff08;一&#xff09;默认的 toString() 方法 &#xff08;二&#xff09;toString() 方法的作用 二、为啥要重写 toString() 方法&#xff1f; &#xff08;一&#xff09;提高代码的可读性 &#xff08;二&#xff09;…...

【论文笔记】Contrastive Learning for Sign Language Recognition and Translation

&#x1f34e;个人主页&#xff1a;小嗷犬的个人主页 &#x1f34a;个人网站&#xff1a;小嗷犬的技术小站 &#x1f96d;个人信条&#xff1a;为天地立心&#xff0c;为生民立命&#xff0c;为往圣继绝学&#xff0c;为万世开太平。 基本信息 标题: Contrastive Learning for…...

Gitlab17.7+Jenkins2.4.91实现Fastapi/Django项目持续发布版本详细操作(亲测可用)

一、gitlab设置&#xff1a; 1、进入gitlab选择主页在左侧菜单的下面点击管理员按钮。 2、选择左侧菜单的设置&#xff0c;选择网络&#xff0c;在右侧选择出站请求后选择允许来自webhooks和集成对本地网络的请求 3、webhook设置 进入你自己的项目选择左侧菜单的设置&#xff…...

一起来看--红黑树

【欢迎关注编码小哥&#xff0c;学习更多实用的编程方法和技巧】 红黑树是一种自平衡的二叉搜索树&#xff0c;广泛应用于计算机科学中&#xff0c;尤其是在实现关联数组和集合时。它的设计旨在确保在最坏情况下&#xff0c;基本动态集合操作&#xff08;如插入、删除和查找&am…...

SpringBoot整合篇 05、Springboot整合Redission

文章目录 前言Redission详细配置步骤pom依赖application.yaml配置类CacheConfigEnvironmentContext RedissionController单测 前言 本篇博客是SpringBoot整合Redission&#xff0c;若文章中出现相关问题&#xff0c;请指出&#xff01; 所有博客文件目录索引&#xff1a;博客…...

供应链系统设计-供应链中台系统设计(六)- 商品中心概念篇

概述 我们在供应链系统设计-中台系统设计系列&#xff08;五&#xff09;- 供应链中台实践概述 中描述了什么是供应链中台&#xff0c;供应链中台主要包含了那些组成部门。包括业务中台、通用中台等概念。为了后续方便大家对于中台有更深入的理解&#xff0c;我会逐一针对中台…...

胡闹厨房练习(三)

ScriptableObject 一、初步了解 1、实质:是一种特殊类型的Unity对象, 2、作用:用于存储大量数据,而不必依附于游戏场景中的某个GameObject。 3、特点: 可以在不增加场景中对象数量的情况下,管理和存储复杂的数据结构、配置信息、游戏状态等。 4、适用:非常适合用来…...

关于ESD(静电放电)等级的划分

关于ESD&#xff08;静电放电&#xff09;等级的划分&#xff0c;主要依据不同的测试模型和测试标准。以下是对HBM&#xff08;人体模型&#xff09;和CDM&#xff08;充电器件模型&#xff09;两种测试模型下ESD等级划分的详细解释&#xff1a; HBM ESD等级划分 HBM ESD等级…...

postgresql|数据库|只读用户的创建和删除(备忘)

CREATE USER read_only WITH PASSWORD 密码 -- 连接到xxx数据库 \c xxx -- 授予对xxx数据库的只读权限 GRANT CONNECT ON DATABASE xxx TO read_only; GRANT USAGE ON SCHEMA public TO read_only; GRANT SELECT ON ALL TABLES IN SCHEMA public TO read_only; GRANT EXECUTE O…...

Rust 异步编程

Rust 异步编程 引言 Rust 是一种系统编程语言,以其高性能、安全性以及零成本抽象而著称。在多核处理器成为主流的今天,异步编程成为了一种提高应用性能、优化资源利用的有效手段。本文将深入探讨 Rust 异步编程的核心概念、常用库以及最佳实践。 异步编程基础 什么是异步…...

2025季度云服务器排行榜

在全球云服务器市场&#xff0c;各厂商的排名和地位并非一成不变&#xff0c;而是由其独特的优势、战略布局和市场适应性共同决定的。以下是根据2025年市场趋势&#xff0c;对主要云服务器厂商在排行榜中占据重要位置的原因和优势进行深度分析&#xff1a; 一、全球“三巨头”…...

Java + Spring Boot + Mybatis 实现批量插入

在 Java 中使用 Spring Boot 和 MyBatis 实现批量插入可以通过以下步骤完成。这里提供两种常用方法&#xff1a;使用 MyBatis 的 <foreach> 标签和批处理模式&#xff08;ExecutorType.BATCH&#xff09;。 方法一&#xff1a;使用 XML 的 <foreach> 标签&#xff…...

浪潮交换机配置track检测实现高速公路收费网络主备切换NQA

浪潮交换机track配置 项目背景高速网络拓扑网络情况分析通信线路收费网络路由 收费汇聚交换机相应配置收费汇聚track配置 项目背景 在实施省内一条高速公路时遇到的需求&#xff0c;本次涉及的主要是收费汇聚交换机的配置&#xff0c;浪潮网络设备在高速项目很少&#xff0c;通…...

安宝特案例丨Vuzix AR智能眼镜集成专业软件,助力卢森堡医院药房转型,赢得辉瑞创新奖

在Vuzix M400 AR智能眼镜的助力下&#xff0c;卢森堡罗伯特舒曼医院&#xff08;the Robert Schuman Hospitals, HRS&#xff09;凭借在无菌制剂生产流程中引入增强现实技术&#xff08;AR&#xff09;创新项目&#xff0c;荣获了2024年6月7日由卢森堡医院药剂师协会&#xff0…...

Linux系统部署KES

1、安装准备 1.版本说明V008R006C009B0014 V008&#xff1a;是version产品的大版本。 R006&#xff1a;是release产品特性版本。 C009&#xff1a;是通用版 B0014&#xff1a;是build开发过程中的构建版本2.硬件要求 #安全版和企业版 内存&#xff1a;1GB 以上 硬盘&#xf…...

数学建模-滑翔伞伞翼面积的设计,运动状态计算和优化 !

我们考虑滑翔伞的伞翼面积设计问题以及运动状态描述。滑翔伞的性能主要取决于伞翼面积、气动特性以及飞行员的重量。我们的目标是建立数学模型来描述滑翔伞的运动状态,并优化伞翼面积的设计。 一、问题分析 滑翔伞在飞行过程中受到重力、升力和阻力的作用。升力和阻力与伞翼面…...

Elastic 获得 AWS 教育 ISV 合作伙伴资质,进一步增强教育解决方案产品组合

作者&#xff1a;来自 Elastic Udayasimha Theepireddy (Uday), Brian Bergholm, Marianna Jonsdottir 通过搜索 AI 和云创新推动教育领域的数字化转型。 我们非常高兴地宣布&#xff0c;Elastic 已获得 AWS 教育 ISV 合作伙伴资质。这一重要认证表明&#xff0c;Elastic 作为 …...

嵌入式学习之系统编程(九)OSI模型、TCP/IP模型、UDP协议网络相关编程(6.3)

目录 一、网络编程--OSI模型 二、网络编程--TCP/IP模型 三、网络接口 四、UDP网络相关编程及主要函数 ​编辑​编辑 UDP的特征 socke函数 bind函数 recvfrom函数&#xff08;接收函数&#xff09; sendto函数&#xff08;发送函数&#xff09; 五、网络编程之 UDP 用…...