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

进程间通信(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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

深度解析RocketMq源码-持久化组件(四) CommitLog

1.绪论 commitLog是rocketmq存储的核心&#xff0c;前面我们介绍了mappedfile、mappedfilequeue、刷盘策略&#xff0c;其实commitlog的核心组件我们基本上已经介绍完成。 2.commitLog的组成 commitLog的核心其实就是MqppedFilequeue&#xff0c;它本质上就是多个mappedFile…

SCI一区TOP|局部强化优化算法(PRO)原理及实现【免费获取Matlab代码】

目录 1.背景2.算法原理2.1算法思想2.2算法过程 3.结果展示4.参考文献5.代码获取 1.背景 2024年&#xff0c;A Taheri受到部分强化效应(PRE)理论启发&#xff0c;提出了局部强化优化算法&#xff08;Partial Reinforcement Optimizer, PRO&#xff09;。 2.算法原理 2.1算法思…

Python数据科学 | 是时候跟Conda说再见了

本文来源公众号“Python数据科学”&#xff0c;仅用于学术分享&#xff0c;侵权删&#xff0c;干货满满。 原文链接&#xff1a;是时候跟Conda说再见了 1 简介 conda作为Python数据科学领域的常用软件&#xff0c;是对Python环境及相关依赖进行管理的经典工具&#xff0c;通…

【漏洞复现】红帆iOffice.net wssRtSyn接口处存在SQL注入

【产品&&漏洞简述】 红帆iOffice.net从最早满足医院行政办公需求&#xff08;传统OA&#xff09;&#xff0c;到目前融合了卫生主管部门的管理规范和众多行业特色应用&#xff0c;是目前唯一定位于解决医院综合业务管理的软件&#xff0c;是最符合医院行业特点的医院综…

SVN学习(001 svn安装)

尚硅谷SVN高级教程(svn操作详解) 总时长 4:53:00 共72P 此文章包含第1p-第p19的内容 介绍 为什么使用版本控制工具 版本控制工具的功能 版本控制简介 客户端服务器结构 c/s结构 服务端的结构&#xff1a; 服务程序 、版本库(存放我们上传的文件) 客户端的三个基本操作&#…

记录一次递归查询导致的 java.lang.StackOverflowError: null

问题截图&#xff1a; 由于作者使用递归统计信息&#xff0c;刚开始这个接口运行得正常&#xff0c;但是上线运行一段时间后接口就出现了&#xff0c;如图的栈溢出错误。可以看出确实是堆栈溢出了&#xff0c;解决栈溢出目前只有两种方式&#xff1a; 第一种调大栈的大小&…

小程序开发的技术难点

小程序开发是一项技术难度较高的工作&#xff0c;需要开发者具备多方面的知识和技能&#xff0c;小程序开发的技术难点主要体现在以下几个方面。北京木奇移动技术有限公司&#xff0c;专业的软件外包开发公司&#xff0c;欢迎交流合作。 1. 多端兼容 小程序需要在微信、支付宝…

Flow Matching For Generative Modeling

Flow Matching For Generative Modeling 一、基于流的&#xff08;Flow based&#xff09;生成模型 生成模型 我们先回顾一下所谓的生成任务&#xff0c;究竟是想要做什么事情。我们认为&#xff0c;世界上所有的图片&#xff0c;是符合某种分布 p d a t a ( x ) p_{data}(…

MES管理系统如何设计生产质量管理功能

在现代制造业中&#xff0c;MES管理系统作为连接企业计划层与车间操作层的关键桥梁&#xff0c;其生产者计量管理功能的设计显得尤为重要。一个完善的MES管理系统生产质量管理模块&#xff0c;不仅要求能够实时、准确地采集和分析生产过程中的质量数据&#xff0c;还需要能够与…

Unity3d 游戏暂停(timeScale=0)引起的deltaTime关联的系列问题解决

问题描述 游戏暂停的功能是通过设置timeScale0实现的&#xff0c;不过在暂停游戏的时候&#xff0c;需要对角色进行预览和设置&#xff0c;为了实现这个功能&#xff0c;是通过鼠标控制相机的操作&#xff0c;为了使相机的操作丝滑&#xff0c;获取鼠标操作系数乘以Time.delta…

如何在React中使用CSS模块,并解释为什么使用它们比传统CSS更有益?

在React中使用CSS模块是一种将CSS类名局部化到单个组件的方法&#xff0c;从而避免了全局作用域中的类名冲突。CSS模块允许你为组件编写样式&#xff0c;并确保这些样式只应用于该组件&#xff0c;而不会影响到其他组件。 以下是在React中使用CSS模块的步骤&#xff1a; 安装C…

Excel 识别数据层次后转换成表格

某列数据可分为 3 层&#xff0c;第 1 层是字符串&#xff0c;第 2 层是日期&#xff0c;第 3 层是时间&#xff1a; A1NAME122024-06-03304:06:12404:09:23508:09:23612:09:23717:02:2382024-06-02904:06:121004:09:231108:09:2312NAME2132024-06-031404:06:121504:09:231620…

FreeBSD在zfs挂接第二块ssd 硬盘

为FreeBSD机器新增加了一块ssd硬盘&#xff1a;骑尘 256G 先格式化分区硬盘 进入bsdconfig 选Disk Management 选择ada1 &#xff0c;也就是新增加的硬盘 选择auto 然后选择Entire Disk 提示信息 The existing partition scheme on this disk (MBR) │ …

如何解决windows自动更新,释放C盘更新内存

第一步&#xff1a;首先关闭windows自动更新组件 没有更新windows需求&#xff0c;为了防止windows自动更新&#xff0c;挤占C盘空间&#xff0c;所以我们要采取停止Windows Update服务。按下WinR打开运行对话框&#xff0c;输入services.msc&#xff0c; 然后按Enter。在服务…

传输大文件之镭速自动清理过期文件

电子文档的普及无疑极大地便利了我们的工作与生活&#xff0c;但随之而来的是如何有效管理这些日益增多的文件。企业面临着存储空间紧张、文件传输复杂、敏感信息泄露等挑战。自动化文件清理的需求日益凸显&#xff0c;这不仅关乎个人对高效工作环境的追求&#xff0c;更是企业…

绘唐3工具—让创作触手可及

绘唐AIGC&#xff08;Artificial Intelligence Generated Creativity&#xff09;是一种新兴的技术&#xff0c;通过人工智能生成创意&#xff0c;让创作更加触手可及。 绘唐3下载地址https://qvfbz6lhqnd.feishu.cn/wiki/KnRawcWQxiFrj5kC8lVcCEypnZc 绘唐AIGC结合了人工智能…

element-plus的form表单组件之checkbox组件

单个checkbox 绑定的响应式的值类型为bool类型&#xff0c;同一个组的checkbox多选其值对应值的数组&#xff0c;类型根据checkbox的value值而来。 label只用来显示具体的值&#xff0c;根据value属性来设置。 element-plus的checkbox提供多种特性。 如单选&#xff0c;多选…

B站广告开户投流是什么政策?要哪些资质?

B站&#xff08;哔哩哔哩&#xff09;作为年轻人喜爱的视频分享社区&#xff0c;其广告价值也日益凸显。为了更好地服务广告主&#xff0c;B站近日对广告开户投流政策进行了更新&#xff0c;云衔科技作为专业的数字营销服务商&#xff0c;也积极响应&#xff0c;为广告主提供一…

【java分布式计算】控制反转和依赖注入(DI IOC AOP)

考试要求&#xff1a;了解控制反转的基本模式&#xff0c;用依赖注入编写程序 目录 控制反转&#xff08;Inversion of Control, IOC&#xff09;&#xff1a; 依赖注入&#xff08;Dependency Injection, DI&#xff09;&#xff1a; 依赖注入的三种实现方式 具体的例子 …

C#——装箱与拆箱详情

装箱与拆箱 装箱: 将值类型转换成引用类型的过程&#xff1b; 拆箱: 把引用类型转为值类型的过程&#xff0c;就是拆箱 装箱 拆箱