Linux 进程间通信---命名管道

1.命名管道的原理

1,如果是具有血缘关系的进程,想要通信我们可以使用匿名管道,如果我们想在不相关的进程之间交换数据,可以使用FIFO文件来做这项工作,它经常被称为命名管道。

2.在内核中,操作系统会打开一个文件,即在内核中,仅有一个文件的inode结构体,文件的files_operations结构体,一个内核级别的文件缓冲区,那么由于这两个进程可能会有不同的打开文件的方式,并且两个文件的执行流不同,读取写入到文件的位置可能也不同,所以分别会给这两个进程各自分配一个文件打开对象。

3.与匿名管道不同,进程间的通信本质是让不同的进程看到同一份资源,在匿名管道中,子进程继承了父进程的文件描述符表,父子进程一个关闭读端,一个关闭写端,从而实现进程间的通信,命名管道这里,两个不同的进程以只读和只写的方式打开文件,也达到了看到同一份资源的目的。

4.那么两个进程如何知道它们两个打开的是同一个文件?因为进行打开的时候是采用的是同路径下的同一个文件名 = 路径 + 文件名 = 具有唯一性。

5.同样的,这个命名管道也管道它也具有管道的特征,即单向通信,并且不需要将缓冲区的内容刷新到磁盘上,命名管道同样也是一个文件,这个文件是内存级文件。

2.创建命名管道

命名管道可以从命令行上创建,命令行方法是使用下面这个命令:

cpp 复制代码
mkfifo filename

命名管道也可以从程序里创建,相关函数有:

cpp 复制代码
int mkfifo(const char *filename,mode_t mode);

mkfifo需要传参,第一个参数传参命名文件的路径,第二个传参命名文件的权限模式,mkfifo的返回值是一个int的变量,如果mkfifo创建命名管道成功,那么就会返回0,如果没有创建成功,创建失败了,那么就会返回-1,并且设置对应的错误码。

cpp 复制代码
  1 #include<stdio.h>
  2 #include<sys/stat.h>
  3 int main(int argc, char *argv[])
  4 {
  5         mkfifo("myfifo",0644);
  6         return 0;
  7 }

删除命名管道可以使用unlink。

3.命名管道的4种情况

读写端正常,管道为空,读端就要阻塞,可以有效保护数据安全

读写端正常, 管道满了,写端就要阻塞,可以有效保护数据安全

读端正常读,写端关闭,读端就会读到0,代表读到了文件(pipe)结束,读端退出,不会阻塞

写端正常写,读端关闭,操作系统就要通过13号信号杀掉正在写入的写端进程

4.进程间编码的实现

makefile

cpp 复制代码
  1 .PHONY:all
  2 all:client server
  3 
  4 client:client.cpp
  5         g++ -o $@ $^ -g -std=c++11
  6 server:server.cpp
  7         g++ -o $@ $^ -g -std=c++11
  8 .PHONY:clean
  9 clean:
 10         rm -f client server

生成client和server可执行程序

pipename.hpp

cpp 复制代码
#pragma once
#include<iostream>
#include<stdio.h>
#include<stdlib.h>
#include <fcntl.h>          
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include<string>
#include<string.h>
#define FILE "./log.txt"
#define MODE 0644
enum 
{
    FIFO_CREATE_ERR=1,
    FIFO_DELETE_ERR,
    FIFO_OPEN_ERR  
};
class Init
{
public:
    Init()
    {
        int n=mkfifo(FILE,MODE);
        if(n<0)
        {
            perror("mkfifo");
            exit(FIFO_CREATE_ERR);
        }
    }
    ~Init()
    {
        int m=unlink(FILE);
        if(m<0)
        {
            perror("mkfifo");
            exit(FIFO_DELETE_ERR);
        }
    }
};

将创建管道文件封装为一个类,创建类时自动创建命名管道。

client.cpp

cpp 复制代码
#include"./pipe.hpp"
int main()
{
    int fd=open(FILE,O_WRONLY);
    if(fd<0)
    {
        perror("open");
        exit(FIFO_OPEN_ERR);
    }
    std::cout<<"clinet open success"<<std::endl;
    std::string line;
    while(true)
    {
        std::cout<<"Pliease enter#";
        getline(std::cin,line);
        int n=write(fd,line.c_str(),line.size());
    }
    close(fd);
    return 0;
}

以只写模式打开文件,不断的从键盘获取数据发送给文件。

server.cpp

cpp 复制代码
#include"./pipename.hpp"
int main()
{
    Init p;
    int fd=open(FILE,O_RDONLY);
    if(fd<0)
    {
        perror("open");
        exit(FIFO_OPEN_ERR);
    }
    while(true)
    {
        char buff[1024];
        int n=read(fd,buff,sizeof(buff));
        if(n>0)
        {
            buff[n]='\0';
            std::cout<<"client say#"<<buff<<std::endl;
        }
        else
        {
            break;
        }
    }
    close(fd);
    return 0;
}

以只读模式打开,并且打印在显示器上面,进程退出时,命名管道也会自动被删除。

运行结果:

相关推荐
BlueBirdssh36 分钟前
linux 内核通过 dts 设备树 配置pcie 控制器 各种参数和中断等, 那freeRTOS 是通过直接设置PCIe寄存器吗
linux
小目标一个亿1 小时前
Windows平台Nginx配置web账号密码验证
linux·前端·nginx
实战项目1 小时前
软件测试自动化框架的设计与实现
运维·自动化
Aotman_1 小时前
Element-UI Message Box弹窗 使用$confirm方法自定义模版内容,修改默认样式
linux·运维·前端
Elastic 中国社区官方博客2 小时前
使用 Elastic 中的 OpenTelemetry 为 Nginx 实现端到端分布式追踪的实用指南
大数据·运维·分布式·elasticsearch·搜索引擎·信息可视化·全文检索
独自破碎E2 小时前
配置ssh解决https不稳定的问题
运维·ssh
heartbeat..2 小时前
零基础学 SQL:DQL/DML/DDL/DCL 核心知识点汇总(附带连接云服务器数据库教程)
java·服务器·数据库·sql
那些年的笔记3 小时前
Linux屏幕旋转方法
linux·运维·服务器
XiaoHu02073 小时前
Linux网络编程套接字
linux·服务器·网络·git
竹之却3 小时前
CentOS 系列,防火墙相关指令
linux·运维·centos