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

1.命名管道

命名管道FIFO是一种通过文件路径标识的特殊文件,能够为不相关进程提供流式通信能力,任意进程只需要通过统一路径打开该文件即可实现跨进程数据交换其内核缓冲区独立于创建者生命周期存在,但本质仍是无消息边界的单向字节流通道。

1.1创建命名管道

命名管道可以从命令上创建

复制代码
mkfifo filename

从程序上创建

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

int main()
{
    mkfifo("h1",0644);
    return 0;
}

mode_t mode是文件掩码用于指定管道权限,规则同open/chmod,实际权限=mode&~umask,能够支持标准宏或自定义八进制值

常用权限

0644所有者读写,其余只读

0600仅所有者读写

0666读写权限最终=0666&~umask

注意mode只控制新建管道的访问权限不含执行权限,

mkfifo函数成功返回0,失败返回-1同时设置errno

1.2匿名管道与命名管道的区别

匿名管道由pipe函数创建并打开

命名管道由mkfifo创建,用open打开

FIFO命名管道与pipe匿名管道之间的唯一区别在于它们创建与打开的方式不同,一旦这些工作完成后就具有相同的语义了

且它们都是单向通道

1.3命名管道的打开方式

当前打开操作为读而打开FIFO时

O_NONBLOCK disable:阻塞直到有进程为写而打开FIFO

O_NONBLOCK enable:立即返回成功

当前打开操作为写而打开FIFO时

O_NONBLOCK disable:阻塞直到有进程为读而打开FIFO

O_NONBLOCK enable:立即返回失败,错误码为ENXIO

2.用命名管道实现文件拷贝

linux内核中规定,FIFO管道必须有读和写两端同时打开,才能正常通信

通过两个进程分别运行不同的程序实现使用命名管道进行文件拷贝

进程A,进行创建命名管道"tp",并以只读的模式打开在当前目录下的普通文件"abc",作为进行拷贝的源文件。

以只写模式打开刚创建的tp管道,后会被阻塞挂起,等待另一个进程B以只读模式打开同一个tp管道,随后将源文件的数据写入tp中。

cpp 复制代码
#include<cstdio>
#include<fcntl.h>
#include<sys/stat.h>
#include<sys/types.h>
#include<cstdlib>
#include<cerrno>
#include<unistd.h>  

#define ERR_EXIT(m)\
do\
{\
    perror(m);\
    exit(EXIT_FAILURE);\
}while(0)

int main(int argc,char* argv[])
{
    if(mkfifo("tp",0644)==-1&&errno!=EEXIST)
    ERR_EXIT("mkfifo");
    int infd;
    infd=open("abc",O_RDONLY);
    if(infd==-1)
    ERR_EXIT("open");
    int outfd;
    outfd=open("tp",O_WRONLY);
    if(outfd==-1)
    ERR_EXIT("open");
    char buf[1024];
    ssize_t n;
    while((n=read(infd,buf,1024))>0)
    {
        write(outfd,buf,n);
    }
    close(infd);
    close(outfd);
    return 0;
}

进程B以只写的形式打开当前目录下普通文件abc.bak,O_CREAT|O_TRUNC指的是文件若不存在则创建,若已存在则清空文件内容。

以只读的形式打开已存在的管道tp,此时写端进程A的阻塞会被解除,两端开始进行连接

随后循环不断读取tp管道内的内容写入文件abc.bak,直到管道数据读取完毕也就是收到EOF时,关闭文件描述符,调用unlink删除管道文件tp,清理资源

cpp 复制代码
#include<cstdio>
#include<fcntl.h>
#include<sys/stat.h>
#include<sys/types.h>
#include<cstdlib>
#include<cerrno>
#include<unistd.h>  
#define ERR_EXIT(m)\
do\
{\
    perror(m);\
    exit(EXIT_FAILURE);\
}while(0)

int main(int argc,char* argv[])
{
    int outfd;
    outfd=open("abc.bak",O_WRONLY|O_CREAT|O_TRUNC,0644);
    if(outfd==-1)
    ERR_EXIT("open");
    int infd;
    infd=open("tp",O_RDONLY);
    if(infd==-1)
    ERR_EXIT("open");
    char buf[1024];
    ssize_t n;
    while((n=read(infd,buf,1024))>0)
    {
        write(outfd,buf,n);
    }
    close(infd);
    close(outfd);
    unlink("tp");
    return 0;
}

3.使用命名管道实现客户端与服务器端通信

这是一个基于命名管道FIFO单向通信服务端,其设计为单客户端模式,单次运行仅负责接收一个客户端所发的消息,并且引入了信号处理机制,通过捕捉信号SIGINT信号也就是ctrl+c,可以确保用户强行中断进程时,能够自动清理残留的管道文件tp

客户端

cpp 复制代码
#include<iostream>
#include<cstring>
#include<fcntl.h>
#include<cerrno>
#include<unistd.h>
using namespace std;

#define ERR_EXIT(m)\
do\
{\
    perror(m);\
    exit(EXIT_FAILURE);\
}while(0);

int main()
{
    int wfd=open("tp",O_WRONLY);
    if(wfd==-1)
    ERR_EXIT("open");
    char buf[1025];
    cout<<"please wait..."<<endl;
    while(1)
    {
        fflush(stdout);
        ssize_t n=read(0,buf,sizeof(buf));//read会从buf[0]开始写
        if(n>0)
        {
            buf[n]=0;
            ssize_t s=write(wfd,buf,n);
            if(s<0)
            ERR_EXIT("write");
        }
        else if(n==0)
        {
            break;
        }
        else
        {
            ERR_EXIT("read");
        }
    }
    if(close(wfd)==-1)
    ERR_EXIT("close");
    return 0;
}

服务器端

cpp 复制代码
#include<iostream>
#include<cstring>
#include<fcntl.h>
#include<cerrno>
#include<unistd.h>
#include<signal.h>
#include<sys/types.h>
#include<sys/stat.h>
using namespace std;

#define ERR_EXIT(m)\
do\
{\
    perror(m);\
    exit(EXIT_FAILURE);\
}while(0);

const char* file_name="tp";
void clean_and_exit(int sig)
{
    unlink(file_name);
    _exit(0);
}

int main()
{
    umask(0);
    unlink(file_name);//删除同名文件
    if(mkfifo(file_name,0644)==-1)
    ERR_EXIT("mkfifo");
    //注册信号处理函数,处理使用ctrl+c发送结束进行的信号
    signal(SIGINT,clean_and_exit);
    int rfd=open(file_name,O_RDONLY);
    if(rfd<0)
    ERR_EXIT("open");
    char buf[1024];
    cout<<"please wait..."<<endl;
    while(1)
    {
        ssize_t n=read(rfd,buf,sizeof(buf));
        if(n>0)
        {
            cout<<"client says# ";
            cout.write(buf,n);//按精度长度输出,避免越界
        }
        else if(n==0)
        {
            cout<<"client disconnected"<<endl;
            break;
        }
        else
        {
            if(errno==EINTR)
            continue;
            ERR_EXIT("read");
        }
    }
    close(rfd);
    unlink(file_name);
    return 0;
}
相关推荐
律宏阔3 小时前
WSL Docker 端口明明空闲,但就是绑不上端口
linux·windows
律宏阔3 小时前
WSL 突然断网,无法 ping 内网或外网
linux·windows
穷人小水滴3 小时前
用容器编译 VirtualBox 虚拟机软件 (ArchLinux, podman)
linux·容器·virtualbox
乱码三千4 小时前
如何优雅地直连无公网 IP 的远程 GPU 服务器
linux·人工智能·程序员
yunwei374 小时前
使用 eBPF 跟踪 Nginx 请求
linux·后端·性能优化
yunwei374 小时前
使用 eBPF 跟踪 MySQL 查询
linux·后端·性能优化
yunwei374 小时前
eBPF 示例教程:使用 XDP 捕获 TCP 信息
linux·后端·性能优化
judezh4 小时前
api 容器一直 unhealthy?我把一个 Agent 运行时的健康检查逐条拆了,查出四个问题
运维·docker
GeW4 小时前
制造业高端化,为什么必须重估Linux和数据库?
linux
百万蹄蹄向前冲4 小时前
双端同步!云服务器装最新Node.js v26.10全过程追踪
服务器·人工智能·node.js