【Linux】进程通信

目录

一、管道通信

二、共享内存

三、消息队列


一、管道通信

管道是由操作系统维护的一个文件,管道通信的本质就是将管道文件作为临界资源,实现不同进程之间的数据读写,但是管道只允许父子进程或者兄弟进程之间的通信。

管道文件本身是全双工机制的,但是在管道通信中,它的工作模式是半双工的,在管道通信之前,读端会关闭对管道文件的写通道,写端会关闭对管道文件的读通道。

当读端在读数据时,若写端关闭,读端会将管道文件中剩余数据读取结束之后再关闭;当写端在写数据时,若读端关闭,写端会被操作系统直接关闭。

那么管道通道如何实现数据交互呢?方法是创建两个管道进行通信。

管道通信方式分为匿名管道命名管道,由 pipe() 系统调用创建并打开,命名管道由 mkfifo() 系统调用创建并由 open() 打开,他们的通信本质是一样的。

// comm.hpp 头文件
#pragma once
#include <iostream>
#include <sys/types.h>
#include <sys/stat.h>
#include <string>
#include <cstring>
#include <cerrno>
#include <cassert>
#include <unistd.h>
#include <fcntl.h>
#include <fstream>#define NAMED_PIPE "./named_pipe"bool create_fifo(const std::string& path) {umask(0);int n = mkfifo(path.c_str(), 0666);if (n == 0) {return true;}else {std::cout << "errno: " << errno << "err string: " << strerror(errno) << std::endl;return false; }
}void remove_fifo(const std::string& path) {int n = unlink(path.c_str());// unlink 函数功能是删除文件,但会在判断此文件状态之后再删除// 若有进程打开此文件,则不会立即删除,等到无进程打开该文件时才会删除// 若此文件有多个链接,则进行连接数减一操作// 执行成功返回 0,失败返回 -1assert(n == 0);
}
// Server
#include "comm.hpp"int main() {std::cout << "server begin" << std::endl;int rfd = open(NAMED_PIPE, O_RDONLY);if (rfd < 0) {bool r = create_fifo(NAMED_PIPE);assert(r);rfd = open(NAMED_PIPE, O_RDONLY);}// readchar buffer[1024];while (true) {ssize_t s = read(rfd, buffer, sizeof(buffer) - 1);if (s > 0) {buffer[s] = 0;std::cout << "client->server# " << buffer << std::endl;}else if (s == 0) {std::cout << "client quit, me too" << std::endl;break;}else {std::cout << "err string: " << strerror(errno) << std::endl;break;}}close(rfd);std::cout << "server end" << std::endl;remove_fifo(NAMED_PIPE);return 0;
}
// Client
#include "comm.hpp"int main() {std::cout << "client begin" << std::endl;int wfd = open(NAMED_PIPE, O_WRONLY, 0666);if (wfd < 0) {exit(1);}// writechar buffer[1024];while (true) {std::cout << "please say# ";fgets(buffer, sizeof(buffer), stdin);if (strlen(buffer) > 0) {buffer[strlen(buffer) - 1] = 0;}if (strcmp(buffer, "quit") == 0) {break;}ssize_t n = write(wfd, buffer, strlen(buffer));assert(n == strlen(buffer));}close(wfd);std::cout << "client end" << std::endl;return 0;
}

二、共享内存

共享内存是由操作系统维护的一块地址空间,它的工作机制是全双工。

// comm.hpp
#pragma once
#include <iostream>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <cerrno>
#include <cstring>
#include <cstdlib>
#include <unistd.h>#define NAME_PATH "."
#define PROJ_ID 0X66
#define MAX_SIZE 4096// 创建共享内存时,通过 ftok() 函数获得 key 值保证共享内存在系统中的唯一性
key_t get_key() {key_t k = ftok(NAME_PATH, PROJ_ID);if (k < 0) {std::cout << errno << ": " << strerror(errno) << std::endl;exit(1);}return k;
}int get_shm_helper(key_t k, int flags) {// shmget : 创建共享内存int shmID = shmget(k, MAX_SIZE, flags);if (shmID < 0) {std::cout << errno << ": " << strerror(errno) << std::endl;exit(1);}return shmID;
}int get_shm(key_t k) {return get_shm_helper(k, IPC_CREAT);
}int create_shm(key_t k) {return get_shm_helper(k, IPC_CREAT | IPC_EXCL | 0600);// IPC_EXCL 不能单独使用,若共享内存不存在则创建,存在则报错
}// 删除共享内存
void del_shm(int shmID) {// shmctl : 控制共享内存if (shmctl(shmID, IPC_RMID, NULL) == -1) {std::cout << errno << ": " << strerror(errno) << std::endl;exit(1);}
}// 关联共享内存
void* attach_shm(int shmID) {void* start = shmat(shmID, NULL, 0);if ((long long)start == -1) {std::cout << errno << ": " << strerror(errno) << std::endl;exit(1);}return start;
}// 取消关联
void del_attach_shm(void* start) {if (shmdt(start) == -1) {std::cout << errno << ": " << strerror(errno) << std::endl;exit(1);}
}
// Server
#include "comm.hpp"int main() {key_t k = get_key();printf("0x%x\n", k);int shmID = create_shm(k);      // 创建共享内存printf("shmID = %d\n", shmID);// 内核为每个共享内存维护一个结构体:struct shmid_ds// struct shmid_ds// {//     struct ipc_perm shm_perm;    /* Ownership and permissions *///     size_t          shm_segsz;   /* Size of segment (bytes) *///     time_t          shm_atime;   /* Last attach time *///     time_t          shm_dtime;   /* Last detach time *///     time_t          shm_ctime;   /* Last change time *///     pid_t           shm_cpid;    /* PID of creator *///     pid_t           shm_lpid;    /* PID of last shmat(2)/shmdt(2) *///     shmatt_t        shm_nattch;  /* No. of current attaches */// };struct shmid_ds ds;shmctl(shmID, IPC_STAT, &ds);printf("获取属性: size(%d) pid(%d) myself(%d) key(0x%x)\n",ds.shm_segsz, ds.shm_cpid, getpid(), ds.shm_perm.__key);sleep(2);char* start = (char*)attach_shm(shmID);     // 关联共享内存printf("attach success, address start: %p\n", start);// 从共享内存中读取数据while (true) {if (*start != '\0') {printf("clent say# %s\n", start);snprintf(start, MAX_SIZE, "\0");    // 打印消息后清空共享内存}}sleep(2);del_attach_shm(start);return 0;
}
// Client
#include "comm.hpp"// 查看ipc资源
// 查看:ipcs -m
// 删除:ipcrm -m shmIDint main() {key_t k = get_key();printf("0x%x\n", k);int shmID = get_shm(k);printf("%d\n", shmID);sleep(2);char* start = (char*)attach_shm(shmID);     // 关联共享内存printf("attch success, address start: %p\n", start);// 将数据写入共享内存int count = 1;while (true) {printf("[please input]# ");char message[1024] = {0};fgets(message, sizeof(message) - 1, stdin);message[strlen(message) - 1] = 0;snprintf(start, MAX_SIZE, "%s [pid(%d)][messageID: %d]\n", message, getpid(), count++);}sleep(2);del_attach_shm(start);return 0;
}

优点:相比于管道而言,共享内存能够支持任意进程之间的通信,而且访问数据的速度也比管道要快。这得益于通信直接访问内存,而管道则需要先通过操作系统访问文件再获得内存数据。

缺点:用于进程间通信时,共享内存本身不支持阻塞等待操作。这是因为当读端读取数据后,数据并不会在内存中清空。因此读端和写端可以同时访问内存空间,即全双工。因为共享内存本质是进程直接访问内存,无法主动停止读取,如果读端不加以限制,那么将持续读取数据。同理,写端也会持续写入数据。换句话说,共享内存本身没有访问控制。 

三、消息队列

消息队列是由操作系统维护的一个数据结构,遵循队列的FIFO原则,半双工机制。

通过消息队列通信的两个进程也分为读端和写端,读端只负责从消息队列中拿数据,写端只负责向消息队列中写数据。

消息队列的优点是异步性和系统解耦,异步性是指发送消息的进程不需等待接收消息的进程的响应,可以继续执行自己的任务,系统解耦是指发送方和接收方都不关心对方进程的状态,只关注消息的发送和接收。

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<pthread.h>
#include<string.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/msg.h>
#include<sys/shm.h>
#include<fcntl.h>
#include<semaphore.h>struct msg_buffer {long type;		//1表示sender发送的正常数据,2表示sender1发送的结束信号,3表示sender2发送的结束信号char text[100];	
};void *sender1_thread() {struct msg_buffer msg;char buf[100];int msqid = msgget((key_t) 2023, 0666|IPC_CREAT);if(msqid == -1) {printf("消息队列创建失败!");exit(-1);}sem_t *mutex = sem_open("mutex", O_CREAT | O_RDWR, 0666, 0);sem_t *sender1 = sem_open("sender1", O_CREAT | O_RDWR, 0666, 0);sem_t *receive1 = sem_open("receive1", O_CREAT | O_RDWR, 0666, 0);sem_t *next_input = sem_open("next_input", O_CREAT | O_RDWR, 0666, 0);while(1) {sem_wait(mutex);sem_wait(next_input);printf("sender1 input: ");gets(buf);msg.type = 1;if(strcmp(buf, "exit") == 0) {strcpy(msg.text, "end1");msgsnd(msqid, (void *)&msg, 100, 0);	// 将消息发送到消息队列中sem_wait(receive1);msgrcv(msqid, (void *)&msg, 100, 2, 0);printf("sender1 receive: %s\n\n", msg.text);sem_post(sender1);sem_post(mutex);sleep(1);return 0;}else {strcpy(msg.text, buf);msgsnd(msqid, (void *)&msg, 100, 0);sem_post(mutex);sleep(1);}}
}void *sender2_thread() {struct msg_buffer msg;char buf[100];int msgid = msgget((key_t) 2023, 0666|IPC_CREAT);if(msgid == -1) {printf("消息队列创建失败!");exit(-1);}sem_t *mutex = sem_open("mutex", O_CREAT | O_RDWR, 0666, 0);sem_t *sender2 = sem_open("sender2", O_CREAT | O_RDWR, 0666, 0);sem_t *receive2 = sem_open("receive2", O_CREAT | O_RDWR, 0666, 0);sem_t *next_input = sem_open("next_input", O_CREAT | O_RDWR, 0666, 0);while(1) {sem_wait(mutex);sem_wait(next_input);printf("sender2 input: ");gets(buf);msg.type = 1;if(strcmp(buf,"exit") == 0) {strcpy(msg.text, "end2");	msgsnd(msgid, (void *)&msg, 100, 0);	sem_wait(receive2);msgrcv(msgid, (void *)&msg, 100, 3, 0);printf("sender2 receive: %s\n\n", msg.text);sem_post(sender2);sem_post(mutex);sleep(1);return 0;}else {strcpy(msg.text, buf);msgsnd(msgid, (void *)&msg, 100, 0);sem_post(mutex);sleep(1);}}
}void *receiver_thread() {int finish1 = 0;int finish2 = 0;struct msg_buffer msg;char buf[100];sem_t *sender1 = sem_open("sender1", O_CREAT | O_RDWR, 0666, 0);sem_t *receive1 = sem_open("receive1", O_CREAT | O_RDWR, 0666, 0);sem_t *sender2 = sem_open("sender2", O_CREAT | O_RDWR, 0666, 0);sem_t *receive2 = sem_open("receive2", O_CREAT | O_RDWR, 0666, 0);sem_t *next_input = sem_open("next_input", O_CREAT | O_RDWR, 0666, 0);int msqid = msgget((key_t) 2023, 0666 | IPC_CREAT);if( msqid == -1) {printf("create failed");exit(-1);}int n;while(1){if(msgrcv(msqid, (void *)&msg, 100, 0, 0) > 0) {printf("Receiver receive: %s\n", msg.text);sem_post(next_input);if(strcmp(msg.text, "end1") == 0) {msg.type = 2;strcpy(msg.text, "over1");n = msgsnd(msqid, (void *)&msg, 100, 0);if(n != 0) {sem_post(receive1);sem_wait(sender1);}finish1 = 1;}else if(strcmp(msg.text, "end2") == 0) {msg.type = 3;strcpy(msg.text, "over2");n = msgsnd(msqid, (void *)&msg, 100, 0);    // 将详细发送到消息队列中if(n != 0) {sem_post(receive2);sem_wait(sender2);}finish2 = 1;}}if(finish1 == 1 && finish2 == 1) {msgctl(msqid, IPC_RMID, 0);	// 删除消息队列exit(0);}}
}int main() {// msgget函数用于创建一个新的消息队列或者获取一个已经存在的消息队列的标识符int msqid = msgget((key_t) 2023, 0666 | IPC_CREAT);msgctl(msqid, IPC_RMID, 0);	// 删除消息队列,释放相应的系统资源sem_unlink("mutex");sem_unlink("sender1");sem_unlink("sender2");sem_unlink("receiver1");sem_unlink("receiver2");sem_unlink("next_input");sem_t *mutex = sem_open("mutex", O_CREAT | O_RDWR, 0666, 0);sem_t *sender1 = sem_open("sender1", O_CREAT | O_RDWR, 0666, 0);sem_t *receive1 = sem_open("receive1", O_CREAT | O_RDWR, 0666, 0);sem_t *sender2 = sem_open("sender2", O_CREAT | O_RDWR, 0666, 0);sem_t *receive2 = sem_open("receive2", O_CREAT | O_RDWR, 0666, 0);sem_t *next_input = sem_open("next_input", O_CREAT | O_RDWR, 0666, 0);pthread_t p1, p2, p3;if(pthread_create(&p1, NULL, sender1_thread, NULL) != 0) {printf("Sender1线程创建失败!");exit(-1);}if(pthread_create(&p2, NULL, sender2_thread, NULL) != 0) {printf("Sender2线程创建失败!");exit(-1);}if(pthread_create(&p3, NULL, receiver_thread, NULL) != 0) {printf("receiver线程创建失败!");exit(-1);}sem_post(mutex);sem_post(next_input);pthread_join(p1, NULL);pthread_join(p2, NULL);pthread_join(p3, NULL);return 0;
}

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

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

相关文章

刷题DAY29 | LeetCode 491-递增子序列 46-全排列 47-全排列 II

491 递增子序列&#xff08;medium&#xff09; 给你一个整数数组 nums &#xff0c;找出并返回所有该数组中不同的递增子序列&#xff0c;递增子序列中 至少有两个元素 。你可以按 任意顺序 返回答案。 数组中可能含有重复元素&#xff0c;如出现两个整数相等&#xff0c;也…

开放签开源电子签章白皮书-简版

开放签开源电子签章白皮书-简版 一、摘要&#xff1a; 开放签电子签章团队源自于电子合同SaaS公司&#xff0c;立志于通过开源、开放的模式&#xff0c;结合团队十多年的行业经验&#xff0c;将电子签章产品更简单、更低门槛的推广到各行各业中。让电子签章应用更简单&#x…

Fetch、Axios 和 jQuery(Ajax) 三种常用的网络请求技术

Fetch、Axios 和 jQuery(Ajax) 是三种常用的网络请求技术&#xff0c;它们各自有着不同的特点和优势。本文将对这三种技术进行详细的介绍和比较&#xff0c;以帮助开发者更好地选择和使用合适的网络请求技术。 一、Fetch Fetch(浏览器自带) 是一种现代的网络请求 API&#xff…

Vue3组件的注册

组件是Vue.js中的一个重要概念&#xff0c;它是一种抽象&#xff0c;是一个可以复用的Vue.js实例。它拥有独一无二的组件名称&#xff0c;可以扩展HTML元素&#xff0c;以组件名称的方式作为自定义的HTML标签。 在大多数系统网页中&#xff0c;网页都包含header、body、footer…

Debezium vs OGG vs Tapdata:如何实时同步 Oracle 数据到 Kafka 消息队列?

随着信息时代的蓬勃发展&#xff0c;企业对实时数据处理的需求逐渐成为推动业务创新和发展的重要驱动力。在这个快速变化的环境中&#xff0c;许多企业选择将 Oracle 数据库同步到 Kafka&#xff0c;以满足日益增长的实时数据处理需求。本文将深入探讨这一趋势的背后原因&#…

数学建模(熵权法 python代码 例子)

目录 介绍&#xff1a; 模板&#xff1a; 例子&#xff1a;择偶 极小型指标转化为极大型&#xff08;正向化&#xff09;&#xff1a; 中间型指标转为极大型&#xff08;正向化&#xff09;&#xff1a; 区间型指标转为极大型&#xff08;正向化&#xff09;&#xff1a…

基于BusyBox的imx6ull移植sqlite3到ARM板子上

1.官网下载源码 https://www.sqlite.org/download.html 下载源码解压到本地的linux环境下 2.解压并创建install文件夹 3.使用命令行配置 在解压的文件夹下打开终端&#xff0c;然后输入以下内容&#xff0c;其中arm-linux-gnueabihf是自己的交叉编译器【自己替换】 ./config…

PyTorch 深度学习(GPT 重译)(三)

六、使用神经网络拟合数据 本章内容包括 与线性模型相比&#xff0c;非线性激活函数是关键区别 使用 PyTorch 的nn模块 使用神经网络解决线性拟合问题 到目前为止&#xff0c;我们已经仔细研究了线性模型如何学习以及如何在 PyTorch 中实现这一点。我们专注于一个非常简单…

拷贝他人maven仓库jar包到自己本地仓库,加载maven依然提示无法下载对应依赖

所遇问题&#xff1a; 拷贝他人maven仓库jar包到自己本地maven仓库repository下的对应依赖位置&#xff0c;重新加载idea的maven依然提示无法下载对应依赖。 解决办法&#xff1a; 在maven->repository找到对应报错依赖路径&#xff0c;删除xxx.repositories 和 xxx.lastU…

【视频图像取证篇】模糊图像增强技术之去噪声类滤波场景应用小结

【视频图像取证篇】模糊图像增强技术之去噪声类滤波场景应用小结 模糊图像增强技术之去噪声类滤波场景应用小结—【蘇小沐】 文章目录 【视频图像取证篇】模糊图像增强技术之去噪声类滤波场景应用小结&#xff08;一&#xff09;去噪声类滤波器1、去块滤波器&#xff08;Deblo…

32.768K晶振X1A000141000300适用于无人驾驶汽车电子设备

科技的发展带动电子元器件的发展电子元器件-“晶振”为现代的科技带来了巨大的贡献&#xff0c;用小小的身体发挥着大大的能量。 近两年无人驾驶汽车热度很高&#xff0c;不少汽车巨头都已入局。但这项技术的难度不小&#xff0c;相信在未来几年里&#xff0c;无人驾驶汽车这项…

软考高级:软件架构风格-独立构件风格概念和例题

作者&#xff1a;明明如月学长&#xff0c; CSDN 博客专家&#xff0c;大厂高级 Java 工程师&#xff0c;《性能优化方法论》作者、《解锁大厂思维&#xff1a;剖析《阿里巴巴Java开发手册》》、《再学经典&#xff1a;《Effective Java》独家解析》专栏作者。 热门文章推荐&am…

MySQL 索引的分类和优化

​ 优质博文&#xff1a;IT-BLOG-CN 索引是什么 &#xff1a; MySQL 官方对索引的定义&#xff1a;索引&#xff08;Index&#xff09;是帮助 MySQL 高效获取数据的数据结构。可以得到索引的本质&#xff1a;索引是数据结构。索引的目的在于提高查询效率。可以简单理解为&#…

力扣爆刷第101天之hot100五连刷91-95

力扣爆刷第101天之hot100五连刷91-95 文章目录 力扣爆刷第101天之hot100五连刷91-95一、62. 不同路径二、64. 最小路径和三、5. 最长回文子串四、1143. 最长公共子序列五、72. 编辑距离 一、62. 不同路径 题目链接&#xff1a;https://leetcode.cn/problems/unique-paths/desc…

实现防抖函数并支持第一次立刻执行(vue3 + ts环境演示)

1、先看一效果&#xff1a; 2、实现思路&#xff1a; 使用定时器setTimeout和闭包实现常规防抖功能&#xff1b;增加immediate字段控制第一次是否执行一次函数&#xff08;true or false&#xff09;&#xff1b;增加一个flag标识&#xff0c;在第一次执行时&#xff0c;将标…

【go从入门到精通】for循环控制

前言 Go 语言提供了 for 循环语句&#xff0c;用于重复执行一段程序逻辑&#xff0c;直到循环条件不再满足时终止。 循环可以用于迭代各种数据结构&#xff08;例如切片、数组、映射或字符串&#xff09;中的元素 本文将很基础的for循环语法&#xff0c;循环嵌套&#…

二、阅读器的开发(初始)-- 1、阅读器简介及开发准备工作

1、阅读器工作原理及开发流程 1.1阅读器工作原理简介 电子书&#xff08;有txt、pdf、epub、mobi等格式&#xff09;->解析&#xff08;书名、作者、目录、封面、章节等&#xff09;->&#xff08;通过阅读器引擎&#xff09;渲染 -> 功能&#xff08;字号、背景色、…

PHP页面如何实现设置独立访问密码

PHP网页如果需要查看信息必须输入密码&#xff0c;验证后才可显示出内容的代码如何实现&#xff1f; 对某些php页面设置单独的访问密码,如果密码不正确则无法查看内容,相当于对页面进行了一个加密。 如何实现这个效果&#xff0c;详细教程可以参考&#xff1a;PHP页面如何实现…

谁将主导未来AI市场?Claude3、Gemini、Sora与GPT-4的技术比拼

【最新增加Claude3、Gemini、Sora、GPTs讲解及AI领域中的集中大模型的最新技术】 2023年随着OpenAI开发者大会的召开&#xff0c;最重磅更新当属GPTs&#xff0c;多模态API&#xff0c;未来自定义专属的GPT。微软创始人比尔盖茨称ChatGPT的出现有着重大历史意义&#xff0c;不亚…

前后端分离项目springsecurity实现用户登录认证快速使用

目录 1、引入依赖 2、创建类继承WebSecurityConfigurerAdapter &#xff08;1&#xff09;重写里面的configure(HttpSecurity http)方法 &#xff08;2&#xff09;重写AuthenticationManager authenticationManagerBean() &#xff08;3&#xff09;密码加密工具 3、继承…