Linux共享内存实例及文件映射编程及实现原理._第1页
Linux共享内存实例及文件映射编程及实现原理._第2页
Linux共享内存实例及文件映射编程及实现原理._第3页
Linux共享内存实例及文件映射编程及实现原理._第4页
Linux共享内存实例及文件映射编程及实现原理._第5页
免费预览已结束,剩余8页可下载查看

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、目录(一)IPC 共享内存和文件映射的区别(二)共享内存实现流程总结1(三)存储映射 I/O (包含实现原理说明) 文件映射 API 补充 4四)五) 六)七)101113IPC 共享存储(包含实现原理说明) 共享内存实现基本原理IPC 共享内存实现机制文件映射的实现机制IPC 共享内存和文件映射的区别1. 文件映射的页框是磁盘文件高速缓存中的页框, 内核线程 pdflush 会将页框中的内容 回写进磁盘,如果是私有映射类型,将会进行写时复制。而IPC共享内存映射的是一种特殊 文件系统中的文件高速缓存,它没有相应的磁盘映像。而文件共享映射会将2. IPC 共享内存只存在于内存中,系统重新启动,

2、数据将会丢失。 数据写回磁盘。而文件在创建时大小例如文件的大小是 123,3. IPC 共享内存的大小是在创建的时候指定,而且大小不能改变, 为 0,此时还不能建立映射, 文件的大小会间接的决定映射区的大小。而要求映射的区域大小是 4096*2,但实际只会分配 4096 的映射空间, 此时引用 4096 以后的 线性空间将引起缺页异常。4. 当第一次读取共享内存时 IPC共享内存对象将分配一个新的页框,而文件映射分配新页框的同时会将磁盘中的数据写入新页框。5. IPC 共享内存不需要写回磁盘操作, 完全是为共享内存而设计, 所以使用效率会更高。6. IPC共享内存对象必须调用 shmctIO显

3、示的撤销,否则会一直保留着,使用key或者 id 号定位一个共享内存对象, key 和 id 号的对应关系并不是固定的。例如,第一次使用 key 建立一个共享内存对象为 shm1 对应的 id 为 id1 ,之后系统重新启动,然后再使用 key 建立 一个共享内存对象 shm2,对应的id是id2,此时id2和id1是不同的。而文件映射使用相同 的路径将会定位相同的磁盘文件。所以效率会高一些。总结: IPC 共享内存和文件映射的实现机制是一样的,文件映射的目的是加快对文件的 读写速度,而 IPC 共享内存就是为了共享内存而设计的, (二)共享内存实现流程总结并加入进程的内存描述符 curren

4、t->mm1. 建立一个线性区对象 struct vm_area_struct这个对象中的 struct file *vm_file 但此时并未修改进中。 函数mmap()和shmat()就是用于建立并注册线性区对象, 指向映射文件的文件对象, vm_page_prot 是线性区中页框的访问许可权。 程的页表,而是注册相应的缺页异常回调函数,注册在对象的vm_ops。2. 当进程第一次访问共享内存区时, 由于相应的页表还未填写, 将产生缺页异常, 并根 据线性地址找到对应的线性区对象,然后调用前边注册过的缺页异常回调函数,并根据 vm_fiIe 文件对象和 vm_page_prot 的信

5、息来填写相应的页表项, 最后重新执行产生缺页异常 的代码。IPC说明:文件映射和 IPC 共享内存映射的物理页框都是磁盘文件的页高速缓存中的, 共享内存使用一种特殊文件系统,这个文件系统并没有对应的磁盘映像,只是复用了文件系 统的框架。更详细的内容参见后边的五,六,七节。F面3, 4节是UNIX环境高级编程对文件映射和IPC共享内存的讲解,已经说明的实现原理说明部分放在括号内。很详细了, 我在它的基础上附加了一些内核实现原理的说明, (三)存储映射 I/O (包含实现原理说明)存储映射 I/O 使一个磁盘文件与存储空间中的一个缓存相映射。 于是当从缓存中取数据, 就相当于读文件中的相应字节。

6、与其类似, 将数据存入缓存, 则相应字节就自动地写入文件。 这样,就可以在不使用 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) ; 返回:若成功则为映射区的起始地址,若出错则为 - 10,这表示由系统选择该映fd 指定要被映射文件勺描述

7、。在映射该文件到一个地址空间之前,先要下面将说明对addr 参数用于指定映射存储区的起始地址。通常将其设置为 射区的起始地址。此函数的返回地址是:该映射区的起始地址。 符( fd 用于定位是哪个磁盘文件的页高速缓存) 打开该文件。 len 是映射的字节数。 off 是要映射字节在文件中的起始位移量 off 值有某些限制) 。12 - 12 显示了一个存储在说明其余参数之前,先看一下存储映射文件的基本情况。图 映射文件。在此图中,“起始地址”是 mmap勺返回值。在图中,映射存储区位于堆和栈之间:这属于实 现 细节,各种实现之间可能不同。12 - 8 。prot 参数说明映射存储区勺保护要求。见

8、表open 方法匹配。 例如, 若该文件是只读打开(对存储映射区的保护是通过设置页表项的对于映射存储区所指定勺保护要求与文件勺 勺,那么对映射存储区就不能指定PROT_WRITE。保护标志来实现勺,如果页表项勺 read/write 标志位为 0,说明页是只读勺,如果进程试图 修改页的内容,将产生段错误,这些保护方案都是由CPU硬件控制的)flag 参数影响映射存储区勺多种属性:? MAP_FIXED 返回值必须等于 addr 。因为这不利于可移植性,所以不鼓励使用此标志。 如果未指定此标志,而且 addr 非 0,则内核只把 addr 视为何处设置映射区的一种建议。通 过将 addr 指定为

9、 0 可获得最大可移植性。? MAP_SHARE这一标志说明了本进程对映射区所进行的存储操作的配置。此标志指定存储操作修改映射文件也就是, 存储操作相当于对该文件 write 。(这里映射的页是包含在文 件的页高速缓存中,用户态进程在读写磁盘的时候,内核先在页高速缓存中增加一个新页, 将所请求的磁盘块写入新页,用户态进程从页高速缓存中取出数据。如果要写入数据,也是 要添加一个页将磁盘中的数据写入该页,然后再将数据写入该页,内核会在一定的时机对磁 盘进行更新。 )(以上的页高速缓存是组织在 inode 的 i_mmaping 对象中,对于一个磁盘文件唯一对应 一个磁盘 inode ,每个磁盘 i

10、node 也唯一对应一个内核 inode ,也就是, 每个磁盘文件只有一 个页高速缓存, 如果两个进程映射的是同一个文件的页高数缓存, 则它们共享相同的物理页)? MAP_PRIVATE 本标志说明,对映射区的存储操作导致创建该映射文件的一个副本。所 有后来对该映射区的存访都是存访该副本, 而不是原始文件。(这里内核用到了写时复制技术, 在相应的页表项中设置写时复制标志,当进程试图修改该页,内核将会产生缺页异常,内核 就把该页框进行复制,并在进程页表中用复制的页来替换原来的页框,显然这个新的页框已 经不在页高速缓存中了,对页框的内容进行修改将不会写回文件,其它进程将无法共享这个 页框。如果本进

11、程还未进行写复制,而其它进程修改了页的内容,本进程是可以获得更新后 的数据)因为映射文件的起动位移量受系统虚存页长度的限制,那么如果映射区的长度不是页长 度的整数倍时,将如何呢?假定文件长12字节,系统页长为 512 字节,则系统通常提供 512字节的映射区,其中后 500 字节被设 0。可以修改这 50 字节,但任何变动都不会在文件中反 映出来。(这是由于内核分配线性区和分配物理内存都是以页为单位)与映射存储区相关有两个信号:SIGSEGV和SIGBUS信号SIGSEGV1常用于指示进程试图存取它不能存取的存储区。如果进程企图存数据到用mmap旨定为只读的映射存储区,那么也产生此信号。如果存

12、取映射区的某个部分,而在存取时这一部分已不存在,则产生SIGBUS信号。例如,用文件长度映射一个文件,但在存访该映射区之前,另一个进程已将该文件截 短。此时,如果进程企图存取对应于该文件尾端部分的映射区,则接收到SIGBUS信号。(对信号的实现机制有待进一步分析)在 fork 之后, 子进程继承存储映射区 (因为子进程复制父进程地址空间, 而存储映射区 是该地址空间中的一部分) ,但是由于同样的理由, exec 后的新程序则不继承此存储映射区。(关闭文件描述符也不影响存储映射区,磁盘文件的页高速缓存并不会因为进程的撤销而撤 销,如果有足够的空闲内存,页高速缓存中的页将长期存在,使其它进程再使用

13、该页时不再 访问磁盘。 )进程终止时,或调用了munmap之后,存储映射区就被自动去除。关闭文件描述符fd并不解除映射区。(关闭存储映射区, 只是撤销进程页表中的相应目录项, 并不影响页高速缓存。 ) #include <sys/types.h>#include <sys/mman.h>int munmap(void addr,size_t len) ; 返回:若成功则为 0,若出错则为 - 1 munmap并不影响被映射的对象一也就是说,调用mu nmap并不使映射区的内容写到磁盘文件上。对于 MAP_SHAREI区磁盘文件的更新,在写到存储映射区时按内核虚存算法自动

14、进行。( 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

15、) )<= 0) printf("open errorn");area = (char *)mmap(0, sizeof(long), PROT_READ| PROT_WRITEM, AP_SHAREDfd, , 0);printf("area:%pn", area);close(fd);*(area + 1) = 'c'size_t msy nc可以用对内存中的 start 指定的地址开始, “或”: ? 1、 MS_ASYNC? 3、MS_INVALIDATE让 文件映射 API 补充 msync 函数的使用原型:? #incl

16、ude <sys/mman.h>? int msync(const void *start,length, int flags);msync 函数用来把映像的文件写入磁盘。调用 映像的更新写入一个被映像的文件,被强行写入到磁盘的内存取从 写入 length 个字节的数据。 flags 可以是下面的一个值或多个的逻辑 调度一次写入操作然后返回? 2、MS_SYN(在 msync返回前写入数据MS INVALIDATE的作用是使映射的页映像到同一文件的映像无效,以便用新数据更新它们( 高速缓存中的内容无效,重新从磁盘写入数据到映射的页高速缓存。可以使用MS_INVALIDATE来测试内

17、核是否进行页高速缓存数据的回写磁盘操作,测试过 程:写一个字符到映射区,然后使用MS_INVALIDATE使映射区的数据失效,并从磁盘写入数size_t len);? flags);? int mlockall 的 flags 指出被加锁或解锁的据,从测试结果看字符会被写入磁盘,也就是说内核几乎在对映射区进行写入操作的同时就 进行了回写磁盘操作) mprotect 函数的使用 mprotect 函数修改在内存映像上的保护模式。函数原型: ? #include <unistd.h>? #include <sys/mmap.h>? int mprotect(const vo

18、id *start, size_t len, int prot);mprotect 把自 start 开始的内存区的保护模式修改为 prot 指定的值,如果执行成功 返回0,如果执行失败,mprotect返回-1,并且设置errno变量。port可以是PROT_READ可 读)、PROT_WRITE可写)、PROT_EXEC可执行)、PROT_NON(E可访问)中的一个或多个 (这 里只是修改本进程页表中的访问控制标志,并不涉及物理页,对其它进程没有影响) 锁定内存原型: ? #include <sys/mman.h>int mlock(const void *start, int

19、 munlock(void *start, size_t len);? int mlockall(int munlockall(void); 以 4 上个函数是对指定的内存映像加锁和解锁,其中 包括 MCL_CURRENT MCL_FUTURE有root权限才能使用它们。start内存区,len指出加锁或解锁的内存区大小。flags 的值可以是MCL_CURREN和MCL_FUTURE之一或者两个都有。MCL_CURRE在调用返回前请求锁住所有内存页面,MCL_FUTUR指出锁住所有增加到进程的地址空间的内存页面。(mlock的操作结果是vma的属性,VM_LOCKED.作用是被lock的内存

20、不参加swap,保证一直存在于内存中.内核中具体的策略执行可以看函数 swap_out_vma.当使用exec的时候,lock失效.fork的子进程也不继承此属性对于实时进程和安全程序 , 此调用很有意义 . 对于加密程序 , 密码不会被 但是 lock 并不能阻止系统休眠的时候内存被存储到磁盘 .mlock,munlock锁.mlock/munlock的操作不可堆叠 . 多次调用 mlock 的一段内存也会被一次指定的地址会被 round down 到一个 page 的边界 . )dump 到磁盘上 .unlock 操作解mremap 函数用于改变一个被映像的文件大小。原型:? #inclu

21、demremap 函数的使用<sys/mman.h>? void *mremap(void *old_addr, size_t old_len, size_t new_len, unsigned long flags);mremap 用指定的 flags 把地址在 old_addr 的内存映像大小从 old_len 调整为 new_len , flags如果为MREMAP_MAYMOV调整此内存映像的地址。成功返回新地址,失败 返回 NULL。(内核要做事情是:改变线性区对象的长度。 内核会检查是否可以直接扩大或者缩小线性区的大小,如果线性对象相邻的线性空间已经被使用了,此时将没法

22、扩大了, 如果此时设置了 MREMAP_MAYMOV志,将会重新分配一块 新的线性空间,显然这个空间的起始地址已经改变)(四) IPC 共享存储(包含实现原理说明)IPC 共享存储允许两个或多个进程共享一给定的存储区。因为数据不需要在客户机和服 务器之间复制,所以这是最快的一种IPC。使用共享存储的唯一窍门是多个进程之间对一给定存储区的同步存取。若服务器将数据放入共享存储区,则在服务器做完这一操作之前,客 户机不应当去取这些数据。通常,信号量被用来实现对共享存储存取的同步。调用的第一个函数通常是shmget,它获得一个共享存储标识符。#include <sys/ipc.h>#inc

23、lude <sys/shm.h>int shmget(key_t key, size_t size, int shmflg);如果参数key为IPC_PRIVATE。则会建立新的共享内存对象其大小由size(单位字节Byte)指定,如果key不为IPC_PRIVATE并且存在键值为 key的共享内存对象,则返回所关联的 id 号,如果不存在键值为 key 的共享内存对象, 那么系统会视参数 shmflg 是否有 IPC_CREAT 来决定是否新建一个共享内存对象。 (每个共享内存对象都对应一个目录对象和一个inode对象,每个 inode 对象都包含一个 address_space

24、 i_mapping 对象, 但是这些对象并没有磁 盘映像,而是为了可以重复利用文件映射中提供的代码,每个进程映射的物理页就存储在 i_mapping 对象里。)存)size( ( bytes为单位)shmflg(以上是一种特殊的文件系统,它并没有挂载在某个目录下,只是为了方便实现共享内 是要建立共享内存的长度。 所有的内存分配操作都是以页为单位的。 实际的大小是进位到 4096 整数倍 )/4096 + 4) * 4096 。(因为线性区和物理内存的分配都是以页主要和一些标志有关,其中有效的包括IPC_CREAT和IPC_EXCL它们的功能与open()的 O_CREA和 O_EXCLf 当

25、。如果共享内存不存在,则创建一个共享内存,否则打开操作。只有在共享内存不存在的时候,新的共享内存才建立,否则就产生错误。IPC_CREAT, shmget() 函数要么返回一个已经存在的共享内存的操作符,要么如果将IPC_CREAT和IPC_EXCL标志一起使用,shmget()-1。IPC_EXEL标志IPC_CREATIPC_EXCL 如果单独使用但是和IPC_CREA1标志一起使用可以用来保证所得的对象是新建的,返回一个新建的共享内存的标识符。 将返回一个新建的共享内存的标识符;如果该共享内存已存在,或者返回 本身并没有太大的意义, 而不是打开已有的对象。-1 , errno 储存错误原

26、因。返回值 成功返回共享内存的标识符;不成功返回参数size小于SHMMIN或大于SHMMAX预建立 key 所致的共享内存,但已经存在。 参数 key 所致的共享内存已经删除。 超过了系统允许建立的共享内存的最大值EINVALEEXISTEIDRMENOSPCENOENTEACCESENOMEMstruct shmid_dsshmid_ds 数据结构表示每个新建的共享内存。当 shmget() 创建了一块新的共享内存后,(SHMALL )。参数key所指的共享内存不存在,参数shmflg也未设IPC_CREAT位。没有权限。核心内存不足。返回一个可以用于引用该共享内存的include/lin

27、ux/shm.hstruct shmid_ds struct ipc_perm shm_perm;int shm_segsz;_kernel_time_t_kernel_time_t_kernel_time_tshmid_ds 数据结构的标识符。/* shm_atime; /* shm_dtime; /* shm_ctime;/* /* /*/*/* operation perms */共享内存的大小 */最后一次附加这个共享内存的时间 最后一次分离这个共享内存的时间 */ 最后一次改变这个共享内存结构的时间 建立这个共享内存的进程识别码 最后一个操作共享内存的进程识别码 附加这个共享内存的进

28、程个数_kernel_ipc_pid_t shm_cpid;_kernel_ipc_pid_t shm_lpid;unsigned short shm_nattch;unsigned short shm_unused; /* compatibility */*/*/*/*/*/以确void *shm_unused2; /* ditto - used by DIPC */ void *shm_unused3; /* unused */struct ipc_perm _kernel_key_tkey; /共享内存对象的 key_kernel_uid_tuid; /共享内存所属的用户识别码 (可以修

29、改_kernel_gid_tgid; /共享内存所属的组识别码 (可以修改 )_kernel_uid_tcuid; /建立共享内对象的用户识别码_kernel_gid_tcgid; /建立共享内对象的组识别码_kernel_mode_t mode; /这个共享内存的读写权限 (可以修改 )unsigned shortseq; /序号IPC 对象。);struct ipc_perm对于每个 IPC 对象,系统共用一个 struct ipc_perm 的数据结构来存放权限信息, 定一个 ipc 操作是否可以访问该;shmctl 函数对共享存储段执行多种操作。#include <sys/typ

30、es.h>#include <sys/ipc.h>#include <sys/shm.h>int shmctl(int shmid, int cmd, struct shmid_ds * buf) ;返回:若成功则为 0,若出错则为 - 1cmd参数指定下列5种命令中一种,使其在 shmid指定的段上执行。? IPC_STAT 对此段取 shmid_ds 结构,并存放在由 buf 指向的结构中。? IPC_SET按buf指向的结构中的值设置与此段相关结构中的下列三个字段:(只能修改这3个字段)shm_perm.uid、shm_perm.gid以及shm_perm.

31、modeb此命令只能由下列两种进程 执行:一种是其有效用户 ID 等于 shm_perm.cuid 或 shm_perm.uid 的进程;另一种是具有超 级用户特权的进程。? IPC_RMID 从系统中删除该共享存储段。因为每个共享存储段有一个连接计数 (shm_nattch 在 shmid_ds 结构中) , 所以除非使用该段的最后一个进程终止或与该段脱接, 否则不会实际上删除该存储段。 不管此段是否仍在使用, 该段标识符立即被删除 , 所以不能再 用 shmat 与该段连接。此命令只能由下列两种进程执行: 一种是其有效用户 ID 等于shm_perm.cuid 或 shm_perm.uid

32、 的进程;另一种是具有超级用户特权的进程。? SHM_LOCK锁住共享存储段。此命令只能由超级用户执行。? SHM_UNLOC解锁共享存储段。此命令只能由超级用户执行。 一旦创建了一个共享存储段,进程就可调用 shmat 将其连接到它的地址空间中。#include <sys/types.h>#include <sys/ipc.h>#include <sys/shm.h>void *shmat(int shmid, void *addr, int flag) ;返回:若成功则为指向共享存储段的指针,若出错则为 - 1 。 共享存储段连接到调用进程的哪个地址上与

33、 addr 参数以及在 flag 中是否指定 SHM_RND 位有关。(1)(2)(3)如果 如果 如果则此段连接到由内核选择的第一个可用地址上。并且没有指定SHM_RND则此段连接到addr所指定的地址上。并且指定了 SHM_RND则此段连接到(addr (addr modSHMLBA) 低边界地址倍数,它总为 0,非 0,非 0,SHM_RN命令的意思是:取整。SHMLBA勺意思是:1个SHMLBA勺倍数。(这在当今是不大可能的)为 0,以便由内核选择地址。如果在f I a g中指定了 SHM_RDONLY,则以只读方式连接此段。此段。(在进程页表项中设置只读标志,试图修改该页时将产生缺页

34、异常,这些都是由 的页寻址硬件控制的)shmat 的返回值是该段所连接的实际地址,如果出错则返回 1。 当对共享存储段的操作已经结束时,则调用 shmdt 脱接该段。注意,这并不从系统中删 除其标识符以及其数据结构。 该标识符仍然存在, 直至某个进程 (一般是服务器) 调用 shmctI(带命令IP C_RMID特地删除它。(连接是进程将共享内存的物理页加入进程页表,脱离是 从页表中撤销该物理页的信息,并不改变实际的物理页)#incIude <sys/types.h>#incIude <sys/ipc.h>#incIude <sys/shm.h>int sh

35、mdt(void * addr);返回:若成功则为 0,若出错则为 - 1addr 参数是以前调用 shmat 时的返回值。下面是和 IPC 共享内存有关的内核参数,可以修改的。addr addr addr所表示的地址上。是 2 的乘方。该算式是将地址向下取最近 除非只计划在一种硬件上运行应用程序所连接到的地址。所以一般应指定 addr,否则不用指定共享段否则以读写方式连接CPU其中 shmall 是所有共享内存段可以使用的最大页个数 shmmni 是一个共享内存段的最小字节数shmmax是一个共享内存段的最大字节数下面是显示系统中已近建立的共享内存对象其中 bytes 是申请共享内存时使用的

36、大小参数,实际的大小是 数倍)/4096 + 4) * 4096。nattch 是附加此共享内存对象的进程数。 例子程序:#include <unistd.h>( ( bytes 进位到 4096 整#include <sys/ipc.h>#include <sys/shm.h>#include <stdio.h>#include <sys/stat.h>#include <sys/types.h> #include <fcntl.h> #define KEY 4 #define SIZE 4096*3 int

37、 main(int argc, char *argv) int shmid = 0, ret = 0; char *shmaddr = 0; struct shmid_ds buf; shmid = shmget(KEY, SIZE, IPC_CREAT | SHM_R); printf("id: %dn", shmid);shmaddr = (char *)shmat(shmid, NULL, 0); *(shmaddr + 4095*3) = 'c'shmdt(shmaddr);使用ipcs - m查看建立的共享内存对象:要理解 IPC 共享内存和文件映

38、射的实现机制,先要理解什么是共享内存,共享内存实现 的基本原理是什么。(五)共享内存实现基本原理CPU要访问某块内存,必须要获得内存的物理地址。会根据机器语言指令中提供的地址,执行地址转换,获得的物理CPU 集成有寻址硬件,地址。1. 实模式 2. 保护模式。= 线性地址。= 线性地址通过分页机制转化为物理地址。CPU有两种转换模式: 实模式下的物理地址 保护模式下的物理地址 启动保护模式:把 CPU控制寄存器CRO中的最高位置1。CPU保护模式寻址方式:图 1 分页机制寻址CR3页目而是用CR3的值说明:CR3控制寄存器的值是物理地址。 由于寻找的是页框的物理地址, 所以 录和页表中存储的物

39、理地址后 12位都为0。也就是这12位的空间不存储物理地址, 于访问控制(可读/可写/CPU特权级别),指示对应的页是否存在等作用。在转换过程中,出现以下情况之一将会引起也异常:涉及的页目录表内的表项或页表内的表项中的P=0,即涉及到的页不在内存;违反也保护属性的规定而对页进行访问。注意:从 2.6.11 版本开始,采用了四级分页模型,但基本原理是一样的。每个进程的CR3的值是不同,进程切换时保存或者恢复CR3的值。只要设置了CPU将自动进行寻址。要将某个物理页加入进程的地址空间,要做的事情就是将物理页的物理地址填写进程页 目录表内的表项和页表内的表项。共享内存实现原理就是:将相同的物理页加入

40、不同进程的地址空间。显然进程中要加入一块物理页,就必须对应一块线性空间,于是就要先申请一块线性空 间,然后根据这块线性空间填写页目录表内的表项和页表内的表项。 (这里可以说明为什么不 同进程中的不同线性地址可以对应相同的物理地址) 。由于 linux 会先分配线性空间, 页表的修改会推后进行。 Linux 通过 vm_area_struct 对 象实现线性区, 当产生缺页异常时, 会根据 vm_area_struct 对象来修改页表, 然后重新执行 产生缺页异常的代码。总的来说,内核实际要做的事情是很多的,但是,内核也提供了很多接口,所以我们要 做的事情还是比较少的。以上就是共享内存实现的基本

41、原理,下面分析一下 IPC 共享内存和内存映射实现机制。 实际上 IPC 共享内存的实现是基于内存映射,原因是:内存映射提供了一些接口,基于内存 映射来实现 IPC 共享内存可以复用这些代码。但是,两者最终的实现原理还是修改页表。(六) IPC 共享内存实现机制2 所示:先看一下内核是如何组织共享内存对象的,如图 图2 共享内存对象组织其中 struct shmid_kernel 就是一个共享内存对象,使用 id radix tree来组织所有的共享内存对象。使用 id 号查找一个共享内存对象。我们现在最关心的问题是:如何根据struct shmid_kernel结构获得对象所拥有的物理内存。

42、是根据 file->f_path.dentry->d_inode struct address_space *mapping = inode->i_mapping mapping 存储着共享内存拥有的物理页,如图 3 所示: 图3 共享内存物理页存储方式 其中 page_tree 用于存储物理页,每个节点的值类型是 struct page * 每个共享内存对象对应一个 inode 对象,这个对象是被多个进程共享,也就是说,进程是通 过 inode 对象获得物理页。这里借用了文件映射的框架, i_mmaping 对象也就是文件的页高 速缓存。并将对象加入到 vm_area_st

43、ruct进程映射共享内存区域的过程:的复制。 将产生缺页异常, 内核根 vm_area_struct 对象,最后将执行以下函数:1. 需要申请一块线性地址空间, 也就是生成一个 vm_area_struct 对象, 自己的地址空间, 当此时并不修改进程页表, 而是把 struct file 对象加入到 对象中,执行以下代码: vma->vm_file = file; get_file(file); error = file->f_op->mmap(file, vma);shm_file注意:这里的 file 是根据 shm_file 生成的一个新的对象,相当于 2. 当第一次

44、访问共享内存块时, 由于相应的页表项还未填写, 据产生异常的线性地址找到对应的static int shmem_fault(struct vm_area_struct *vma, struct vm_fault *vmf) struct inode *inode = vma->vm_file->f_path.dentry->d_inode;int error;int ret;if (loff_t)vmf->pgoff << PAGE_CACHE_SHIFT) >= i_size_read(inode) return VM_FAULT_SIGBUS;error = shmem_getpage(inode, vmf->pgoff, &vmf->page, SGP_CACHE, &ret);if (error)return (error = -ENOMEM) ? VM_FAULT_OOM : VM_FAULT_SIGBUS); return ret | VM_FAULT_LOCKED; 本函数的功能是:根据产生缺页异常的线性地址找到对应的物理页,并将这个物理页加 入页表。以上说明的也就是内存映射实现机制,现在做个总结:调用do_mmap()函数生成并注册一个线性对象,同时也

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论