进程间的通信(一)管道

本文详细介绍了Linux中的匿名管道和命名管道,包括它们的定义、特点、创建方法以及读写规则。特别强调了匿名管道适用于父子进程间的通信,而命名管道则支持无亲缘关系进程间的通信。此外,还比较了两者在创建和打开方式上的区别。
摘要由CSDN通过智能技术生成

进程间的通信(一)管道

什么是管道

管道是Unix中最古老的进程间通信的形式。
我们把从一个进程连接到另一个进程的一个数据流称为一个“管道”
在这里插入图片描述

管道分为匿名管道(pipe)和命名管道(mkfifo)

匿名管道

什么是匿名管道

在Linux中,匿名管道(pipe)是一种用于进程间通信的机制,特别适用于具有亲缘关系的进程(如父子进程)之间的通信。匿名管道实际上是由内核管理的一块缓冲区,通过让不同的进程都能访问同一块缓冲区来实现进程间通信。

匿名管道的特点

•单向通信:管道是一个只能单向通信的通信信道。数据只能从一个方向流动,即从一个进程写入,然后从另一个进程读取。
•面向字节流:管道是面向字节流的,这意味着数据以字节为单位进行传输。
•父子通信:匿名管道主要用于父子进程或具有亲缘关系的进程之间的通信。
•自带同步机制:管道具有原子性写入的特性,并且自带同步机制。

创建一个匿名管道

#include <unistd.h>
功能:创建一无名管道
原型
int pipe(int fd[2]);
参数
fd:文件描述符数组,其中fd[0]表示读端, fd[1]表示写端
返回值:成功返回0,失败返回错误代码

创造匿名管道

int main()
{
	int pipefd[N] = {0};
    int n = pipe(pipefd);
}

在这里插入图片描述

特别适用于具有亲缘关系的进程(如父子进程)之间的通信

例如,父进程可以首先创建匿名管道,然后创建子进程。子进程通过管道的写端向管道中写入数据,而父进程则通过管道的读端从管道中读取数据,并且父子进程关掉不用的描述符,这种方式实现了父子进程之间的数据传递。
在这里插入图片描述

代码如下

int main()
{
    int pipefd[N] = {0};
    int n = pipe(pipefd);	//创造管道
    if (n < 0)
        return 1;

    // child -> w, father->r,这里子进程负责写,父进程负责读
    pid_t id = fork();		//创造子进程
    if (id < 0)				//创造子进程失败
        return 2;
    if (id == 0)		
    {
        // child
        close(pipefd[0]);	//子进程关掉读端

        // IPC code
        Writer(pipefd[1]);	//Writer函数为写入函数

        close(pipefd[1]);	//写完后关掉子进程写端文件描述符
        exit(0);			//子进程退出
    }
    // father
    close(pipefd[1]);		//父进程关掉写端

    // IPC code
    Reader(pipefd[0]); 		//Reader函数为读取函数
    
    close(pipefd[0]);		//读完后关掉父进程读端文件描述符

    int status = 0;
    pid_t rid = waitpid(id, &status, 0);  //回收子进程
    return 0;
}

匿名管道读写规则

O_NONBLOCK 指的是是否是非阻塞读取或写入

•当没有数据可读时
O_NONBLOCK disable:read调用阻塞,即进程暂停执行,一直等到有数据来到为止。
O_NONBLOCK enable:read调用返回-1,errno值为EAGAIN。

•当管道满的时候
O_NONBLOCK disable: write调用阻塞,直到有进程读走数据
O_NONBLOCK enable:调用返回-1,errno值为EAGAIN

如果所有管道写端对应的文件描述符被关闭,则read返回0
如果所有管道读端对应的文件描述符被关闭,则write操作会产生信SIGPIPE,进而可能导致write进程退出

命名管道

什么是命名管道

在Linux中,命名管道(named pipe)也被称为FIFO(First In First Out,先进先出)文件,它允许没有亲缘关系的进程之间进行通信与匿名管道不同,命名管道在文件系统中有一个对应的文件名,因此任何进程都可以通过文件名来访问它。

命名管道的特点

•命名和持久性:命名管道在文件系统中有一个唯一的名称,并且可以在多个进程之间共享和持久存在,直到显式地被删除。
•双向通信:虽然通常用作单向的数据流,但命名管道在技术上支持双向通信。两个进程可以同时打开同一个命名管道,一个用于写入,另一个用于读取。
•阻塞与非阻塞:命名管道的读写操作可以是阻塞的或非阻塞的,这取决于打开管道时设置的标志。
•字节流:命名管道传输的数据是字节流,与匿名管道类似。

创建一个命名管道

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

mkfifo [OPTION]... NAME...

OPTION:可选参,用于指定一些操作选项,下面详细介绍。
NAME:必选参,用于指定要创建的有名管道的名称。

其中mkfifo 命令的常用选项如下:
-m 权限:设置管道的权限,其格式与 chmod 命令相同。
-Z 文件类型:指定创建管道的 SELinux 上下文。
--help:显示帮助信息。
--version:显示版本信息。

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

#include <sys/types.h>, #include <sys/stat.h>
函数功能:创建有名管道。
原型:
int mkfifo(const char*filename,mode_t mode)。

参数1(filename):是将要在文件系统中创建的一个专用文件。
参数2(mode):用来规定FIFO的读写权限。
函数返回值:成功返回0,失败返回-1

创造命名管道

int main(int argc, char *argv[])
{
	mkfifo("p2", 0644);
	return 0;
}

它允许没有亲缘关系的进程之间进行通信

例如,假设有两个进程A和B,它们想要通过命名管道进行通信。可以首先使用mkfifo命令创建一个命名管道,然后进程client和server分别打开该命名管道,一个用于写入,另一个用于读取。进程client向管道中写入数据,进程server从管道中读取数据,从而实现了进程间的通信。

大致代码如下
comm.hpp

class Init
{
public:
    Init()
    {
        // 创建管道
        int n = mkfifo(FIFO_FILE, MODE);
    }
    
    ~Init()
    {
        int m = unlink(FIFO_FILE);
    }
};

client.cc

int main()
{
    int fd = open(FIFO_FILE, O_WRONLY);	//以写方式打开命名管道
	
	//向管道中写入数据
    string line;
    while(true)
    {
        cout << "Please Enter@ ";
        getline(cin, line);

        write(fd, line.c_str(), line.size());
    }

    close(fd);
    return 0;
}

server.cc

int main()
{
    // 打开管道
    int fd = open(FIFO_FILE, O_RDONLY); // 等待写入方打开之后,自己才会打开文件,向后执行, open 阻塞了!

 
	//从管道中读取数据
    while (true)
    {
        char buffer[1024] = {0};
        int x = read(fd, buffer, sizeof(buffer));
        if (x > 0)
        {
            buffer[x] = 0;
            cout << "client say# " << buffer << endl;
        }
        else if (x == 0)
        {
            log(Debug, "client quit, me too!, error string: %s, error code: %d", strerror(errno), errno);
            break;
        }
        else
            break;
    }

    close(fd);
    return 0;
}

命名管道的打开规则

•如果当前打开操作是为读而打开FIFO时
O_NONBLOCK disable:阻塞直到有相应进程为写而打开该FIFO
O_NONBLOCK enable:立刻返回成功

•如果当前打开操作是为写而打开FIFO时
O_NONBLOCK disable:阻塞直到有相应进程为读而打开该FIFO
O_NONBLOCK enable:立刻返回失败,错误码为ENXIO

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

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

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

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

  • 27
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值