目录
管道如何通信
管道的访问控制机制:
匿名管道
匿名管道数据传输的原理
如何使用(代码案例)
用C/C++代码编译实现父子进程间通信案例 :
思路
实现
命名管道
为什么要有命名管道
回归进程间通信的本质
匿名管道的短板
命名管道的原理
如何创建命名管道
如何使用(代码案例)
用C/C++代码编译实现 client进程 和 server进程的通信案例 :
思路
实现
管道,是Linux下常用的进程间通信手段,具体的通信方法是 父进程打开管道文件,被子进程继承,通过文件描述符fd,父子进程能看到相同的内存级文件,而后 一个进程往管道中写,另一个进程读,完成信息的传送。
而打开管道文件与普通文件不同的是,前者具有访问控制,即同步和互斥机制,也就是具有原子性:
1.从管道中读取数据时,如果管道为空,或正在被写入,执行读取的进程会阻塞等待
2.往管道中写入数据时,如果管道满了,或正在被读取,执行写入的进程会阻塞等待
3.当写端关闭后,读端读取到EOF
以上三点机制,使得管道能像文件一样操作的同时,避同时免了使用普通文件进程间通信的 极度的 不安全性。并且,数据在管道文件中的读写是彻底的内存级别的,即不与磁盘交互,读和写皆是在内存中,效率也有一定的保证。
注意管道文件只是单向的,也就是一个进程读,一个进程写的模式,原生规定好的,使用需要符合规范
匿名管道的通信特点是,通过系统调用pipe(int fd[2]),直接在内核中创建一个内存级文件,并自动被调用它的函数分别以读和写的方式打开两次,获得两个fd。存放在输出型参数fd数组中,fd[0]代表读端fd,fd[1]代表写端fd。两个参与通信的进程能分别通过read和write这样的系统调用接口,以文件的方式对管道进行读写操作,实现数据的传递。
pipe在内存中创建匿名管道文件1.子进程 进程从标准输入按行读取信息
2.通过管道传入到父进程进程中
3.由父进程进程读取并输出到标准输出流。
1.调用pipe(int*fd[2])创建匿名通信管道
2.fork()创建子进程,子进程会继承fd[2]。
子进程:
(1)关闭读端:close(fd[0])
注:管道只支持单向通信,这里要求子写父读,你也可以实现为父写子读.
(2)从标准输入(键盘)读取按行读入
(3)将从键盘读入的内容写入管道
父进程
(1)关闭写端 close(fd[1])
(2)从管道读取数据知道读到文件结尾
(3)等待子进程退出
#include
#include
#include
#include
#include
#include
#include
#include
#define buffer_size (128)
int main(){
int fd[2]={0,0};
int pipe_ret=pipe(fd);
if(pipe_ret) return errno;//调用出错返回错误码
pid_t child_pid=fork();
if(child_pid==-1) return errno;
if(child_pid==0){
//child
//关闭不需要的端口
close(fd[0]);
char child_buffer[buffer_size];
while(1){
//从键盘读入数据
ssize_t read_size=read(0,child_buffer,buffer_size-1);
//保证安全性
assert(read_size
运行效果:
首先明确 进程间通信的本质是 "让不同的进程,访问到同一份资源"
匿名管道如何使不同进程访问到同一份资源?
父进程分别以读和写两次打开管道文件,而后创建子进程继承文件表述符,父子进程能够访问到同一份资源,此时能够实现一个进程写一个进程读,进而就具备了通信间通信的客观条件。
但是这样通过继承管道文件的文件描述符fd,来访问到同一份资源的方式有一个明显的缺陷,那就是匿名管道只能用于有血缘关系的进程间通信
而命名管道则解决了这个问题,能让没有血缘关系的进程也能访问到同一个管道文件
在磁盘上创建一个命名管道文件,利用文件名来表示管道的唯一性,只要打开相同的文件名,就能访问到相同的管道文件。两个进程通过open分别以读和写打开文件,利用write和read就能完成进程间通信,也不需要血缘关系。
需要注意的是,文件名只是用来标识唯一性,数据的传输更匿名管道一样,完全是内存级的。
方法一:系统指令 mkfifo
方法二:系统调用 mkfifo(const char* 文件名,mode_t 文件权限)
成功返回0,失败返回-1(包括文件已经存在的情况),并设置好退出码。
1.client 进程从标准输入按行读取信息
2.通过管道传入到server进程中
3.由server进程读取并输出到标准输出流。
1.创建 client.c server.c myfifo.hpp 在头文件中定义命名管道的路径
2.client 和 server 都先调用mkfifo。若返回-1根据errno判断是否存在。若errno不为-1则终止进程返回退出码
3.client 调用open 以只写的方式打开,server以只写的方式打开管道文件
4.client调用write server调用read 完成通信
头文件: myfifo.hpp
#pragma once
#include
#include
#include
#include
#include
#include
#include
#include
//文件路径 隐藏文件
#define fifo_path "./.myfifo"
#define fifo_mode 0666
//缓冲区大小
#define buffer_size 128
源文件:client.c server.c
client.c:
#include"myfifo.hpp"
int main(){
//创建命名管道
int mkfifo_ret=mkfifo(fifo_path,fifo_mode);
//判断是否存在
if(mkfifo_ret==-1&&errno!=EEXIST)
return errno;
//打开管道 只写
int myfifo_fd= open(fifo_path,O_WRONLY);
char client_buffer[buffer_size];
while(1){
//从标准输入读取数据
printf("请输入:> ");
fflush(stdout);
ssize_t read_ret=read(0,client_buffer,buffer_size-1);
//保证安全性
assert(read_ret
server.c
#include"myfifo.hpp"
int main(){
//创建命名管道
int mkfifo_ret=mkfifo(fifo_path,fifo_mode);
//判断是否存在
if(mkfifo_ret==-1&&errno!=EEXIST)
return errno;
//打开管道 只读
int myfifo_fd= open(fifo_path,O_RDONLY);
char server_buffer[buffer_size];
//从管道接收数据
while(1){
ssize_t read_ret=read(myfifo_fd,server_buffer,buffer_size-1);
if(read_ret==0)
break;
//保证安全性
assert(read_ret<=buffer_size);
//输出到标准输出
printf("服务器收到:> %s",server_buffer);
}
close(myfifo_fd);
return 0;
}
运行效果