目录
1、简介
1.1、共享内存区的优缺点
1.2、对共享内存区的操作
1.3、共享内存区的方法
1.4、普通 I/O 与存储映射 I/O 比较
2、mmap()函数
3、munmap()函数
4、mprotect()函数
4、msync()函数
5、示例
5.1、父进程子进程通过mmap在内存映射文件中给计数器持续加1
5.2、父子进程共享信号量与计数的结构体,通过内存信号量实现。
6、匿名内存映射
6.1、BSD匿名内存映射
6.2、/dev/zero匿名内存映射
7、访问内存映射对象
8、shm_open()函数
9、shm_unlink()函数
10、ftruncate()函数
11、fstat()函数
12、mlockall()函数/munlockall()函数
13、mlock()函数
14、System V共享内存区
14.1、shmget()函数
14.2、shmat()函数
14.3、shmctl()函数
12.4、shmctl()函数
1、共享内存区是IPC形式中最快的。当内存区映射到共享它的进程的地址空间,这些进程间数据的传递就不再涉及内核(进程不再通过执行任何进入内核的系统调用来彼此传递数据)。
2、把显示的I/O操作变换成存取内存单元,可以往往能简化程序,又是还能改善性能。
注:对于管道、FIFO和消息队列,进程要交换信息时,这些消息必须经由内核传递。换句话说,数据在内核和进程间进行,往往开销很大(比纯粹在内核中或单个进程内数据的开销大)。通过让进程共享一个内存区,可以绕过上述问题,所有说共享内存区是IPC形式中最快的。
一旦内存映射了一个文件,就不能再使用read,write和lseek来访问该文件,而只是存取已由mmap映射到该文件的内存位置。
注:不是所有的文件都能进行内存映射。例如,把一个访问终端或嵌套字的描述符映射到内存将导致mmap返回一个错误。这些类型的描述符必须使用read和write(或它们的变体)来访问。
注:这两种技术都需要调用mmap,差别在于作为mmap的参数之一的描述符的获取方法:调用open或通过shm_open。
1)使用存储映射 I/O 在进行大数据量操作时比较有效;对于少量数据,使用普通 I/O 方式更加方便!
2)存储映射 I/O 在处理大量数据时效率高,对于少量数据处理不是很划算,所以存储映射 I/O 会在视频图像处理方面用的比较多,譬如Framebuffer 编程(LCD 编程)。
把一个文件或Posix共享内存区对象映射到调用进程的地址空间(对于同一个共享内存区对象,调用mmap的每个进程所得到的mmap返回值可能不同)。使用该函数有3个目的:
参数 addr:可以指定描述符fd应被映射到的进程内空间的起始地址(通常被指定为一个空指针,让内核自己去选择起始地址)。
参数 length:映射区的大小(从被映射文件开头起第offset个字节处开始算)。
参数 port:内存映射的保护,常见值为代表可写访问的PORT_READ|PORT_WRITE。
注:PORT_NONE的作用。linux - mmap内存保护PROT_NONE的目的是什么? - Thinbug
参数 flags:MAP_SHARED和MAP_PRIVATE这两个标志必须指定一个,并可有选择地或上MAP_FIXED。
参数 fd:描述符
参数 offset:偏移量,通常设置为0。
返回值:成功情况下,函数的返回值便是映射区的起始地址;发生错误时,返回(void *)-1, 通常使用MAP_FAILED 来表示, 并且会设置 errno 来指示错误原因
注:mmap成功返回后,fd参数可以关闭,该操作对于由mmap建立的映射关系没有影响。
注:对于 mmap()函数,参数 addr 和 offset 在不为 NULL 和 0 的情况下, addr 和 offset 的值通常被要求是系统页大小的整数倍,可通过 sysconf()函数获取页大小:
从某个进程的地址空间删除一个映射关系。如果被映射区时使用MAP_PRIVATE标志映射的,那么调用进程对它所做的变动都会被丢弃。
参数 addr:由mmap返回的地址,指定待解除映射地址范围的起始地址,它必须是系统页大小的整数倍。
参数 length:映射区的大小。
返回值:成功则为0,失败则为-1。
注:再次访问这些地址将导致向调用进程产生一个SIGSEGV信号。
使用系统调用mprotect()可以更改一个现有映射区的保护要求。
参数 addr: 地址范围的起始地址
参数 len:该地址范围的大小。
参数 prot:要设置的地址范围的保护要求的类型
返回值:成功返回 0; 失败将返回-1,并且会设置 errno 来只是错误原因。
同步硬盘上的文件内容和内存映射区的内容,使其一致。
注:如果修改了处于内存映射到某个文件的内存区的内容,那么内核将在稍后某个时刻相应地更新文件(前提是它是一个MAP_SHARED内存区)。
参数 addr:由mmap返回的地址。
参数 length:映射区的大小。
参数 flags:MS_ASYNC和MS_SYNC中必须指定一个。它们的区别是,一旦写操作已由内核排入队列,MS_ASYNC即返回。而MS_SYNC则要等到写操作完成后才返回。如果指定了MS_INVALIDATE,那么与其最终副本不一致的文件数据的所有内存中副本都失效。
返回值:成功则为0,失败则为-1。
注:通过调用sem_unlink从系统中删除该信号量的名字,但是尽管这么一来删除了它的路径名,对于已经打开的信号量却没影响。这样做即使本程序终止了,该路径名也从系统中删除。
如果调用mmap的目的是提供一个将穿越fork由父子进程共享的映射内存区,那么可以简化上述情况。
对/dev/zero设备文件的任何读,所返回的是所请求数目的全为0的字节。write到该设备的任何数据被直接丢弃掉,就像write到/dev/null设备一样。
内存映射一个普通文件时,内存映射区的大小通常等于该文件的大小。然而文件大小和内存映射区大小可以不同(内核允许给mmap指定一个大于该对象大小的大小参数,但是访问不了该对象以远的部分)。
内核允许我们读写最后一页中映射区以远的地方(内核的内存保护是以页面为单位的),但是写向这部分扩展区的任何内容都不会写到文件中。
注:SIGBUS意味着在内存映射区内访问,但是已超过底层支撑对象的大小。SIGSEGV意味着已在内存映射区以远访问。
打开一个共享内存区。
参数 name:共享内存区名字
参数 oflag:必须或者含有O_RDONLY标志,或者O_RDWR标志,还可以指定如下标志:O_CREAT,O_EXCL或O_TRUNC。
参数 mode:指定权限位。
返回值:若成功则为非负描述符,若出错则为-1。
删除一个共享内存区的名字。
参数 name:共享内存区名字
返回值:成功则为0,出错则为-1。
注:删除一个名字不会影响对于底层支持对象的现有引用,直到对于该对象的引用全部关闭为止。
修改普通文件或共享内存区对象的大小。
如果文件的大小大于length,额外的数据就被丢弃。
如果文件的大小小于length,那么改文件的修改以及其大小是否增长是未加说明的。
对于普通文件,把它的大小扩展到length字节的可移植方法是:先lseek到偏移为length-1处,然后write 1个字节的数据。
参数 fd:描述符
参数 length:长度
返回值:成功则为0,出错则为-1。
当打开一个已存在的共享内存区对象时,可调用fstat函数获取有关该对象的信息。
参数 fd:描述符
参数 buf:保存对象信息的结构体指针。
返回值:成功贼0,出错则为-1.
注:当fd指代一个共享内存区对象时,只有四个成员含有信息(st_mode、st_uid、st_gid、st_size)。
mlockall()使调用进程的整个内存空间常驻内存。
munlockall()撤销这种锁定
mlock()会使调用进程地址空间的某个指定范围常驻内存,该函数的参数指定了这个范围的起始地址以及从该地址算起的字节数。
munlock则撤销某个内存区的锁定。
System V共享内存区在概念是类似Posix共享内存区。对于每个System V共享内存区,内核维护如下的信息结构。
创建一个共享内存区,或者访问一个已存在的共享内存区。
注:创建新的共享内存区时,该内存区被初始化为size字节的0。
参数 key:ftok的返回值,也可以是IPC_PRIVATE。
参数 size:指定内存区的大小(创建时不为0,访问时为0)。
参数 shm:读写权限位,可以与IPC_CREAT或IPC_CREAT|IPC_EXCL按位或。
返回值:成功则为共享内存区对象,出错则为-1。
注:Posix共享内存区对象的大小可在任何时刻通过调用ftruncate修改,而System V共享内存区对象的大小是在调用shmget创建时固定的。
将共享内存区对象附接到调用进程的地址空间。
参数 shm_id:共享内存区对象
参数 shm_addr:
参数 shm:SHM_RND、SHME_RDONLY。
默认情况下,只要调用进程具有某个共享内存的读写权限,它附接该内存区就能够同时读写该内存区。shm参数也可以指定SHM_RDONLY值,它限定只读访问。
返回值:成功则为共享内存区对象,出错则为-1。
断接内存区。
参数 shm_addr:共享内存起始地址。
返回值:成功则为0,出错则为-1。
注:当一个进程终止时,当前附接的所有共享内存区都将自动断接。
对共享内存区的多种操作。
参数 shm_id:共享内存区对象
参数 command:操作命令
从系统中删除由shmid标识的共享内存区并拆除
注:拆除指释放和回收对应的数据结构,包括删除存放在上面的数据(拆除要到该共享内存区的引用计数变为0时才进行)。
参数 buf:shmid_ds结构体指针。
有话要说...