📟作者主页:慢热的陕西人
🌴专栏链接:Linux
📣欢迎各位大佬👍点赞🔥关注🚓收藏,🍉留言
本博客主要内容讲解了什么是命名管道,匿名管道和命名管道的区别,并且实现了一个案例来实践操作
文章目录
- Linux命名管道
- 1.概念
- 2.创建一个命名管道
- 3.匿名管道与命名管道的区别
- 4.我们实现一个即时的输入输出
- 5.完整代码
 
Linux命名管道
1.概念
- 管道应用的一个限制就是只能在具有共同祖先(具有亲缘关系)的进程间通信
- 如果我们想在不相关的进程之间交换数据,可以使用FIFO文件来做这项工作,它经常被称为命名管道
- 命名管道是一种特殊类型的文件
2.创建一个命名管道
命名管道可以从命令行上创建,命令行方法是使用下面这个命令 :
mkfifo filename
见见猪跑:
向管道写入:
echo hello > fifo:我们发现程序卡住了,细节是我们查看fifo的文件的大小的时候,它却是0,因为我们知道管道是内存级文件,他的内容不会刷新到磁盘,所以我们看到的文件大小就是0,当我们cat < fifo也就是将fifo中的内容重定向输出到cat中打印出了hello。
那么上面的操作,是通过一个管道文件,让两个无关的进程实现的进程间的通信。那么我们知道进程间的通信就是要让两个进程看到同一个文件,那么这里我们是如何做到同一个文件呢?答案是路径,文件的唯一性就是由路径表示的!
那么我们到底是如何使用命名管道实现两个进程间的通信的:
①我们需要创建一个管道文件
这里我们需要用到一个系统接口
mkfifo
#include<iostream> #include<sys/stat.h> #include<sys/types.h> #include<string.h> #include<cerrno> #include "comm.hpp" using namespace std; int main() { //1.创建管道文件,我们今天只需要创建一次 int n = mkfifo(filename.c_str(),mode); if(n != 0) //创建失败 { cout << errno << " : "<< strerror(errno)<< endl; return 1; } return 0; }运行一下:
我们发现确实创建了文件:但是文件的权限却不是我们想要的
0666,其实我们一下就能看出来原因是我们的umask不为零影响到了结果
我们查看一个操作系统的接口:
umask
#include<iostream> #include<sys/stat.h> #include<sys/types.h> #include<string.h> #include<cerrno> #include "comm.hpp" using namespace std; int main() { //1.创建管道文件,我们今天只需要创建一次 umask(0); //只影响当前进程的umask int n = mkfifo(filename.c_str(),mode); if(n != 0) //创建失败 { cout << errno << " : "<< strerror(errno)<< endl; return 1; } return 0; }运行一下看看:
②让读写端进程分别按照自己的需求打开文件
//1.不需要创建管道文件,我只需要打开对应的文件即可! int wfd = open(filename.c_str(), O_WRONLY); if(wfd < 0) { cerr << errno << " : "<< strerror(errno)<< endl; return 1; } //1.创建管道文件,我们今天只需要创建一次 umask(0); //只影响当前进程的umask int n = mkfifo(filename.c_str(),mode); if(n != 0) //创建失败 { cout << errno << " : "<< strerror(errno)<< endl; return 1; }③开始通信
//3.开始通信 char buffer[NUM]; while(true) { buffer[0] = 0; //初始化第一个位置为\0 //读文件 ssize_t n = read(rfd, buffer, sizeof(buffer) - 1); if(n > 0) { //读成功了 buffer[n] = 0; printf("%s\n", buffer); fflush(stdout); } else if(n == 0) { //写端关闭 cout << "client quit, me too" << endl; break; } else { //错误 cout << errno << " : "<< strerror(errno)<< endl; return 1; } } //2.开始通信 char buffer[NUM]; while(true) { cout << "请输入要写入的信息#"; char* msg = fgets(buffer, sizeof(buffer), stdin); assert(msg); (void)msg; buffer[strlen(buffer) - 1] = 0; //当我们写入quit的时候退出写端 if(strcasecmp(buffer, "quit") == 0) break; ssize_t n = write(wfd, buffer, strlen(buffer)); assert(n >= 0); (void)n; }运行结果:
3.匿名管道与命名管道的区别
- 匿名管道由pipe函数创建并打开。
- 命名管道由mkfifo函数创建,打开用open
- FIFO(命名管道)与pipe(匿名管道)之间唯一的区别在它们创建与打开的方式不同,一但这些工作完成之后,它们具有相同的语义
4.我们实现一个即时的输入输出
就是我们输入管道的时候不用回车,而是选用输入一个字符就从管道输出:
我们要在client.cc端改变其向文件写入的方式,我们读取到字符c中,然后没输入一个字符就同步;
    while(true)
    {
        system("stty raw");  // 使终端驱动处于一次一字符模式
        char c = getchar();
        system("stty cooked");// 使终端驱动回到一次一行模式 
        ssize_t n = write(wfd, &c, sizeof(char));
        assert(n >= 0); 
        (void)n;
    }
运行结果:

5.完整代码
client.cc
#include<iostream>
#include<sys/stat.h>
#include<sys/types.h>
#include<fcntl.h>
#include<unistd.h>
#include<string.h>
#include<cerrno>
#include<cassert>
#include"comm.hpp"
using namespace std;
int main()
{
    //1.不需要创建管道文件,我只需要打开对应的文件即可!
    int wfd = open(filename.c_str(), O_WRONLY);
    if(wfd < 0)
    {
        cerr << errno << " : "<< strerror(errno)<<  endl;
        return 1;
    }
    //2.开始通信
    char buffer[NUM];
    while(true)
    {
        // cout << "请输入要写入的信息#";
        // char* msg = fgets(buffer, sizeof(buffer), stdin);
        // assert(msg);
        // (void)msg;
        // buffer[strlen(buffer) - 1] = 0;
        system("stty raw");  // 使终端驱动处于一次一字符模式
        char c = getchar();
        system("stty cooked");// 使终端驱动回到一次一行模式 
        // //当我们写入quit的时候退出写端
        // if(strcasecmp(buffer, "quit") == 0) break;
        ssize_t n = write(wfd, &c, sizeof(char));
        assert(n >= 0); 
        (void)n;
    }
    return 0;
}
server.cc
#include<iostream>
#include<sys/stat.h>
#include<sys/types.h>
#include<fcntl.h>
#include<unistd.h>
#include<string.h>
#include<cerrno>
#include "comm.hpp"
using namespace std;
int main()
{
    //1.创建管道文件,我们今天只需要创建一次
    umask(0); //只影响当前进程的umask
    int n = mkfifo(filename.c_str(),mode);
    if(n != 0) //创建失败
    {
        cout << errno << " : "<< strerror(errno)<<  endl;
        return 1;
    }
    cout << "创建文件成功!"<< endl;
    //2.打开文件
    int rfd = open(filename.c_str(), O_RDONLY);
    if(rfd < 0) //打开错误
    {
        cout << errno << " : "<< strerror(errno)<<  endl;
        return 2;
    }
    cout << "打开管道成功" << endl;
    //3.开始通信
    char buffer[NUM];
    while(true)
    {
        buffer[0] = 0; //初始化第一个位置为\0
        //读文件
        ssize_t n = read(rfd, buffer, sizeof(buffer) - 1);
        if(n > 0)
        {
            //读成功了
            printf("%c", buffer[0]);
            fflush(stdout);
        }
        else if(n == 0)
        {
            //写端关闭
            cout << "client quit, me too" << endl;
            break;
        }
        else
        {
            //错误
            cout << errno << " : "<< strerror(errno)<<  endl;
            return 1;
        }
    }
    //4.关闭文件
    close(rfd);
    unlink(filename.c_str());
    return 0;
}
comm.hpp
#pragma once
#include <iostream>
#include <string>
using namespace std;
#define NUM 65535 //管道缓冲区大小
string filename = "./fifo"; //文件名
mode_t mode = 0666; //打开方式
到这本篇博客的内容就到此结束了。
如果觉得本篇博客内容对你有所帮助的话,可以点赞,收藏,顺便关注一下!
如果文章内容有错误,欢迎在评论区指正



























