共享内存( shared memory ) :共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的 IPC 方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号量,配合使用,来实现进程间的同步和通信。
通信模型:
- 获取key值 :ftok()
- 创建/获取共享内存 :shmget()
- 挂接共享内存 :shmat()
- 脱接共享内存 :shmdt()
- 删除共享内存 :shmctl()
使用的头文件
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/shm.h>
ftok()
//获取key值, key值是System V IPC的标识符,成功返回key,失败返回-1设errno
//同pathname+同 proj_id==>同key_t;
key_t ftok(const char *pathname, int proj_id);
pathname :文件名
proj_id: 1~255的一个数,表示project_id
shmget()
//创建/获取共享内存,成功返回共享内存的标识符shmid,失败返回-1设errno
int shmget(key_t key, size_t size, int shmflg); //多设为int shmid=... 和shmat()一起用比较好看
key :ftok()的返回值
size:共享内存的大小,实际会按照页的大小(PAGE_SIZE)来分配。0表示获取已经分配好的共享内存
shmflg:具体的操作标志
- IPC_CREAT:若不存在则创建, 需要在shmflg中"|权限信息", eg: |0664; 若存在则打开
- IPC_EXCL:与IPC_CREAT搭配使用, 若存在则创建失败==>报错,set errno
- 0 :获取已经存在的共享内存
shmat()
//挂接共享内存,成功返回映射内存的地址,失败返回(void*)-1设errno
void *shmat(int shmid, const void *shmaddr, int shmflg);
shmid: shmget()的返回值
shmaddr
- NULL表示由系统选择 (同mmap())
- 非NULL且shflg是SHM_RND,会按照页对齐的原则从shmaddr开始找最近的地址开始分配分,否则shmaddr指定的地址必须是页对齐的
- shmflg :操作的标志, 给0即可
- SHM_RDONLY表示挂接到该共享内存的进程必须有读权限
- SHM_REMAP (Linux-specific)表示如果要映射的共享内存已经有现存的内存,那么就将旧的替换
shmdt()
//脱接共享内存,成功返回0,失败返回-1设errno
int shmdt(const void *shmaddr);
//脱接shm
int res=shmdt(pv);
if(-1==res)
perror("shmdt"),exit(-1);
shmctl()
//共享内存管理,成功返回0,失败返回-1设errno
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
shmid:共享内存的id,由shmget()返回
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 */
...
};
//<sys/ipc.h>
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 */
};
cmd
- IPC_STAT表示从内核中拷贝关于这个shmid的信息到buf指向的shmid_ds中
- IPC_SET 将buf指向的shmid_ds的信息写入到内核的结构体中,同时更新成员shm_ctime
- IPC_RMID销毁共享内存
- IPC_INFO(Linux-specific)返回系统对共享内存的限制写入到buf指向的时shminfo结构体中
//_GNU_SOURCE
struct shminfo {
unsigned long shmmax; /* Maximum segment size */
unsigned long shmmin; /* Minimum segment size; always 1 */
unsigned long shmmni; /* Maximum number of segments */
unsigned long shmseg; /* Maximum number of segments that a process can attach; unused within kernel */
unsigned long shmall; /* Maximum number of pages of shared memory, system-wide */
};
//shmmni, shmmax, and shmall 可以童工/proc里的同名文件进行修改
- SHM_INFO(Linux-specific) 返回一个shm_info结构体来表示该共享内存消耗的系统资源
//_GNU_SOURCE
struct shm_info {
int used_ids; /* # of currently existing segments */
unsigned long shm_tot; /* Total number of shared memory pages */
unsigned long shm_rss; /* # of resident shared memory pages */
unsigned long shm_swp; /* # of swapped shared memory pages */
unsigned long swap_attempts; /* Unused since Linux 2.4 */
unsigned long swap_successes;/* Unused since Linux 2.4 */
};
- SHM_STAT(Linux-specific) 为IPC_STAT返回一个shmid_ds结构结构体,不同的是shmid的参数不是一个标识符,而是内核中一个包含了系统中所有共享内存信息的索引
- SHM_LOCK防止系统将共享内存放到swap区,IPC_STAT读到的信息中SHM_LOCKED标记就被设置了
- SHM_UNLOCK 解除锁定,即允许共享内存被系统放到swap区
//使用IPC_RMID删除共享内存
int res=shmctl(shmid,IPC_RMID,NULL);
if(-1==res)
perror("shmctl"),exit(-1);
测试代码:
comm.h
1 #ifndef _COMM_H_
2 #define _COMM_H_
3
4 #include<stdio.h>
5 #include<sys/ipc.h>
6 #include<sys/shm.h>
7
8 #define PATHNAME "."
9 #define PROJ_ID 0x6666
10
11 int creat(int size);
12 int get(int size);
13 int destory(int shmid);
14
15 #endif
comm.c
1 #include"comm.h"
2
3 static int com(int size,int flags)
4 {
5 key_t key=ftok(PATHNAME,PROJ_ID);
6 if(key<0)
7 {
8 perror("ftok");
9 return -1;
10 }
11
12 int shmid=shmget(key,size,flags);
13 if(shmid<0)
14 {
15 perror("shmget");
16 return -2;
17 }
18 return shmid;
19 }
20
21 int creat(int size)
22 {
23 return com(size,IPC_CREAT|IPC_EXCL|0666);
24 }
25 int get(int size)
26 {
27 return com(size,IPC_EXCL);
28 }
29 int destory(int shmid)
30 {
31 if(shmctl(shmid,IPC_RMID,NULL)>0)
32 {
33 perror("shmctl");
34 return -1;
35 }
36 return 0;
37 }
38
server.c
1 #include"comm.h"
2
3 int main()
4 {
5 int shmid=creat(4097);
6 char *addr=shmat(shmid,NULL,0);
7
8 while(1)
9 {
10 printf("%s\n",addr);
11 sleep(1);
12 }
13 shmdt(addr);
14 destory(shmid);
15 return 0;
16 }
client.c
1 #include"comm.h"
2
3 int main()
4 {
5 int shmid=get(0);
6 char *addr=shmat(shmid,NULL,0);
7
8 int i=0;
9 while(1)
10 {
11 addr[i++]='A';
12 addr[i]='\0';
13 sleep(1);
14 }
15 return 0;
16 }
运行结果:
没有运行时在监控下root用户的共享内存为0,不存在
先运行./server,可以看到共享内存存在,而且挂接数nattch为1
再运行./client,可以看到挂接数变为2,说明server与client看到了同一个共享内存
结束server和client进程之后,挂接数变为0