共享内存IPC原理
共享内存是在内存中单独开辟的一段内存空间,这段内存空间有自己的数据结构,包括访问权限,空间大小和最近一次的访问时间等等,数据结构定义如下:
49 /* Data structure describing a shared memory segment. */
50 struct shmid_ds
51 {
52 struct ipc_perm shm_perm; /* operation permission struct */
53 size_t shm_segsz; /* size of segment in bytes */
54 __time_t shm_atime; /* time of last shmat() */
55 unsigned long int __unused1;
56 __time_t shm_dtime; /* time of last shmdt() */
57 unsigned long int __unused2;
58 __time_t shm_ctime; /* time of last change by shmctl() */
59 unsigned long int __unused3;
60 __pid_t shm_cpid; /* pid of creator */
61 __pid_t shm_lpid; /* pid of last shmop */
62 shmatt_t shm_nattch; /* number of current attaches */
63 unsigned long int __unused4;
64 unsigned long int __unused5;
65 };
共享内存是用户态的空间,所以在使用共享内存传输信息的时候,不需要进行在用户态和内核态的缓冲区进行多次复制,也节省了相当一部分的时间,所以共享内存是进程间最快的通信方式。
下面来举例说明共享内存传输数据和管道传输数据的不同之处:
管道
管道在传输数据的过程中,先将相应文件复制到用户态缓冲区,在将信息从用户态缓冲区复制到内核缓冲区(管道),再将其从内核缓冲区复制到接收信息的用户进程缓冲区,再将信息复制到相应文件,一共进行4次复制操作,两次内核态与用户态之间的操作
共享内存
因为共享内存就是用户区的一块空间,直接将相应文件复制到共享内存,再将其复制到接受进程的相应文件,期间不涉及用户态与内核态的切换,也只需复制两次
Linux下共享内存管理
1.创建共享内存
函数shmget:
/* Get shared memory segment. */
extern int shmget (key_t __key, size_t __size, int __shmflg) __THROW;
参数分析:
key:key_t类型的key值,可自己定义或者用函数ftok创建出来
size:开辟的共享内存的大小
shmflg:用当识共享内存的创建标识,包括:
IPC_CREAT:如果不存在就创建,存在则返回shmid
IPC_EXEL:如果存在就报错返回
IPC_NOWAIT:不等待直接返回
2.共享内存的控制
函数shmctl:
/* Shared memory control operation. */
extern int shmctl (int __shmid, int __cmd, struct shmid_ds *__buf) __THROW;
参数分析:
shmid:由函数shmget的返回值确定
cmd:为要执行的参数,操作包括:
IPC_RMID:删除共享内存
IPC_SET:设置ipc_perm参数
IPC_STAT:设置ipc_perm参数
IPC_INFO:获取共享内存信息
buf:要设置或者接收信息的载体
3.映射共享内存对象
函数shmat:
void *shmat(int shmid, const void *shmaddr, int shmflg);
参数分析:
shmid:操作共享内存的句柄
shmaddr:指定共享内存映射的起始地址,大于0,如果设置为0,系统会自动寻找需要映射
的起始位置。
shmflg:用来指定共享内存的访问权限和映射条件,包括:
/* mode for attach */
#define SHM_RDONLY 010000 /* read-only access */只读
#define SHM_RND 020000 /* round attach address to SHMLBA boundary */
#define SHM_REMAP 040000 /* take-over region on attach */
#define SHM_EXEC 0100000 /* execution access */
将shmflg设置为0,则默认的是有读写的权限
返回可以传递信息的共享内存的首地址,任意类型
4.分离共享内存对象(解除映射关系)
函数shmdt:
int shmdt(const void *shmaddr);
参数分析:
之前函数shmat返回的地址
综合运用:
实现一个动态读取写入信息的例子
写端
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <unistd.h>
4 #include <sys/ipc.h>
5 #include <sys/shm.h>
6 #include <string.h>
7 #include <fcntl.h>
8 #include <sys/wait.h>
9
10 int main(int argc, char* argv[])
11 {
12 key_t key = ftok(argv[1], 100);
13 int shmid = shmget(key, 128, 0664|IPC_CREAT);
14 void* ptr = NULL;
15
16 while(1){
17 ptr = shmat(shmid, 0, 0);
18 printf("please input >");
19 fgets((char*)ptr, 128, stdin);
20 fflush(stdout);
21 fflush(stdin);
22 }
23
24 shmdt(ptr);
25 shmctl(shmid, IPC_RMID, NULL);
26 return 0;
27 }
~
读端
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <unistd.h>
4 #include <sys/ipc.h>
5 #include <sys/shm.h>
6 #include <string.h>
7 #include <fcntl.h>
8 #include <sys/wait.h>
9
10 int main(int argc, char* argv[])
11 {
12 key_t key = ftok(argv[1], 100);
13 int shmid = shmget(key, 128, 0664|IPC_CREAT);
14 void* ptr = NULL;
15
16 while(1){
17 ptr = shmat(shmid, 0, 0);
18 printf("recive>%s", (char*)ptr);
19 sleep(1);
20 }
21
22 shmdt(ptr);
23 shmctl(shmid, IPC_RMID, NULL);
24 return 0;
25 }