0. 前言
共享内存可以说是最有用的进程间通信方式,也是最快的IPC 形式。两个不同的进程A、B共享内存的基本原理是,同一块物理内存映射到进程A、B各自的进程地址空间。进程A可以即时看到B对共享内存中数据的更新,反之亦然。
如图:
共享内存特点:
- 共享内存是最快的IPC形式,因为内存映射到共享它的进程的地址空间,这些进程数据传递就不再涉及到内核了,也就是说说不再通过执行进入内核的系统调用来传递彼此的数据,所以,它的速度是最快的。
- 共享内存的生命周期随进程,也需要显示地删除。
- 共享内存没有互斥与同步机制,因此,我们在使用时,需要自己添加。
1. 创建共享内存
系统调用shmget() 创建或获取一个已经存在的共享内存的标识符,函数原型:
#include <sys/shm.h> //里面包含 #include <sys/ipc.h>
int shmget(key_t key, size_t size, int shmflg);
参数:
- key 共享内存的关键字,可以通过ftok() 创建,详细看进程间通信——消息队列
- size 共享内存的大小
- shmflg 有两个选项,IPC_CREAT 表示内核中没有此共享内存则创建它。IPC_EXCL 当和IPC_CREAT一起使用时,如果共享内存已经存在,则返回错误。
返回值:成功返回标识符,否则返回-1。通过errno和perror函数可以查看错误信息。
2. 连接共享内存
shmat() 可以获取一个共享内存的地址,并将其连接到进程中,函数原型:
#include <sys/shm.h>
void *shmat(int shmid, const void *shmaddr, int shmflg);
参数:
- shmid 共享内存的标识符。
- shmaddr 指定的连接地址。若shmaddr取NULL,表示核心自动选择一个地址;若不为NULL且shmflg⽆SHM_RND标记,则以shmaddr为连接地址;若不为NULL且shmflg设置了SHM_RND标记,则连接的地址会⾃自动向下调整为SHMLBA的整数倍。公式:shmaddr - (shmaddr % SHMLBA)
- shmflg:它的两个可能取值是SHM_RND和SHM_RDONLY(只读共享内存)
返回值:成功返回一个指针,指向共享内存的第⼀个节;失败返回-1
3. 脱离共享内存
当一个进程不在需要共享内存时,用shmdt() 把共享内存从进程地址空间中脱离,但不等于将共享内存段从系统内核中删除。函数原型:
#include <sys/shm.h>
int shmdt(const void *shmaddr);
参数:shmaddr 为shmat() 函数的连接共享内存的地址。
返回值:成功返回0;失败返回-1
4. 控制共享内存
同进程间通信——共享内存 ,在共享内存中标识符的属性被记录在一个shmid_ds的结构中:
struct shmid_ds{
struct ipc_perm shm_perm; /* operation permission struct */
size_t shm_segsz; /* size of segment in bytes */
time_t shm_amime; /* time of last shmat() */
time_t shm_dtime; /* time of last shmdt() */
time_t shm_ctime; /* time of last change by shmctl() */
pid_t shm_cpid; /* pid of creator */
pid_t shm_lpid; /* pid of last shmdt */
shmatt_t shm_nattach; /* number of current attaches */
...
}
通过 shmctl() 可以对共享内存进行控制或一些属性的修改,函数原型:
#include <sys/shm.h>
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
参数:
- shmid:共享内存的标识码
- cmd:有三个可选的值,在此我们使用IPC_RMID
选项 | 功能 |
IPC_STAT | 把msqid_ds结构中的数据设置为共享内存的当前关联值 |
IPC_SET | 进程有足够权限的前提下,把共享内存的当前关联值设置为msqid_ds数据结构中给出的值 |
IPC_RMID | 删除共享内存段 |
5. 实例
test_shm.h
#ifndef __TEST_SHARED_MEMORY_INCLUDE__
#define __TEST_SHARED_MEMORY_INCLUDE__
//include this header file for shared memory
#include <sys/shm.h>
#define shm_key "shm_key"
#define SHARE_BUF 16*1024
typedef struct{
int flag;
} shm_buf_t;
#define END 1000
int create_shm();
int get_shm();
int remove_shm(int shmid);
#endif //#ifndef __TEST_SHARED_MEMORY_INCLUDE__
test_shm.c
#include <stdio.h>
#include "test_shm.h"
static int create_shm_common(int flags)
{
printf("create shared memory\n");
key_t key = ftok(shm_key, 's');
if (key == -1) {
perror("ftok error.\n");
return -1;
}
printf("key is 0x%x\n", key);
int shmid = shmget(key, SHARE_BUF, flags);
if (shmid == -1) {
perror("shmget error.\n");
return -1;
}
printf("shmid is %d\n", shmid);
return shmid;
}
int create_shm()
{
return create_shm_common(IPC_CREAT | IPC_EXCL | 0644);
}
int get_shm()
{
return create_shm_common(IPC_CREAT);
}
int remove_shm(int shmid)
{
if (shmctl(shmid, IPC_RMID, NULL) == -1) {
perror("shmctl error.\n");
return -1;
}
return 0;
}
test_read.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "test_shm.h"
// server for testing
int main()
{
printf("main of server for reading shm.\n");
int shmid = create_shm();
shm_buf_t *shbuf = shmat(shmid, 0, SHM_RDONLY);
if (shbuf == (void*) -1L) {
perror("shmat error.\n");
return -1;
}
printf("test_read: Memory attched at %X\n", (int)shbuf);
int n = 100;
while (1) {
printf("test_read: buf->flag = %d\n", shbuf->flag);
if (shbuf->flag == END || n < 0)
break;
sleep(1);
}
return 0;
}
test_write.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <memory.h>
#include "test_shm.h"
// client for testing
int main()
{
printf("main of server for writing shm.\n");
int shmid = get_shm();
shm_buf_t *shbuf = shmat(shmid, NULL, 0);
if (shbuf == (void*) -1L) {
perror("shmat error.\n");
return -1;
}
printf("test_write: Memory attched at %X\n", (int)shbuf);
memset(shbuf, 0, SHARE_BUF);
shm_buf_t *buf = malloc(sizeof(shm_buf_t));
int n = 10;
while (n > 0) {
if (n == 1) {
buf->flag = END;
} else {
buf->flag = n;
sleep(2);
}
printf("test_write: buf->flag = %d\n", buf->flag);
memcpy(shbuf, buf, sizeof(shm_buf_t));
n--;
}
free(buf);
return 0;
}
运行结果:
./test_read
main of server for reading shm.
create shared memory
key is 0x7300a4fe
shmid is 9273347
test_read: Memory attched at 3F52D000
test_read: buf->flag = 10
test_read: buf->flag = 9
test_read: buf->flag = 9
test_read: buf->flag = 8
test_read: buf->flag = 8
test_read: buf->flag = 7
test_read: buf->flag = 7
test_read: buf->flag = 6
test_read: buf->flag = 6
test_read: buf->flag = 5
test_read: buf->flag = 5
test_read: buf->flag = 4
test_read: buf->flag = 4
test_read: buf->flag = 3
test_read: buf->flag = 3
test_read: buf->flag = 1000
./test_write
main of server for writing shm.
create shared memory
key is 0x7300a4fe
shmid is 9273347
test_write: Memory attched at 65DBA000
test_write: buf->flag = 10
test_write: buf->flag = 9
test_write: buf->flag = 8
test_write: buf->flag = 7
test_write: buf->flag = 6
test_write: buf->flag = 5
test_write: buf->flag = 4
test_write: buf->flag = 3
test_write: buf->flag = 2
test_write: buf->flag = 1000