-
2022-03-18 17:32:24
不同进程之间通信,通常可以用共享内存/消息队列/信号量/管道等方法,在Linux系统下提供了相关的库函数来方便使用。
A. 共享内存
共享内存就相当于开辟了一块物理内存空间,不同的进程通过虚拟地址的映射都访问到同一块物理内存,这样就能直接在内存读写数据。下面说一下具体用到的函数:
# 查看当前系统的共享内存状态。 ipcs -m
1. shmget创建共享内存
函数原型:int shmget(key_t key , size_t size , int shmflag)
key:共享内存的标识符,一般通过ftok函数得到。
size:申请的内存大小,一般为4k的倍数。
shmflag:共享内存的权限标志,一般有3种情况:
1)IPC_CREATE:如果存在与key值对应的共享内存,则直接返回共享内存ID;否则先创建共享内存,再返回共享内存ID。
2)IPC_CREATE | IPC_EXCL:如果存在与key值对应的共享内存,则返回-1;否则先创建共享内存,再返回共享内存ID。
3)0。如果存在与key值对应的共享内存,则返回共享内存ID;否则返回-1。
2. shmat挂载共享内存
函数原型:void *shmat(int shmid, const void *shmaddr, int shmflg)
shmid:就是shmget成功调用后的返回值。
shmaddr:指定进程空间映射的虚拟地址,一般直接设为NULL。
shmflag:一般可以直接设为0;设为SHM_RDONLY表示只读模式。
3. shmdt卸载共享内存
意思就是说断开与共享内存的连接,禁止本进程访问该共享内存。
函数原型:int shmdt(const void *shmaddr)
shmaddr:就是shmat返回的虚拟地址。
成功则返回0;否则返回-1。
4. shmctl管理共享内存
用于控制该共享内存,包括获取、改变内存状态,销毁内存等。
函数原型:int shmctl(int shmid, int cmd, struct shmid_ds *buf)
shmid:共享内存ID,就是shmget调用成功后的返回值。
cmd:控制指令,一般包括以下三种:
1)IPC_STAT:得到共享内存的状态,把共享内存的shmid_ds结构复制到buf中。
2)IPC_SET:改变共享内存的状态,把buf所指的shmid_ds结构中的uid、gid、mode复制到共享内存的shmid_ds结构内。
3)IPC_RMID:删除这片共享内存
buf:buf是一个结构指针,它指向共享内存模式和访问权限的结构。
成功调用则返回0;否则返回-1。
5. 共享内存C++范例
server:读内存
#include <sys/shm.h> #include <sys/ipc.h> #include <iostream> #include <unistd.h> int main() { key_t key = ftok(".", 1); int shmId = shmget(key, 4096, IPC_CREAT); char *addr = (char*)shmat(shmId, NULL, SHM_RDONLY); int input = 20; while (input--) { sleep(1); std::cout << addr << std::endl; } if (shmdt(addr) == -1) { perror("shmdt fail"); return -1; } if (shmctl(shmId, IPC_RMID, NULL) == -1) { perror("shmctl fail"); return -2; } return 0; }
client:写内存
#include <sys/shm.h> #include <sys/ipc.h> #include <iostream> #include <unistd.h> int main() { key_t key = ftok(".", 1); int shmId = shmget(key, 4096, 0); if (shmId == -1) { perror("shmget fail"); return -1; } char *addr = (char*)shmat(shmId, NULL, 0); int input = 20; int i = 0; while (input--) { sleep(1); addr[i++] = 'A'; std::cout << "在第" << i << "个位置写入了A." << std::endl; } if (shmdt(addr) == -1) { perror("shmdt fail"); return -1; } if (shmctl(shmId, IPC_RMID, NULL) == -1) { perror("shmctl fail"); return -2; } return 0; }
B. 消息队列
消息队列可以理解为是一个消息的链表,有写权限的进程可以向消息队列中添加消息,有读权限的进程可以从消息队列中读走消息,本质就是个数据结构。
msgget创建消息队列
函数原型:extern int msgget(key_t key, int _msgflg);
第一个参数key是由ftok创建的key值。
第二个参数_msgflg的低位用来确定消息队列的访问权限,如0770为文件的访问类型;此外还可以附加以下参数值(通过或的方式与权限一起使用):
IPC_CREAT:如果key不存在,则创建;存在,则直接返回。
IPC_EXCL:如果key存在,返回失败。
IPC_NOWAIT:如果需要等待,则直接返回错误。
msgctl控制消息队列
函数原型:extern int msgctl(int _msqid, int _cmd, struct msqid_ds *_buf);
待续~
C. 管道
管道本质上是内核的一块缓存,主要分无名管道和有名管道。无名管道只能用在具有亲缘关系的进程,有名管道可以用在两个不相干的进程。
待续~
D. 信号量
信号量的本质是数据操作锁,它本身不具有数据交换的功能,而是通过控制其他的通信资源(文件,外部设备)来实现进程间通信,它本身只是一种外部资源的标识。信号量在此过程中负责数据操作的互斥、同步等功能。
更多相关内容 -
C++共享内存进程间通信 demo
2017-05-10 17:58:16C++共享内存进程间通信 FileMapping用于进程间快速通信 -
c# 用共享内存实现进程通信
2021-04-03 13:00:15c# 用共享内存实现进程通信 开几个程序都可以访问同样的内存数据 [DllImport("Kernel32.dll", CharSet = CharSet.Auto)] public static extern IntPtr CreateFileMapping(int hFile, IntPtr lpAttributes, ... -
win32下进程间通信(共享内存)实例分析
2021-01-01 06:24:40进程间通讯(即:同机通讯)和数据交换有多种方式:消息、共享内存、匿名(命名)管道、邮槽、Windows套接字等多种技术。“共享内存”(shared memory)可以定义为对一个以上的进程是可见的内存或存在于多个进程的虚拟... -
进程间通信(IPC):共享内存和消息队列原理详解
2021-01-27 13:17:53如果一个进程不能影响其他进程或受其他进程影响,那么该进程是独立的,换句话说,不与任何其他进程共享数据的进程是独立的; 如果一个进程能影响其他进程或受其他进程所影响,那么该进程是协作的。换句话说,与其他... -
西安邮电大学Linux高级编程进程间通信_共享内存 进程间通信
2020-11-20 06:44:58第 5 章 System V 进程间通信 1 2 System V IPC 基础 消息队列 3 信号量通信机制 4 共享内存 信号量基本概念 ? 信号量是操作系统中解决进程或线程同步与互斥的最重要机 制之一 Linux 内核提供 System V 的信号量机制... -
c#共享内存demo-两个进程间通过共享内存实现通信
2018-02-18 08:44:38分为3个工程:proA,proB,ShareMemLib 其中proA,proB是用wpf写的两个模拟进程程序 ShareMemLib将共享内存代码封装成lib,定义了发送者和监听者 两个进程在实例化Share...至此成功完成了两个进程间通过共享内存实现通信 -
进程间通信之共享内存
2021-09-15 14:08:51共享内存实现进程间通信,是操作系统在实际物理内存开辟一块空间,一个进程在自己的页表中,将该空间和进程地址空间上的共享区的一块地址空间形成映射关系。另外一进程在页表上,将同一块物理空间和该进程地址空间...目录
接博客:进程间通信之管道
一.共享内存实现进程间通信的原理
共享内存实际是操作系统在实际物理内存中开辟的一段内存。
共享内存实现进程间通信,是操作系统在实际物理内存开辟一块空间,一个进程在自己的页表中,将该空间和进程地址空间上的共享区的一块地址空间形成映射关系。另外一进程在页表上,将同一块物理空间和该进程地址空间上的共享区的一块地址空间形成映射关系。
当一个进程往该空间写入内容时,另外一进程访问该空间,会得到写入的值,即实现了进程间的通信。
要实现进程间通信需要两个进程能够看到同一块空间,系统开辟的共享内存就是两进程看到的同一资源。
注意:共享内存实现进程间通信是进程间通信最快的。
关于页表的补充:
进程地址空间里有一个内核区域,它们也会在实际物理内存开辟空间,也会有页表与那块空间形成映射关系,这个页表叫做内核级页表。因为内核只有一个,所以每个进程都相同的。说明进程都共用实际物理内存上的内核空间。
除内核空间以外的空间,与实际物理空间之间的页表,称为用户级页表。每个进程可能不同。
二.管理共享内存的数据结构
共享内存实现进程间通信不只仅限于两个进程之间,可以用于多个进程之间。并且系统中可能会有多个进程在进行多个通信。所以系统需要将这些通信的进程管理起来。如果不管理,操作系统怎么知道这块共享内存挂接了哪个进程等信息。
如何管理?先描述和组织。
查看内核源代码,可以看到系统描述共享内存的数据结构如下:
/* Obsolete, used only for backwards compatibility and libc5 compiles */ struct shmid_ds { struct ipc_perm shm_perm; /* operation perms */ int shm_segsz; /* size of segment (bytes) *///共享内存空间大小 __kernel_time_t shm_atime; /* last attach time *///挂接时间 __kernel_time_t shm_dtime; /* last detach time *///取消挂接时间 __kernel_time_t shm_ctime; /* last change time *///改变时间 __kernel_ipc_pid_t shm_cpid; /* pid of creator */ __kernel_ipc_pid_t shm_lpid; /* pid of last operator */ unsigned short shm_nattch; /* no. of current attaches *///进程挂接数 unsigned short shm_unused; /* compatibility */ void *shm_unused2; /* ditto - used by DIPC */ void *shm_unused3; /* unused */ };
描述共享内存的数据结构里保存了一个ipc_perm结构体,这个结构体保存了IPC(进程将通信)的关键信息。
/* Obsolete, used only for backwards compatibility and libc5 compiles */ struct ipc_perm { __kernel_key_t key;//共享内存的唯一标识符 __kernel_uid_t uid; __kernel_gid_t gid; __kernel_uid_t cuid; __kernel_gid_t cgid; __kernel_mode_t mode; //权限 unsigned short seq; };
其中的key是共享内存的唯一标识。
三.共享内存函数
- ftok函数
作用:算出一个唯一的key返回。
参数:第一个是地址,第二个是至少8为的项目id,不能为0,两参数可以是任意值,但是要符合格式。
返回值:ftok如果成功返回一个key值,如果失败返回-1。如果失败了再重新填写参数即可。
ftok中的参数可以随便填写,但是要符合格式,ftok只是利用参数,再运用一套算法,算出一个唯一的key值返回。这个key值可以传给共享内存参数,作为struct ipc_perm中唯一标识共享内存的key。
ftok函数并没有涉及内核层面。
#pragma once #define PATHNAME "./" #define PROJ_ID 0x666 #include<stdio.h> #include<sys/types.h> #include<sys/ipc.h> #include"com.h" int main(){ key_t k = ftok(PATHNAME,PROJ_ID); if(k==-1){ perror("ftok error"); return 1; } printf("ftok : %d\n",k); return 0; }
- shmget函数
作用:创建一个共享内存
参数:
key:为共享内存的名字,一般是ftok的返回值。
size:共享内存的大小,以page为单位,大小为4096的整数倍。
shmflg:权限标志,常用两个IPC_CREAT和IPC_EXCL,一般后面还加一个权限,相当于文件的权限。
IPC_CREAT:创建一个共享内存返回,已存在打开返回
IPC_EXCL:配合着IPC_CREAT使用,共享内存已存在出错返回。
使用:IPC_CREAT | IPC_EXCL | 0666
返回值:
成功返回一个非负整数,即共享内存的标识码,失败返回-1。
为什么已经有一个key来标识共享内存,还需要一个返回值来标识共享内存?因为key是内核级别的,供内核标识,shmget返回值是用户级别的,供用户使用的。
#include<stdio.h> #include<sys/types.h> #include<sys/ipc.h> #include<sys/shm.h> #include"com.h" int main(){ key_t k = ftok(PATHNAME, PROJ_ID); if (k == -1){ perror("ftok error"); return 1; } printf("ftok : %d\n", k); int shmid = shmget(k, SIZE, IPC_CREAT | IPC_EXCL | 0666); if (shmid == -1){ perror("shmget error"); return 1; } printf("shmid : %d\n", shmid); return 0; }
我们发现当进程创建了一个共享内存,没有释放,进程结束后,共享内存还在,所以第二次执行程序,会报错(报错是因为IPC_EXCL)。
这里得出一个结论:IPC(进程将通信)资源生命周期不随进程,而是随内核的,不释放会一直占用,除非重启。所以,shmget创建的共享内存要释放掉,不然会内存泄漏。
可以用命令行来释放共享内存:ipcrm -m shmid(shmget返回值)
也可以用下面的函数来释放共享内存
- shmctl函数
作用:用于控制共享内存
参数: shmid:共享内存的标识
cmd:以什么方式来控制共享内存。IPC_RMID是释放共享内存
buf:指向一个共享内存的数据结构 。struct shmid_ds
返回值:成功返回0,失败返回-1。
1 #include<stdio.h> 2 #include<sys/types.h> 3 #include<sys/ipc.h> 4 #include<sys/shm.h> 5 #include"com.h" 6 7 int main(){ 8 key_t k = ftok(PATHNAME,PROJ_ID);//获取一个唯一标识符key 9 if(k==-1){ 10 perror("ftok error"); 11 return 1; 12 } 13 14 printf("ftok : %d\n",k); 15 16 int shmid = shmget(k,SIZE,IPC_CREAT | IPC_EXCL | 0666);//创建共享内存 17 if(shmid == -1){ 18 perror("shmget error"); 19 return 1; 20 } 21 printf("shmid : %d\n",shmid); 22 23 int sh = shmctl(shmid,IPC_RMID,NULL);//删除共享内存 24 if(sh == -1){ 25 perror("shmctl"); 26 return 1; 27 } 28 return 0; 29 }
现在就可以执行多次:
- shmat函数
作用:使创建的共享内存与调用该函数进程的进程地址空间参数关联。
参数:
shmid:共享内存的标识,shmget的返回值。
shmaddr:指定进程地址空间连接的地址。如果设置为null,默认让系统定要关联的地址。
shmflg: 权限,常见有两个SHM_RDONLY(只读)和SHM_REMAP(重新映射一个进程地址空间没这样shmaddr不能为空)。设为0,系统默认。
返回值:
返回映射到进程地址空间共享区的开始地址。
- shmdt函数
作用:删除共享内存与进程地址空间的映射关系,将页表映射关系删除,释放进程地址空间。
参数:
shmaddr:共享内存映射到进程地址空间的地址。shmat返回值。
返回值:
成功返回0,失败返回-1
shmat和shmdt要一起使用才起作用。
1 #include<stdio.h> 2 #include<unistd.h> 3 #include<sys/types.h> 4 #include<sys/ipc.h> 5 #include<sys/shm.h> 6 #include"com.h" 7 8 int main(){ 9 key_t k = ftok(PATHNAME,PROJ_ID); 10 if(k==-1){ 11 perror("ftok error"); 12 return 1; 13 } 14 15 printf("ftok : %d\n",k); 16 int shmid = shmget(k,SIZE,IPC_CREAT | IPC_EXCL | 0666); 17 if(shmid == -1){ 18 perror("shmget error"); 19 return 1; 20 } 21 printf("shmid : %d\n",shmid); 22 //与进程地址空间产生关联 23 char *str = (char *)shmat(shmid, NULL, 0); 24 //10秒后删除关联 25 sleep(10); 26 shmdt(str); 27 28 //int sh = shmctl(shmid,IPC_RMID,NULL); 29 //if(sh == -1){ 31 // return 1; 32 //} 33 return 0; 34 }
程序10秒钟删除共享内存与进程地址空间的关联:
四.实现进程间通信
实现进程间通信步骤:
- 创建共享内存
- 共享内存关联进程
- 删除共享内存与进程的关联
- 释放共享内存
使用到上面的函数。
实现一个用户client与服务器server之间的简单通信。
需要client和server都和共享内存关联。client端不创建共享内存,不释放共享内存,server端创建共享内存,并且释放共享内存。
1 #pragma once 2 3 #define PATHNAME "./" 4 #define PROJ_ID 0x666 5 6 #define SIZE 4096
client代码:写入端
1 #include<stdio.h> 2 #include<unistd.h> 3 #include<sys/types.h> 4 #include<sys/ipc.h> 5 #include<sys/shm.h> 6 #include"com.h" 7 8 int main(){ 9 key_t k = ftok(PATHNAME,PROJ_ID); 10 if(k==-1){ 11 perror("ftok error"); 12 return 1; 13 } 14 //不创建共享内存,只是为了得到shmid 15 int shmid = shmget(k, SIZE, 0); 16 if(shmid == -1){ 17 perror("shmget error"); 18 return 1; 19 } 20 //与进程地址空间产生关联 21 char *str = (char *)shmat(shmid, NULL, 0); 22 char c='a'; 23 for(;c<='z';c++){ 24 str[c-'a']=c; 25 sleep(5); 26 } 27 //删除关联 28 shmdt(str); 29 //不用释放共享内存,服务器端释放 30 31 return 0; 32 }
server代码:读出端
1 #include<stdio.h> 2 #include<unistd.h> 3 #include<sys/types.h> 4 #include<sys/ipc.h> 5 #include<sys/shm.h> 6 #include"com.h" 7 8 int main(){ 9 key_t k = ftok(PATHNAME,PROJ_ID); 10 if(k==-1){ 11 perror("ftok error"); 12 return 1; 13 } 14 //服务器关不要加IPC_EXCL,已存在不要保存,直接返回 15 int shmid = shmget(k,SIZE,IPC_CREAT | IPC_EXCL | 0666); 16 if(shmid == -1){ 17 perror("shmget error"); 18 return 1; 19 } 20 //与进程地址空间产生关联 21 char *str = (char *)shmat(shmid, NULL, 0); 22 //读数据 23 while(1){ 24 printf("%s\n",str); 25 sleep(1); 26 } 27 //删除关联 28 shmdt(str); 29 int sh = shmctl(shmid,IPC_RMID,NULL); 30 if(sh == -1){ 31 perror("shmctl"); 32 return 1; 33 } 34 return 0; 35 }
代码输出,每隔5秒写端往共享内存中写一个字符,读端每个一秒读一下数据。
这里有一个问题:因为写端比读端慢,相比较于管道,当写端比读端慢时,没有写入时,读端要进入阻塞状态,但是上面发现使用共享内存的读端一直在读,并没有阻塞。
这里得出一个结论:共享内存实现的进程间通信底层不提供任何同步与互斥机制。如果想让两进程很好的合作起来,在IPC里要有信号量来支撑。
注意:两进程之间用通过同样的规则即ftok参数要一样,来获取同样的key值,然后在创建共享内存时,用这个key值唯一标识共享内存,所以两进程时通过同样的key值来实现看到同一份资源。
补充:
-
linux讲解通过共享内存实现进程间的通信
2017-05-23 20:59:12讲解了下linux共享内存函数 -
Python进程间通信之共享内存详解
2020-09-21 02:28:44主要为大家详细介绍了Python进程间通信之共享内存的相关资料,具有一定的参考价值,感兴趣的小伙伴们可以参考一下 -
使用共享内存及信号量实现进程间通信例子
2019-03-28 09:51:42代码实现了共享内存和信号量的结合,实现进程间通信及其同步问题。通过此代码可以理解共享内存及信号量基本函数的使用及实现原理。 -
嵌入式系统/ARM技术中的进程间通信之共享内存(一)
2020-11-09 13:19:16因为数据不需要在各个进程之间复制,所以这是最快的一种进程间通信方式。使用共享内存时的关键点在于如何在多个进程之间对一给定的存储区进行同步访问。 例如若一个进程正在将数据放入共享内存区,则在它做完这一... -
C#进程间通信-共享内存代码实例
2013-12-04 08:03:58C#进程间通信-共享内存代码实例。用实现了2个进程,他们之间使用共享内存方式进行通信。 -
Linux环境进程间通信(五) 共享内存(上)
2015-02-27 09:55:48Linux环境进程间通信(五) 共享内存(上) -
Qt共享内存实现进程间通信(ShareMemory).zip
2021-03-29 17:29:10Qt共享内存实现进程间通信(QSharedMemory) 源代码 -
Windows进程间通信之共享内存
2013-05-13 21:02:09共享内存方式实现进程间通信。详细看我的博客:http://blog.csdn.net/pengguokan/article/details/8921346 -
linux通过共享内存实现进程之间的通信
2017-05-23 21:13:22自己做的一个ppt通过共享内存的方式实现进程间的通信 -
QT间进程通信之共享内存
2013-12-09 15:42:50比如说QQ的聊天的客户端,这里有个个性头象,当点击QQ音乐播放器的时候,启动QQ音乐播放(启动一QQ音乐播放器的进程)这时QQ音乐播放器里也有一个个性头像,这两者间的头像一样,现用共享内存的方法实现。 -
QT 进程间通信——共享内存-附件资源
2021-03-05 15:21:49QT 进程间通信——共享内存-附件资源 -
共享内存实现进程间通信示例程序
2017-07-12 06:44:50http://blog.csdn.net/ezhchai/article/details/74992980文章中讲解的示例程序完整工程文件 -
进程间通信-共享内存
2022-04-23 13:40:45共享内存是System V版本的一个进程间通信方式。共享内存,顾名思义就是允许两个不相关的进程访问同一个逻辑内存,共享内存是两个正在运行的进程之间共享和传递数据的一种非常有效的方式。不同进程之间共享的内存通常...共享内存
共享内存的理解
共享内存是System V版本的一个进程间通信方式。共享内存,顾名思义就是允许两个不相关的进程访问同一个逻辑内存,共享内存是两个正在运行的进程之间共享和传递数据的一种非常有效的方式。不同进程之间共享的内存通常为同一段物理内存。进程可以将同一段物理内存连接到他们自己的地址空间中,所有的进程都可以访问共享内存中的地址。如果某个进程向共享内存写入数据,所做的改动将立即影响到可以访问同一段共享内存的任何其他进程。
特别提醒:共享内存并未提供同步机制,也就是说,在第一个进程结束对共享内存的写操作之前,并无自动机制可以阻止第二个进程开始对它进行读取,所以我们通常需要用其他的机制来同步对共享内存的访问,例如信号量。
共享内存通信原理
在之前学习管道时就说过多个进程之间要实现通信的话,就得让它们看到同一个资源,通过这个资源来实现通信。
在共享内存中是让他们看到同一块内存。在Linux中,每个进程都有属于自己的进程控制块(PCB)和地址空间(Addr Space),并且都有一个与之对应的页表,负责将进程的虚拟地址与物理地址进行映射,通过内存管理单元(MMU)进行管理。两个不同的虚拟地址通过页表映射到物理空间的同一区域,它们所指向的这块区域即共享内存
当两个进程通过页表将虚拟地址映射到物理地址时,在物理地址中有一块共同的内存区,即共享内存,这块内存可以被两个进程同时看到。这样当一个进程进行写操作,另一个进程读操作就可以实现进程间通信。但是,我们要确保一个进程在写的时候不能被读,因此我们使用信号量来实现同步与互斥 。
但是我们要怎么判断两个进程是挂载到了同一块内存空间,因为内存空间中可能不止一个共享内存,这里我们了解一个创建key值的函数ftok()函数,这里的key值就是这块共享内存空间的唯一标识符。两个进程通过同一个key值挂载到同一块共享内存。
key_t ftok(const char* pathname,int proj_id);
参数解释:
*pathname就是你指定的文档名
proj_id是子序号
返回值:成功返回一个key值,失败返回-1。
nattch是共享内存的挂载计数器,对于一个共享内存,实现采用的是引用计数的原理,当进程脱离共享存储区后,计数器减一,挂载成功时,计数器加一,只有当计数器变为零时,才能被删除。当进程终止时,它所附加的共享存储区都会自动脱离。
共享内存的接口函数以及指令
指令
1查看系统中的共享储存
ipcs -m
2手动删除系统中的共享内存
ipcrm -m [shmid]
接口函数
1、创建共享内存
int shmget(key_t key,size_t size, int shmflg)
参数解释:
key: ftok()函数生成的key值,标识系统中唯一的IPC资源。
size:我们要申请的共享内存大小,最好是页的整数倍,一页是4k字节。
shmflg:如果要创建新的共享内存,需要使用IPC_CREAT,IPC_EXCL,如果是已经存在的,可以使用IPC_CREAT或者直接传0。
返回值:
成功时返回一个新建或已经存在的的共享内存标识符,取决于shmflg的参数。失败返回-1并设置错误码。
2、挂载共享内存
void* shmat(int shmid,const void *shmaddr,int shmflg);
参数解释:
shmid:共享内存的标识符。
shmaddy:shmaddr=0,则存储段连接到由内核选择的第一个可用地址上(推荐使用)。
shmflg:若指定了SHM_RDONLY位,则以只读方式链接共享内存,否则以读写方式链接。
返回值:
成功返回共享存储段的指针(虚拟地址),并且内核将使其与该共享存储段相关的shmid_ds结构中的shm_nattch计数器加1(类似于引用计数);出错返回-1 。
3、去关联共享内存
当进程之间完成通信时,就需要去关联,需要将该进程从之前连接的共享内存上脱离下来。
int shmdt(const void* shmaddr);
参数解释:
shmaddr:shmat()函数成功的返回值。
返回值
成功返回0,并将shmid_ds结构体中的 shm_nattch计数器减1;出错返回-1
4、释放共享内存
当shm_nattch为0时就需要释放这块共享内存,否则就会一直占用下去,因为共享内存的生命周期是跟随内核的。
int shmctl(int shmid,int cmd,struct shmid_ds* buf);
参数解释:
shmid:共享内存标识符。
cmd:指定执行的操作,设置为IPC_RMID时表示释放共享内存。
buf:暂时不需要了解,设置为NULL就行。
返回值:
成功返回0,失败返回-1.
模拟共享内存
//server.c #include<stdio.h> #include<unistd.h> #include"comm.h" #include<sys/types.h> #include<sys/ipc.h> #include<sys/shm.h> int main() { //ftok创建key值 key_t k=ftok(PATHNAME,PROJ_ID); //根据key值创建共享内存 int shmid=shmget(k,SIZE,IPC_CREAT|IPC_EXCL|0666); //判断是否创建成功 if(shmid<0) { perror("shmget"); return 1; } //挂载共享内存 char* str=(char*)shmat(shmid,NULL,0); //int i=0; while(1) { //i++; sleep(1); printf("%s\n",str); } //去关联 shmdt(str); //释放共享内存 shmctl(shmid,IPC_RMID,NULL); return 0; }
//client.c #include<stdio.h> #include<unistd.h> #include"comm.h" #include<sys/types.h> #include<sys/ipc.h> #include<sys/shm.h> int main() { //ftok创建key值 key_t k=ftok(PATHNAME,PROJ_ID); //根据key值找到共享内存 int shmid=shmget(k,SIZE,0); //判断是否创建成功 if(shmid<0) { perror("shmget"); return 1; } //挂载共享内存 char* str=(char*)shmat(shmid,NULL,0); char c='a'; for(;c<='z';c++) { str[c-'a']=c; sleep(2); } //去关联 shmdt(str); //这里不用释放共享内存,只需要释放一次就可以,server.c已经释放了。 //shmctl(shmid,IPC_RMID,NULL); return 0; }
-
进程间通信(共享内存)
2021-05-10 18:21:38共享内存、消息队列、信号量 提示:这里可以添加系列文章的所有文章的目录,目录需要自己手动添加 例如:第一章 Python 机器学习入门之pandas的使用 提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助...共享内存
一、共享内存
共享内存 : 用于进程间的数据共享
1.共享内存实现通信的原理
开辟一块物理内存空间, 各个进程将同一块物理内存空间映射到自己的虚拟地址空间中, 通过虚拟地址进行访问, 进而实现数据共享
共享内存是最快的进程间通信方式, 因为通过虚拟地址空间映射后, 直接通过虚拟地址访问物理内存, 相较于其他方式少了两步数据拷贝的操作.2.操作流程
1.创建或打开共享内存int shmget(key_t key, size_t size, int shmflg);
2.与进程建立映射关系
key: 标识符- -通过相同的标识符, 多个进程就可以打开同一块共享内存;
size: 要创建的共享内存大小;
shmflg: 打开方式 + 权限; IPC_CREAT: 不存在则创建, 存在则打开;
IPC_EXCL与IPC_CREAT搭配使用, 文件存在则报错, 不存在则创建打开;
返回值: 成功返回非负整数–操作句柄; 失败返回-1;void *shmat(int shmid, const void *shmaddr, int shmflg);
3.对共享内存进行内存操作
shmid: shmget返回的操作句柄;
shmaddr: 映射的首地址, 通常置NULL;
shflg: SHM_RDONLY-只读; 0-可读可写;
返回值: 成功返回映射后的首地址; 失败返回(void*)-1;
凡是涉及到内存操作的接口, 都能对共享内存进行操作;memcpy, strcpy, printf...... 4.与进程间解除映射关系int shmdt(const void *shmaddr);
shmaddr: shmat返回的映射首地址;
返回值: 成功返回0; 失败返回-1;
5.删除共享内存int shmctl(int shmid, int cmd, struct shmid_ds *buf);
buf: 用于获取或设置共享内存属性的, 简单使用置NULL即可;
shmid: shmget返回的操作句柄;
cmd: 要对共享内存进行的操作类型;
IPC_CMD: 标记要删除的共享内存段;
映射连接数为0时删除共享内存; 禁止新的映射连接
返回值: 成功返回0; 失败返回-1;二、代码实践
1.创建共享内存
代码如下(示例):
1 #include <stdio.h> 2 #include <unistd.h> 3 #include <stdlib.h> 4 #include <sys/shm.h> 5 #define IPC_KEY 0x01234567 6 #define PROJ_ID 0x01234567 7 int main() 8 { 9 //key_t key = ftok("./", PROJ_ID); 10 //shmget(标识符, 大小, 打开方式和权限) 11 int shmid = shmget(IPC_KEY, 32, IPC_CREAT | 0664); 12 if(shmid < 0){ 13 perror("shmget error"); 14 return -1; 15 } 16 return 0; 17 }
ipcs: 查看系统内核当中所有的进程间通信资源; ipcs -m: 查看共享内存; ipcs -q: 查看消息队列;ipcs -s: 查看信号量;2.映射到虚拟地址
代码如下(示例):
16 //shmat(句柄, 映射首地址, 访问方式) 17 void *shm_start = shmat(shmid, NULL, 0); 18 if(shm_strat == (void*)-1){ 19 perror("shmat error"); 20 return -1; 21 } 22 while(1){ 23 printf("%s\n", (char*)shm_start); 24 sleep(1); 25 } 26 shmdt(shm_start); 27 //shmctl(句柄, 操作类型, 信息结构) 28 shmctl(shmid, IPC_RMID, NULL); 29 return 0; 30 }
3.修改共享内存(一个写, 一个读)
关闭read, write仍然一直在修改, 关闭write, read也一直在读, 两边互不影响
两边同时打开, 映射连接数为2;
总结
共享内存:
通信原理: 开辟一块物理内存, 多个进程将同一块内存映射到自己的虚拟地址空间, 进行访问, 进而实现数据共享.
特性:
1.效率最高的进程间通信方式;
2.声明周期随内核;
注意事项: 各个进程对共享内存的操作都是不安全的操作 -
QT实现共享内存进行进程间通信
2021-01-23 13:23:10QT实现共享内存进行进程间通信 -
C#使用共享内存实现进程间的通信
2021-09-26 11:47:32进程通信有多种方式,比如socket、管道、共享内存。c#直接提供了共享内存的相关库,但直接使用起来还是不太方便,需要使用Marshal处理内存对齐以及托管非托管转换的问题,本文提供一种,将上述操作包装,借助反射的... -
Windows下进程间通信——共享内存
2022-02-07 11:19:06window API 共享内存 文件映射 CreateFileMapping -
最强版VB程序间共享内存,进程通信-共享变量,支持结构
2021-05-28 23:35:26程序间共享内存,用于数据交换,非常强大的示例,解决你进程间数据共享的问题 不错,解决了传输问题,可以共享N个字符串。整数等类型 改造一下,进程通讯就可以用了 我用它做串口数据共亭。。不错。。 挺好用的我... -
c++进程间通信方式——共享内存(文件映射)
2022-03-27 21:14:03共享内存是最快的 IPC 方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号量,配合使用,来实现进程间的同步和通信。 一、原理 共享内存允许两个或多个进程共享一个给定的存储... -
共享内存实现进程间通信
2020-05-14 23:45:43采用共享内存实现进程间通信。方便实在,代码中忘了释放资源了,下载后的同学记得释放掉共享内存。很实用。 -
linux无亲缘关系间进程同步通信实现(互斥锁+条件变量+共享内存模式)
2021-04-03 21:49:19linux无亲缘关系间进程同步通信实现(互斥锁+条件变量+共享内存模式)