【Linux】进程通信----管道通信(上) https://developer.aliyun.com/article/1565747
3.管道特征
1.管道的生命周期随进程,进程退出,管道释放
2.管道可以用来进行具有血缘关系的进程间通信(常用于父子通信)
3.管道是面向字节流的
4.半双工—单向通信(特殊)
5.互斥与同步机制——对共享资源进行保护的方案
💫 命名管道
概念:
我们前面已经知道:匿名管道应用的一个限制就是只能在具有共同祖先(具有亲缘关系)的进程间通信。那如果两个毫不相干的进程间通信交互呢?如果我们想在不相关的进程之间交换数据,可以使用FIFO文件来做这项工作,它经常被称为命名管道。命名管道是一种特殊类型的文件
1.mkfifo
使用说明
NAME mkfifo - make FIFOs (named pipes) SYNOPSIS #include <sys/types.h> #include <sys/stat.h> int mkfifo(const char *pathname, mode_t mode); RETURN VALUE On success mkfifo() returns 0. In the case of an error, -1 is returned (in which case, errno is set appropriately).
在当前路径下直接创建命名管道:
mkfifo named_pipe
往管道文件写东西:
2.创建管道文件
分为三个文件:comm.hpp:公共文件(同一份资源),server.cc:读取端,clinet.cc:写入端
comm.hpp文件(同一份资源)
#pragma once #include <iostream> #include <sys/types.h> #include <sys/stat.h> #include <string> #include <cerrno> #include <cstring> #include <cassert> #define NAMED_PIPE "/tmp/mypipe.name" bool createFifo(const std::string &path) { umask(0); int n = mkfifo(path.c_str(),0666); if(n==0) return true; else { std::cout<<"errno:"<<errno<<"err string:"<<strerror(errno)<<std::endl; return false; } }
server.cc:
#include "comm.hpp" int main() { bool ret = createFifo(NAMED_PIPE); assert(ret); (void)ret; return 0; }
运行:
3.删除管道文件
使用说明
unlink 注意头文件,函数的参数以及返回值这三个主要部分:
NAME unlink - remove a directory entry SYNOPSIS #include <unistd.h> int unlink(const char *path); RETURN VALUE Upon successful completion, 0 shall be returned. Otherwise, -1 shall be returned and errno set to indicate the error. If -1 is returned, the named file shall not be changed.
在comm.hpp中封装好删除的函数:
void removeFifo(const std::string &path) { int n = unlink(path.c_str()); assert(n==0); (void)n;//防止n没使用而警告 }
在server.cc中进行调用:
#include "comm.hpp" int main() { bool ret = createFifo(NAMED_PIPE); assert(ret); (void)ret; removeFifo(NAMED_PIPE); return 0; }
至此,创建和删除管道文件的操作我们实现完毕。下面进入通信环节
4.通信
说明:
其实在了解完了匿名管道之后,对于命名管道我们能够更好的理解:
client.cc(写端):
#include "comm.hpp" int main() { int wfd = open(NAMED_PIPE,O_WRONLY); if(wfd<0) exit(1); //write char buffer[1024]; while(true) { std::cout<<"Please Say:"; fgets(buffer,sizeof(buffer),stdin); //if(strlen(buffer)>0) buffer[strlen(buffer)-1] = 0; ssize_t n = write(wfd,buffer,strlen(buffer)); assert(n==strlen(buffer)); (void)n; } close(wfd); return 0; }
server.cc(读端):
#include "comm.hpp" int main() { bool ret = createFifo(NAMED_PIPE); assert(ret); (void)ret; int rfd = open(NAMED_PIPE,O_RDONLY); if(rfd<0) exit(1); //read char buffer[1024]; while(true) { ssize_t s = read(rfd,buffer,sizeof(buffer)-1); if(s>0) { buffer[s] = 0; std::cout<<"client->server" <<buffer<<std::endl; } else if(s==0) { std::cout<<"client quit,俺也一样"<<std::endl; break; } else { std::cout<<"err string:"<<strerror(errno)<<std::endl; break; } } close(rfd); removeFifo(NAMED_PIPE); return 0; }
进行通信:
读端多出一行空行:写端输入之后多按了回车,修改为buffer[strlen(buffer)-1] = 0;
if(strlen(buffer) > 0) buffer[strlen(buffer) - 1] = 0;
🌙 管道通信的优化
管道通信的优点有以下几点:
- 管道通信是简单易用的,只需要使用系统调用 pipe 或 mkfifo 就可以创建一个管道文件,然后使用文件操作函数来读写数据。
- 管道通信是安全的,匿名管道只能用于具有亲缘关系的进程间通信,命名管道可以通过文件权限来控制访问。
- 管道通信是面向字节流的,不需要事先约定数据的格式,也不需要考虑字节序的问题。
管道通信的缺点有以下几点:
- 管道通信是单向的,如果要实现双向通信,需要创建两个管道。
- 管道通信是阻塞式的,如果读端没有数据可读或者写端没有空间可写,进程会被阻塞。
- 管道通信是缓冲区有限的,如果写入数据过多而读出数据过少,会导致缓冲区满而无法继续写入。
- 管道通信是不可靠的,如果读端或者写端被关闭,另一端可能会收到错误的信号或者返回值。
改进管道通信性能和效率的方法有以下几点:
- 使用双向管道,可以实现两个进程之间的双向通信,而不需要创建两个单向管道。双向管道可以通过 socketpair 系统调用来创建,返回两个文件描述符,分别表示管道的两端。
- 使用非阻塞模式,可以避免进程在读写管道时被阻塞,提高并发性能。非阻塞模式可以通过 fcntl 系统调用来设置文件描述符的 O_NONBLOCK 标志。
- 使用自定义协议,可以根据通信的需求和场景,设计合适的数据格式和交互方式,提高数据传输的效率和可靠性。自定义协议可以包括数据包的长度、类型、校验码等信息。
- 调整管道缓冲区的大小,可以根据数据量的大小和频率,选择合适的缓冲区大小,避免缓冲区溢出或者空闲浪费。管道缓冲区的大小可以通过 fcntl 系统调用来设置 F_SETPIPE_SZ 标志,并且可以通过 /proc/sys/fs/pipe-max-size 来修改最大容量。
🌟结束语
今天内容就到这里啦,时间过得很快,大家沉下心来好好学习,会有一定的收获的,大家多多坚持,嘻嘻,成功路上注定孤独,因为坚持的人不多。那请大家举起自己的小手给博主一键三连,有你们的支持是我最大的动力💞💞💞,回见。