深入理解和实现Windows进程间通信(管道)

news2024/11/26 18:53:48

进程间通信(IPC,Inter-Process Communication)是指在不同进程之间的数据传输。进程是操作系统分配资源和调度的独立单位,每个进程都有自己独立的地址空间,一个进程无法直接访问另一个进程的数据。因此,当需要数据共享或任务协作时,就必须使用IPC机制。

常见的进程间通信方法

常见的进程间通信方法有:

  1. 管道(Pipe)
  2. 消息队列
  3. 共享内存
  4. 信号量
  5. 套接字

下面,我们将详细介绍管道的原理以及具体实现。

什么是管道?

管道是共享内存的一部分,用于促进进程之间的通信。
Windows支持两种类型的管道:匿名管道和命名管道。
这两种管道虽然在使用场景和功能上有所区别,但底层实现原理都是基于操作系统的内核管理,涉及内存管理、数据同步和进程间安全通信。

原理

管道.png
管道是一种存储在文件系统之外的文件,没有名称或任何其他特定属性。但它有两个文件描述符,我们可以像处理文件一样处理它。

内核空间的数据缓冲区

Windows操作系统中的管道通信依赖于内核空间中的缓冲区来暂存从一个进程输出的数据,直到另一个进程读取这些数据。这些缓冲区完全由操作系统的内核控制,并且与任何特定进程的用户空间内存隔离。

  1. 数据的写入和读取
  • 当一个进程使用WriteFile系统调用向管道写入数据时,数据首先从该进程的用户空间复制到内核空间的缓冲区。
  • 另一个进程通过ReadFile系统调用从管道中读取数据,实际上是从内核空间的缓冲区获取数据。
  1. 缓冲区关联
  • 缓冲区的大小通常在创建管道时指定,这影响数据传输的效率和进程阻塞的行为。
  • 如果缓冲区已满,写操作会阻塞,直到有足够的空间可用;如果缓冲区为空,读操作同样会阻塞,直到缓冲区中有数据可读。

通过管道写入的数据不存在任何进程的内存中,而是由操作系统内核管理的内存。

文件描述符和句柄

Windows系统使用文件句柄来代表管道。这些句柄提供了对管道的引用,使得应用程序可以通过标准的文件I/O接口与管道交互。

  • 句柄的作用:句柄抽象了对管道的操作,允许进程通过调用如CreatePipe(创建匿名管道)或CreateNamedPipe(创建命名管道)等API来创建和管理管道。

同步和异步操作

为了支持高校的数据传输,Windows的管道机制支持同步和异步操作模式。这些模式决定了操作系统如何处理进程间的数据传输和进程的执行状态。

  1. 同步操作
  • 在同步模式下,进程在执行读写操作时如果遇到阻塞(如等待数据可用或缓冲区有空间),则进程会挂起,直到操作可以继续为止。
  1. 异步操作
  • 异步操作允许进程继续执行其他任务,而不是在管道操作完成前阻塞。这是通过使用重叠I/O实现的,其中管道操作与进程的其他活动并行进行。

安全性和访问控制

Windows的管道机制包括复杂的安全属性和访问控制列表(ACL),这些功能确保了管道通信的安全性。

  • 安全属性:在创建命名管道时,可以指定安全属性,这些属性定义了哪些用户或进程可以访问管道,以及他们的访问权限。

匿名管道和命名管道

匿名管道

  1. 用途和特点
  • 匿名管道通常用于父子进程间的通信。
  • 只支持半双工通信(单向数据流),即数据只能在一个方向上流动。
  • 匿名管道没有名称,因此不能跨越不相关的进程使用,仅在有共同祖先的进程间有效
  1. 实现和使用
  • 在类Unix系统下,匿名管道通过pipe()创建
  • 在Windows系统下,通过CreatePipe()创建
  • 由于管道是无名的,所以它们在文件系统中没有表示
  1. 生命周期
  • 匿名管道的生命周期通常与创建它们的进程相绑定,当这些进程结束或显式地关闭管道时,管道也会被销毁。

命名管道

  1. 用途和特点
  • 命名管道可以在没有直接关系的进程之间使用,例如不同用户运行的进程。
  • 支持全双工通信(双向数据流)。
  • 命名管道通过文件系统中的名称识别,这允许不同的进程通过打开同一个名字的管道来进行通信。
  1. 实现和使用
  • 在类Unix系统下,通过mkfifo()创建命名管道。
  • 在Windows系统下,通过CreateNamedPipe()创建命名管道。
  • 命名管道可以配置为持久存在,即使创建它的进程已经终止。
  1. 网络通信
  • 命名管道除了支持单机内不同进程间的通信外,也可以配置用于网络中不同计算机间的通信。

匿名管道和命名管道的主要区别

  • 生命周期:匿名管道通常在进程结束时销毁,而命名管道可以独立于创建它的进程存在。
  • 通信范围:匿名管道仅限于有共同祖先的进程间通信,而命名管道可以在任何能访问给定名称的进程间使用。
  • 通信能力:匿名管道仅支持半双工通信,命名管道支持全双工通信。
  • 命名和可见性:匿名管道无名,只能通过文件描述符或句柄在相关进程间传递;命名管道有固定名称,通过文件系统访问。

实现

这里只实现Windows系统下的命名通道。

注意:
Windows系统下,命名通道的名称有规范限制:
\\.\pipe\作为管道名称的前缀,后面跟着通道的具体名称,例如:
\\.\pipe\Mypipe

服务端程序代码

#include <windows.h>
#include <iostream>

int pipeServer()
{
	HANDLE hPipe;
	/*
	* Windows系统下,管道名称命名规范:
	*	\\.\pipe\pipename
	*	其中'\\.\pipe\'是固定的前缀,表示这是一个命名管道
	*/
	LPCWSTR pipeName = L"\\\\.\\pipe\\MyPipe";
	char buffer[1024];
	DWORD bytesRead;
	DWORD bytesWritten;
	BOOL success;

	// 创建命名管道
	hPipe = CreateNamedPipe(
		pipeName,              // 管道名称
		PIPE_ACCESS_DUPLEX,    // 双向访问
		PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, // 数据类型和读取模式
		1,                     // 最大实例数
		1024,                  // 输出缓冲区大小
		1024,                  // 输入缓冲区大小
		0,                     // 客户端超时时间
		NULL                   // 默认安全属性
	);

	if (hPipe == INVALID_HANDLE_VALUE) {
		std::cerr << "Failed to create pipe." << std::endl;
		return 1;
	}

	std::cout << "Named pipe created successfully." << std::endl;

	// 等待客户端连接
	std::cout << "Waiting for client connection..." << std::endl;
	success = ConnectNamedPipe(hPipe, NULL) ?
		TRUE : (GetLastError() == ERROR_PIPE_CONNECTED);

	if (success) {
		std::cout << "Client connected." << std::endl;
	}
	else {
		CloseHandle(hPipe);
		std::cerr << "Failed to make connection on named pipe." << std::endl;
		return 1;
	}

	// 读写循环
	while (true) {
		// 从客户端读取消息
		success = ReadFile(
			hPipe,
			buffer,
			sizeof(buffer),
			&bytesRead,
			NULL
		);

		if (!success || bytesRead == 0) {
			if (GetLastError() == ERROR_BROKEN_PIPE) {
				std::cerr << "Client disconnected." << std::endl;
			}
			else {
				std::cerr << "Read failed." << std::endl;
			}
			break;
		}

		buffer[bytesRead] = '\0'; // 确保字符串结束符
		std::cout << "Client says: " << buffer << std::endl;

		// 检查是否退出
		if (strcmp(buffer, "quit") == 0) {
			std::cout << "Client initiated termination." << std::endl;
			break;
		}

		// 发送响应到客户端
		const char* response = "Hello, client!";
		success = WriteFile(
			hPipe,
			response,
			(DWORD)strlen(response),
			&bytesWritten,
			NULL
		);

		if (!success) {
			std::cerr << "Failed to send data." << std::endl;
			break;
		}
	}

	// 关闭句柄
	CloseHandle(hPipe);
	std::cout << "Connection closed." << std::endl;
	return 0;
}

int main()
{
    pipeServer();
    std::cin.get();
}

输出结果:
image.png

客户端程序代码

#include <windows.h>
#include <iostream>

int pipeClient() {
	HANDLE hPipe;
	/* 
	* Windows系统下,管道名称命名规范:
	*	\\.\pipe\pipename
	*	其中'\\.\pipe\'是固定的前缀,表示这是一个命名管道
	*/
	LPCWSTR pipeName = L"\\\\.\\pipe\\MyPipe";
	BOOL success;
	char buffer[1024];
	DWORD bytesWritten;
	DWORD bytesRead;

	// 尝试连接到命名管道
	hPipe = CreateFile(
		pipeName,              // 管道名称
		GENERIC_READ | GENERIC_WRITE, // 读写访问
		0,                     // 不共享
		NULL,                  // 默认安全属性
		OPEN_EXISTING,         // 打开现有的管道
		0,                     // 默认属性
		NULL                   // 不使用模板文件
	);

	if (hPipe == INVALID_HANDLE_VALUE) {
		std::cerr << "Failed to connect to the pipe." << std::endl;
		return 1;
	}

	std::cout << "Connected to the pipe." << std::endl;

	// 向服务端发送消息
	const char* message = "Hello, server!";
	success = WriteFile(
		hPipe,                 // 管道句柄
		message,               // 发送的数据
		(DWORD)strlen(message), // 数据大小
		&bytesWritten,         // 写入的字节数
		NULL                   // 不重叠
	);

	if (!success) {
		std::cerr << "Failed to send data." << std::endl;
		CloseHandle(hPipe);
		return 1;
	}

	std::cout << "Message sent to server, waiting for response..." << std::endl;

	// 从服务端读取响应
	success = ReadFile(
		hPipe,                 // 管道句柄
		buffer,                // 接收缓冲区
		sizeof(buffer),        // 缓冲区大小
		&bytesRead,            // 读取的字节数
		NULL                   // 不重叠
	);

	if (!success || bytesRead == 0) {
		if (GetLastError() == ERROR_BROKEN_PIPE) {
			std::cerr << "Server has disconnected." << std::endl;
		}
		else {
			std::cerr << "Read failed." << std::endl;
		}
		CloseHandle(hPipe);
		return 1;
	}

	buffer[bytesRead] = '\0'; // 确保字符串结束符
	std::cout << "Received: " << buffer << std::endl;

	// 关闭句柄
	CloseHandle(hPipe);
	return 0;
}

int main()
{
	pipeClient();
	std::cin.get();
}

输出结果:
image.png

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1845481.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Linux】进程信号2——阻塞信号,捕捉信号

1.阻塞信号 1.1. 信号其他相关常见概念 在开始内容之前&#xff0c;先介绍一些信号的专业名词&#xff1a; 实际执行信号的处理动作称为信号递达&#xff08;Delivery&#xff09;信号从产生到递达之间的状态&#xff0c;称为信号未决&#xff08;Pending&#xff09;&#…

Numpy: np.memmap详细用法

文章目录 0. 引言1. 基本用法2. 参数说明3. 例子3.1 读取内存映射文件3.2 修改内存映射文件 4. 使用场景5. 注意事项 0. 引言 np.memmap 是 NumPy 提供的一种用于内存映射大文件的类&#xff0c;允许大文件不完全加载到内存中&#xff0c;而是通过内存映射的方式部分加载。这在…

使用 Llama 3 开源和 Elastic 构建 RAG

作者&#xff1a;Rishikesh Radhakrishnan 使用开源的 Llama 3 和 Elastic 构建 RAG Llama 3 是 Meta 最近推出的开源大型语言模型。这是 Llama 2 的后继者&#xff0c;根据已发布的指标&#xff0c;这是一个重大改进。与 Gemma 7B Instruct、Mistral 7B Instruct 等最近发布的…

用于快速充电站的 AC/DC 转换器概述

电动汽车构成了未来实现可持续交通部门的有前途技术的主要部分。AC/DC 转换器是扩展和改进 EV 功能的骨干组件。本文概述了 AC/DC 转换器、充电站类型、传统两电平 (2L) AC/DC 转换器面临的问题以及使用多电平转换器 (MLC) 的重要性。 AC/DC 充电器示意图&#xff08;&#xff…

01 Shell编程规范与变量

1、Shell脚本概述 在一些复杂的Linux维护工作中&#xff0c;大量的重复性的输入和交互操作不仅费力费时&#xff0c;而且容易出错&#xff0c;而编写一个恰到好处的Shell脚本程序&#xff0c;可以批量处理、自动化地完成一系列维护任务&#xff0c;大大减轻管理员的负担。 Sh…

我在淄博,给唐朝古树办“身份证” | 《社区开放麦》首期预告

&#xff08;预约直播&#xff09; 或许每一个男孩都曾向往成为英雄&#xff0c;时常在内心预演用魔法拯救世界的场景。而回到没有超能力的现实生活&#xff0c;平凡人即便只有一技之长&#xff0c;也能拥有属于自己的“高光时刻”。 在开源社区&#xff0c;有这样一位青年对…

【机器学习 复习】第2章 线性回归及最大熵模型

一、概念 1.回归就是用一条曲线对数据点进行拟合&#xff0c;该曲线称为最佳拟合曲线&#xff0c;这个拟合过程称为回归。 2.一个自变量 叫 一元线性回归&#xff0c;大于一个自变量 叫 多元线性回归。 &#xff08;1&#xff09;多元回归&#xff1a;两个x&#xff0c;一个…

idea解决git代码冲突,提交代码冲突如何有效解决

当在提交代码的时候遇到问题冲突&#xff0c;是已经进行git commit , 但是在 git push 的时候&#xff0c;出现提交代码问题冲突 处理方式&#xff1a; 在IDEA 左下角&#xff0c;找到git 比如在git commint 之前忘记了 git pull ,那么很容易在git push 的时候出现问题&…

简单了解MyBatis

MyBatis 1、快速入门 MyBatis中文手册官网MyBatis中文网 1.1、创建数据表添加数据 create table user(id int auto_increment primary key comment 主键id,name varchar(20) comment 姓名,age int comment 年龄,gender char(1) comment 性别&#xff08;1&#xff1a;男, 2…

AI写论文:如何结合AI工具和传统教学方法?

Sora——这个让人在24年初引爆AI圈的新产品&#xff0c;它究竟会如何改变我们的教育领域呢&#xff1f; 从gpt到Sora&#xff0c;从对话型的ai到游戏和短剧制作的新风口&#xff0c;我们从23年到24年一个接一个地被震惊&#xff01; Sora能够根据文本提示生成高质量的视频内容…

Java实现自动定时任务配置并判断当天是否需要执行示例

最近接到一个需求&#xff0c;用户要能配置一个定时循环的任务&#xff0c;就是和手机闹钟循环差不多&#xff0c;设置好比如周一、周二、周三&#xff0c;那么在这几天内这个任务就需要自动执行 需求不复杂&#xff0c;首先我需要判断当前是周几&#xff0c;当然用户说了让我…

北航数据结构与程序设计图部分选填题

一、 抓两个关键信息&#xff1a;无向图&#xff0c;邻接表。无向图中&#xff0c;边&#xff08;vi&#xff0c;vj&#xff09;要在vi的链表中记录一次&#xff0c;再以&#xff08;vj&#xff0c;vi&#xff09;的形式在vj的链表中记录一次。 每个边都要记录两次&#xff0c…

大创项目推荐 酒店评价的情感倾向分析

前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 酒店评价的情感倾向分析 该项目较为新颖&#xff0c;适合作为竞赛课题方向&#xff0c;学长非常推荐&#xff01; &#x1f9ff; 更多资料, 项目分享&#xff1a; https://gitee.com/dancheng-senior/post…

qemu 安装ubuntu22.04虚拟机 -纯命令行-可ssh-带网络-编译安装 linux kernel-编译安装 kernel module

1&#xff0c;预备系统盘数据 1.1 下载光盘 注意需要 liver-server $ wget https://releases.ubuntu.com/22.04.4/ubuntu-22.04.4-live-server-amd64.iso 1.2 挂载并拷贝 $ sudo mkdir /mnt/iso_ubuntu-22.04.4-live-server-amd64 $ sudo mount ubuntu-22.04.4-live-ser…

采用java+B/S架构开发的工业级UWB(Ultra-Wideband)室内定系统源码UWB定位系统技术接口及技术特点

采用javaB/S架构开发的工业级UWB&#xff08;Ultra-Wideband&#xff09;室内定系统源码UWB定位系统技术接口及技术特点 UWB&#xff08;Ultra-Wideband&#xff09;定位技术本身并不直接连接蓝牙或其他无线通信技术进行定位。然而&#xff0c;在实际应用中&#xff0c;UWB定位…

RecyclerVIew->加速再减速的RecyclerVIew平滑对齐工具类SnapHelper

XML文件 ItemView的XML文件R.layout.shape_item_view <?xml version"1.0" encoding"utf-8"?> <FrameLayout xmlns:android"http://schemas.android.com/apk/res/android"android:layout_width"100dp"android:layout_heig…

Ubuntu 安装 CloudCompare

步骤&#xff1a; sudo apt install flatpakflatpak install flathub org.cloudcompare.CloudCompare此时会有报错&#xff1a; error: No remote refs found similar to ‘flathub’执行 flatpak remote-add --if-not-exists flathub https://flathub.org/repo/flathub.fla…

【大模型驯化-gradio】成功解决gradio出现httpcore.ReadTimeout: timed out问题

【大模型驯化-gradio】成功解决gradio出现httpcore.ReadTimeout: timed out问题 本次修炼方法请往下查看 &#x1f308; 欢迎莅临我的个人主页 &#x1f448;这里是我工作、学习、实践 IT领域、真诚分享 踩坑集合&#xff0c;智慧小天地&#xff01; &#x1f387; 免费获取…

LeetCode刷题之HOT100之LRU缓存

2024/6/21 酷暑难耐&#xff0c;离开空调我将不知道能否《活着》&#xff0c;昨天跑步感觉全身的热无法排出去&#xff0c;出门那种热浪一阵一阵打过来&#xff0c;一点风都舍不得给我。早早的来到实验室&#xff0c;也没多早&#xff0c;九点哈哈&#xff0c;做题啦&#xff0…

Linux【实操篇-文件目录类命令】

05【实操篇-文件目录类命令】 1.pwd 显示当前工作目录的绝对路径 pwd:print working directory 打印工作目录 到现在为止&#xff0c;我们还不知道自己在系统的什么地方。在浏览器上&#xff0c;我们能够通过导航栏上的url&#xff0c;了解到自己在互联网上的具体坐标。相似的…