进程间通信之命名管道

命名管道

1.命名管道概念

管道应用的一个限制就是只能在具有共同祖先(具有亲缘关系)的进程间通信,这种通信可以用匿名管道来实现。如果我们想在不相关的进程之间交换数据,可以使用FIFO文件来做这项工作,它经常被称为命名管道

1.什么是命名管道?

命名管道(named pipe)也称为FIFO,它是一种文件类型,创建一个FIFO文件类似于创建一个普通文件。FIFO解决了只有具有亲缘关系的进程间才能通信的问题。并且命名管道是一种特殊类型的文件。

2.匿名管道与命名管道的区别?

  1. 匿名管道由pipe函数创建并打开。
  2. 命名管道由mkfifo函数创建,打开用open
  3. FIFO(命名管道)与pipe(匿名管道)之间唯一的区别在它们创建与打开的方式不同,一但这些工作完成之后,它们具有相同的语义

2.创建一个命名管道

先认识一下mkfifo函数:mkfifo是一个系统调用,用于创建一个命名管道(named pipe),也称为FIFO。
在这里插入图片描述
用mkfifo创建一个管道文件:
在这里插入图片描述
可以看到,管道文件的标志是p,并且大小为零;因为管道文件的数据不需要刷新到磁盘里,所以管道文件的大小一直为零,用vim打开管道文件那就gg。

mkfifo也是一个函数调用,成功返回0,失败返回-1。
,

int mkfifo(const char *pathname, mode_t mode)
返回值int
参数pathname :创建管道的路径。也就是在这个路径下创建一个管道。不带路径,只有文件名,那么默认在当前路径下。
mode_t mode :创建管道的权限。比如0666,0664等,系统下默认掩码umask()=0002。

命名管道的打开规则:

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

2.1用命名管道实现通信

要求:进程A 向管道当中写 “i am process A”,进程B 从管道当中读 并且打印到标准输出。
进程A代码如下:

#include <iostream>
#include <cstring>
#include <cerrno>
#include <cassert>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
using namespace std;

int main()
{
    //1.创建管道文件mkfifo
    umask(0); //这个设置并不影响系统的默认配置,只会影响当前进程
    int n = mkfifo("./fifo", 0666);
    if (n == -1) //失败返回-1,打印错误码并且退出1
    {
        cout << errno << " : " << strerror(errno) << endl;
        return 1;
    }
    //2.让进程A开启管道文件
    int wfd = open("fifo", O_WRONLY);
    if (wfd == -1)//失败返回-1,打印错误码并且退出2
    {
        cout << errno << " : " << strerror(errno) << endl;
        return 2;
    }
    //3.向管道中写入"i am process A"
    while (true)
    {
        char buffer[1024] = "i am process A";
        ssize_t m = write(wfd, buffer, strlen(buffer));
        assert(m > 0);
        (void)m;
        sleep(1);
    }
    close(wfd);
    unlink("fifo");//去掉管道文件的链接,也就是删除fifo文件

    return 0;
}

进程B代码如下:

#include <iostream>
#include <cstring>
#include <cerrno>
#include <cassert>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
using namespace std;

int main()
{
    //1.不需创建管道文件,只需要打开对应的文件即可
    int rfd = open("fifo", O_RDONLY);
    if (rfd == -1)//失败返回-1,打印错误码并且退出1
    {
        cout << errno << " : " << strerror(errno) << endl;
        return 1;
    }
    //2.可以进行常规通信了
    while (true)
    {
        char buffer[1024];
        int n = read(rfd, buffer, sizeof(buffer) - 1);
        assert(n >= 0);
        cout << "我是B进程,读取到的数据是:" << buffer << endl;
        sleep(1);
    }
    close(rfd);

    return 0;
}

makefile中代码如下:

.PHONY:all
all:server client

server:server.cc
	g++ -o $@ $^ -std=c++11
client:client.cc
	g++ -o $@ $^ -lncurses -std=c++11 

.PHONY:clean
clean:
	rm -f server client

如果A进程报如下错误:是因为fifo管道文件存在,将fifo文件手动删除即可(rm -f fifo:rm删除文件;-f:强制删除;-r:递归删除,一般用于删除目录等树状结构)。(所以文件末尾需要unlink(“fifo”);//删掉创建的管道文件)
在这里插入图片描述
运行结果如下,可以在B进程看到A进程的消息
在这里插入图片描述

2.2用命名管道实现server&client通信

要求:client从键盘读入数据,并且发送到管道;server从管道读取数据,并且打印到标准输出。

comm.hpp中代码如下:

#pragma once //防止头文件重复引用

#include <iostream>
#include <cstdio>
#include <cerrno>
#include <cstring>
#include <cassert>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#define NUM 1024

const std::string fifoname = "./fifo";
uint32_t mode = 0666;

server.cc中代码如下:

#include "comm.hpp"

int main()
{
    umask(0); 
    int n = mkfifo(fifoname.c_str(), mode);
    if(n != 0)
    {
        std::cout << errno << " : " << strerror(errno) << std::endl;
        return 1;
    }
    cout << "create fifo file success" << endl;
    
    int rfd = open(fifoname.c_str(), O_RDONLY);
    if(rfd < 0 )
    {
        std::cout << errno << " : " << strerror(errno) << std::endl;
        return 2;
    }
    std::cout << "open fifo success, begin ipc" << std::endl;

    char buffer[NUM];
    while(true)
    {
        buffer[0] = 0;
        ssize_t n = read(rfd, buffer, sizeof(buffer) - 1);
        if(n > 0)
        {
            buffer[n] = 0;
            std::cout << "client# " << buffer << std::endl;
        }
        else if(n == 0)
        {
            std::cout << "client quit, me too" << std::endl;
            break;
        }
        else 
        {
            std::cout << errno << " : " << strerror(errno) << std::endl;
            break;
        }
    }

    // 关闭不要的fd
    close(rfd);

    unlink(fifoname.c_str());

    return 0;
}

client.cc中代码如下:

#include "comm.hpp"

int main()
{
    int wfd = open(fifoname.c_str(), O_WRONLY);
    if(wfd < 0)
    {
        std::cerr << errno << ":" << strerror(errno) << std::endl;
        return 1;
    }

 
    char buffer[NUM];
    while(true)
    {
        std::cout << "请输入你的消息# ";
        char* msg = fgets(buffer, sizeof(buffer), stdin);
        assert(msg);
        (void)msg;
        
        sleep(1);

        buffer[strlen(buffer) - 1] = 0;

        if(strcasecmp(buffer, "quit") == 0) break;

        ssize_t n = write(wfd, buffer, strlen(buffer));
        assert(n >= 0);
        (void)n;
    }

    close(wfd);

    return 0;
}

makefile中代码如下:

.PHONY:all
all:server client

server:server.cc
	g++ -o $@ $^ -std=c++11
client:client.cc
	g++ -o $@ $^ -lncurses -std=c++11

.PHONY:clean
clean:
	rm -f server client

运行结果如下:
在这里插入图片描述

  • 16
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 13
    评论
### 回答1: Dart 是 Google 开发的编程语言,可以用来开发移动应用和 web 应用。命名管道是 UNIX 系统中的一种 IPC(进程间通信)方式,可以在不同进程之间传递数据。在 Dart 中,可以使用 dart:io 库中的 NamedPipe 类来操作命名管道。 下面是一个简单的 Unix 示例,演示了如何在两个不同的 Dart 程序之间使用命名管道进行通信: 程序1(sender.dart) ```dart import 'dart:io'; main() async { var pipe = await NamedPipeServer.create('/tmp/my_pipe'); var sink = pipe.sink; sink.write('Hello from sender!'); await sink.flush(); await sink.close(); } ``` 程序2(receiver.dart) ```dart import 'dart:io'; main() async { var pipe = await NamedPipeClient.connect('/tmp/my_pipe'); var input = pipe.input; var message = await input.read(); print(message); await input.close(); } ``` 在这个示例中,程序 1 使用 NamedPipeServer.create() 创建了一个名为 /tmp/my_pipe 的命名管道,并向其中写入了一条消息 "Hello from sender!"。程序 2 使用 NamedPipeClient.connect() 连接到了同一个命名管道,并从中读取了消息,最后打印出来。 ### 回答2: Dart是一种跨平台的编程语言,支持在不同的操作系统上进行开发。在Unix系统中,进程间通信是一个非常重要的概念。命名管道是一种用于在不同进程之间进行通信的机制。 在Dart中,可以使用dart:io包来实现使用命名管道进行进程间通信的功能。首先,需要创建一个命名管道。可以使用File类的static方法mkfifo来创建一个命名管道文件。 具体示例代码如下: import 'dart:io'; void main() { String pipePath = './mypipe'; // 命名管道的文件路径 Process.start('mkfifo', [pipePath]).then((Process process) { print('命名管道已创建'); // 接下来可以进行进程间通信的操作 }); } 上面的示例代码中,首先使用Process.start方法调用mkfifo命令来创建一个命名管道文件,然后通过then回调函数来监听创建命名管道的结果,并在命名管道创建成功后进行进一步的处理。 在创建命名管道后,就可以使用命名管道进行进程间通信了。可以使用File类来打开命名管道,并进行读写操作。具体的进程间通信的操作可以根据需求进行自定义。 需要注意的是,使用命名管道进行进程间通信时,需要确保读、写操作的顺序正确,以避免出现阻塞的情况。此外,命名管道也可以用于进程间的同步操作,例如等待另一个进程完成某个指定的任务后再进行下一步操作。 总而言之,Dart可以通过使用dart:io包来实现在Unix系统中使用命名管道进行进程间通信的功能。通过创建命名管道文件,然后使用File类打开该文件进行读写操作,可以实现不同进程之间的通信与同步。具体的操作可以根据需求进行自定义。 ### 回答3: 在Unix系统中,进程间通信是非常重要的,为了实现进程间的数据传递,可以使用命名管道。Dart语言也提供了类似的方法来实现进程间通信命名管道是一种特殊的文件,它可以用来实现进程间的读写操作。在Unix示例中,首先我们需要创建一个命名管道,在Dart中可以使用File类的create方法来创建文件。创建命名管道的操作类似于创建普通文件,只是我们需要给文件一个特殊的名称,通常以“/tmp/”开头。 在创建完命名管道之后,我们可以使用File类的open方法来打开管道。在Unix示例中,我们需要使用O_RDONLY或者O_WRONLY标识符来指定管道的读写模式。然后,我们可以使用File类的read方法来读取管道中的数据,使用write方法来写入数据。 在进程间通信时,通常会有一个进程负责写入数据,另一个进程负责读取数据。这两个进程可以通过访问同一个命名管道来实现通信。读取进程会在打开管道之后一直等待数据的到来,直到写入进程向管道中写入数据。然后读取进程会读取数据并进行相应的处理。 需要注意的是,在使用命名管道时,需要保证写入和读取的进程顺序是正确的,否则可能会产生读写阻塞的问题。此外,写入和读取的进程在使用管道时需要进行同步,以免出现数据竞争的情况。 总结起来,Dart语言可以使用命名管道来实现进程间通信,类似于Unix中的示例。使用命名管道可以在进程间传递数据,实现数据的读写操作。但是需要注意保证进程的顺序和同步,以确保通信的正确性和稳定性。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ly@눈_눈

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值