0

0

Linux进程间通信怎么实现

王林

王林

发布时间:2023-05-21 16:28:52

|

1801人浏览过

|

来源于亿速云

转载

    共享内存

    • 共享内存可以说是最有用的进程间通信方式,也是最快的ipc形式,两个不同的进程a、b共享内存的意思就是:同一块物理内存被映射到进程a、b各自的进程地址空间,进程a可以同时看到进程b对共享内存中数据的更新,反之亦然。

    • 由于个多个进程共享同一块内存区域,必然需要某种同步机制、互斥锁和信号量都可以。

    好处: 效率高,进程可以直接读写内存,而不需要复制任何数据,而管道、消息队列等通信方式,则需要在内核和用户空间进行四次数据复制。

    并且只有在解除映射时,共享内存的内容才会写会文纪念

    共享内存通过内核对象,使得不同的进程在自己的虚拟地址空间上分配一块空间映射到相同的物理内存空间上,这块物理内存空间对于映射到上面的每个进程而言都是可以访问的。(临界资源)

    Linux进程间通信怎么实现

    共享内存就是允许两个不相关的进程访问同一个逻辑内存

    共享内存是在两个正在运行的进程之间共享和传递数据的一种非常有效的方式。

    不同进程之间共享的内存通常安排为同一段物理内存。

    进程可以将同一段共享内存连接到它们自己的地址空间中,所有进程都可以访问共享内存中的地址,就好像它们是由用C语言函数malloc()分配的内存一样。

    而如果某个进程向共享内存写入数据,所做的改动将立即影响到可以 访问同一段共享内存的任何其他进程。

    mmap()及其相关的系统调用

    mmap是linux操作系统提供给用户空间调用的内存映射函数,很多人仅仅只是知道可以通过mmap完成进程间的内存共享和减少用户态到内核态的数据拷贝次数,但是并没有深入理解mmap在操作系统内部是如何实现的,原理是什么

    Linux进程间通信怎么实现

    使用mmap()系统调用,进程们可以通过映射同一个普通文件来实现内存共享。普通文件被映射到进程地址空间后,进程可以访问普通内存一样对文件进行访问,不必再调用read和write操作。 

    注意: mmap并不是完全为了IPC而设计的,只是IPC的一种应用方式,它本身提供了一种像访问普通内存一样的访问对普通文件进行操作的方式。

    通过使用带有特殊权限集的虚拟内存段来实现。当对这种虚拟内存段进行读写时,操作系统会去读写与之对应的磁盘文件部分。

    mmap 函数创建一个指向一段内存区域的指针,该内存区域与可以通过一个打开的文件描述符访问的文件的内容相关联

    解释如下:

    mmap()

    #include 
    
    void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);

    可以通过传递 offset 参数来改变经共享内存段访问的文件中数据的起始偏移值。

    打开的文件描述符由 fd 参数给出。

    可以访问的数据量(即内存段的长度)由 length 参数设置。

    可以通过 addr 参数来请求使用某个特定的内存地址。如果它的取值是零,结果指针就将自动分配。若不遵循此做法,则会降低程序的可移植性,因为可用地址范围在不同系统上是不同的。

    prot 参数用于设置内存段的访问权限。它是下列常数值的按位或的结果

    • PROT_READ 内存段可读。

    • PROT_WRITE 内存段可写。

    • PROT_EXEC 内存段可执行。

    • PROT_NONE 内存段不能被访问。

    flags 参数控制程序对该内存段的改变所造成的影响:

    Linux进程间通信怎么实现

    mmap()用于共享内存的量和两种方式如下:

    使用普通文件提供的内存映射,适用于任何进程间,使用该方式需要先打开或者创建一个文件,再调用ngmmap,典型调用代码如下:

    fd = open(name.falg.mode);
    if(fd < 0)
    ptr = mmap(NULL,len.PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);

    使用特殊文件提供的内存映射,适用于具有亲缘关系的进程之间,由于父子进程特殊的亲缘关系,在父进程中先调用mmap,调用fork,那么在代用fork之后,子进程可以继承父进程匿名映射后的地址空间,同样也继承mmap返回的地址,这样父子进程就可以通过映射区域进行通信了。(注意:一般来说,子进程单独维护从父进程继承而来的一些变量,而mmap()返回的地址由父子进程共同维护)【具体使用实现敬请期待博主整理】

    munmap()

    用于解除内存映射,取消参数start所指的映射内存的起始地址,参数length则是欲取消的内存大小,当进程结束或者利用exec相关函数来执行其他程序时,映射内存会自动解除,但关闭对应的文件描述符时不会解除映射。

    #include 
    
    int munmap(void *addr, size_t length);

    共享内存的使用

    与信号量一样,在Linux中也提供了一组函数接口用于使用共享内存,而且使用共享共存的接口还与信号量的非常相似,而且比使用信号量的接口来得简单。它们声明在头文件 sys/shm.h 中。 

    Android AsyncChannel源码分析 WORD版
    Android AsyncChannel源码分析 WORD版

    本文档主要讲述的是Android AsyncChannel源码分析;AsyncChannel类用于处理两个Handler之间的异步消息传递,消息传递的Handler可以出于同一进程,也可以处于不同进程,不同进程之间的Handler消息传递使用Android的Binder通信机制来实现。希望本文档会给有需要的朋友带来帮助;感兴趣的朋友可以过来看看

    下载

    1.获取或创建内核对象,并且制定共享内存的大小(系统分配物理空间是,按照页进行分配)

    int shmget(key_t key, int size, int flag);

    只是创建内核对象,并申请物理空间

    • key_t key:与信号量的semget函数一样,程序需要提供一个参数key(非0整数),它有效地为共享内存段命名,不同的进程通过相同的key值来访问同一块共享内存

    • int size:size以字节为单位指定需要共享的内存容量

    • int flag:falg是权限标志,它的作用与open函数的mode参数一样,如果要想在key标识的共享内存不存在时,创建它的话,可以与IPC_CREAT做或操作。共享内存的权限标志与文件的读写权限一样,举例来说,0644,它表示允许一个进程创建的共享内存被内存创建者所拥有的进程向共享内存读取和写入数据,同时其他用户创建的进程只能读取共享内存。

    返回值

    • 调用成功后,shmget()函数会返回一个非负整数,用作后续共享内存函数的共享内存标识符,该标识符与key相关。

    • 调用失败返回-1.

    2.分配自己虚拟地址空间映射到共享内存的物理空间上

    void *shmat(int shmid,const void *addr, int flag);
    • shmid:shmid是由shmget()函数返回的共享内存标识。

    • void *addr:addr指定共享内存连接到当前进程中的地址位置,通常为NULL,表示让系统来选择共享内存的地址。

    • int flag:flag是一组标志位,通常为0。

    调用成功时返回一个指向共享内存第一个字节的指针,如果调用失败返回-1.

    3.断开当前进程与共享内存的映射

    不使用删除而使用断开的原因是因为:也许还存在其他的进程会继续使用这块共享内存

    int shmdt(const void *addr);

    4.操作共享内存的方法

    int shmctl(int shmid, int cmd, struct shmid_t *buf);
    • int shmid:shmid是shmget()函数返回的共享内存标识符。

    • int cmd:command是要采取的操作,它可以取下面的三个值 :

    IPC_STAT:把shmid_ds结构中的数据设置为共享内存的当前关联值,即用共享内存的当前关联值覆盖shmid_ds的值。IPC_SET:如果进程有足够的权限,就把共享内存的当前关联值设置为shmid_ds结构中给出的值IPC_RMID:删除共享内存段
    • struct shmid_t *buf:buf是一个结构指针,它指向共享内存模式和访问权限的结构

    因为有连接计数器,除非最后一个进程与该共享段断开连接,则删除该共享段。否则,并不会真正删除该共享段,但是共享内存的内核对象会被立即删除,不能使用shmat方法与该段连接。 

    一个进程调用该方法删除后,不会影响之前已经和该共享存储段连接的进程

    下面我们利用共享内存来进行一个简单的测试:

    Linux进程间通信怎么实现

    Linux进程间通信怎么实现

    完成下面的过程,

    Linux进程间通信怎么实现

    成功在共享内存中读到了数据

    Linux进程间通信怎么实现

    #include
    #include
    #include
    #include
    #include
    
    #include
    #include
    #include
    
    #include"sem.h"
    
    #define READSEM 1
    #define WRITESEM 0
    
    int main()
    {
    	int shmid = shmget((key_t)1234,128,0664 | IPC_CREAT);
    	assert(shmid != -1);
    
    	char *ptr = (char*)shmat(shmid,NULL,0);
    	assert(ptr != (char*)-1);
    	
    	int initVal[] = {1,0};
    	int semid = SemGet(1234,intVal,2);
    	assert(semid != -1);
    	
    	//A进程写
    	while(1)
    	{
    		SemP(semid,WRITESEM);
    		printf("Input:");
    		
    		fgets(ptr,127,stdin);
    		
    		SemV(semid,READSEM);
    		
    		if(strncmp(ptr,"end",3) == 0)
    		{
    			break;
    		}
    	}
    	
    	shmdt(ptr);
    	exit(0);
    }
    #include
    #include
    #include
    #include
    #include
    
    #include
    #include
    #include
    
    #include"sem.h"
    
    #define READSEM 1
    #define WRITESEM 0
    
    int main()
    {
    	int shmid = shmget((key_t)1234,128,0664 | IPC_CREAT);
    	assert(shmid != -1);
    
    	char *ptr = (char*)shmat(shmid,NULL,0);
    	assert(ptr != (char*)-1);
    	
    	int initVal[] = {1,0};
    	int semid = SemGet(1234,intVal,2);
    	assert(semid != -1);
    	
    	//B进程读
    	while(1)
    	{
    		SemP(semid,READSEM);
    		
    		if(strncmp(ptr,"end",3) == 0)
    		{
    			break;
    		}
    		
    		int i = 0;
    		for(;i < strlen(ptr) - 1;i++)
    		{
    			printf("%c",toupper(ptr[i]));
    			fflush(stdout);
    			sleep(1);
    		}
    		printf("\n");
    		SemV(semid,WRITESEM);
    	}
    	
    	shmdt(ptr);
    	exit(0);
    }

    从上面的代码中我们可以看出: 

    共享内存是最快的IPC,在通信过程中少了两次数据的拷贝。(相较于管道)

    命令管理共享内存

    • 查看 ipcs -m

    • 删除 ipcrm -m shmid

    相关专题

    更多
    C语言变量命名
    C语言变量命名

    c语言变量名规则是:1、变量名以英文字母开头;2、变量名中的字母是区分大小写的;3、变量名不能是关键字;4、变量名中不能包含空格、标点符号和类型说明符。php中文网还提供c语言变量的相关下载、相关课程等内容,供大家免费下载使用。

    378

    2023.06.20

    c语言入门自学零基础
    c语言入门自学零基础

    C语言是当代人学习及生活中的必备基础知识,应用十分广泛,本专题为大家c语言入门自学零基础的相关文章,以及相关课程,感兴趣的朋友千万不要错过了。

    607

    2023.07.25

    c语言运算符的优先级顺序
    c语言运算符的优先级顺序

    c语言运算符的优先级顺序是括号运算符 > 一元运算符 > 算术运算符 > 移位运算符 > 关系运算符 > 位运算符 > 逻辑运算符 > 赋值运算符 > 逗号运算符。本专题为大家提供c语言运算符相关的各种文章、以及下载和课程。

    348

    2023.08.02

    c语言数据结构
    c语言数据结构

    数据结构是指将数据按照一定的方式组织和存储的方法。它是计算机科学中的重要概念,用来描述和解决实际问题中的数据组织和处理问题。数据结构可以分为线性结构和非线性结构。线性结构包括数组、链表、堆栈和队列等,而非线性结构包括树和图等。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

    255

    2023.08.09

    c语言random函数用法
    c语言random函数用法

    c语言random函数用法:1、random.random,随机生成(0,1)之间的浮点数;2、random.randint,随机生成在范围之内的整数,两个参数分别表示上限和下限;3、random.randrange,在指定范围内,按指定基数递增的集合中获得一个随机数;4、random.choice,从序列中随机抽选一个数;5、random.shuffle,随机排序。

    583

    2023.09.05

    c语言const用法
    c语言const用法

    const是关键字,可以用于声明常量、函数参数中的const修饰符、const修饰函数返回值、const修饰指针。详细介绍:1、声明常量,const关键字可用于声明常量,常量的值在程序运行期间不可修改,常量可以是基本数据类型,如整数、浮点数、字符等,也可是自定义的数据类型;2、函数参数中的const修饰符,const关键字可用于函数的参数中,表示该参数在函数内部不可修改等等。

    518

    2023.09.20

    c语言get函数的用法
    c语言get函数的用法

    get函数是一个用于从输入流中获取字符的函数。可以从键盘、文件或其他输入设备中读取字符,并将其存储在指定的变量中。本文介绍了get函数的用法以及一些相关的注意事项。希望这篇文章能够帮助你更好地理解和使用get函数 。

    629

    2023.09.20

    c数组初始化的方法
    c数组初始化的方法

    c语言数组初始化的方法有直接赋值法、不完全初始化法、省略数组长度法和二维数组初始化法。详细介绍:1、直接赋值法,这种方法可以直接将数组的值进行初始化;2、不完全初始化法,。这种方法可以在一定程度上节省内存空间;3、省略数组长度法,这种方法可以让编译器自动计算数组的长度;4、二维数组初始化法等等。

    595

    2023.09.22

    俄罗斯搜索引擎Yandex最新官方入口网址
    俄罗斯搜索引擎Yandex最新官方入口网址

    Yandex官方入口网址是https://yandex.com;用户可通过网页端直连或移动端浏览器直接访问,无需登录即可使用搜索、图片、新闻、地图等全部基础功能,并支持多语种检索与静态资源精准筛选。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

    1

    2025.12.29

    热门下载

    更多
    网站特效
    /
    网站源码
    /
    网站素材
    /
    前端模板

    精品课程

    更多
    相关推荐
    /
    热门推荐
    /
    最新课程
    PostgreSQL 教程
    PostgreSQL 教程

    共48课时 | 6.2万人学习

    Git 教程
    Git 教程

    共21课时 | 2.3万人学习

    关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
    php中文网:公益在线php培训,帮助PHP学习者快速成长!
    关注服务号 技术交流群
    PHP中文网订阅号
    每天精选资源文章推送

    Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号