news 2026/6/6 17:33:17

进程间通信(3)---命名管道

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
进程间通信(3)---命名管道

命名管道

匿名管道:只能用来进行具有血缘关系的进程进行进程间通信(常用于父子)

如果两个进程不相关?该如何进行通信呢?

进程A:打开了一个文件a/b/c.txt

进程B:打开了一个文件a/b/c.txt

内核中,操作系统会不会把这个a/b/c.txt文件(inode/文件内容等),在内存中加载两次?--不会,因为没有必要!

给进程B也要创建struct file,从而让指针指向它自己打开的文件,互不影响。

但是这个新创建的struct file,OS会发现两个访问的是同一个文件。因为在OS中经过路径解析后,会看到被打开文件的inode number,OS要管理的就是inode跟文件内核缓冲区。所以OS认为没有必要再把对应的属性内容加载到内存里。由此得下图。

两个进程在各自的视角里,都有一个自己的struct file,但是最终访问的都是同一个文件

所以,父子进程可以向同一个显示器文件进行打印

匿名管道是通过父子继承看到同一份资源

让不同的进程,看到了同一份资源!----通过打开同一路径下的同一个文件看到同一份资源---文件有路径,有名字--路径具有唯一性---命名管道

要让Linux内核支持管道文件!---只需要被打开,不需要刷新!

文件级 代码级

mkfifo创建命名管道

root@iZ5waahoxw3q2bZ:~/linux-learning/linux/26-6-6# ll total 8 drwxr-xr-x 2 root root 4096 Jun 6 08:50 ./ drwxr-xr-x 36 root root 4096 Jun 6 08:50 ../ root@iZ5waahoxw3q2bZ:~/linux-learning/linux/26-6-6# mkfifo fifo root@iZ5waahoxw3q2bZ:~/linux-learning/linux/26-6-6# ll total 8 drwxr-xr-x 2 root root 4096 Jun 6 08:50 ./ drwxr-xr-x 36 root root 4096 Jun 6 08:50 ../ prw-r--r-- 1 root root 0 Jun 6 08:50 fifo|

这样就能创建管道,以p开头---管道文件类型

再打开一个终端

第一个终端

root@iZ5waahoxw3q2bZ:~/linux-learning/linux/26-6-6# echo "hello fifo">fifo

第二个终端

root@iZ5waahoxw3q2bZ:~/linux-learning/linux/26-6-6# cat < fifo hello fifo

这两个其实也可以说是一个小型的命名管道通信,两个进程

删除管道

root@iZ5waahoxw3q2bZ:~/linux-learning/linux/26-6-6# unlink fifo

如果想要用代码去实现出来,只需要让进程把对应的管道文件创建出来,后面操作就是文件操作,然后就可以直接通信了

1.

server.cc

#include<iostream> #include<string> #include<sys/types.h> #include<sys/stat.h> #include<fcntl.h> #include"comm.hpp" int main() { umask(0); int n = mkfifo(FIFO_FILE,0666); if(n !=0) { std::cerr<<"mkdir fifo error"<<std::endl; return 1; } return 0; }

comm.hpp

#pragma once #define FIFO_FILE "fifo"

Makefile

.PHONY:all all:client server client:client.cc g++ -o $@ $^ -std=c++11 server:server.cc g++ -o $@ $^ -std=c++11 .PHONY:clean clean: rm -f client server

client.cc

#include<iostream> #include<string> #include<sys/types.h> #include<sys/stat.h> #include<fcntl.h> #include"comm.hpp" int main() { return 0; }
root@iZ5waahoxw3q2bZ:~/linux-learning/linux/26-6-6/fifo# make g++ -o client client.cc -std=c++11 g++ -o server server.cc -std=c++11 root@iZ5waahoxw3q2bZ:~/linux-learning/linux/26-6-6/fifo# ./server root@iZ5waahoxw3q2bZ:~/linux-learning/linux/26-6-6/fifo# ll total 64 drwxr-xr-x 2 root root 4096 Jun 6 09:19 ./ drwxr-xr-x 3 root root 4096 Jun 6 08:57 ../ -rwxr-xr-x 1 root root 17032 Jun 6 09:19 client* -rw-r--r-- 1 root root 151 Jun 6 09:12 client.cc -rw-r--r-- 1 root root 38 Jun 6 09:07 comm.hpp prw-rw-rw- 1 root root 0 Jun 6 09:19 fifo| -rw-r--r-- 1 root root 154 Jun 6 09:12 Makefile -rwxr-xr-x 1 root root 17408 Jun 6 09:19 server* -rw-r--r-- 1 root root 294 Jun 6 09:19 server.cc

server跟client只要做文件操作就可以了

client.cc

#include<iostream> #include<string> #include<sys/types.h> #include<sys/stat.h> #include<fcntl.h> #include<unistd.h> #include"comm.hpp" int main() { int fd = open(FIFO_FILE,O_WRONLY);//以写的方式打开 if(fd < 0) { std::cerr<<"open fifo error"<<std::endl; return 2; } char buffer[1024]; while(true) { std::cout<<"Please Enter# "; std::string message; std::cin >> message; int n = write(fd,message.c_str(),message.size()); if(n > 0) { buffer[n]=0; std::cout<<"client say# "<<buffer<<std::endl; } } close(fd); return 0; }

server.cc

#include<iostream> #include<string> #include<sys/types.h> #include<sys/stat.h> #include<fcntl.h> #include<unistd.h> #include"comm.hpp" int main() { umask(0); int n = mkfifo(FIFO_FILE,0666); if(n !=0) { std::cerr<<"mkdir fifo error"<<std::endl; return 1; } int fd = open(FIFO_FILE,O_RDONLY);//以读的方式打开 if(fd < 0) { std::cerr<<"open fifo error"<<std::endl; return 2; } char buffer[1024]; while(true) { int n = read(fd,buffer,sizeof(buffer)-1); if(n > 0) { buffer[n]=0; std::cout<<"client say# "<<buffer<<std::endl; } } close(fd); return 0; }
root@iZ5waahoxw3q2bZ:~/linux-learning/linux/26-6-6/fifo# unlink fifo root@iZ5waahoxw3q2bZ:~/linux-learning/linux/26-6-6/fifo# ll total 64 drwxr-xr-x 2 root root 4096 Jun 6 09:36 ./ drwxr-xr-x 3 root root 4096 Jun 6 08:57 ../ -rwxr-xr-x 1 root root 17032 Jun 6 09:19 client* -rw-r--r-- 1 root root 666 Jun 6 09:35 client.cc -rw-r--r-- 1 root root 38 Jun 6 09:07 comm.hpp -rw-r--r-- 1 root root 154 Jun 6 09:12 Makefile -rwxr-xr-x 1 root root 17408 Jun 6 09:19 server* -rw-r--r-- 1 root root 706 Jun 6 09:32 server.cc root@iZ5waahoxw3q2bZ:~/linux-learning/linux/26-6-6/fifo# make clean rm -f client server

make

终端1

root@iZ5waahoxw3q2bZ:~/linux-learning/linux/26-6-6/fifo# make g++ -o client client.cc -std=c++11 g++ -o server server.cc -std=c++11 root@iZ5waahoxw3q2bZ:~/linux-learning/linux/26-6-6/fifo# ./server client say# 你好 client say# 在吗 client say# 666 client say# xxx ^C

终端2

root@iZ5waahoxw3q2bZ:~/linux-learning/linux/26-6-6/fifo# ./client Please Enter# 你好 client say# dD1Iþ Please Enter# 在吗 client say# dD1Iþ Please Enter# 666 client say# dD1 Please Enter# xxx client say# dD1 Please Enter# ^C

从客户端进程向服务端发送消息

2.

调用unlink把指定目录下的文件删除一个文件

client.cc

#include<iostream> #include<string> #include<sys/types.h> #include<sys/stat.h> #include<fcntl.h> #include<unistd.h> #include"comm.hpp" int main() { //write int fd = open(FIFO_FILE,O_WRONLY);//以写的方式打开 if(fd < 0) { std::cerr<<"open fifo error"<<std::endl; return 1; } std::cout<<"open fifo success"<<std::endl; //写入操作 std::string message; int cnt = 1; pid_t id = getpid(); while(true) { std::cout<<"Please Enter#"; std::getline(std::cin,message); message += ",message number:" + std::to_string(cnt++) + ",[" + std::to_string(id) + "]"; write(fd,message.c_str(),message.size()); } close(fd); return 0; }

server.cc

#include<iostream> #include<string> #include<sys/types.h> #include<sys/stat.h> #include<fcntl.h> #include<unistd.h> #include"comm.hpp" int main() { umask(0); //新建管道 int n = mkfifo(FIFO_FILE,0666); if(n < 0) { std::cerr<<"mkfifo error"<<std::endl; return 1; } std::cout<<"mkfifo success"<<std::endl; //打开 int fd = open(FIFO_FILE,O_RDONLY);//以读的方式打开 if(fd<0) { std::cerr<<"open file error"<<std::endl; return 2; } std::cout<<"open fifo success"<<std::endl; //正常的read while(true) { char buffer[1024]; int number = read(fd,buffer,sizeof(buffer)-1); if(number>0) { buffer[number] = 0; std::cout<<"Client Say#"<<buffer <<std::endl; } else { //TODO } } //关闭 close(fd); //删除管道文件 n = unlink(FIFO_FILE); if(n==0) { std::cout<<"remove fifo success"<<std::endl; } else { std::cout<<"remove fifo failed"<<std::endl; } return 0; }

comm.hpp

#pragma once #define FIFO_FILE "fifo"

Makefile

.PHONY:all all:client server client:client.cc g++ -o $@ $^ -std=c++11 server:server.cc g++ -o $@ $^ -std=c++11 .PHONY:clean clean: rm -f client server

命名管道在实现通信的时候,如果作为读取一方,假设write(写入)方没有执行open的时候,read(读取)方,就要在open内部阻塞,直到有人把管道文件打开了,open才会返回。

server.cc

#include<iostream> #include<string> #include<sys/types.h> #include<sys/stat.h> #include<fcntl.h> #include<unistd.h> #include"comm.hpp" int main() { umask(0); //新建管道 int n = mkfifo(FIFO_FILE,0666); if(n < 0) { std::cerr<<"mkfifo error"<<std::endl; return 1; } std::cout<<"mkfifo success"<<std::endl; //打开,write 方没有执行open的时候,read方,就要在open内部进行阻塞 //直到有人把管道文件打开了,open才会返回! int fd = open(FIFO_FILE,O_RDONLY);//以读的方式打开 if(fd<0) { std::cerr<<"open file error"<<std::endl; return 2; } std::cout<<"open fifo success"<<std::endl; //正常的read while(true) { char buffer[1024]; int number = read(fd,buffer,sizeof(buffer)-1); if(number>0) { buffer[number] = 0; std::cout<<"Client Say#"<<buffer <<std::endl; } else if(number == 0) { std::cout<<"client quit! me too!"<<std::endl; break; } else { std::cerr<<"read error"<<std::endl; break; } } //关闭 close(fd); //删除管道文件 n = unlink(FIFO_FILE); if(n==0) { std::cout<<"remove fifo success"<<std::endl; } else { std::cout<<"remove fifo failed"<<std::endl; } return 0; }
root@iZ5waahoxw3q2bZ:~/linux-learning/linux/26-6-6.2/fifo# unlink fifo root@iZ5waahoxw3q2bZ:~/linux-learning/linux/26-6-6.2/fifo# make clean rm -f client server root@iZ5waahoxw3q2bZ:~/linux-learning/linux/26-6-6.2/fifo# make g++ -o client client.cc -std=c++11 g++ -o server server.cc -std=c++11

可以看到确实是两个进程

root@iZ5waahoxw3q2bZ:~# ps axj | grep -E 'client|server'

3.

client.cc

#include"comm.hpp" int main() { FileOper writertfile(PATH,FILENAME); writertfile.OpenForWrite(); writertfile.Write(); writertfile.Close(); return 0; }

comm.hpp

#pragma once #include <iostream> #include <string> #include <string> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #define PATH "." #define FILENAME "fifo" // 命名管道文件 class NamedFifo { public: NamedFifo(const std::string &path, const std::string &name) : _path(path), _name(name) { _fifoname = _path + "/" + _name; umask(0); // 新建管道 int n = mkfifo(_fifoname.c_str(), 0666); if (n < 0) { std::cerr << "mkfifo error" << std::endl; } else { std::cout << "mkfifo success" << std::endl; } } ~NamedFifo() { // 删除管道文件 int n = unlink(_fifoname.c_str()); if (n == 0) { std::cout << "remove fifo success" << std::endl; } else { std::cout << "remove fifo failed" << std::endl; } } private: std::string _path; std::string _name; std::string _fifoname; }; // 文件操作 class FileOper { public: FileOper(const std::string &path, const std::string &name) : _path(path), _name(name), _fd(-1) { _fifoname = _path + "/" + _name; } void OpenForRead() { //打开,write 方没有执行open的时候,read方,就要在open内部进行阻塞 //直到有人把管道文件打开了,open才会返回! _fd = open(_fifoname.c_str(), O_RDONLY); // 以读的方式打开 if (_fd < 0) { std::cerr << "open file error" << std::endl; return; } std::cout << "open fifo success" << std::endl; } void OpenForWrite() { // write _fd = open(_fifoname.c_str(), O_WRONLY); // 以写的方式打开 if (_fd < 0) { std::cerr << "open fifo error" << std::endl; return; } std::cout << "open fifo success" << std::endl; } void Write() { // 写入操作 std::string message; int cnt = 1; pid_t id = getpid(); while (true) { std::cout << "Please Enter#"; std::getline(std::cin, message); message += ",message number:" + std::to_string(cnt++) + ",[" + std::to_string(id) + "]"; write(_fd, message.c_str(), message.size()); } } void Read() { // 正常的read while (true) { char buffer[1024]; int number = read(_fd, buffer, sizeof(buffer) - 1); if (number > 0) { buffer[number] = 0; std::cout << "Client Say#" << buffer << std::endl; } else if (number == 0) { std::cout << "client quit! me too!" << std::endl; break; } else { std::cerr << "read error" << std::endl; break; } } } void Close() { if (_fd > 0) close(_fd); } ~FileOper() { } private: std::string _path; std::string _name; std::string _fifoname; int _fd; };

Makefile

.PHONY:all all:client server client:client.cc g++ -o $@ $^ -std=c++11 server:server.cc g++ -o $@ $^ -std=c++11 .PHONY:clean clean: rm -f client server

server.cc

#include"comm.hpp" int main() { //创建管道文件 NamedFifo fifo(PATH,FILENAME);//.当前路径下,文件名fifo // 文件操作了 FileOper readerfile(PATH,FILENAME); readerfile.OpenForRead(); readerfile.Read(); readerfile.Close(); return 0; }
root@iZ5waahoxw3q2bZ:~/linux-learning/linux/26-6-6.2/fifo# make g++ -o client client.cc -std=c++11 g++ -o server server.cc -std=c++11

可以看到右边ctrl+c后ll,管道文件也不存在了,被删除了

命名管道:特性和匿名管道是一致的,只有一点不同,命名管道可以用来进行不相关的进程进行进程间通信!

匿名管道与命名管道的区别

• 匿名管道由pipe函数创建并打开。

• 命名管道由mkfifo函数创建,打开用open

• FIFO(命名管道)与pipe(匿名管道)之间唯⼀的区别在它们创建与打开的方式不同,⼀但这些 ⼯作完成之后,它们具有相同的语义。

命名管道的打开规则

• 如果当前打开操作是为读而打开FIFO时
◦ O_NONBLOCKdisable:阻塞直到有相应进程为写而打开该FIFO
◦ O_NONBLOCKenable:立即返回成功

• 如果当前打开操作是为写而打开FIFO时
◦ O_NONBLOCKdisable:阻塞直到有相应进程为读而打开该FIFO
◦ O_NONBLOCKenable:立即返回失败,错误码为ENXIO

如果我们打开文件是失败的,代码就不应该往后走了,但我们还想让它打印出错误

打印错误信息,并立即终止程序

#define ERR_EXIT(m) \ do{\ perror(m);\ exit(EXIT_FAILURE);\ }while(0)

正常应该一行写,但是可读性不好,用"\"做续行

comm.hpp

#pragma once #include <iostream> #include <cstdio> #include <string> #include <string> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #define PATH "." #define FILENAME "fifo" #define ERR_EXIT(m) \ do \ { \ perror(m); \ exit(EXIT_FAILURE); \ } while (0) // 命名管道文件 class NamedFifo { public: NamedFifo(const std::string &path, const std::string &name) : _path(path), _name(name) { _fifoname = _path + "/" + _name; umask(0); // 新建管道 int n = mkfifo(_fifoname.c_str(), 0666); if (n < 0) { ERR_EXIT("mkfifo"); } else { std::cout << "mkfifo success" << std::endl; } } ~NamedFifo() { // 删除管道文件 int n = unlink(_fifoname.c_str()); if (n == 0) { ERR_EXIT("unlink"); } else { std::cout << "remove fifo failed" << std::endl; } } private: std::string _path; std::string _name; std::string _fifoname; }; // 文件操作 class FileOper { public: FileOper(const std::string &path, const std::string &name) : _path(path), _name(name), _fd(-1) { _fifoname = _path + "/" + _name; } void OpenForRead() { // 打开,write 方没有执行open的时候,read方,就要在open内部进行阻塞 // 直到有人把管道文件打开了,open才会返回! _fd = open(_fifoname.c_str(), O_RDONLY); // 以读的方式打开 if (_fd < 0) { ERR_EXIT("open"); } std::cout << "open fifo success" << std::endl; } void OpenForWrite() { // write _fd = open(_fifoname.c_str(), O_WRONLY); // 以写的方式打开 if (_fd < 0) { ERR_EXIT("open"); } std::cout << "open fifo success" << std::endl; } void Write() { // 写入操作 std::string message; int cnt = 1; pid_t id = getpid(); while (true) { std::cout << "Please Enter#"; std::getline(std::cin, message); message += ",message number:" + std::to_string(cnt++) + ",[" + std::to_string(id) + "]"; write(_fd, message.c_str(), message.size()); } } void Read() { // 正常的read while (true) { char buffer[1024]; int number = read(_fd, buffer, sizeof(buffer) - 1); if (number > 0) { buffer[number] = 0; std::cout << "Client Say#" << buffer << std::endl; } else if (number == 0) { std::cout << "client quit! me too!" << std::endl; break; } else { std::cerr << "read error" << std::endl; break; } } } void Close() { if (_fd > 0) close(_fd); } ~FileOper() { } private: std::string _path; std::string _name; std::string _fifoname; int _fd; };

感谢你的观看,期待我们下次再见!

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/6 17:30:21

深入解析STM32 USB中断驱动原理与固件开发实践

1. 从被动响应到主动处理&#xff1a;STM32 USB固件驱动的核心逻辑 搞嵌入式开发&#xff0c;尤其是涉及到USB通信&#xff0c;很多朋友一开始都会觉得有点“玄学”。明明代码写好了&#xff0c;设备插上去就是没反应&#xff0c;或者数据传输时快时慢&#xff0c;甚至直接卡死…

作者头像 李华
网站建设 2026/6/6 17:30:04

Windows任务栏美化革命:用TranslucentTB打造透明桌面新体验

Windows任务栏美化革命&#xff1a;用TranslucentTB打造透明桌面新体验 【免费下载链接】TranslucentTB A lightweight utility that makes the Windows taskbar translucent/transparent. 项目地址: https://gitcode.com/gh_mirrors/tr/TranslucentTB 你是否厌倦了Wind…

作者头像 李华