文章目录
思维导图如下通信原理优势运行流程编程接口编程实例思维导图如下
通信原理
多个进程共享物理内存的同一块区域(通常称之为“段”:segment)抛弃了内核态消息转存处理的过程,让两个进程直接通过一块内存进行通信我们普通的像PIPE,FIFO,消息队列等的通信方式如下图:
这种方式的通信不论读写,都需要内核态(系统调用 read,write,pipe,mkfifo,msgget,msgsnd,msgrcv等)的介入,而且都需要经过数据从虚拟地址空间到物理地址空间的拷贝。
而共享内存的通信方式则都避免了以上的通信问题,直接为两个进程开辟相同的内存空间进行数据交互。
优势
减少了内存的拷贝(从用户拷贝到内核,从内核拷贝到用户)减少了2次系统调用(系统调用比较消耗性能,因为CPU处理系统调用时需要从用户态切换到内核态),提高了系统性能运行流程
获取共享内存对象的ID将共享内存映射至本进程虚拟内存空间的某个区域(每个用户进程操作系统都有3G的虚拟进程空间)不同进程对这块内存进行读写、传输数据当进程不再使用这块共享内存时,解除映射关系当没有进程需要共享内存的时候,则删除该共享内存编程接口
获取共享内存的对象ID ,创建或打开一个共享内存对象
a. 头文件<sys/types.h> <sys/shm.h>
b.int shmget (key_t key, size_t size, int shmflg);
c. 函数参数
key
IPC对象的键值,一般为IPC_PRIVATE 或者ftok返回的key值size
共享内存的大小,一般为物理内存页(4K)的整数倍shmflg
:
IPC_CREAT :如果不存在指定的key值,那么就创建一个
IPC_EXCL: 若key值指定的内存存在,且指定了IPC_CEAT,则回复EXIST错误
IPC_HUGETLB 使用巨页(huge page)
d. 返回值:共享内存的标识ID
映射共享内存:
a. 头文件<sys/shm.h>
b.void *shmat(int shmid,const void *shmaddr,int shmflg);
c. 功能:将shmid标识的共享内存引入到当前进程的虚拟地址空间
d. 函数参数:
shmid
共享内存的IPC对象IDshmaddr
若为NULL:共享内存会被attach到一个合适的虚拟地址空间,建议使用NULL
不为NULL:系统会根据参数以及地址边界对齐等分配一个合适的地址shmflg
IPC_RDONLY:附加只读权限,不指定的话返回默认是读写权限
IPC_REMAP:替换位于shmaddr处的任意既有映射(共享内存的段或者内存映射)
SHM_RND:将shmaddr四舍五入为SHMMLBA字节的倍数
e. 返回值:共享内存段的地址
解除共享内存映射
a. 头文件<sys/shm.h>
b.int shmdt(const void * shmaddr);
c. 功能:解除内粗映射,将共享内存分离出当前进程的地址空间
d. 函数参数:
shmaddr
共享内存地址
e. TIPS:
通过fork创建的子进程会继承父进程所附加的共享内存,父子进程可以通过共享内存进行IPC通信,在exec系统调用中,所有附加的共享内存段都会被分离函数shmdt
仅仅是使进程和共享内存脱离关系,将共享内存的引用计数是减1当共享内存的引用计数为0的时候,调用shmctl
的IPC_RMID命令才会删除共享内存
设置共享内存属性
a. 头文件<sys/shm.h>
b.int shmctl(int shmid,int cmd,struct shmid_ds *buf);
c. 函数功能:获取/设置共享内存对象属性
d. 函数参数
-shmid
共享内存对象的ID
-cmd
:
IPC_RMID :删除共享内存及关联的shmid_ds数据结构
IPC_STAT: 将该共享内存关联的shmid_ds数据结构拷贝到参数buf中
IPC_SET: 使用buf中的数据更新与该共享内存对象相关联的shmid_ds
IPC_INFO:获取系统共享内存相关的信息
IPC_LOCK: 将一个共享内存段锁进内存,防止被swap出去
IPC_UNLOCK: 将一个共享内存解锁
shmid_ds *buf
数据结构如下
struct shmid_ds {struct ipc_perm shm_perm;/* operation permissions */int shm_segsz; /* size of segment in bytes */pid_t shm_lpid;/* pid of last shm op */pid_t shm_cpid;/* pid of creator */short shm_nattch; /* # of current attaches */time_t shm_atime; /* last shmat() time*/time_t shm_dtime; /* last shmdt() time */time_t shm_ctime; /* last change by shmctl() */void *shm_internal; /* sysv stupidity */};
编程实例
写端shm_write.c
:
#include<stdio.h>#include <unistd.h>#include <stdlib.h>#include <strings.h>#include <sys/types.h>#include <sys/ipc.h>#include <sys/shm.h>int main(){//key_t key = ftok("./",111);key_t key = 12345;//根据key值生成共享内存对象的唯一标识int shm_id = shmget(key,4096,IPC_CREAT|0666);//将当前进程加入共享内存,这里设置共享内存地址为NULL时//系统会自动为当前进程分配一个合适的内存空间,并返回共享内存的地址char *shm_p = shmat(shm_id , NULL, 0);memset (shm_p,0,4096);//从标准输入获取内容到共享内存的地址fgets(shm_p,4096,stdin);//等待输入完成,删除共享内存sleep(10);//当前进程创建的共享内存引用计数为0的时候,删除共享内存shmctl(shm_id,IPC_RMID,NULL);return 0;}
读端shm_read.c
#include<stdio.h>#include <unistd.h>#include <stdlib.h>#include <strings.h>#include <sys/types.h>#include <sys/ipc.h>#include <sys/shm.h>int main() {// key_t key = ftok("./",111);key_t key = 12345;int shm_id = shmget(key, 4096, 0666);//和写端访问到了相同的共享内存区域,并获取内容存放到指针变量char *shm_p = shmat(shm_id , NULL, 0);printf("From SHM: %s\n",shm_p);//删除当前进程与共享内存的映射关系,同时共享内存的引用计数减一shmdt(shm_p);return 0;}
通过命令ipcs -m
可以查看系统共享内存
需要先执行写入,再去运行读端读出;以上输出如下: