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;
}
相关推荐
gooxi_hui1 小时前
海量存力,智驭未来丨国鑫4U60盘位高密度存储服务器SL401-G4重磅上市
运维·服务器·人工智能
吴爃1 小时前
小微企业 SRE 稳定性建设
运维·稳定性·小微企业
天空'之城2 小时前
Linux 系统编程 10:线程同步
linux·开发语言·系统编程·线程同步
河铃旅鹿2 小时前
在Ubuntu系统上为Android交叉编译OpenSSL
android·linux·ubuntu
开开心心_Every2 小时前
带OCR识别的电子发票打印工具
运维·自动化·ocr·电脑·powerpoint·音视频·lua
长孙豪翔2 小时前
引发事件的问题
java·linux·数据库
小张成长计划..2 小时前
【Linux】7:第一个系统程序-进度条
linux·运维·服务器
枳实-叶2 小时前
【Linux驱动开发】第23天:spi_driver 的 probe / remove 函数实现规范
linux·驱动开发·c#
李子琪。2 小时前
云计算虚拟化技术全解析:从理论到实践
linux·centos·云计算