【嵌入式Linux】<总览> 进程间通信(更新中)

news2024/11/25 2:28:02

文章目录

前言

一、管道

1. 概念

2. 匿名管道

3. 有名管道

二、内存映射区

1. 概念

2. mmap函数

3. 进程间通信(有血缘关系)

4. 进程间通信(没有血缘关系)

5. 拷贝文件


前言

在文章【嵌入式Linux】<总览> 多进程中已经介绍了进程的相关概念和创建多个进程的方法。本篇聚焦于进程间通信的方式,若涉及版权问题请联系本人删除。


一、管道

1. 概念

  • 管道是进程间通信的方式之一,其数据的流动是单向的。
  • 管道本质上是内核中的一块内存,即内存缓冲区。这块内存中的数据存储在环形队列中,其默认空间为4K。这个环形队列的队头就是读指针,队尾就是写指针。
  • 由于管道是内核中的内存,需要使用系统调用的文件IO函数read和write函数来读写。每次读完后,数据相当于出队了!读写默认是阻塞:①读管道:管道中没有数据就会阻塞,直到有数据到来;②写管道:管道空间满了就会阻塞,直到有数据出队。通过fcntl函数能修改为非阻塞。
  • 管道是独立于任何进程的,并且充当了两个进程用于数据通信的载体,只要两个进程能够得到同一个管道的入口和出口(读端和写端的文件描述符),那么他们之间就可以通过管道进行数据的交互。
  • 当读端关闭了,管道破裂,写端进程直接退出;当写端关闭了,读端就会将管道中剩余内容读取完,最后返回0。

2. 匿名管道

【1】介绍:匿名管道是管道的一种,其没有具体的名字,只能实现拥有血缘关系的进程进行通信。

【2】创建匿名管道:pipe函数

#include <unistd.h>

int pipe(int pipefd[2]);

//参数说明:pipefd是传出的参数
    //pipefd[0]对应读端的文件描述符
    //pipefd[1]对应写端的文件描述符

//返回值:0表示成功,-1表示失败

【3】进程通信的注意事项:在创建子进程之后,父进程中的读端和写端文件描述符都会被复制到子进程中。若子进程只有写的需求,那么可以将读端的文件描述符给close。若父进程只有读的需求,那么可以将写端的文件描述符给close。

程序示例:子进程执行"ps"命令,并将结果写入管道中;父进程读管道,将结果显示在终端上。

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <sys/fcntl.h>
#include <string.h>

int main(int argc, char **argv)
{
	/* 1.创建匿名管道 */
	int fd[2];
	int pipeRet = pipe(fd);
	if (pipeRet < 0) {
		perror("创建管道失败");
		return -1;
	}

	/* 2.创建子进程 */
	pid_t pid = fork();
	if (pid < 0) {
		perror("创建子进程失败");
		return -1;
	}

	/* 3.子进程操作 */
	if (pid == 0) {
		close(fd[0]);//关闭管道的读端
		dup2(fd[1], STDOUT_FILENO);//重定向标准输出
		execlp("ps", "ps", NULL);//执行ps程序
		perror("execlp错误");
	}

	/* 4.父进程操作 */
	if (pid > 0) {
		close(fd[1]);//关闭管道的写端
		//循环读取管道中的数据
		char buf[4096];
		while (1) {
			memset(buf, 0, sizeof(buf));//清空缓存
			int len = read(fd[0], buf, sizeof(buf));//读取管道
			if (len <= 0) {//异常或读完
				break;
			}
			printf("%s", buf);//输出内容至终端
		}
		close(fd[0]);
		//等待子进程
		wait(NULL);
	}
	return 0;
}

3. 有名管道

【1】介绍:有名管道在磁盘上有实体文件,文件类型为p(不存储真实数据)。

  • 有名管道可以称为fifo.
  • 有名管道的磁盘大小永远是0,因为有名管道依旧是将数据存储到内存缓冲区。打开这个磁盘文件,就能获取对应的文件描述符。
  • 任意两个进程都能利用有名管道来进行通信。

【2】创建有名管道:

  • 方式一:终端命令"mkfifo  有名管道名字"
  • 方式二:调用函数mkfifo,其细节如下:
#include <sys/types.h>
#include <sys/stat.h>

int mkfifo(const char *pathname, mode_t mode);

//参数说明:
    //pathname: 保存的文件名
    //mode: 创建的管道文件权限
//返回值:成功返回0,失败返回-1

【3】进程间通信注意事项:有名管道的读写首先需要调用open函数来打开管道文件,若管道文件中只有读端或只有写端被打开,那么就会阻塞在open函数,直到两端都被打开。

程序示例:写两个程序,一个作为写管道程序,一个作为读管道程序。①写管道:创建有名管道文件,打开该文件,向有名管道中写入数据,关闭文件。②读管道:打开有名管道文件,读取数据,并显示到终端上,关闭文件。

写管道程序:

#include <stdio.h>
#include <sys/types.h>
#include <sys/fcntl.h>
#include <unistd.h>
#include <sys/stat.h>

//写管道程序
int main(int argc, char **argv)
{
	/* 1.创建有名管道 */
	int mkRet = mkfifo("./pipefile", 0664);
	if (mkRet < 0) {
		perror("创建有名管道失败");
		return -1;
	}

	/* 2.打开有名管道 */
	int fd = open("./pipefile", O_WRONLY);
	if (fd < 0) {
		perror("管道文件打开失败");
		return -1;
	}

	/* 3.循环写入数据 */
	for (int i = 0; i < 5; ++i) {
		char writeBuf[200] = {0};
		sprintf(writeBuf, "Hello, Can! 我在努力学习中... %d\n", i);
		int writeRet = write(fd, writeBuf, sizeof(writeBuf));
		if (writeRet < sizeof(writeBuf)) {
			printf("写入错误");
			break;
		}
		sleep(1);//保证能够写入
	}

	/* 4.关闭管道文件 */
	close(fd);
	return 0;
}

读管道程序:

#include <stdio.h>
#include <sys/types.h>
#include <sys/fcntl.h>
#include <unistd.h>

//读管道程序
int main(int argc, char **argv)
{
	/* 1.打开有名管道 */
	int fd = open("./pipefile", O_RDONLY);
	if (fd < 0) {
		perror("管道文件打开失败");
		return -1;
	}

	/* 2.循环读取数据并打印 */
	while (1) {
		char readBuf[1024] = {0};
		int readRet = read(fd, readBuf, sizeof(readBuf));
		if (readRet <= 0) {//异常或读完
			break;
		}
		printf("%s", readBuf);//打印读取的内容
	}

	/* 3.关闭管道文件 */
	close(fd);
	return 0;
}


二、内存映射区

1. 概念

  • 通过mmap函数创建内存映射区是实现进程间通信的方法之一。
  • 内存映射区位于每个进程的用户区(用于加载动态库的那个区域)。
  • 内存映射区的读写是非阻塞的。
  • 内存映射区创建成功之后,得到映射区内存的起始地址,使用内存操作函数读写数据。
  • 机制:多个进程将内存映射区和同一个磁盘文件进行映射。当其中一个进程修改了它的内存映射区,数据会自动同步到磁盘文件。同时,和该磁盘文件建立映射关系的进程中的内存映射区的数据也会实时变化,因此能够实现进程间的通信。
  • 注意:内存映射区使用完后通过munmap函数释放。
#include <sys/mman.h>

int munmap(void *addr, size_t length)

//参数说明:
    //addr: 内存映射区的起始地址,就是mmap函数的返回值
    //length: 内存映射区的大小,与mmap函数的第二个参数相同

//返回值:成功返回0,失败返回-1

2. mmap函数

【1】头文件:#include <sys/mman.h>

【2】函数原型:void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);

【3】参数说明:

  • addr:从动态库加载区的什么位置开始创建内存映射区,一般为NULL来委托内核分配
  • length:创建的内存映射区的大小(单位:字节),实际大小是按4000的整数倍去分配
  • prot:对内存映射区的操作权限
    • PROT_READ: 读内存映射区
    • PROT_WRITE: 写内存映射区
    • PROT_READ | PROT_WRITE:读写内存映射区
  • flags:用于确定是否共享内存映射区
    • MAP_SHARED: 多个进程可以共享数据,进行映射区的数据同步
    • MAP_PRIVATE: 映射区数据是私有的,不能同步给其他进程
  • fd:对应打开的磁盘文件的文件描述符。内存映射区通过此与硬盘文件建立联系。
  • offset:要求>=0并且是4000的倍数,表示硬盘文件从偏移到的位置进行映射。

【4】返回值:成功返回内存映射区的首地址,失败返回MAP_FAILED其实就是(void *) -1

【5】注意事项:

  • length必须要大于0。
  • prot一般都是PROT_READ | PROT_WRITE。
  • 内存映射区创建成功之后, 关闭参数中的文件描述符fd不会影响进程间通信。

3. 进程间通信(有血缘关系)

父进程fork子进程,子进程就会将父进程的虚拟地址空间进行复制。因此,对于有血缘关系的进程来说,通过内存映射区的方式来进行通信是简单的。

【程序实例】如下代码,父进程创建子进程,父进程向内存映射区中写数据,子进程从内存映射区中读数据。注意:磁盘文件的大小不能为0,否则会报错误:Bus error (core dumped)。

#include <stdio.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/wait.h>

int main(int argc, char **argv)
{
	/* 1.打开磁盘文件 */
	int fd = open("./test.txt", O_RDWR);
	if (fd < 0) {
		perror("磁盘文件打开错误");
		return -1;
	}

	/* 2.创建内存映射区 */
	void * ptr = mmap(NULL, 4000, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
	if (ptr == MAP_FAILED) {
		perror("mmap失败");
		close(fd);
		return -1;
	}

	/* 3.创建子进程,复制父进程的内存映射区 */
	pid_t pid = fork();
	if (pid < 0) {
		perror("fork失败");
		close(fd);
		return -1;
	}

	/* 4.父进程:写数据到内存映射区 */
	if (pid > 0) {
		const char *content = "Hello, Can!";
		memcpy(ptr, content, strlen(content)+1);
		wait(NULL);
	}

	/* 5.子进程:从内存映射区中读数据 */
	if (pid == 0) {
		sleep(1);//由于读写非阻塞,这里保证父进程先行
		printf("从内存映射区读取的数据:%s\n", (char*)ptr);
	}

	/* 6.关闭硬盘文件,释放内存映射区 */
	close(fd);
	munmap(ptr, 4000);
	return 0;
}

4. 进程间通信(没有血缘关系)

进程间没有血缘关系,需要各自创建内存映射区,并且对应的磁盘文件必须是同一个。

【程序实例】两个进程打开同一个磁盘文件,然后各自构建内存映射区。一个进程向内存映射区写数据,另一个进程读数据。

写数据的进程代码:

#include <stdio.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/wait.h>

//写数据的进程
int main(int argc, char **argv)
{
	/* 1.打开磁盘文件 */
	int fd = open("./test.txt", O_RDWR);
	if (fd < 0) {
		perror("磁盘文件打开错误");
		return -1;
	}

	/* 2.创建内存映射区 */
	void * ptr = mmap(NULL, 4000, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
	if (ptr == MAP_FAILED) {
		perror("mmap失败");
		close(fd);
		return -1;
	}

	/* 3.向内存映射区中写数据 */
	const char *content = "------Hello, Can!------";
	memcpy(ptr, content, strlen(content)+1);
	sleep(1);

	/* 4.关闭硬盘文件,释放内存映射区 */
	close(fd);
	munmap(ptr, 4000);
	return 0;
}

读数据的进程代码:

#include <stdio.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/wait.h>

//读数据的进程
int main(int argc, char **argv)
{
	/* 1.打开磁盘文件 */
	int fd = open("./test.txt", O_RDWR);
	if (fd < 0) {
		perror("磁盘文件打开错误");
		return -1;
	}

	/* 2.创建内存映射区 */
	void * ptr = mmap(NULL, 4000, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
	if (ptr == MAP_FAILED) {
		perror("mmap失败");
		close(fd);
		return -1;
	}

	/* 3.从内存映射区中读数据 */
	printf("从内存映射区中读数据:%s\n", (char*)ptr);

	/* 4.关闭硬盘文件,释放内存映射区 */
	close(fd);
	munmap(ptr, 4000);
	return 0;
}

5. 拷贝文件

鉴于内存映射区和磁盘文件的同步关系,可以用来拷贝文件,其流程如下:

①打开原文件,计算原文件大小size,并映射到内存映射区A。

②打开目标文件,拓展大小为size,并映射到内存映射区B。

③拷贝内存映射区A的内容到内存映射区B。

④关闭所有文件,释放所有内存映射区。

注意:mmap中还是需要MAP_SHARED,否则vim和cat都无法识别内容。

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <string.h>
#include <stdio.h>

int main(int argc, char **argv)
{
	/* 1.打开原文件,并映射到内存映射区A */
	int fd1 = open("./test.txt", O_RDWR);
	if (fd1 < 0) {
		perror("打开原文件失败");
		return -1;
	}
	void *ptrA = mmap(NULL, 4000, PROT_READ | PROT_WRITE, MAP_SHARED, fd1, 0);
	if (ptrA == MAP_FAILED) {
		perror("内存映射区A构建失败");
		return -1;
	}
	int size = lseek(fd1, 0, SEEK_END);//原文件大小
	
	/* 2.打开目标文件,拓展大小,并映射到内存映射区B */
	int fd2 = open("./output.txt", O_RDWR | O_CREAT, 0664);
	if (fd2 < 0) {
		perror("打开输出文件失败");
		return -1;
	}
	ftruncate(fd2, size);//将输出文件拓展到size大小
	void *ptrB = mmap(NULL, 4000, PROT_READ | PROT_WRITE, MAP_SHARED, fd2, 0);
	if (ptrB == MAP_FAILED) {
		perror("内存映射区B构建失败");
		return -1;
	}

	/* 3.拷贝A的空间到B */
	memcpy(ptrB, ptrA, size);

	/* 4.关闭文件,释放所有内存映射区 */
	munmap(ptrA, 4000);
	munmap(ptrB, 4000);
	close(fd1);
	close(fd2);
	return 0;
}

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

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

相关文章

RAID详解及配置实战

目录 一、RAID磁盘阵列及详解 1.1 了解RAID 1.1.1 简单理解 1.1.2 对比了解 1.2 RAID磁盘阵列介绍 1.3 RAID功能实现 1.4 RAID实现的方式 1.5 RAID级别详解 1.5.1 RAID -0 1.5.2 RAID -1 1.5.3 RAID -5 1.5.4 RAID -10&#xff08;RAID 10&#xff09; 1.6 阵列卡…

2024年最新通信安全员考试题库,随时在线刷题

46.劳动防护用品不同于一般的商品&#xff0c;直接涉及到劳动者的生命安全和身体健康&#xff0c;故生产经营单位为从业人员提供的劳动防护用品必须符合&#xff08; &#xff09;或行业标准。 A.地方标准 B.国家标准 C.消防标准 D.LA劳安认证 答案:B 47.矿山、金属冶炼、…

本地可以Run大模型吗?Llama3?安排!

➡️ 前言 本地可以跑大模型吗? ChatGPT发布之后&#xff0c;引起了AI领域剧烈震动&#xff0c;从2023年3月百度发布新一代大预言模型文心一言开始&#xff0c;大模型如雨后春笋般不断涌现&#xff0c;国内阿里巴巴的通义千问、华为的盘古大模型、科大讯飞的星火认知大模型、…

大学网页制作作品1

作品须知&#xff1a;1.该网页作品预计分为5个页面&#xff08;其中1个登录页面&#xff0c;1个首页主页面&#xff0c;3个分页面&#xff09;&#xff0c;如需要可自行删改增加页面。&#xff08;总共约800行html,1200行css,100行js&#xff09; 2.此网页源代码只用于学习和模…

面相对象程序设计

面相对象程序设计包含内容如下 局域网聊天程序设网页浏览器设计电子日历记事本的设计 以其中的一个的报告进行举例 1需求与总体设计 1 1.1需求分析 1 1.2总体设计方案 1 1.2&#xff0e;1系统功能分析以及功能表 1 1.3系统类图的关系以及表之间的联系 2 2详细设计 3 2.1 Manag…

Linux系统学习——指令三

Linux系统学习——指令三 Linux系统学习——指令三chmod — 文件执行权限添加文件执行权限去除文件执行权限 查找文件中特定关键字使用vi编辑文件并查找特定关键字文本文件查找特定关键字1: 使用 grep 命令2: 使用 find 命令3: 使用 awk 命令4: 使用 sed 命令5: 使用 ag 命令&a…

RS232自由转Profinet协议网关模块连接1200PLC与扫码枪通讯及手动清零案例

一、RS232和Profinet这两种通讯接口的特点和应用场景&#xff1a; RS232是一种串行通讯接口标准&#xff0c;常用于连接计算机和外部设备&#xff0c;传输速率较低但稳定可靠。Profinet则是一种工业以太网通讯协议&#xff0c;具有高速、实时性强的特点&#xff0c;适用于工业…

群智优化:探索BP神经网络的最优配置

群智优化&#xff1a;探索BP神经网络的最优配置 一、数据集介绍 鸢尾花数据集最初由Edgar Anderson测量得到&#xff0c;而后在著名的统计学家和生物学家R.A Fisher于1936年发表的文章中被引入到统计和机器学习领域数据集特征&#xff1a; 鸢尾花数据集包含了150个样本&#…

【计算机毕业设计】167校园失物招领微信小程序

&#x1f64a;作者简介&#xff1a;拥有多年开发工作经验&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的项目或者毕业设计。 代码可以私聊博主获取。&#x1f339;赠送计算机毕业设计600个选题excel文件&#xff0c;帮助大学选题。赠送开题报告模板&#xff…

适合实习生使用的工作笔记软件

作为一名初入职场的实习生&#xff0c;我每天都面临着海量的信息和工作任务。刚开始时&#xff0c;我常常手忙脚乱&#xff0c;生怕漏掉任何重要细节。对于实习生来说&#xff0c;好的工作笔记软件不仅能帮助我们系统地整理工作信息&#xff0c;还能提高工作效率&#xff0c;确…

数据结构---二叉树前中后序遍历

1. 某完全二叉树按层次输出&#xff08;同一层从左到右&#xff09;的序列为 ABCDEFGH 。该完全二叉树的前序序列为() A: ABDHECFG B: ABCDEFGH C: HDBEAFCG D: HDEBFGCA 2. 二叉树的先序遍历和中序遍历如下&#xff1a;先序遍历: EFHIGJK; 中序遍历: HFIEJKG. 则二叉…

最新AIGC系统源码-ChatGPT商业版系统源码,自定义ChatGPT指令Promp提示词,AI绘画系统,AI换脸、多模态识图理解文档分析

目录 一、前言 系统文档 二、系统演示 核心AI能力 系统快速体验 三、系统功能模块 3.1 AI全模型支持/插件系统 AI模型提问 文档分析 ​识图理解能力 3.2 GPts应用 3.2.1 GPTs应用 3.2.2 GPTs工作台 3.2.3 自定义创建Promp指令预设应用 3.3 AI专业绘画 3.3.1 文…

Day60 代码随想录打卡|回溯算法篇---组合

题目&#xff08;leecode T77&#xff09;&#xff1a; 给定两个整数 n 和 k&#xff0c;返回范围 [1, n] 中所有可能的 k 个数的组合。 你可以按 任何顺序 返回答案。 方法&#xff1a;本题最直观的解法是使用暴力for循环遍历法&#xff0c;根据k的大小定for循环的嵌套次数&…

Java高级重点知识点-14-Set接口、HashSet底层原理讲解

文章目录 Set接口 (HashSet 、LinkedHashSet)HashSet底层原理(重点理解) Set接口 (HashSet 、LinkedHashSet) 无序不重复 HashSet集合 HashSet 是根据对象的哈希值来确定元素在集合中的存储位置&#xff0c;因此具有良好的存取和查找性能。 public class HashSetDemo {publ…

经典游戏案例:仿植物大战僵尸

学习目标&#xff1a;仿植物大战僵尸核心玩法实现 游戏画面 项目结构目录 部分核心代码 using System; using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.SceneManagement; using Random UnityEngine.Random;public enum…

数字图像分析(第一部分)

文章目录 第2章 图像数字化数字化采样与量化像素的邻域像素的距离图像采集网络**离散直线性**距离变换**第3章 图像变换可分离和正交图像变换2D DFT变换及其本质**哈达玛变换KL变换(PCA)第4章 形态学二值形态学膨胀和腐蚀开启和闭合击中-击不中变换二值形态学实用算法噪声滤除目…

DWC USB2.0协议学习1--产品概述

本章开始学习记录DWC_otg控制器&#xff08;新思USB2.0&#xff09;的特点、功能和应用。 新思USB 2.0 IP主要有两个文档需要参考&#xff1a; 《DesignWare Cores USB 2.0 Hi-Speed On-TheGo (OTG) Data book》 《DesignWare Cores USB 2.0 Hi-Speed On-TheGo (OTG) Progra…

数值分析笔记(三)函数逼近

最佳平方逼近 函数逼近是使用一种简单易算的函数来近似表示一个复杂函数。 该问题可转化为求解线性方程组 G n C F n ​ G_{n}CF_{n}​ Gn​CFn​​ 其中&#xff0c;系数 C ( c 0 , c 1 , ⋯ , c n ) T , F n ( ( f , φ 0 ) , ( f , φ 1 ) , ⋯ , ( f , φ n ) ) T C(c…

私域电商的新篇章:构建深度连接与高效生态

大家好&#xff0c;我是电商领域的探索者&#xff0c;今天我想和大家分享关于私域电商的一些心得与洞见。在这个数字化飞速发展的时代&#xff0c;如何构建与用户之间更为紧密、深入的连接&#xff0c;以及如何通过私域生态来挖掘用户的更大价值&#xff0c;成为了我们关注的焦…

Studio One 6.6.2中文破解版安装图文激活教程

Studio One 6.6.2中文破解版做为新生代音乐工作站&#xff0c;凭借更低的价格和完备的功能&#xff0c;获得了音乐人和直播行业工作者的青睐&#xff0c;尤其是对硬件声卡的适配支持更好&#xff0c;特别适合用来配合线上教学和电商带货。 最近网上出现不少关于StudioOne不能用…