嵌入式学习笔记--Linux系统编程阶段--DAY07进程间通信--存储映射和共享内存
1.存储映射
存储映射 I/O (Memory-mapped I/O) 使一个磁盘文件与存储空间中的一个缓冲区相映射。于是当从缓冲区中取数据,就相当于读文件中的相应字节。于此类似,将数据存入缓冲区,则相应的字节就自动写入文件。这样,就可在不适用 read 和 write 函数的情况下,使用地址(指针)完成 I/O 操作。 使用存储映射这种方法,首先应通知内核,将一个指定文件映射到存储区域中。这个映射工作可以通过mmap 函数来实现。
这个文件仅仅是用来进程间通信的桥梁。

1.1mmap函数映射
mmap函数:建立映射区
void *mmap(void *addr, size_t length, int prot, int flags,int fd, off_t offset);
参数解释
addr 地址,填 NULL(让系统自己找一个合理的地址)
length 长度 要申请的映射区的长度
prot 权限(读还是写)
PROT_READ 可读
PROT_WRITE 可写
flags 标志位
MAP_SHARED 共享的 -- 对映射区的修改会影响源文件(一般是这个)
MAP_PRIVATE 私有的
fd 文件描述符 需要打开一个文件
offset 指定一个偏移位置 ,从该位置开始映射(一般写0,不偏移)
返回值
成功 返回映射区的首地址
失败 返回 MAP_FAILED ((void *) -1)
1.2munmap函数
munmap函数:解除映射(断开当前进程和磁盘文件的映射关系,不影响其他进程的映射)
int munmap(void *addr, size_t length);
参数解释
addr 映射区的首地址
length 映射区的长度
返回值
成功 返回 0
失败 返回 -1
1.3truncate函数
truncate函数:拓展文件的大小
一般磁盘映射是新建一个文件,这个文件新建的时候,大小是0,因此里面无法存储数据,所以需要拓展文件的大小
int truncate(const char *path, off_t length);
参数解释
path 要拓展的文件
length 要拓展的长度
1.4代码案例
不相关的进程间通信
写代码:
#define _POSIX_SOURCE
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include<string.h>
#include <sys/mman.h>int main(int argc, char const *argv[])
{//1.通过open打开文件int fd = open("temp",O_RDWR |O_CREAT,0666);//新建的文件无大小//2.拓展文件的大小truncate("temp",16);//3.建立映射char *buff = (char *)mmap(NULL,16,PROT_READ | PROT_WRITE,MAP_SHARED ,fd,0);//4.使用内存区域strcpy(buff,"hello mmap");//5.断开映射munmap(buff,16);close(fd);return 0;
}
读代码:
#define _POSIX_SOURCE
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include<string.h>
#include <sys/mman.h>
#include <unistd.h>
#include <sys/types.h>int main(int argc, char const *argv[])
{//1.通过open打开文件int fd = open("temp",O_RDWR |O_CREAT,0666);//新建的文件无大小//2.拓展文件的大小truncate("temp",16);//3.建立映射char *buff = (char *)mmap(NULL,16,PROT_READ | PROT_WRITE,MAP_SHARED ,fd,0);//4.使用内存区域printf("收到数据%s\n",buff);printf("收到数据%s\n",buff);//5.断开映射munmap(buff,16);close(fd);return 0;
}
除非被覆盖,否则数据一直在
2.共享内存
2.1共享内存理论
共享内存允许两个或者多个进程共享给定的存储区域。(进程间通信的最快的方式)
共享内存的特点
1、 共享内存是进程间共享数据的一种最快的方法。 一个进程向共享的内存区域写入了数据, 共享这个内存区域的所有进程就可以立刻看到其中的内容。
2、 使用共享内存要注意的是多个进程之间对一个给定存储区访问的互斥。 若一个进程正在向共享内存区写数据, 则在它做完这一步操作前, 别的进程不应当去读、 写这些数据。
在 ubuntu 部分版本中共享内存限制值如下 共享存储区的最小字节数:
1 共享存储区的最大字节数: 32M
共享存储区的最大个数: 4096
每个进程最多能映射的共享存储区的个数: 4096
2.2共享内存的API
shmget函数:创建或打开一块共享内存区,即获得一个共享内存标识符
#include <sys/ipc.h>
#include <sys/shm.h>
int shmget(key_t key, size_t size,int shmflg);
功能:
创建或打开一块共享内存区
参数:
key:IPC 键值(需要ftok函数)
size:该共享存储段的长度(字节)
shmflg:标识函数的行为及共享内存的权限。
参数:shmflg:
IPC_CREAT:如果不存在就创建
IPC_EXCL:如果已经存在则返回失败
位或权限位:共享内存位或权限位后可以设置共享内存的访问权限,格式 和 open 函数的 mode_t 一样,但可执行权限未使用。
返回值:
成功:返回共享内存标识符。
失败:返回-1。
使用 shell 命令操作共享内存
查看共享内存
ipcs -m --内存
ipcs -q --队列
ipcrm -m shmid --删除shmid的共享内存
代码案例:
创建一个共享内存,获取标识符:
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/shm.h>int main(int argc, char const *argv[])
{ // 1.获取唯一key值key_t key = ftok("/home/qf/桌面", 2504);// 2.得到唯一共享内存标识(分配物理内存)int shm_id = shmget(key,16,IPC_CREAT | 0666) ; printf("%d\n",shm_id);return 0;
}
shmat函数:将一个共享内存段映射到调用进程的数据段中,即建立进程与物理内存的映射
#include <sys/types.h>
#include <sys/shm.h>
void *shmat(int shmid, const void *shmaddr,int shmflg);
函数功能:
将一个共享内存段映射到调用进程的数据段中。
参数:
shmid:共享内存标识符。
shmaddr:共享内存映射地址(若为 NULL 则由系 统自动指 定),推荐使用 NULL。
shmflg:共享内存段的访问权限和映射条件(映射时的读写关系)
0:共享内存具有可读可写权限。
SHM_RDONLY:只读。
SHM_RND:(shmaddr 非空时才有效)(自己申请的虚拟地址来进行映射(例如malloc来的),建议shmaddr选NUMM)
没有指定 SHM_RND 则此段连接到 shmaddr 所指定的地址上(shmaddr 必需 页对齐)。 指定了 SHM_RND 则此段连接到 shmaddr- shmaddr%SHMLBA 所表示的地址 上。
返回值:
成功:返回共享内存段映射地址
失败:返回 -1
代码案例:建立内存与进程间的映射
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/shm.h>int main(int argc, char const *argv[])
{ // 1.获取唯一key值key_t key = ftok("/home/qf/桌面", 2504);// 2.得到唯一共享内存标识(分配物理内存)int shm_id = shmget(key,16,IPC_CREAT | 0666) ; printf("%d\n",shm_id);//3.建立进程和物理内存间的映射char *p = (char *)shmat(shm_id,NULL,0);//4.断开映射,只断开自己return 0;
}
shmdt函数:将共享内存和当前进程分离(仅仅是断开联系并不删除共享内存)。
#include <sys/types.h>
#include <sys/shm.h>
int shmdt(const void *shmaddr);
功能:
将共享内存和当前进程分离(仅仅是断开联系并不删除共享内存)。
参数:
shmaddr:共享内存映射地址。
返回值:
成功返回 0
失败返回 -1
shmctl:共享内存控制函数
#include <sys/ipc.h>
#include <sys/shm.h>
int shmctl(int shmid, int cmd,struct shmid_ds *buf);
功能:
共享内存空间的控制。
参数:
shmid:共享内存标识符。
cmd:函数功能的控制。
buf:shmid_ds 数据类型的地址,用来存放或修改共享内存的属性。
cmd:函数功能的控制
IPC_RMID:删除。
IPC_SET:设置 shmid_ds 参数。
IPC_STAT:保存 shmid_ds 参数。
SHM_LOCK:锁定共享内存段(超级用户)。
SHM_UNLOCK:解锁共享内存段。
返回值:
成功返回 0
失败返回 -1
注意:
SHM_LOCK 用于锁定内存,禁止内存交换。并不代表共享内存被锁定后,禁止其它进程访问。其真正的意义是:被锁定的内存不允许被交换到虚拟内存中(即只针对当前进程)。这样做的优势在于让共享内存一直处于内存中,从而提高程序性能
读写代码案例:
写:
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/shm.h>int main(int argc, char const *argv[])
{ // 1.获取唯一key值key_t key = ftok("/home/qf/桌面", 2504);// 2.得到唯一共享内存标识(分配物理内存)int shm_id = shmget(key,16,IPC_CREAT | 0666) ; printf("%d\n",shm_id);//3.建立进程和物理内存间的映射char *p = (char *)shmat(shm_id,NULL,0);strcpy(p,"hello world!");//4.断开映射,只断开自己shmdt(p);return 0;
}
读:
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/shm.h>int main(int argc, char const *argv[])
{ // 1.获取唯一key值key_t key = ftok("/home/qf/桌面", 2504);// 2.得到唯一共享内存标识(分配物理内存)int shm_id = shmget(key,16,IPC_CREAT | 0666) ; printf("%d\n",shm_id);//3.建立进程和物理内存间的映射char *p = (char *)shmat(shm_id,NULL,SHM_RDONLY);printf("收到数据:%s\n",p);//4.断开映射,只断开自己return 0;
}