system V共享内存
作用:用于多个进程间数据共享
特性:最快的进程间通信方式,共享内存区是最快的IPC形式。一旦这样的内存映射到共享它的进程的地址空间,这些进程间数据传递不再涉及到内核,换句话说是进程不再通过执行进入内核的系统调用来传递彼此的数据
原理:开辟出一块物理内存地址,然后多个进程都映射到自己的虚拟地址空间中,通过虚拟地址直接访问物理内存中的数据
相当于是进程直接看到的数据。举个例子,管道就是从写端的数据拷贝到管道缓冲区,再从管道缓冲区拷贝到自己的进程空间,相对于共享内存是多了两次拷贝
共享内存示意图
常见的共享内存操作
共享内存数据结构
struct shmid_ds { struct ipc_perm shm_perm; /* operation perms */ int shm_segsz; /* size of segment (bytes) */ __kernel_time_t shm_atime; /* last attach time */ __kernel_time_t shm_dtime; /* last detach time */ __kernel_time_t shm_ctime; /* last change time */ __kernel_ipc_pid_t shm_cpid; /* pid of creator */ __kernel_ipc_pid_t shm_lpid; /* pid of last operator */ unsigned short shm_nattch; /* no. of current attaches */ unsigned short shm_unused; /* compatibility */ void *shm_unused2; /* ditto - used by DIPC */ void *shm_unused3; /* unused */ };
共享内存函数
shmget函数
功能:用来创建共享内存
原型
int shmget(key_t key, size_t size, int shmflg);
参数
key:这个共享内存段名字
size:共享内存大小,一般是PAGE_SIZE的整数倍(4096字节)
shmflg:由九个权限标志构成,它们的用法和创建文件时使用的mode模式标志是一样的
返回值:成功返回一个非负整数,即该共享内存段的标识码;失败返回-1
shmflg: IPC CREAT | IPC EXCL | 0664
IPC_ CREAT
如果共享内存不存在则创建打开,若已经存在则直接打开
IPC_ EXCL
与IPC_ CREAT搭配使用,共享内存不存在则创建打开,若存在则报错返回 ,一个程序只能启动一次的程序
mode_ flags
共享内存的访问权限0664
shmat函数
功能:
将共享内存段连接到进程地址空间
原型
void *shmat(int shmid, const void shmaddr, int shmflg);
参数
shmid: 共享内存标识 shmaddr:指定连接的地址
shmflg:它的两个可能取值是SHM_RND和SHM_RDONLY(只读,前提是具备读的权限)
返回值:成功返回一个指针,指向共享内存第一个节;失败返回(void)-1
shmdt函数
功能:将共享内存段与当前进程脱离
原型 int shmdt(const void *shmaddr);
参数
shmaddr: 由shmat所返回的指针
返回值:
成功返回0;失败返回-1
注意:将共享内存段与当前进程脱离不等于删除共享内存段
shmctl函数
功能:用于控制共享内存
原型
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
参数
shmid:由shmget返回的共享内存标识码
cmd:将要采取的动作(有三个可取值)
buf:指向一个保存着共享内存的模式状态和访问权限的数据结构
返回值:成功返回0;失败返回-1
注意:这里常用的是IPC_RMID,但是并非直接删除,而是做一个标记,删除最终还是由操作系统进行
这里我们思考一个问题:
多个进程,访问同一个共享内存,突然有一个进程要删除了共享内存,如何避免其他进程不会出现错误呢?
其实共享内存是有个当前的映射连接计数(表示现在有多少进程正在访问)所以这里的RMID叫做标记删除,并不是真的删除,而是标记一下, 被标记的共享内存将不再接受新的映射而是等当前的映射连接计数为0时,再实际删除删除由系统完成,进程所做的删除操作,其实只是标记一下。
简单举例:
举例2
测试代码结构
# ls client.c comm.c comm.h Makefile server.c # cat Makefile .PHONY:all all:server client client:client.c comm.c gcc -o $@ $^ server:server.c comm.c gcc -o $@ $^ .PHONY:clean clean: rm -f client server
comm.h
#ifndef _COMM_H_ #define _COMM_H_ # include <stdio.h> # include <sys/types.h> # include <sys/ipc.h> # include <sys/shm.h> # define PATHNAME "." # define PROJ_ID 0x6666 int createShm(int size); int destroyShm(int shmid); int getShm(int size); # endif