Linux共享内存实例及文件映射编程及实现原理
- 格式:rtf
- 大小:190.44 KB
- 文档页数:11
linux共享内存实现原理Linux共享内存是一种高效的进程间通信方式,它允许多个进程访问同一块物理内存,从而实现数据的共享和交换。
本文将介绍Linux 共享内存的实现原理。
1. 共享内存的创建在Linux中,共享内存是通过系统调用shmget()来创建的。
shmget()函数的原型如下:int shmget(key_t key, size_t size, int shmflg);其中,key是共享内存标识符,size是共享内存大小,shmflg是标识位,用于指定共享内存的权限等信息。
当调用shmget()函数时,内核会为该共享内存分配内存空间,并返回共享内存标识符。
应用程序可以通过该标识符访问共享内存。
2. 共享内存的映射为了访问共享内存,应用程序需要把共享内存映射到自己的进程空间中。
这可以通过系统调用shmat()来实现。
shmat()函数的原型如下:void *shmat(int shmid, const void *shmaddr, int shmflg);其中,shmid是共享内存标识符,shmaddr是进程中共享内存映射的起始地址,shmflg是标识位,用于指定共享内存的映射方式等信息。
调用shmat()函数后,内核会把共享内存映射到应用程序的进程空间中,并返回共享内存的起始地址。
此时,应用程序就可以直接访问共享内存中的数据。
3. 共享内存的同步由于多个进程可以同时访问共享内存,因此需要对共享内存进行同步,以避免数据的不一致性和冲突。
Linux提供了多种同步机制,例如信号量、互斥锁和条件变量等。
应用程序可以利用这些机制来实现共享内存的同步。
4. 共享内存的销毁当共享内存不再使用时,应该及时将其销毁,以释放系统资源。
共享内存的销毁可以通过系统调用shmctl()来实现。
shmctl()函数的原型如下:int shmctl(int shmid, int cmd, struct shmid_ds *buf);其中,shmid是共享内存标识符,cmd是命令,用于指定要执行的操作,buf是共享内存的属性结构体。
Linux进程间通信共享内存共享内存(Shared Memory)共享内存区域是被多个进程共享的一部分物理内存。
如果多个进程都把该内存区域映射到自己的虚拟地址空间,则这些进程就都可以直接访问该共享内存区域,从而可以通过该区域进行通信。
共享内存是进程间共享数据的一种最快的方法,一个进程向共享内存区域写入了数据,共享这个内存区域的所有进程就可以立刻看到其中的内容。
这块共享虚拟内存的页面,出现在每一个共享该页面的进程的页表中。
但是它不需要在所有进程的虚拟内存中都有相同的虚拟地址。
图共享内存映射图象所有的System VIPC对象一样,对于共享内存对象的访问由key控制,并要进行访问权限检查。
内存共享之后,对进程如何使用这块内存就不再做检查。
它们必须依赖于其它机制,比如System V的信号灯来同步对于共享内存区域的访问。
每一个新创建的共享内存对象都用一个shmid_kernel数据结构来表达。
系统中所有的shmid_kernel数据结构都保存在shm_segs向量表中,该向量表的每一个元素都是一个指向shmid_kernel数据结构的指针。
shm_segs向量表的定义如下:struct shmid_kernel*shm_segs[SHMMNI];SHMMNI为128,表示系统中最多可以有128个共享内存对象。
数据结构shmid_kernel的定义如下:struct shmid_kernel{struct shmid_ds u;/*the following are private*/unsigned long shm_npages;/*size of segment(pages)*/unsigned long*shm_pages;/*array of ptrs to frames-SHMMAX*/struct vm_area_struct*attaches;/*descriptors for attaches*/};其中:shm_pages是该共享内存对象的页表,每个共享内存对象一个,它描述了如何把该共享内存区域映射到进程的地址空间的信息。
最为高效的进程间通信方式
进程直接读写内存,不需要任何数据的拷贝
•为了在多个进程间交换信息,内核专门留出了一块内存区
•由需要访问的进程将其映射到自己私有地址空间
•进程直接读写这一内存区而不需要进行数据的拷贝,提高了效率
多个进程共享一段内存,需要依靠某种同步机制,如互斥锁和信号量等
l共享内存编程步骤:
1. 创建共享内存
•函数shmget()
•从内存中获得一段共享内存区域
2. 映射共享内存
•把这段创建的共享内存映射到具体的进程空间中
•函数shmat()
3. 使用这段共享内存
•可以使用不带缓冲的I/O读写命令对其进行操作
4. 撤销映射操作: 函数shmdt()
5. 删除共享内存: 函数shctl()。
165 表7.8shmat 用法 所需头文件#include <sys/types.h> #include <sys/ipc.h> #include <sys/shm.h> 函数原型 char *shmat(int shmid, const void *shmaddr, int shmflg)shmid :要映射的共享内存区标识符shmaddr :将共享内存映射到指定地址(若为0则表示系统自动分配地址并把该段共享内存映射到调用进程的地址空间)SHM_RDONLY :共享内存只读参数shmflg 默认0:共享内存可读写 成功:被映射的段地址 返回值出错:-1 表7.9shmdt 用法 所需头文件#include <sys/types.h> #include <sys/ipc.h> #include <sys/shm.h> 函数原型 int shmdt(const void *shmaddr)参数 shmaddr :被映射的共享内存段地址成功:0 返回值出错:-1 表7.10shmctl 用法 所需头文件#include <sys/types.h> #include <sys/ipc.h> #include <sys/shm.h> 函数原型 int shmctl(int shmid, int cmd, struct shmid_ds *buf)参数 shmid 共享内存标识符cmd 表示对共享内存的属性执行的相关命令,主要有:IPC_STAT 表示得到共享内存的状态,把共享内存的shmid_ds 结构复制到buf 中;IPC_SET :改变共享内存的状态,把buf 所指的shmid_ds 结构中的uid 、gid 、mode 复制到共享内存的shmid_ds 结构内IPC_RMID :删除该共享内存buf :共享内存管理结构体成功:0返回值 出错:-1 7.5.3 共享内存实例【例 7-10】请设计一个程序,实现父子进程之间通过共享内存进行通信。
linux共享内存简单介绍以及编码演⽰共享内存的基本概念 共享内存区是最快的IPC形式。
⼀旦这样的内存映射到共享它的进程的地址空间,这些进程间数据传递不再涉及到内核,换句话说是进程不再通过执⾏进⼊内核的系统调⽤来传递彼此的数据。
下图是共享内存⽰意图: 下图是⽤管道或者消息队列传递数据⽰意图: 内核为每个IPC对象维护⼀个数据结构下图是⽤共享内存传递数据⽰意图:System V共享内存API:⾸先了解⼀下下⾯结构体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 */...};共享内存函数#include <sys/ipc.h>#include <sys/shm.h>int shmget(key_t key, size_t size, int shmflg);void *shmat(int shmid, const void *shmaddr, int shmflg);int shmdt(const void *shmaddr);int shmctl(int shmid, int cmd, struct shmid_ds *buf);下⾯依次介绍⼀下上述函数的⼀些使⽤⽅法shmget函数功能:⽤来创建共享内存原型:intshmget(key_t key, size_t size, intshmflg);参数:key:这个共享内存段名字size:共享内存⼤⼩shmflg:由九个权限标志构成,它们的⽤法和创建⽂件时使⽤的mode模式标志是⼀样的返回值:成功返回⼀个⾮负整数,即该共享内存段的标识码;失败返回-1shmat函数功能:将共享内存段连接到进程地址空间原型:void *shmat(intshmid, const void *shmaddr, intshmflg);参数:shmid: 共享内存标识shmaddr:指定连接的地址shmflg:它的两个可能取值是SHM_RND和SHM_RDONLY返回值:成功返回⼀个指针,指向共享内存第⼀个节;失败返回-1shmaddr为NULL,核⼼⾃动选择⼀个地址shmaddr不为NULL且shmflg⽆SHM_RND标记,则以shmaddr为连接地址。
Linux下进程间通信⽅式——共享内存1.什么是共享内存?共享内存就是允许两个或多个进程共享⼀定的存储区。
就如同 malloc() 函数向不同进程返回了指向同⼀个物理内存区域的指针。
当⼀个进程改变了这块地址中的内容的时候,其它进程都会察觉到这个更改。
因为数据不需要在客户机和服务器端之间复制,数据直接写到内存,不⽤若⼲次数据拷贝,所以这是最快的⼀种IPC。
注:共享内存没有任何的同步与互斥机制,所以要使⽤信号量来实现对共享内存的存取的同步。
共享内存特点和优势当中共享内存的⼤致原理相信我们可以看明⽩了,就是让两个进程地址通过页表映射到同⼀⽚物理地址以便于通信,你可以给⼀个区域⾥⾯写⼊数据,理所当然你就可以从中拿取数据,这也就构成了进程间的双向通信,⽽且共享内存是IPC通信当中传输速度最快的通信⽅式没有之⼀,理由很简单,客户进程和服务进程传递的数据直接从内存⾥存取、放⼊,数据不需要在两进程间复制,没有什么操作⽐这简单了。
再者⽤共享内存进⾏数据通信,它对数据也没啥限制。
最后就是共享内存的⽣命周期随内核。
即所有访问共享内存区域对象的进程都已经正常结束,共享内存区域对象仍然在内核中存在(除⾮显式删除共享内存区域对象),在内核重新引导之前,对该共享内存区域对象的任何改写操作都将⼀直保留;简单地说,共享内存区域对象的⽣命周期跟系统内核的⽣命周期是⼀致的,⽽且共享内存区域对象的作⽤域范围就是在整个系统内核的⽣命周期之内。
缺陷但是,共享内存也并不完美,共享内存并未提供同步机制,也就是说,在⼀个服务进程结束对共享内存的写操作之前,并没有⾃动机制可以阻⽌另⼀个进程(客户进程)开始对它进⾏读取。
这明显还达不到我们想要的,我们不单是在两进程间交互数据,还想实现多个进程对共享内存的同步访问,这也正是使⽤共享内存的窍门所在。
基于此,我们通常会⽤平时常谈到和⽤到信号量来实现对共享内存同步访问控制。
与共享内存有关的函数所有的函数共⽤头⽂件#include <sys/types.h>#include <sys/ipc.h>#include <sys/shm.h> 创建共享内存——>shmget() 函数int shmget(key_t key, size_t size, int shmflg);//成功返回共享内存的ID,出错返回-1(1)第⼀个参数key是长整型(唯⼀⾮零),系统建⽴IPC通讯(消息队列、信号量和共享内存)时必须指定⼀个ID值。
linux进程间的通信(C):共享内存⼀、共享内存介绍共享内存是三个IPC(Inter-Process Communication)机制中的⼀个。
它允许两个不相关的进程访问同⼀个逻辑内存。
共享内存是在两个正在进⾏的进程之间传递数据的⼀种⾮常有效的⽅式。
⼤多数的共享内存的实现,都把由不同进程之间共享的内存安排为同⼀段物理内存。
共享内存是由IPC为进程创建⼀个特殊的地址范围,它将出现在该进程的地址空间中。
其他进程可以将同⼀段共享内存连接它们⾃⼰的地址空间中。
所有进程都可以访问共享内存中的地址,就好像它们是由malloc分配的⼀样。
如果某个进程向共享内存写⼊了数据,所做的改动将⽴刻被可以访问同⼀段共享内存的任何其他进程看到。
⼆、共享内存的同步共享内存为在多个进程之间共享和传递数据提供了⼀种有效的⽅式。
但是它并未提供同步机制,所以我们通常需要⽤其他的机制来同步对共享内存的访问。
我们通常是⽤共享内存来提供对⼤块内存区域的有效访问,同时通过传递⼩消息来同步对该内存的访问。
在第⼀个进程结束对共享内存的写操作之前,并⽆⾃动的机制可以阻⽌第⼆个进程开始对它进⾏读取。
对共享内存访问的同步控制必须由程序员来负责。
下图显⽰了共享内存是如何共存的:图中的箭头显⽰了每个进程的逻辑地址空间到可⽤物理内存的映射关系。
三、共享内存使⽤的函数1. #include <sys/shm.h>2.3. int shmget(key_t key, size_t size, int shmflg);4. void *shmat(int shm_id, const void *shm_addr, int shmflg);5. int shmdt(const void *shm_addr);6. int shmctl(int shm_id, int cmd, struct shmid_ds *buf);1. shmget函数该函数⽤来创建共享内存:1. int shmget(key_t key, size_t size, int shmflg);参数:key : 和信号量⼀样,程序需要提供⼀个参数key,它有效地为共享内存段命名。
第16章共享内存
实验目的
●巩固掌握进程同步概念
●理解Linux关于共享内存的概念
●掌握Linux支持进程间内存共享的系统调用
●学习利用共享内存,进行进程间通信
实验内容
建立一个基于共享内存机制的,用以经典同步问题readers/writers的解决方案。
writer 从用户处获得输入,然后将其写入共享内存,reader从共享内存获取信息,然后再在屏幕上打印出来。
实验原理
Unix System V提供了一系列进程通讯机制,即IPC机制,它是进程间通信(Inter Processs Communication)的缩写。
System V IPC机制包括消息队列、共享内存和信号量,其中共享内存(shared memory)是效率最高的IPC机制,它允许多个进程共享一段特殊内存区域中的数据,可以象读写普通内存一样读写共享内存,而不需要通过其它任何特殊方式。
我们首先看看共享内存API的使用,即如何用共享内存的系统调用进行编程,以及使用这些系统调用过程中应该注意的一些问题。
然后,介绍共享内存在Linux中的实现,同时分析一些疑点。
最后,我们一起进行一个编程实验,使你对共享内存有更感性的认识。
它是一个使用共享内存来完成进程间通信的程序,希望你在学习了这个实验后,能够自己动手编写共享内存方面的程序,从而加深对Linux中的共享内存机制的理解。
16.1 进程间通信和共享内存
16.1.1 进程间通信
复杂的程序大多都会涉及到某种形式的进程间通信。
把应用程序设计成一组彼此通信。
Linux内存映射文件内存映射文件,是由一个文件到一块内存的映射。
Win32提供了允许应用程序把文件映射到一个进程的函数(CreateFileMapping)。
内存映射文件与虚拟内存有些类似,通过内存映射文件可以保留一个地址空间的区域,同时将物理存储器提交给此区域,内存文件映射的物理存储器来自一个已经存在于磁盘上的文件,而且在对该文件进行操作之前必须首先对文件进行映射。
使用内存映射文件处理存储于磁盘上的文件时,将不必再对文件执行I/O操作,使得内存映射文件在处理大数据量的文件时能起到相当重要的作用。
有两种类型的内存映射文件:1.共享型,在线性区页上的任何写操作都会修改磁盘上的文件;而且如果进程对共享映射中的一个页进行写,那么这种修改对于其他映射了这同一文件的所有进程来说都是可见的。
所以内存映射文件也可以作为进程通信的一种方式。
2.私有型,当进程创建的映射只是为读文件,而不是写文件时才会使用这种映射。
出于这种目的,私有映射的效率要比共享映射的效率更高。
但是对私有映射页的任何写操作都会使内核停止映射该文件中的页。
因此写操作既不会改变磁盘上的文件,对访问相同文件的其他进程也是不可见的。
但是私有内存映射中的页会因为其他进程对文件的修改而更新。
内存映射文件通过mmap和munmap系统调用来实现.mmap调用实际上就是一个内存对象vma的创建过程, mmap的调用格式是:void * mmap(void *start, size_t length, int prot , int flags, int fd, off_t offset);它的任何修改对其它进程都不可见. 而MAP_SHARED则无论修改与否都使用同一副本, 任何进程对页面的修改对其它进程都是可见的.mmap系统调用的实现过程是:1.先通过文件系统定位要映射的文件;2.权限检查, 映射的权限不会超过文件打开的方式, 也就是说如果文件是以只读方式打开, 那么则不允许建立一个可写映射;3.创建一个vma对象, 并对之进行初始化;4.调用映射文件的mmap函数, 其主要工作是给vm_ops向量表赋值;5.把该vma链入该进程的vma链表中, 如果可以和前后的vma合并则合并;6.如果是要求VM_LOCKED(映射区不被换出)方式映射, 则发出缺页请求, 把映射页面读入内存中.munmap(void * start, size_t length)该调用可以看作是 mmap的一个逆过程. 它将进程中从start开始length长度的一段区域的映射关闭, 如果该区域不是恰好对应一个vma, 则有可能会分割几个或几个vma.msync(void * start, size_t length, int flags):把映射区域的修改回写到后备存储中. 因为munmap时并不保证页面回写, 如果不调用msync, 那么有可能在munmap后丢失对映射区的修改. 其中flags可以是MS_SYNC, MS_ASYNC, MS_INVALIDATE, MS_SYNC要求回写完成后才返回, MS_ASYNC发出回写请求后立即返回, MS_INVALIDATE使用回写的内容更新该文件的其它映射. 该系统调用是通过调用映射文件的sync函数来完成工作的.brk(void * end_data_segement)将进程的数据段扩展到end_data_segement指定的地址, 该系统调用和mmap的实现方式十分相似, 同样是产生一个vma, 然后指定其属性. 不过在此之前需要做一些合法性检查, 比如该地址是否大于mm->end_code, end_data_segement和mm->brk之间是否还存在其它vma等等. 通过brk产生的vma映射的文件为空, 这和匿名映射产生的vma相似, 关于匿名映射不做进一步介绍. 库函数malloc 就是通过brk实现的.。
目录(一)IPC共享内存和文件映射的区别 1(二)共享内存实现流程总结 1(三)存储映射I/O(包含实现原理说明) 2文件映射API补充 4(四)IPC共享存储(包含实现原理说明) 6(五)共享内存实现基本原理10(六)IPC共享内存实现机制11(七)文件映射的实现机制13(一)IPC共享内存和文件映射的区别1. 文件映射的页框是磁盘文件高速缓存中的页框,内核线程pdflush会将页框中的内容回写进磁盘,如果是私有映射类型,将会进行写时复制。
而IPC共享内存映射的是一种特殊文件系统中的文件高速缓存,它没有相应的磁盘映像。
2. IPC共享内存只存在于内存中,系统重新启动,数据将会丢失。
而文件共享映射会将数据写回磁盘。
3. IPC共享内存的大小是在创建的时候指定,而且大小不能改变,而文件在创建时大小为0,此时还不能建立映射,文件的大小会间接的决定映射区的大小。
例如文件的大小是123,而要求映射的区域大小是4096*2,但实际只会分配4096的映射空间,此时引用4096以后的线性空间将引起缺页异常。
4. 当第一次读取共享内存时IPC共享内存对象将分配一个新的页框,而文件映射分配新页框的同时会将磁盘中的数据写入新页框。
5. IPC共享内存不需要写回磁盘操作,完全是为共享内存而设计,所以使用效率会更高。
6. IPC共享内存对象必须调用shmctl()显示的撤销,否则会一直保留着,使用key或者id号定位一个共享内存对象,key和id号的对应关系并不是固定的。
例如,第一次使用key 建立一个共享内存对象为shm1对应的id为id1,之后系统重新启动,然后再使用key建立一个共享内存对象shm2,对应的id是id2,此时id2和id1是不同的。
而文件映射使用相同的路径将会定位相同的磁盘文件。
总结:IPC共享内存和文件映射的实现机制是一样的,文件映射的目的是加快对文件的读写速度,而IPC共享内存就是为了共享内存而设计的,所以效率会高一些。
(二)共享内存实现流程总结1. 建立一个线性区对象struct vm_area_struct 并加入进程的内存描述符current->mm 中。
函数mmap()和shmat()就是用于建立并注册线性区对象,这个对象中的struct file *vm_file 指向映射文件的文件对象,vm_page_prot是线性区中页框的访问许可权。
但此时并未修改进程的页表,而是注册相应的缺页异常回调函数,注册在对象的vm_ops。
2. 当进程第一次访问共享内存区时,由于相应的页表还未填写,将产生缺页异常,并根据线性地址找到对应的线性区对象,然后调用前边注册过的缺页异常回调函数,并根据vm_file文件对象和vm_page_prot的信息来填写相应的页表项,最后重新执行产生缺页异常的代码。
说明:文件映射和IPC共享内存映射的物理页框都是磁盘文件的页高速缓存中的,IPC 共享内存使用一种特殊文件系统,这个文件系统并没有对应的磁盘映像,只是复用了文件系统的框架。
更详细的内容参见后边的五,六,七节。
下面3,4节是《UNIX环境高级编程》对文件映射和IPC共享内存的讲解,已经说明的很详细了,我在它的基础上附加了一些内核实现原理的说明,实现原理说明部分放在括号内。
(三)存储映射I/O(包含实现原理说明)存储映射I/O使一个磁盘文件与存储空间中的一个缓存相映射。
于是当从缓存中取数据,就相当于读文件中的相应字节。
与其类似,将数据存入缓存,则相应字节就自动地写入文件。
这样,就可以在不使用read和write的情况下执行I/O。
为了使用这种功能,应首先告诉内核将一个给定的文件映射到一个存储区域中。
这是由mmap函数实现的。
#include <sys/types.h>#include <sys/mman.h>void * mmap(void addr, size_t len, int prot, int flag, int fd, off_t off) ;返回:若成功则为映射区的起始地址,若出错则为- 1addr参数用于指定映射存储区的起始地址。
通常将其设置为0,这表示由系统选择该映射区的起始地址。
此函数的返回地址是:该映射区的起始地址。
fd指定要被映射文件的描述符(fd用于定位是哪个磁盘文件的页高速缓存)。
在映射该文件到一个地址空间之前,先要打开该文件。
len是映射的字节数。
off是要映射字节在文件中的起始位移量(下面将说明对off值有某些限制)。
在说明其余参数之前,先看一下存储映射文件的基本情况。
图12 - 12显示了一个存储映射文件。
在此图中,“起始地址”是mmap的返回值。
在图中,映射存储区位于堆和栈之间:这属于实现细节,各种实现之间可能不同。
prot参数说明映射存储区的保护要求。
见表12 - 8。
对于映射存储区所指定的保护要求与文件的open方法匹配。
例如,若该文件是只读打开的,那么对映射存储区就不能指定PROT _WRITE。
(对存储映射区的保护是通过设置页表项的保护标志来实现的,如果页表项的read/write标志位为0,说明页是只读的,如果进程试图修改页的内容,将产生段错误,这些保护方案都是由CPU硬件控制的)flag参数影响映射存储区的多种属性:? MAP_FIXED 返回值必须等于addr。
因为这不利于可移植性,所以不鼓励使用此标志。
如果未指定此标志,而且addr非0,则内核只把addr视为何处设置映射区的一种建议。
通过将addr指定为0可获得最大可移植性。
? MAP_SHARED 这一标志说明了本进程对映射区所进行的存储操作的配置。
此标志指定存储操作修改映射文件—也就是,存储操作相当于对该文件write。
(这里映射的页是包含在文件的页高速缓存中,用户态进程在读写磁盘的时候,内核先在页高速缓存中增加一个新页,将所请求的磁盘块写入新页,用户态进程从页高速缓存中取出数据。
如果要写入数据,也是要添加一个页将磁盘中的数据写入该页,然后再将数据写入该页,内核会在一定的时机对磁盘进行更新。
)(以上的页高速缓存是组织在inode的i_mmaping对象中,对于一个磁盘文件唯一对应一个磁盘inode,每个磁盘inode也唯一对应一个内核inode,也就是,每个磁盘文件只有一个页高速缓存,如果两个进程映射的是同一个文件的页高数缓存,则它们共享相同的物理页)? MAP_PRIVATE 本标志说明,对映射区的存储操作导致创建该映射文件的一个副本。
所(这里内核用到了写时复制技术,有后来对该映射区的存访都是存访该副本,而不是原始文件。
在相应的页表项中设置写时复制标志,当进程试图修改该页,内核将会产生缺页异常,内核就把该页框进行复制,并在进程页表中用复制的页来替换原来的页框,显然这个新的页框已经不在页高速缓存中了,对页框的内容进行修改将不会写回文件,其它进程将无法共享这个页框。
如果本进程还未进行写复制,而其它进程修改了页的内容,本进程是可以获得更新后的数据)因为映射文件的起动位移量受系统虚存页长度的限制,那么如果映射区的长度不是页长度的整数倍时,将如何呢?假定文件长12字节,系统页长为512字节,则系统通常提供512字节的映射区,其中后500字节被设0。
可以修改这50字节,但任何变动都不会在文件中反映出来。
(这是由于内核分配线性区和分配物理内存都是以页为单位)与映射存储区相关有两个信号: SIGSEGV和SIGBUS。
信号SIGSEGV通常用于指示进程试图存取它不能存取的存储区。
如果进程企图存数据到用mmap指定为只读的映射存储区,那么也产生此信号。
如果存取映射区的某个部分,而在存取时这一部分已不存在,则产生SIGBUS 信号。
例如,用文件长度映射一个文件,但在存访该映射区之前,另一个进程已将该文件截短。
此时,如果进程企图存取对应于该文件尾端部分的映射区,则接收到SIGBUS信号。
(对信号的实现机制有待进一步分析)在fork之后,子进程继承存储映射区(因为子进程复制父进程地址空间,而存储映射区是该地址空间中的一部分),但是由于同样的理由,exec后的新程序则不继承此存储映射区。
(关闭文件描述符也不影响存储映射区,磁盘文件的页高速缓存并不会因为进程的撤销而撤销,如果有足够的空闲内存,页高速缓存中的页将长期存在,使其它进程再使用该页时不再访问磁盘。
)进程终止时,或调用了munmap之后,存储映射区就被自动去除。
关闭文件描述符fd并不解除映射区。
(关闭存储映射区,只是撤销进程页表中的相应目录项,并不影响页高速缓存。
)#include <sys/types.h>#include <sys/mman.h>int munmap(void addr,size_t len) ;返回:若成功则为0,若出错则为- 1munmap 并不影响被映射的对象—也就是说,调用munmap并不使映射区的内容写到磁盘文件上。
对于MAP_SHARED区磁盘文件的更新,在写到存储映射区时按内核虚存算法自动进行。
(pdflush内核线程用于刷新脏页)例子程序:#include <unistd.h>#include <stdio.h>#include <sys/stat.h>#include <sys/types.h>#include <fcntl.h>#include <sys/mman.h>int main(int argc, char *argv[]){int fd, i, counter;pid_t pid;char *area = NULL;if((fd = open("test", O_RDWR) )<= 0)printf("open error\n");area = (char *)mmap(0, sizeof(long), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);printf("area:%p\n", area);close(fd);*(area + 1) = 'c';}文件映射API补充msync函数的使用原型:?? #include <sys/mman.h>?? int msync(const void *start, size_t length, int flags);msync函数用来把映像的文件写入磁盘。
调用msync可以用对内存中的映像的更新写入一个被映像的文件,被强行写入到磁盘的内存取从start指定的地址开始,写入length个字节的数据。