进程线程间的通信

news2024/11/29 2:41:39

进程和线程之间有很多种方法进行通信,如下是需要掌握的通信方式:

  • 无名管道(pipe)
  • 有名管道(fifo)
  • 信号(signal)
  • 共享内存(mmap)

本文章代码存放在GitHub中的UNIX_Coding中,需要自行查看

https://github.com/Scholar618/UNIX_Codings/tree/main/Linux

无名管道

定义:在内核中开辟一块内存作为通道连接两个进程之间的通信,它一般是单向的

特点 :

  1. 只能在具有公共祖先的两个进程之间使用
  2. 单工(单向)的通信模式,具有固定的读写端
  3. 无名管道创建时会返回两个文件描述符,分别用于读写管道

无名管道创建

int pipe(int pfd[2]);
  • 成功时返回0,失败时返回EOF
  • pfd包含两个元素的整形数组,用来保存文件描述符
  • pfd[0]用于读管道,pfd[1]用于写管道

示例程序:

#include <stdio.h>
#include <unistd.h>
#include <string.h>
int main() {
	int pfd[2];
	int re;
	char buf[20];
	pid_t pid;
	re = pipe(pfd);
	if(re < 0) {
		perror("pipe");
		return 0;
	}
	pid = fork();
	if(pid < 0) {
		perror("fork");
		return 0;
	} else if(pid == 0) { // 子进程
		while(1) {
			strcpy(buf, "I love Linux!");
			write(pfd[1], buf, strlen(buf));
			sleep(1);
		}
	} else { // 父进程	
		while(1) {
			re = read(pfd[0], buf, 20);
			if(re > 0) {
				printf("read pipe = %s\n", buf);
			}
		}
	}
	return 0;
}

管道可以由大于等于两个进程共享

例如一个父进程进行读数据,两个子进程进行写数据
示例程序:

#include <stdio.h>
#include <unistd.h>
#include <string.h>
int main(){
    int pfd[2];
    int i;
    int re;
    char buf[40]={0};
    pid_t pid;
    re = pipe(pfd);
    if(re<0){
        perror("pipe");
        return 0;
    }
    printf("%d,%d\n",pfd[0],pfd[1]);
 
    for(i=0;i<2;i++){
        pid = fork();
        if(pid<0){
            perror("fork");
            return 0;
        }else if(pid>0){
 
        }else{
            break;   
             
        }
    }
    if(i==2){
        close(pfd[1]);
        while(1){
            memset(buf,0,40);
            re=read(pfd[0],buf,40);
            if(re>0){
                printf("%s\n",buf);
            }    
        }
        return 0;
    }
 
    if(i==1){
        close(pfd[0]);
        while(1){
            strcpy(buf,"this is 2 process");
            write(pfd[1],buf,strlen(buf));
            usleep(930000);
        }
        return 0;
    }
    if(i==0){
        close(pfd[0]);
        while(1){
            strcpy(buf,"this is 1 process");
            write(pfd[1],buf,strlen(buf));
            sleep(1);
        }
 
        return 0;
    }
 
 
}

无名管道的读写特性

读管道:

  1. 管道中有数据,read返回实际读到的字节数
  2. 管道中无数据:
    1. 管道写端被全部关闭,read返回0(假设读到文件末尾)
    2. 写段没有被完全关闭,read阻塞等待(以后可能会有数据传递,此时让出CPU)

写管道:

  1. 管道读端全部被关闭,进程异常终止(也可以捕捉SIGPIPE信号,使进程不终止)
  2. 管道读端没有全部关闭:
    1. 管道已满,write阻塞(管道大小64K)
    2. 管道未满,write将数据写入,并返回实际写入的字节数

有名管道

特点

  • 有名管道可以使非亲缘关系的两个进程相互通信
  • 通过路径名来操作,在文件系统中可见,但内容存放在内存中
  • 文件IO来操作有名管道
  • 遵循先进先出原则
  • 不支持leek操作
  • 单工读写

有名管道的创建

#include <fcntl.h>
#include <unistd.h>

int mkfifo(const char *path, mode_t mode);
  • 成功时返回0,失败时返回EOF
  • path创建的管道文件路径
  • mode管道文件的权限,如0666

示例代码:fifor.c(读管道)

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

int main() {
	int re;
	int fd;
	char buf[32];
	fd = open("/myfifo", O_RDONLY);
	if(fd < 0) {
		perror("open");
		return 0;
	}
	while(1) {
		re = read(fd, buf, 32);
		if(re > 0) {
			printf("read fifo = %s",buf);
		} else if(re == 0) {
			exit(0);
		}
	}
}

fifow.c(写管道)

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

int main() {
	int re;
	int fd;
	char buf[32];
	re = mkfifo("/myfifo", 0666);
	if(re < 0) {
		perror("mkfifo");
//		return 0;
	}
	fd = open("/myfifo", O_WRONLY|O_NONBLOCK);
	if(fd < 0) {
		perror("open");
		return 0;
	}
	printf("after open!\n");
	while(1) {
		fgets(buf, 32, stdin);
		write(fd, buf, strlen(buf));
	}

}

有名管道注意事项:

  1. 程序不能以O_RDWR(读写)模式打开FIFO文件进行读写操作
  2. 第二个参数中的选项O_NONBLOCK,选项O_NONBLOCK表示非阻塞,如果没有这个选项为阻塞的

共享内存

概念:

  • 共享内存也叫内存映射
  • 共享内存可以使用mmap()映射普通文件
  • 使一个磁盘文件与内存中的一个缓冲区相映射,进程可以像普通内存那样对文件进行访问,不必再调用write,read

函数定义:

void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);	
  • 功能:创建共享内存映射
  • 函数返回值:成功返回创建的映射区首地址,失败返回MAP_FAILED( ((void *) -1) ),设置errno值
  • 参数:
    • addr:指定要映射的内存地址,一般设置为 NULL 让操作系统自动选择合适的内存地址。
    • length:必须>0。映射地址空间的字节数,它从被映射文件开头 offset 个字节开始算起。

    • prot:指定共享内存的访问权限。可取如下几个值的可选:PROT_READ(可读), PROT_WRITE(可写), PROT_EXEC(可执行), PROT_NONE(不可访问)。

    • flags:由以下几个常值指定:MAP_SHARED(共享的) MAP_PRIVATE(私有的), MAP_FIXED(表示必须使用 start 参数作为开始地址,如果失败不进行修正),其中,MAP_SHARED , MAP_PRIVATE必选其一,而 MAP_FIXED 则不推荐使用。MAP_ANONYMOUS(匿名映射,用于血缘关系进程间通信)

    • fd:表示要映射的文件句柄。如果匿名映射写-1。

    • offset:表示映射文件的偏移量,一般设置为 0 表示从文件头部开始映射。

注意事项:

(1) 创建映射区的过程中,隐含着一次对映射文件的读操作,将文件内容读取到映射区。

(2) 当MAP_SHARED时,要求:映射区的权限应 <=文件打开的权限(出于对映射区的保护),如果不满足报非法参数(Invalid argument)错误。

当MAP_PRIVATE时候,mmap中的权限是对内存的限制,只需要文件有读权限即可,操作只在内存有效,不会写到物理磁盘,且不能在进程间共享。

(3) 映射区的释放与文件关闭无关,只要映射建立成功,文件可以立即关闭。

(4) 用于映射的文件大小必须>0,当映射文件大小为0时,指定非0大小创建映射区,访问映射地址会报总线错误,指定0大小创建映射区,报非法参数错误(Invalid argument)

(5) 文件偏移量必须为0或者4K的整数倍(不是会报非法参数Invalid argument错误).

(6)映射大小可以大于文件大小,但只能访问文件page的内存地址,否则报总线错误 ,超出映射的内存大小报段错误

信号

信号是软件中断,是一种处理异步事件的方法,例如终端用户输入中断键,会通过信号机制停止程序。例如kill -9 程序号,等等都是信号机制。

概念:

使用kill -l命令查看所有信号编号,注意哦,不存在编号为0的信号。编号为0的信号有新的用途。

一些常用信号解释:

 产生信号的方式: 

  • 用户按下某些终端键时,引发终端产生的信号,例如(Ctrl + C)产生中断信号(SIGNALINT)
  • 硬件异常产生信号:除数为0或者无效的内存引用等。会有硬件检测出来,并通知内核,然后内核为该进程产生信号。例如:对执行一个无效内存引用的进程产生(SIGSEGV)信号
  • 进程调用kill函数将任意信号发送给另一个进程或进程组,但是,接收信号进程必须和发送信号进程的所有者完全相同。或者发送信号进程是root用户
  • 用户使用kill命令
  • 当检测到某种软件条件已经发生,并通知有关进程时也会产生信号。

处理信号的方式:

  • 忽略信号:两种信号不能被忽略,SIGKILL(9)和SIGSTOP(19)
  • 捕捉信号:在用户函数中,执行用户希望对这种事件进行的处理。
  • 执行系统默认动作:如上图,执行默认动作,注意,对大多数信号的系统默认动作是终止该进程。

函数定义

#include <signal.h>

// 1.
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);

// 2.
void (*signal(int signo, void (*func) (int))) (int);

signo是信号名, func值常量是SIG_IGN、常量SIG_DFL或当接到此信号后要调用的函数地址

  • SIG_IGN:向内核表示忽略此信号(SIGKILL和SIGSTOP)不能被忽略
  • SIG_DFL:表示接到此信号后的动作是系统默认动作。
  • 函数地址:信号发生时,调用该函数,称为捕捉信号,称函数为信号处理程序、信号捕捉函数。

#include <signal.h>头文件中,有如下定义:

#define SIG_ERR (void(*)())-1   // 指定一个无效的处理程序
#define SIG_DFL (void(*)())0    // 使用默认的处理方式
#define SIG_IGN (void(*)())1    // 忽略信号

共享内存

允许两个或多个进程共享一个给定的存储区,在多个进程之间同步访问一个给定的存储区

函数定义

#include <sys/shm.h>

int shmctl(int shmid, int cmd, struct shmid_ds *buf);

注意:共享内存实现进程间通信是进程间通信最快的。

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

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

相关文章

libhv之hloop源码分析

1int hloop_run(hloop_t* loop) hloop_run总结&#xff1a; 1.loop状态判断和管理 2.loop的flags管理&#xff08;HLOOP_FLAG_QUIT_WHEN_NO_ACTIVE_EVENTS&#xff0c;HLOOP_FLAG_RUN_ONCE&#xff0c;HLOOP_FLAG_AUTO_FREE&#xff09; 3.创建custom event通信fd&#xff0c;方…

Docker consul容器服务更新与发现

Docker consul容器服务更新与发现 一、什么事服务注册与发现二、什么是consul三、consul部署1、consul服务器2、registrator服务器3、consul-template 一、什么事服务注册与发现 服务注册与发现是微服务架构中不可或缺的重要组件。起初服务都是单节点的&#xff0c;不保障高可…

MyBatis学习笔记之高级映射及延迟加载

文章目录 环境搭建&#xff0c;数据配置多对一的映射的思路逻辑级联属性映射association分布查询 一对多的映射的思路逻辑collection分布 环境搭建&#xff0c;数据配置 t_class表 t_stu表 多对一的映射的思路逻辑 多对一&#xff1a;多个学生对应一个班级 多的一方是st…

STM32 中断优先级管理(一)

STM32 NVIC 中断优先级管理 CM3内核支持256个中断&#xff0c;其中包含了16个内核中断和240个外部中断&#xff0c;并且有256级的可编程中断设置。 但STM32并没有使用CM3内核的全部东西&#xff0c;只用了一部分。 STM32有84个中断&#xff0c;包括16个内核中断和68个可屏蔽…

Windows数据类型LPSTR学习

Windows在C语言的基础之上又定义了一些Windows下的数据类型&#xff1b;下面学习一下LPSTR&#xff1b; LPSTR和LPWSTR是Win32和VC所使用的一种字符串数据类型。LPSTR被定义成是一个指向以NULL(‘\0’)结尾的32位ANSI字符数组指针&#xff0c;而LPWSTR是一个指向以NULL结尾的64…

Fiddler使用教程|渗透测试工具使用方法Fiddler

提示&#xff1a;如有问题可联系我&#xff0c;24小时在线 文章目录 前言一、Fiddler界面介绍二、菜单栏1.菜单Fiddler工具栏介绍Fiddler命令行工具详解 前言 网络渗透测试工具&#xff1a; Fiddler是目前最常用的http抓包工具之一。 Fiddler是功能非常强大&#xff0c;是web…

探秘MySQL底层架构:设计与实现流程

前言 Mysql&#xff0c;作为一款优秀而广泛使用的数据库管理系统&#xff0c;对于众多Java工程师来说&#xff0c;几乎是日常开发中必不可少的一环。无论是存储海量数据&#xff0c;还是高效地检索和管理数据&#xff0c;Mysql都扮演着重要的角色。然而&#xff0c;除了使用My…

全网最牛,Jmeter接口自动化-读取用例执行并结果回写(详细整理)

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 1、环境准备 下载…

(vue)vue项目获取日期 时间 星期

(vue)vue项目获取日期 时间 星期 效果&#xff1a; 代码&#xff1a; <div class"top-time"><div class"time-currentDate">{{ currentDate }}<div class"time-img"><img src"/assets/image/icon-time.png" a…

曲面显示器和平面显示器有什么区别?

电脑显示器的选择非常重要。除了现在平面显示器和曲面显示器也很流行之外&#xff0c;曲面显示器和平面显示器有什么区别呢&#xff1f;我们来看看曲面显示器和平面显示器的优缺点 如今&#xff0c;曲面屏显示器已成为主流。为电脑配置一个好的显示器非常重要。市场上有许多计算…

记录联想拯救者R720重装系统

文章目录 bios里找不到U盘启动项2023.7.23重装系统后数据记录C盘内存修改默认AppData的路径&#xff08;亲测&#xff0c;没用&#xff09; bios里找不到U盘启动项 制作好启动盘后&#xff0c;开机按F2进入bios后&#xff0c;找不到U盘启动项&#xff0c;如下图所示&#xff1…

常见面试题之设计模式--责任链模式

1. 概述 在现实生活中&#xff0c;常常会出现这样的事例&#xff1a;一个请求有多个对象可以处理&#xff0c;但每个对象的处理条件或权限不同。例如&#xff0c;公司员工请假&#xff0c;可批假的领导有部门负责人、副总经理、总经理等&#xff0c;但每个领导能批准的天数不同…

(原创)自定义DialogFragment以及解决其内存泄漏问题

前言 日常开发中&#xff0c;dialog是常见的功能&#xff0c;我们时常需要弹出来一些弹框提示用户 今天就定义了一个方便的dialog基类BaseSimpleDialogFragment&#xff0c; 支持快速地显示一个dialog 主要功能有&#xff1a; initAnimation&#xff1a;设置入场和出场动画 ge…

【青书学堂】管理学基础(直播课) 第一学期 考试

【青书学堂】计算机组装与维护(直播课) 第一学期 考试 标题最终成绩:83.34 分 注意:答案仅供参考 第1题 单选题 梅奥的霍桑试验表明( )。 A:非正式组织对组织目标的达成是有害的 B:非正式组织对组织目标的达成是有益的 C:企业应采取一切措施来取缔非正式组织 D:企业应该…

【Unity3D日常开发】Unity3D中比较string字符串的常用方法

推荐阅读 CSDN主页GitHub开源地址Unity3D插件分享简书地址我的个人博客 大家好&#xff0c;我是佛系工程师☆恬静的小魔龙☆&#xff0c;不定时更新Unity开发技巧&#xff0c;觉得有用记得一键三连哦。 一、前言 字符串string的比较有很多方法&#xff0c;比如&#xff1a; …

【动态规划】简单多状态

文章目录 动态规划&#xff08;简单多状态&#xff09;1. 按摩师2. 打家劫舍 ||3. 删除并获得点数4. 粉刷房子5. 最佳买卖股票时机含冷冻期6. 买卖股票的最佳时机含手续费7. 买卖股票的最佳时机 |||8. 买卖股票的最佳时机 IV 动态规划&#xff08;简单多状态&#xff09; 1. 按…

dp算法篇Day12

“我悲喜都&#xff0c;只换来这一场无声的野火。” 56、完全平方数 (1) 题目解析 ​​​​​​ 把题目解释到了这个份上&#xff0c;你很难不把思路转移到考虑 "背包问题上"。 (2) 算法原理 class Solution { public:int numSquares(int n) {int m sqrt(n);vec…

【Python基础】VS2019中使用Python及安装Python包

【Python基础】VS2019中使用Python及安装Python包 文章目录 前言一、VS2019中安装Python环境二、Python环境变量配置三、安装Python包总结 前言 要使用Python语言来写一些程序&#xff0c;使用哪个IDE是个问题&#xff0c;若是专业开发Python&#xff0c;PyCharm无疑是最佳选择…

RTI无线电层析成像Matlab仿真数据生成

文章目录 概述初始化环境参数逆面积椭圆模型 概述 无线电层析成像是一种通过获取一定区域内多对相对固定的无线通信节点间的某种测量数据后,按照一定的数学处理方法,对区域内的障碍物目标以图像的形式 展现出来的成像技术。 开山之作&#xff1a; J. Wilson and N. Patwari, …

【小白必看】Python爬虫实战:获取阴阳师网站图片并自动保存

文章目录 前言导入模块伪装自己发送请求获取地址列表获取所有背景的地址创建文件夹保存图片文件完整代码运行效果部分图片展示结束语 前言 本文介绍了一个使用Python编写的程序&#xff0c;用于获取指定网页的背景图片并保存到本地。在程序中使用了requests模块发送HTTP请求&a…