IPC - 管道(pipe) - 应用

关于IPC

IPC,即Inter-Process Communication(进程间通信)。
我们都知道,进程一段程序的执行过程,是系统进程资源分配和调度的基本单位

那么为什么要引入进程间通信呢?或者说,进程间通信能带来哪些好处?

为什么需要IPC?

  • 数据传输
  • 共享数据
  • 通知事件
  • 资源共享
  • 进程控制

上面几点都很好理解,我们可以着重看一下最后一点:进程控制
有一些进程希望能够完全控制另一个进程的执行(比如Debug进程),此时控制进程希望能够拦截到另一进程的所有异常,并能够及时知道它的状态改变。

这一点,相信编程过的大家都很有体会吧,我们在写完一段代码,调试程序的时候,打断点分步执行进入执行跳出执行等等,每执行一步,我们可以通过监视器、或者查看内存中的数据来检查程序是否按照我们预想的那样执行,从而发现错误,修正错误。
如果没有进程间通信的话,进程与进程之间你执行你的,我执行我的,井水不犯河水,那么,如何实现调试的功能呢?

IPC的方法?

  1. 无名管道(pipe)
  2. 有名管道(fifo)
  3. 共享内存
  4. 消息队列
  5. 信号量
  6. Socket

今天介绍的重点就是无名管道pipe

关于管道

我们都知道每个进程都有自己的4G虚拟地址空间在这里插入图片描述
对于每个进程来说,3G的用户空间是独有的,1G的内核空间是共享的
基于此,管道本质上就是内核空间中的一块缓存

对于无名管道而言:

  • 必须在关系进程中进程(父子进程或兄弟进程);
  • pipe系统调用,管道由父进程建立;

函数原型:

#include <unistd.h>
int pipe(int fields[2])

其中的fields是我们传入的数组,也是一个传出参数。
fields[0]读端
fields[1]写端

对于命名管道而言:

  • 没有任何关系的进程间也可以通信;
  • 通过mkfifo系统调用创建。

关于 " | "

在Linux系统中," | " 就代表了管道。
比如说我们想通过查看Linux系统中的用户信息:
cat /etc/passwd
在这里插入图片描述
我们会看到非常多的信息。
此时,我只想看root用户的信息,该怎么做呢?
cat /etc/passwd | grep root
在这里插入图片描述
对于该命令的解读是:
在这里插入图片描述

pipe实现 " | "

通过对上面命令的解读,我们会发现,需要两个子进程,分别做catgrep的工作,然后将cat的执行结果写入管道,grep从管道中读取内容并且过滤

注意:
我们通过前面命令可以看到:cat的执行结果是默认输出到屏幕(标准输出)的,而grep的默认读取也是标准输入
所以,我们为了实现通信,需要将标准输入和标准输出进行重定向(使用dup2())。

代码如下:

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <wait.h>

char *cmd1[3] = {"/bin/cat", "/etc/passwd", NULL};
char *cmd2[3] = {"/bin/grep", "root", NULL};

int main()
{
    int fd[2];
    if (pipe(fd) < 0)
    {
        perror("pipe error");
        exit(1);
    }
    int i = 0;
    pid_t pid;
    for (; i < 2; i++)
    {
        pid = fork();
        if (pid < 0)
        {
            perror("fork error");
            exit(1);
        }
        else if (pid == 0) // child process
        {
            if (i == 0) // 第一个子进程,负责向管道写入数据
            {
                // 关闭读端
                close(fd[0]);

                // 将标准输出重定向到管道的写端
                // 下面命令执行的结果会写入到管道中
                // 而不是输出到屏幕
                if (dup2(fd[1], STDOUT_FILENO) != STDOUT_FILENO)
                {
                    perror("dup2 error");
                }
                close(fd[1]);

                // 调用exec函数执行cat命令
                if (execvp(cmd1[0], cmd1) < 0)
                {
                    perror("execvp error");
                    exit(1);
                }
                break;
            }
            if (i == 1) // 第二个子进程,负责从管道读取数据
            {
                // 关闭写端
                close(fd[1]);

                /*
                 * 将标准输入重定向到管道的读端
                 * 下面命令grep的执行是从管道的读端
                 * 读取内容,而不是从标准输入读取
                 */
                if (dup2(fd[0], STDIN_FILENO) != STDIN_FILENO)
                {
                    perror("dup2 error");
                    exit(1);
                }
                close(fd[0]);

                // 调用exec函数执行grep命令
                if (execvp(cmd2[0], cmd2) < 0)
                {
                    perror("execvp error");
                    exit(1);
                }
                break;
            }
        }
        else // parent process
        {
            if (i == 1)
            {
                // 父进程要等到子进程全部创建完毕才会去回收
                close(fd[0]);
                close(fd[1]);
                wait(0);
                wait(0);
            }
        }
    }

    exit(0);
}

运行结果对比:
在这里插入图片描述

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Dart Windows 中使用命名管道进行 IPC 通信的代码示例如下: ```dart import 'dart:io'; void main() async { // 创建命名管道 var server = await ServerSocket.bind('\\\\.\\pipe\\my_pipe'); print('Server listening on pipe: \\\\.\\pipe\\my_pipe'); // 等待客户端连接 var client = await server.first; print('Client connected from ${client.address.address}'); // 发送数据 client.write('Hello, client!'); // 接收数据 var data = await client.first; print('Received: $data'); // 关闭管道 await client.close(); await server.close(); } ``` ```dart import 'dart:io'; void main() async { // 连接到命名管道 var client = await Socket.connect('\\\\.\\pipe\\my_pipe'); print('Connected to pipe: \\\\.\\pipe\\my_pipe'); // 接收数据 var data = await client.first; print('Received: $data'); // 发送数据 client.write('Hello, server!'); // 关闭管道 await client.close(); } ``` 这是一个简单的示例,它显示了如何在 Dart 中使用命名管道进行 IPC 通信。需要注意的是, 在 Windows 中,命名管道的名称必须以 "\\.\pipe\" 开头。 ### 回答2: Dart是一种通过Dart编程语言开发应用程序的跨平台解决方案。在Windows平台上,我们可以使用Win32 API来实现命名管道(Named Pipes)IPC通信。 首先,我们需要安装并配置Dart环境以在Windows平台上开发应用程序。在Dart环境配置完成后,我们可以使用dart:ffi库来访问Win32 API。 下面是一个简单的代码示例,展示了如何使用Dart和Win32 API来实现命名管道IPC通信。这个示例中包含了服务端和客户端两部分的代码: 服务端代码(Server.dart): import 'dart:ffi'; import 'package:ffi/ffi.dart'; import 'package:win32/win32.dart'; void main() { // 创建命名管道对象 var pipeHandle = CreateNamedPipe( TEXT("\\\\.\\pipe\\myPipe"), // 命名管道的名称 PIPE_ACCESS_DUPLEX, // 可读可写的管道 PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, PIPE_UNLIMITED_INSTANCES, // 允许多个客户端连接 0, // 输出缓冲区大小 0, // 输入缓冲区大小 0, //默认超时时间 nullptr // 默认安全属性 ); if (pipeHandle == INVALID_HANDLE_VALUE) { print('命名管道创建失败'); return; } // 等待客户端连接,并处理消息 ConnectNamedPipe(pipeHandle, nullptr); final buffer = calloc<Uint8>(128); // 用于接收消息的缓冲区 while (ReadFile(pipeHandle, buffer, 128, nullptr, nullptr) != 0) { final message = utf8.decode(buffer.asTypedList()); print('接收到客户端的消息:$message'); memset(buffer, 0, 128); // 清空缓冲区 } // 断开命名管道连接并释放资源 DisconnectNamedPipe(pipeHandle); CloseHandle(pipeHandle); } 客户端代码(Client.dart): import 'dart:ffi'; import 'package:ffi/ffi.dart'; import 'package:win32/win32.dart'; void main() { // 打开命名管道 var pipeHandle = CreateFile( TEXT("\\\\.\\pipe\\myPipe"), // 命名管道的名称 GENERIC_READ | GENERIC_WRITE, // 可读可写 0, // 暂不支持共享模式 nullptr, // 默认安全属性 OPEN_EXISTING, // 打开已经存在的命名管道 0, nullptr); if (pipeHandle == INVALID_HANDLE_VALUE) { print('命名管道打开失败'); return; } final message = 'Hello, Server!'; final buffer = calloc<Uint8>(128); // 将消息写入命名管道 WriteFile(pipeHandle, message.toNativeUtf8(), message.length, nullptr, nullptr); // 从命名管道读取服务端的响应 ReadFile(pipeHandle, buffer, 128, nullptr, nullptr); final response = utf8.decode(buffer.asTypedList()); print('接收到服务端的响应:$response'); // 关闭命名管道 CloseHandle(pipeHandle); } 这个示例展示了如何使用Dart和Win32 API来实现命名管道IPC通信。在服务端代码中,我们创建了一个命名管道对象,并等待客户端连接。随后,我们可以通过管道进行读写操作。在客户端代码中,我们打开已经存在的命名管道,并通过管道发送消息给服务端,并接收服务端的响应。 请注意,为了能够编译和运行上述示例代码,您需要安装Dart SDK、Win32 API相关库,并在Dart环境中配置好FFI支持。同时,还需要导入dart:ffi和package:win32库以访问Win32 API。 希望以上示例对您有所帮助!如果您还有其他问题,请随时提问。 ### 回答3: Dart是一种跨平台编程语言,可以用于开发桌面应用程序。而Windows命名管道是一种用于在本地进程间进行通信的机制,可以在Windows操作系统上实现进程间的数据交换。 在Dart中使用Windows命名管道进行进程间通信涉及到与系统底层API的交互,可以通过dart:ffi库将Dart代码链接到Windows的动态链接库中,并使用相应的API来操作命名管道。 下面是一个示例代码,演示了在Dart中使用Windows命名管道进行进程间通信: ```dart import 'dart:ffi'; import 'dart:io'; // 导入Windows的动态链接库 final DynamicLibrary kernel32 = DynamicLibrary.open('kernel32.dll'); // 定义相关的Win32 API函数 final CreateNamedPipe = kernel32.lookupFunction< IntPtr Function(Pointer<Utf16> lpName, Uint32 dwOpenMode, Uint32 dwPipeMode, Uint32 nMaxInstances, Uint32 nOutBufferSize, Uint32 nInBufferSize, Uint32 nDefaultTimeOut, Pointer<Void> lpSecurityAttributes), int Function(Pointer<Utf16> lpName, int dwOpenMode, int dwPipeMode, int nMaxInstances, int nOutBufferSize, int nInBufferSize, int nDefaultTimeOut, Pointer<Void> lpSecurityAttributes)>( 'CreateNamedPipeW', ); final ConnectNamedPipe = kernel32.lookupFunction< Int32 Function( IntPtr hNamedPipe, Pointer<Void> lpOverlapped), int Function( int hNamedPipe, Pointer<Void> lpOverlapped)>('ConnectNamedPipe'); final WriteFile = kernel32.lookupFunction< Int32 Function(IntPtr hFile, Pointer<Void> lpBuffer, Uint32 nNumberOfBytesToWrite, Pointer<Uint32> lpNumberOfBytesWritten, Pointer<Void> lpOverlapped), int Function(int hFile, Pointer<Void> lpBuffer, int nNumberOfBytesToWrite, Pointer<Uint32> lpNumberOfBytesWritten, Pointer<Void> lpOverlapped)>( 'WriteFile', ); final ReadFile = kernel32.lookupFunction< Int32 Function(IntPtr hFile, Pointer<Void> lpBuffer, Uint32 nNumberOfBytesToRead, Pointer<Uint32> lpNumberOfBytesRead, Pointer<Void> lpOverlapped), int Function(int hFile, Pointer<Void> lpBuffer, int nNumberOfBytesToRead, Pointer<Uint32> lpNumberOfBytesRead, Pointer<Void> lpOverlapped)>( 'ReadFile', ); final CloseHandle = kernel32.lookupFunction<Int32 Function(IntPtr hObject), int Function(int hObject)>('CloseHandle'); // 主函数 void main() { // 创建命名管道 final pipeName = '\\\\.\\pipe\\MyPipe'; final pipeHandle = CreateNamedPipe( pipeName.toNativeUtf16(), 3, 0, 255, 65536, 65536, 0, Pointer.fromAddress(0), ); // 等待客户端连接 final connectResult = ConnectNamedPipe(pipeHandle, Pointer.fromAddress(0)); if (connectResult != 0) { print('连接已建立'); // 向管道写入数据 final message = 'Hello, World!'; final buffer = message.toNativeUtf16(); final bufferLength = buffer.length * 2; final bytesWrittenPtr = Pointer<Uint32>.allocate(); WriteFile(pipeHandle, buffer.cast(), bufferLength, bytesWrittenPtr, Pointer.fromAddress(0)); print('写入成功'); // 从管道读取数据 final bufferSize = 255; final bufferPtr = Pointer<Uint16>.allocate(count: bufferSize); final bytesReadPtr = Pointer<Uint32>.allocate(); ReadFile(pipeHandle, bufferPtr.cast(), bufferSize * 2, bytesReadPtr, Pointer.fromAddress(0)); final receivedMessage = bufferPtr.cast<Utf16>().unpackString(bytesReadPtr.value); print('收到消息: $receivedMessage'); // 关闭管道句柄 CloseHandle(pipeHandle); } } ``` 以上代码演示了如何在Dart中使用Windows命名管道进行进程间通信。首先使用`CreateNamedPipe`函数创建了一个命名管道,然后使用`ConnectNamedPipe`函数等待客户端的连接。连接建立后,向管道写入数据使用`WriteFile`函数,然后从管道读取数据使用`ReadFile`函数。最后通过`CloseHandle`函数关闭管道句柄。 需要注意的是,上述代码仅适用于Windows操作系统,且需要在Windows系统上编译和运行。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值