共享内存
共享内存
共享内存是一种最为高效的进程间通信方式,进程可以直接读写共享内存而不需要数据的拷贝。
内核专门留出了一块内存区,这段内存区可以由需要访问的进程将其映射到自己的私有地址空间。
由于多个进程共享一段内存,因此也需要依靠某种同步机制,如互斥锁和信号量等。
- 创建共享内存
SYNOPSIS
#include <sys/ipc.h>
#include <sys/shm.h>
int shmget(key_t key, size_t size, int shmflg);
key: IPC_PRIVATE - 会建立新共享内存对象
size: 共享内存大小
shmflg: 权限位
return - 共享内存段标识符
- 映射共享内存
把创建的共享内存映射到具体的进程空间去。
SYNOPSIS
void *shmat(int shmid, const void *shmaddr, int shmflg);
shmid: 要映射的共享内存标识符
shmaddr: 映射的地址
shmflg: SHM_RDONLY: 共享内存只读; 0: 可读写
return - 被映射的段地址
- 撤销共享内存
SYNOPSIS
int shmdt(const void *shmaddr);
shmaddr: 被映射的共享内存地址
- 共享内存的控制
SYNOPSIS
#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: shmid_ds结构体。
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 */
...
};
实例1:
这里要介绍的一个命令是 ipcs,这是用于报告进程间通信机制状态的命令。它可以查看共享内存、消息队列等各种进程间通信机制的情况。
也可通过shell执行ipcrm -m shmid
来删除共享内存。
#include <stdio.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#define BUFSIZE 2048
int main()
{
int shmid;
char *shmaddr;
if((shmid = shmget(IPC_PRIVATE, BUFSIZE, 0666)) < 0){
perror("shmget");
exit(1);
}else{
printf("created shared-memory: %d\n", shmid);
}
system("ipcs -m");
if((shmaddr = shmat(shmid, 0, 0)) < (char *)0){
perror("shmat");
exit(1);
}else{
printf("attached shared-memory\n");
}
system("ipcs -m");
if(shmdt(shmaddr) < 0){
perror("shmdt");
exit(1);
}else{
printf("detached shared-memory\n");
}
system("ipcs -m");
if(shmctl(shmid, IPC_RMID, NULL) < 0){
perror("shmctl");
exit(1);
}else{
printf("delete shared-memory\n");
}
system("ipcs -m");
exit(0);
}
结果如下:
xxx@xxx-pc:~/Documents$ ./a.out
created shared-memory: 2162702
------ Shared Memory Segments --------
key shmid owner perms bytes nattch status
0x00000000 2162702 xxx 666 2048 0
attached shared-memory
------ Shared Memory Segments --------
key shmid owner perms bytes nattch status
0x00000000 2162702 xxx 666 2048 1
detached shared-memory
------ Shared Memory Segments --------
key shmid owner perms bytes nattch status
0x00000000 2162702 xxx 666 2048 0
delete shared-memory
------ Shared Memory Segments --------
key shmid owner perms bytes nattch status
ftok()函数
系统建立IPC通讯 (消息队列、信号量和共享内存) 时必须指定一个ID值。通常情况下,该id值通过ftok函数得到。
SYNOPSIS
#include <sys/types.h>
#include <sys/ipc.h>
key_t ftok(const char *pathname, int proj_id);
proj_id: 取值(1~255)
在一般的UNIX实现中,是将文件的索引节点号取出(通过ls -i
),前面加上子序号(proj_id)得到key_t的返回值。
如指定文件的索引节点号为65538,换算成16进制为0x010002,而你指定的ID值为38,换算成16进制为0x26,则最后的key_t返回值为0x26010002。
如果要确保key_t值不变,要么确保ftok的文件不被删除,要么不用ftok,指定一个固定的key_t值。
实例2:
实现一进程写共享内存,一进程读共享内存并且读好之后释放共享内存空间。
写进程p2:
#include <stdio.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <string.h>
#define BUFSIZE 2048
int main()
{
int shmid;
char *shmaddr;
key_t key;
char *str = "shmat, shmdt - System V shared memory operations\n";
if((key = ftok("/tmp", 0x03)) < 0){
perror("ftok error");
exit(1);
}
if((shmid = shmget(key, BUFSIZE, IPC_CREAT|IPC_EXCL|0666)) < 0){
perror("shmget");
exit(1);
}
if((shmaddr = shmat(shmid, 0, 0)) < (char *)0){
perror("shmat");
exit(1);
}
memcpy(shmaddr, str, strlen(str));
if(shmdt(shmaddr) < 0){
perror("shmdt");
exit(1);
}
/*
if(shmctl(shmid, IPC_RMID, NULL) < 0){
perror("shmctl");
exit(1);
}
*/
exit(0);
}
读进程p1:
#include <stdio.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <sys/shm.h>
int main()
{
int shmid;
char *shmaddr;
key_t key;
if((key = ftok("/tmp", 0x03)) < 0){
perror("ftok error");
exit(1);
}
/* get shared-memory according to key */
if((shmid = shmget(key, 0, 0)) < 0){
perror("shmget");
exit(1);
}
if((shmaddr = shmat(shmid, 0, 0)) < (char *)0){
perror("shmat");
exit(1);
}
printf("%s\n", shmaddr);
if(shmdt(shmaddr) < 0){
perror("shmdt");
exit(1);
}
if(shmctl(shmid, IPC_RMID, NULL) < 0){
perror("shmctl");
exit(1);
}
exit(0);
}
结果如下:
xxx@xxx-pc:~/Documents$ ./p2
xxx@xxx-pc:~/Documents$ ipcs -m
------ Shared Memory Segments --------
0x03080001 2359308 xxx 666 2048 0
xxx@xxx-pc:~/Documents$ ./p1
shmat, shmdt - System V shared memory operations
如果先运行./p1,共享内存不存在,报错:
xxx@xxx-pc:~/Documents$ ./p1
shmget: No such file or directory
如果重复运行./p2,此时共享内存已存在,加了IPC_EXCL标志,会报错:
xxx@xxx-pc:~/Documents$ ./p2
xxx@xxx-pc:~/Documents$ ./p2
shmget: File exists