linux进程间通信--共享内存
2021-03-06
共享内存
共享内存是System V版本的最后一个进程间通信方式。共享内存,顾名思义就是允许两个不相关的进程访问同一个逻辑内存,共享内存是两个正在运行的进程之间共享和传递数据的一种非常有效的方式。不同进程之间共享的内存通常为同一段物理内存。进程可以将同一段物理内存连接到他们自己的地址空间中,所有的进程都可以访问共享内存中的地址。如果某个进程向共享内存写入数据,所做的改动将立即影响到可以访问同一段共享内存的任何其他进程。
函数介绍
共享内存相关函数有shmget()
,shmat()
,shmdt()
,shmctl()
shmget – 用来获得共享内存区域的ID。
函数定义
1
2
3
4
| #include <sys/ipc.h>
#include <sys/shm.h>
int shmget(key_t key, size_t size, int shmflg);
|
参数:
- key: 提供一个参数key(0-32位整数),通过key查找或创建对应的共享内存段
- size: size以字节为单位指定需要共享大的内存容量
- shmflag: 权限标志,需要与IPC对象存取权限(如0600)进行
|
运算来确定信号量集的存取权限- IPC_CREAT: 如果不存在键值与key相等的共享内存,则新建一个共享内存,如果存在这样的共享内存,则返回共享内存的标识符
- IPC_EXCL: 与IPC_CREATE配合使用,如果内核中不存在键值与key相等的共享内存,则新建一个共享内存;如果存在这样的共享内存则报错
返回值:
- 成功: 返回共享内存的标识符
- 失败: -1,错误原因存于errno中
错误代码:
- EINVAL:参数size小于SHMMIN或大于SHMMAX
- EEXIST:预建立key所指的共享内存,但已经存在
- EIDRM:参数key所指的共享内存已经删除
- ENOSPC:超过了系统允许建立的共享内存的最大值(SHMALL)
- ENOENT:参数key所指的共享内存不存在,而参数shmflg未设IPC_CREAT位
- EACCES:没有权限
- ENOMEM:核心内存不足
shmat – 把共享内存对象映射到调用进程的地址空间
函数定义
1
2
3
4
| #include <sys/types.h>
#include <sys/shm.h>
void *shmat(int shmid, const void *shmaddr, int shmflg);
|
参数:
- shmid: 共享内存标识符
- shmaddr: 指定共享内存出现在进程内存地址的什么位置,直接指定为
NULL
让内核自己决定一个合适大的地址位置 - shmflag: SHM_RDONLY为只读模式,其它为读写模式
返回值
- 成功: 附加好的共享内存地址
- 失败: -1 错误原因存于errno中
错误代码:
- EACCES:没有权限以指定方式连接共享内存
- EINVAL:无效的参数shmid或shmaddr
- ENOMEM:核心内存不足
shmdt – 断开共享内存的连接
1
2
3
4
| #include <sys/types.h>
#include <sys/shm.h>
int shmdt(const void *shmaddr);
|
参数: shmaddr: 连接的共享内存的起始地址
返回值:
errno:
shmctl – 完成对共享内存的控制
1
2
3
4
| #include <sys/ipc.h>
#include <sys/shm.h>
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
|
参数
- shmid: 共享内存标识符
- cmd:
- IPC_STAT: 得到共享内存的状态,把共享内存的shmid_ds结构复制到buf中
- IPC_SET: 改变共享内存的状态,把buf所指的shmid_ds结构中的uid、gid、mode复制到共享内存的shmid_ds结构内
- IPC_RMID: 删除这片共享内存
- buf: 共享内存管理结构体。具体说明参见共享内存内核结构定义部分
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
| 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 ipc_perm {
key_t __key; /* Key supplied to shmget(2) */
uid_t uid; /* Effective UID of owner */
gid_t gid; /* Effective GID of owner */
uid_t cuid; /* Effective UID of creator */
gid_t cgid; /* Effective GID of creator */
unsigned short mode; /* Permissions + SHM_DEST and
SHM_LOCKED flags */
unsigned short __seq; /* Sequence number */
};
|
返回值:
errno:
- EACCES:参数cmd为IPC_STAT,却没有权限读取该共享内存
- EFAULT:参数buf指向无效的内存地址
- EIDRM:标识符为shmid的共享内存已被删除
- EINVAL:无效的参数cmd或shmid
- EPERM:参数cmd为IPC_SET或IPC_RMID,却无足够的权限执行
示例
头文件
1
2
3
4
5
6
7
8
9
10
| // shm.h
#ifndef _SHMDATA_H_HEADER
#define _SHMDATA_H_HEADER
#define TEXT_SZ 2048
struct shared_use_st
{
int written;//作为一个标志,非0:表示可读,0表示可写
char text[TEXT_SZ];//记录写入和读取的文本
};
#endif
|
write
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
| #include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include "shm.h"
#include <errno.h>
int main()
{
key_t key = 1234;
struct shared_use_st *shared;
int size;
int shmid;
int running = 1;
shmid = shmget(key, sizeof(struct shared_use_st), 0666|IPC_CREAT);
if(shmid == -1) {
perror("shmget");
return -1;
}
printf("memory attached at 0x%X\n", shmid);
shared = shmat(shmid, 0, 0);
if(!shared) {
perror("shmat");
return -1;
}
shared->written = 0;
while(running) {
if(shared->written != 0) {
printf("wait.....\n");
sleep(1);
}
else
{
printf("enter buf:");
fgets(shared->text, BUFSIZ, stdin);
shared->written = 1;
if(strncmp(shared->text, "exit", 4) == 0) {
running = 0;
}
}
}
printf("shadt\n");
if(shmdt(shared) == -1) {
perror("shmdt");
return -1;
}
// printf("shmctl\n");
// if(shmctl(shmid, IPC_RMID, 0) == -1) {
// perror("shmctl");
// return -1;
// }
return 0;
}
|
read
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
| #include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include "shm.h"
#include <errno.h>
int main()
{
key_t key = 1234;
struct shared_use_st *shared;
int size;
int shmid;
int running = 1;
shmid = shmget(key, sizeof(struct shared_use_st), 0666|IPC_CREAT);
if(shmid == -1) {
peror("shmget");
return -1;
}
printf("memory attached at 0x%X\n", shmid);
shared = shmat(shmid, 0, 0);
if(!shared) {
peror("shmat");
return -1;
}
shared->written = 0;
while(running) {
if(shared->written != 0) {
shared->written = 0;
printf("read text:%s\n", shared->text);
if(strncmp(shared->text, "exit", 4) == 0) {
running = 0;
}
}
else
{
sleep(1);
}
}
printf("shadt\n");
if(shmdt(shared) == -1) {
perror("shmdt");
return -1;
}
printf("shmctl\n");
if(shmctl(shmid, IPC_RMID, 0) == -1) {
perror("shmctl");
return -1;
}
return 0;
}
|
总结
共享内存中的数据,从来不写入到实际磁盘文件中去;而通过mmap()映射普通文件实现的共享内存通信可以指定何时将数据写入磁盘文件中。 注:系统V共享内存机制实际是通过shm文件系统中的文件实现的,shm文件系统的安装点在交换分区上,系统重新引导后,所有的内容都丢失。
共享内存是随内核持续的,即使所有访问共享内存的进程都已经正常终止,共享内存区仍然存在(除非显式删除共享内存),在内核重新引导之前,对该共享内存区域的任何改写操作都将一直保留。
通过调用mmap()映射普通文件进行进程间通信时,一定要注意考虑进程何时终止对通信的影响。而通过共享内存实现通信的进程则不然。