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中的数据是字节流,消息之间没有边界,这意味这多条消息被送到一个进程中时,发送者和接收者需要约定一种规则来分隔消息。有几种做法:
- 每条消息可以使用换行符之类的分隔字符结束;
- 每条消息包含一个固定大小的头,头中包含一个表示消息长度的字段,该字段表示消息中剩余部分的长度;
- 使用固定长度的消息,让服务器总是读取这个大小固定的消息。
接下来的示例将使用第三种。
什么是迭代式服务器?迭代表示服务器会在读取并处理完当前客户端之后才去处理下一个客户端。另一种设计方法是并发式服务器,使用单独的子线程/进程来处理客户端请求。
// 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可以创建一个新管道: #include <unistd.h> int pipe(int filedes[2]);成功的pipe调用会在数组filedes中返回两个打开的文件描述符,读取端为filedes[0],写入端为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 应用程序
作者:来自 Elastic Gustavo Llermaly 使用 Ollama 通过 Go 创建 RAG 应用程序来利用本地模型。 关于各种开放模型,有很多话要说。其中一些被称为 Mixtral 系列,各种规模都有,而一种可能不太为人所知的是 openbiollm,这…...
Windows平台ROBOT安装
Windows环境下ROBOT的安装,按照下文进行部署ROBOT的前提是你的python已安装并且环境变量已设置好. 一、安装setuptools 1、下载后安装 https://pypi.python.org/pypi/setuptools/ 下载你需要的包 setuptools-75.6.0.tar.gz 解压下载的包在命令行中进入该包,敲击如下命令后…...
【动态规划篇】穿越算法迷雾:约瑟夫环问题的奇幻密码
欢迎拜访:羑悻的小杀马特.-CSDN博客 本篇主题:带你众人皆知的约瑟夫环问题 制作日期:2024.12.29 隶属专栏:C/C题海汇总 目录 引言: 一约瑟夫环问题介绍: 11问题介绍: 1.2起源与历史背景&…...
代码随想录算法训练营第51期第32天 | 理论基础、509. 斐波那契数、70. 爬楼梯、746. 使用最小花费爬楼梯
理论基础 动态规划:dp,每一个状态都是由上个状态推导出来的,因为我是先写完三道题再看理论的,所以有点感概; 确定dp数组(dp table)以及下标的含义确定递推公式dp数组如何初始化确定遍历顺序举…...
爱思唯尔word模板
爱思唯尔word模板 有时候并不一定非得latex https://download.csdn.net/download/qq_38998213/90199214 参考文献书签链接...
每日一题 354. 俄罗斯套娃信封问题
354. 俄罗斯套娃信封问题 需要对信封排序 ,重点是再宽度相同时,逐步减少其高度 class Solution { public:int maxEnvelopes(vector<vector<int>>& envelopes) {sort(envelopes.begin(),envelopes.end(),[](const vector<int>&a,const v…...
ASP.net网站的注册、登录和密码修改的操作详解
一、进入注册、登录和密码修改操作详解 ASP.net网站为用户提供不同权限状态下的操作界面。根据用户登录状态,页面会显示不同的选项。 已登录用户的操作 图1 登录后操作界面 当用户已登录系统时,会显示以下内容和功能: 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 因其易用性、动态计算图和高效性而日益流行,成为实现深度学习模型的首选。如果你想探索这个工具并学习如何在 Ubuntu 上安装 PyTorch,本指南将对你有所帮助! 在本教程中,我们将引导你完成在 Ubuntu 系统上使用 Pip…...
8-Gin 中间件 --[Gin 框架入门精讲与实战案例] 【文末有测试代码】
路由中间件 Gin 是一个用 Go (Golang) 编写的 HTTP web 框架。它以性能好、中间件支持灵活著称,非常适合用来构建微服务或 RESTful API 服务。下面我将提供三个使用 Gin 的路由中间件的完整示例。 示例 1: 简单的日志记录中间件 这个中间件会在每个请求处理前后打…...
【潜意识Java】深入详细理解分析Java中的toString()方法重写完整笔记总结,超级详细。
目录 一、toString() 方法是啥? (一)默认的 toString() 方法 (二)toString() 方法的作用 二、为啥要重写 toString() 方法? (一)提高代码的可读性 (二)…...
【论文笔记】Contrastive Learning for Sign Language Recognition and Translation
🍎个人主页:小嗷犬的个人主页 🍊个人网站:小嗷犬的技术小站 🥭个人信条:为天地立心,为生民立命,为往圣继绝学,为万世开太平。 基本信息 标题: Contrastive Learning for…...
Gitlab17.7+Jenkins2.4.91实现Fastapi/Django项目持续发布版本详细操作(亲测可用)
一、gitlab设置: 1、进入gitlab选择主页在左侧菜单的下面点击管理员按钮。 2、选择左侧菜单的设置,选择网络,在右侧选择出站请求后选择允许来自webhooks和集成对本地网络的请求 3、webhook设置 进入你自己的项目选择左侧菜单的设置ÿ…...
一起来看--红黑树
【欢迎关注编码小哥,学习更多实用的编程方法和技巧】 红黑树是一种自平衡的二叉搜索树,广泛应用于计算机科学中,尤其是在实现关联数组和集合时。它的设计旨在确保在最坏情况下,基本动态集合操作(如插入、删除和查找&am…...
SpringBoot整合篇 05、Springboot整合Redission
文章目录 前言Redission详细配置步骤pom依赖application.yaml配置类CacheConfigEnvironmentContext RedissionController单测 前言 本篇博客是SpringBoot整合Redission,若文章中出现相关问题,请指出! 所有博客文件目录索引:博客…...
供应链系统设计-供应链中台系统设计(六)- 商品中心概念篇
概述 我们在供应链系统设计-中台系统设计系列(五)- 供应链中台实践概述 中描述了什么是供应链中台,供应链中台主要包含了那些组成部门。包括业务中台、通用中台等概念。为了后续方便大家对于中台有更深入的理解,我会逐一针对中台…...
胡闹厨房练习(三)
ScriptableObject 一、初步了解 1、实质:是一种特殊类型的Unity对象, 2、作用:用于存储大量数据,而不必依附于游戏场景中的某个GameObject。 3、特点: 可以在不增加场景中对象数量的情况下,管理和存储复杂的数据结构、配置信息、游戏状态等。 4、适用:非常适合用来…...
关于ESD(静电放电)等级的划分
关于ESD(静电放电)等级的划分,主要依据不同的测试模型和测试标准。以下是对HBM(人体模型)和CDM(充电器件模型)两种测试模型下ESD等级划分的详细解释: HBM ESD等级划分 HBM ESD等级…...
【Python】 -- 趣味代码 - 小恐龙游戏
文章目录 文章目录 00 小恐龙游戏程序设计框架代码结构和功能游戏流程总结01 小恐龙游戏程序设计02 百度网盘地址00 小恐龙游戏程序设计框架 这段代码是一个基于 Pygame 的简易跑酷游戏的完整实现,玩家控制一个角色(龙)躲避障碍物(仙人掌和乌鸦)。以下是代码的详细介绍:…...
SpringBoot+uniapp 的 Champion 俱乐部微信小程序设计与实现,论文初版实现
摘要 本论文旨在设计并实现基于 SpringBoot 和 uniapp 的 Champion 俱乐部微信小程序,以满足俱乐部线上活动推广、会员管理、社交互动等需求。通过 SpringBoot 搭建后端服务,提供稳定高效的数据处理与业务逻辑支持;利用 uniapp 实现跨平台前…...
如何为服务器生成TLS证书
TLS(Transport Layer Security)证书是确保网络通信安全的重要手段,它通过加密技术保护传输的数据不被窃听和篡改。在服务器上配置TLS证书,可以使用户通过HTTPS协议安全地访问您的网站。本文将详细介绍如何在服务器上生成一个TLS证…...
HBuilderX安装(uni-app和小程序开发)
下载HBuilderX 访问官方网站:https://www.dcloud.io/hbuilderx.html 根据您的操作系统选择合适版本: Windows版(推荐下载标准版) Windows系统安装步骤 运行安装程序: 双击下载的.exe安装文件 如果出现安全提示&…...
MySQL用户和授权
开放MySQL白名单 可以通过iptables-save命令确认对应客户端ip是否可以访问MySQL服务: test: # iptables-save | grep 3306 -A mp_srv_whitelist -s 172.16.14.102/32 -p tcp -m tcp --dport 3306 -j ACCEPT -A mp_srv_whitelist -s 172.16.4.16/32 -p tcp -m tcp -…...
PostgreSQL——环境搭建
一、Linux # 安装 PostgreSQL 15 仓库 sudo dnf install -y https://download.postgresql.org/pub/repos/yum/reporpms/EL-$(rpm -E %{rhel})-x86_64/pgdg-redhat-repo-latest.noarch.rpm# 安装之前先确认是否已经存在PostgreSQL rpm -qa | grep postgres# 如果存在࿰…...
LOOI机器人的技术实现解析:从手势识别到边缘检测
LOOI机器人作为一款创新的AI硬件产品,通过将智能手机转变为具有情感交互能力的桌面机器人,展示了前沿AI技术与传统硬件设计的完美结合。作为AI与玩具领域的专家,我将全面解析LOOI的技术实现架构,特别是其手势识别、物体识别和环境…...
DBLP数据库是什么?
DBLP(Digital Bibliography & Library Project)Computer Science Bibliography是全球著名的计算机科学出版物的开放书目数据库。DBLP所收录的期刊和会议论文质量较高,数据库文献更新速度很快,很好地反映了国际计算机科学学术研…...
在 Visual Studio Code 中使用驭码 CodeRider 提升开发效率:以冒泡排序为例
目录 前言1 插件安装与配置1.1 安装驭码 CodeRider1.2 初始配置建议 2 示例代码:冒泡排序3 驭码 CodeRider 功能详解3.1 功能概览3.2 代码解释功能3.3 自动注释生成3.4 逻辑修改功能3.5 单元测试自动生成3.6 代码优化建议 4 驭码的实际应用建议5 常见问题与解决建议…...
Git 命令全流程总结
以下是从初始化到版本控制、查看记录、撤回操作的 Git 命令全流程总结,按操作场景分类整理: 一、初始化与基础操作 操作命令初始化仓库git init添加所有文件到暂存区git add .提交到本地仓库git commit -m "提交描述"首次提交需配置身份git c…...
