命名管道,让无血缘进程通信
命名管道:
是有标识符的管道,其他进程可以通过管道标识符寻找到管道,以p开头的文件
匿名的是竖划线
- 匿名管道通过子进程继承父进程做到的
fork函数中,父子进程通过管道通信的实质是fork会 继承 文件描述符表的特性做到的- 命名管道通过管道文件,文件在磁盘上有唯一的路径,通过路径找到对应的资源
命名管道中,两个进程打开磁盘上的文件在内存中只打开了一份,通信时候数据不会刷新到磁盘上,磁盘上的文件进行了符号处理,识别成管道文件
通过文件的路径,路径具有唯一性,多个进程可以通过路径打开同一个文件,同一个文件中的inode,就是同一个缓冲区
两个进程要通信先让他们看到同一个路径
一个进程把管道文件创建好了,另一个进程不需要创建
mkfifo函数
- 第一个参数文件路径
- 第二个参数文件权限
函数创建,mkfifo(库函数)
对端不写且退出,客户端是写端如何退出
不加1,\0是c的标准
头文件
#pragma once
#include <iostream>
#include <cstdio>
#include <string>
#include <cstring>
#include <cerrno>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#define IPC_PATH "./.fifo" //命名管道的路径
服务端
#include "comm.h"
using namespace std;
int main()
{
umask(0);
if(mkfifo(IPC_PATH, 0600) != 0)
{
cerr << "mkfifo error" << endl;
return 1;
}
int pipeFd = open(IPC_PATH, O_RDONLY);
//正常的通信过程
char buffer[1024];
while(true)
{
ssize_t s = read(pipeFd, buffer, sizeof(buffer)-1);
if(s > 0)
{
buffer[s] = '\0';
cout << "客户端->服务器# " << buffer << endl;
}
else if(s == 0)//如果管道中没有数据,那就阻塞了,如果返回0,证明对端关闭了
{
cout << "服务的退出";
break;
}
else
{
//do nothing
cout << "read: " << strerror(errno) << endl;
break;
}
}
close(pipeFd);
cout << "服务端退出啦" << endl;
unlink(IPC_PATH);
return 0;
}
客户端进程
#include "comm.h"
using namespace std;
int main()
{
int pipeFd = open(IPC_PATH, O_WRONLY);
if(pipeFd < 0)
{
cerr << "open: " << strerror(errno) << endl;
return 1;
}
#define NUM 1024
char line[NUM];
while(true)
{
printf("请输入你的消息# ");
fflush(stdout);
memset(line, 0, sizeof(line));
// fgets -> C -> line结尾自动添加\0
if(fgets(line, sizeof(line), stdin) != nullptr)
{
//abcd\n\0
line[strlen(line) - 1] = '\0';
write(pipeFd, line, strlen(line));
}
else
{
break;
}
}
close(pipeFd);
cout << "客户端退出啦" << endl;
return 0;
}
命令创建:
mkfifo +文件名
→ 创建一个管道文件,文件类型标识符是p,p代表管道文件管道文件的性质:
- 管道是一块内存,并不是进程操作文件往文件中去读写,
- 管道文件作用就是标识内核中的buffer;
- 或者说,不同进程就能通过管道文件内核中的管道
用read函数操作管道文件和普通文件操作一样先左边读,右边写,左边可执行程序在open时被阻塞左边后读,右边先写运行成功
- 这个管道只适用于同一主机下的进程间通信,不适于不同主机下的进程间通信;
- 不同主机间的通信就是用网络实现的
向管道中写的时候,小于pipe_buf(4096)会保证原子性(多线程)