linux内存映射文件(linux查看已安装的软件包)
大家好,关于linux内存映射文件很多朋友都还不太明白,不过没关系,因为今天小编就来为大家分享关于linux查看已安装的软件包的知识点,相信应该可以解决大家的一些困惑和问题,如果碰巧可以解决您的问题,还望关注下本站哦,希望对各位有所帮助!
linux共享内存和mmap的区别
共享内存的创建
根据理论:
1.共享内存允许两个或多个进程共享一给定的存储区,因为数据不需要来回复制,所以是最快的一种进程间通信机制。共享内存可以通过mmap()映射普通文件(特殊情况下还可以采用匿名映射)机制实现,也可以通过系统V共享内存机制实现。应用接口和原理很简单,内部机制复杂。为了实现更安全通信,往往还与信号灯等同步机制共同使用。
mmap的机制如:就是在磁盘上建立一个文件,每个进程存储器里面,单独开辟一个空间来进行映射。如果多进程的话,那么不会对实际的物理存储器(主存)消耗太大。
shm的机制:每个进程的共享内存都直接映射到实际物理存储器里面。
结论:
1、mmap保存到实际硬盘,实际存储并没有反映到主存上。优点:储存量可以很大(多于主存)(这里一个问题,需要高手解答,会不会太多拷贝到主存里面???);缺点:进程间读取和写入速度要比主存的要慢。
2、shm保存到物理存储器(主存),实际的储存量直接反映到主存上。优点,进程间访问速度(读写)比磁盘要快;缺点,储存量不能非常大(多于主存)
使用上看:如果分配的存储量不大,那么使用shm;如果存储量大,那么使用shm。
参看百度:
mmap就是一个文件操作
看这些百度的描述:
mmap()系统调用使得进程之间通过映射同一个普通文件实现共享内存。普通文件被映射到进程地址空间后,进程可以向访问普通内存一样对文件进行访问,不必再调用read(),write()等操作。成功执行时,mmap()返回被映射区的指针,munmap()返回0。失败时,mmap()返回MAP_FAILED[其值为(void*)-1],munmap返回-1。errno被设为以下的某个值 EACCES:访问出错EAGAIN:文件已被锁定,或者太多的内存已被锁定EBADF:fd不是有效的文件描述词EINVAL:一个或者多个参数无效 ENFILE:已达到系统对打开文件的限制ENODEV:指定文件所在的文件系统不支持内存映射ENOMEM:内存不足,或者进程已超出最大内存映射数量 EPERM:权能不足,操作不允许ETXTBSY:已写的方式打开文件,同时指定MAP_DENYWRITE标志SIGSEGV:试着向只读区写入 SIGBUS:试着访问不属于进程的内存区参数fd为即将映射到进程空间的文件描述字,
一般由open()返回,同时,fd可以指定为-1,此时须指定 flags参数中的MAP_ANON,表明进行的是匿名映射(不涉及具体的文件名,避免了文件的创建及打开,很显然只能用于具有亲缘关系的进程间通信)
相关文章参考:
mmap函数是unix/linux下的系统调用,来看《Unix Netword programming》卷二12.2节有详细介绍。
mmap系统调用并不是完全为了用于共享内存而设计的。它本身提供了不同于一般对普通文件的访问方式,进程可以像读写内存一样对普通文件的操作。而Posix或系统V的共享内存IPC则纯粹用于共享目的,当然mmap()实现共享内存也是其主要应用之一。
mmap系统调用使得进程之间通过映射同一个普通文件实现共享内存。普通文件被映射到进程地址空间后,进程可以像访问普通内存一样对文件进行访问,不必再调用read(),write()等操作。mmap并不分配空间,只是将文件映射到调用进程的地址空间里,然后你就可以用memcpy等操作写文件,而不用write()了.写完后用msync()同步一下,你所写的内容就保存到文件里了.不过这种方式没办法增加文件的长度,因为要映射的长度在调用mmap()的时候就决定了.
简单说就是把一个文件的内容在内存里面做一个映像,内存比磁盘快些。
基本上它是把一个档案对应到你的virtual memory中的一段,并传回一个指针。
重写总结:
1、mmap实际就是操作“文件”。
2、映射文件,除了主存的考虑外。shm的内存共享,效率应该比mmap效率要高(mmap通过io和文件操作,或“需要写完后用msync()同步一下”);当然mmap映射操作文件,比直接操作文件要快些;由于多了一步msync应该可以说比shm要慢了吧???
3、另一方面,mmap的优点是,操作比shm简单(没有调用比shm函数复杂),我想这也是许多人喜欢用的原因,包括nginx。
缺点,还得通过实际程序测试,确定!!!
修正理解(这也真是的,这个网站没办法附加;只能重写了):
今天又细心研究了一下,发现百度这么一段说明:
2、系统调用mmap()用于共享内存的两种方式:
(1)使用普通文件提供的内存映射:适用于任何进程之间;此时,需要打开或创建一个文件,然后再调用mmap();典型调用代码如下:
fd=open(name, flag, mode);
if(fd<0)
...
ptr=mmap(NULL, len, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);通过mmap()实现共享内存的通信方式有许多特点和要注意的地方,我们将在范例中进行具体说明。
(2)使用特殊文件提供匿名内存映射:适用于具有亲缘关系的进程之间;由于父子进程特殊的亲缘关系,在父进程中先调用mmap(),然后调用fork()。那么在调用fork()之后,子进程继承父进程匿名映射后的地址空间,同样也继承mmap()返回的地址,这样,父子进程就可以通过映射区域进行通信了。注意,这里不是一般的继承关系。一般来说,子进程单独维护从父进程继承下来的一些变量。而mmap()返回的地址,却由父子进程共同维护。
看了一下windows“内存映射文件”:
内存映射文件与虚拟内存有些类似,通过内存映射文件可以保留一个地址空间的区域,同时将物理存储器提交给此区域,只是内存文件映射的物理存储器来自一个已经存在于磁盘上的文件,而非系统的页文件,而且在对该文件进行操作之前必须首先对文件进行映射,就如同将整个文件从磁盘加载到内存。由此可以看出,使用内存映射文件处理存储于磁盘上的文件时,将不必再对文件执行I/O操作,这意味着在对文件进行处理时将不必再为文件申请并分配缓存,所有的文件缓存操作均由系统直接管理,由于取消了将文件数据加载到内存、数据从内存到文件的回写以及释放内存块等步骤,使得内存映射文件在处理大数据量的文件时能起到相当重要的作用。另外,实际工程中的系统往往需要在多个进程之间共享数据,如果数据量小,处理方法是灵活多变的,如果共享数据容量巨大,那么就需要借助于内存映射文件来进行。实际上,内存映射文件正是解决本地多个进程间数据共享的最有效方法。
这里再总结一次:
1、mmap有两种方式,一种是映射内存,它把普通文件映射为实际物理内存页,访问它就和访问物理内存一样(这也就和shm的功能一样了)(同时不用刷新到文件)
2、mmap可以映射文件,不确定会不会像windows“内存映射文件”一样的功能,如果是,那么他就能映射好几G甚至好几百G的内存数据,对大数据处理将提供强大功能了???
3、shm只做内存映射,和mmap第一个功能一样!只不过不是普通文件而已,但都是物理内存。
一文搞懂Linux内存映射实现(一)
下面介绍一下Linux内存映射的实现
一、基础概念
1、mmap文件映射
mmap是一种内存映射文件的方法,将一个文件映射到进程的地址空间,建立文件磁盘地址和进程虚拟地址的一种对应关系,如此进程通过读取相应的虚拟地址就可以直接读取相应文件中的内容。mmap是一种内存映射文件的方法,将一个文件映射到进程的地址空间,建立文件磁盘地址和进程虚拟地址的一种对应关系,如此进程通过读取相应的虚拟地址就可以直接读取相应文件中的内容。
这样映射的最大好处是进程可用直接访问内存,避免了频繁的使用read/write等文件系统的系统调用。需要注意的是mmap并不分配物理内存,它所做的最重要的工作就是为进程映射区的虚拟地址建立页表项
从图上可以看出进程的虚拟地址空间,是由多个虚拟内存区域构成的。如图所示的text数据段,初始数据段,bss数据段,堆,栈都是一个个独立的虚拟内存区域。而为内存映射服务的地址空间处在堆和栈之间的空余部分。
2、进程的虚拟地址空间
Linux内核使用vm_area_struct结构来表示一个独立的虚拟内存区域,由于每个虚拟内存区域功能和内部机制都不尽相同,因此一个进程会使用多个vm_area_struct结构来分别表示不同类型的虚拟内存区域。即我们在上一页看到的text数据段,bss数据段等等。每个vm_area_struct都对应虚拟地址空间上一段连续的地址,它们之间使用链表或者树形结构链接,方便进程进行快速的查找/访问。
这里我们可用看到vm_area_struct结构中的一些字段,其中包括虚拟内存区的起始和结束的地址;vm_flags是该虚拟内存区的标志位。如果虚拟区域映射的是磁盘文件或者设备文件的话,那么vm_inode指向该文件的inode索引节点
这里有一个重要的vm_ops的字段,它是一个指向vm_operations_struct结构体的指针,在vm_operation_struct结构体中,定义了与该虚拟内存区操作相关的接口,其中包括了:open,close,fault等等这些操作。
每个虚拟内存区域都必须在vm_operations_struct结构中实现这些操作
一个进程的全部虚拟地址空间由mm_struct结构体来管理的,它里面包括了进程虚拟空间的一些管理的信息,包括进程pgd页表的地址等等,另外它还有一个指向,虚拟内存区链表的指针mmap。
最好,在进程描述符中有一个mm字段,指向mm_struct结构,这些共同组成了linux中进程虚拟地址空间的抽象描述
3、Linux中的字符设备驱动
最后是Linux中有关设备驱动的概念。
我们知道,所有的设备在linxu里面都是以设备文件的形式存在的,设备文件允许应用程序通过标准输入输出系统调用,与驱动程序进行交互,既然都是文件,当然也可以进行mmap映射,这是一种操作设备的方法。
有关设备和驱动的东西是一块很大的领域,此处以今天要使用的字符设备驱动为例,简单介绍它的基本内容。在用户程序看来,操作一个设备就是对设备文件的读写,而其具体的实现过程则是相应的驱动程序来完成的。
如上图示,方框中就是一个设备驱动的主要内容,它其中自然少不了对模块的加载和卸载函数,它们主要完成设备的初始化和删除的。它使用struct_cdev结构体来抽象描述一个字符设备,而每个cdev结构体就由一个dev_t类型的设备号来唯一指定,设备号分为主(major)设备号和次设备号,主设备号用来表明设备类型,次设备号用来表明其编号
这里可以看到由一个file_operations结构体,该结构体是linux文件系统中的一个非常重要的结构体,linux的VFS虚拟文件系统能够将不同类型的文件系统统一管理,并且为用户提供一个统一的接口,就是通过file_operations结构体实现的。
而在设备文件中,它主要用来存储驱动模块提供的对设备进行各种操作的函数,对于普通文件的read、write,驱动程序需要将其转化为对应的对设备的操作,就是通过该结构体(file_operations)完成的。它其中包括许多的钩子函数,包括read,release,mmap等等。read是进程在读设备文件时要做的,release则是在进程调用close时所要做的工作,它用来释放一些系统资源。最后是mmap,不同的文件有自己定义的mmap钩子,比如ext3文件系统对应了一个叫做generic_file_mmap的一个钩子函数
今天要做的主要工作,就是为一个虚拟字符设备编写其驱动模块,在其驱动中完成设备空间,即内核空间到用户空间的映射。
二、具体实现
进入源码,看内存映射具体的实现过程。驱动程序源码map_driver.c
完整源码如下:
驱动程序大概有三部分组成,1-模块的装载卸载; 2-file_operations结构体和mmap函数;3-vm_operations_struct结构体和fault函数。
首先是模块的装载函数,它所要完成的工作是两个,一是设备的注册,二是在内核中为设备申请一块内存。
设备的注册由register_chrdev这个函数来实现,这里需要指定设备的主设备号MAP_DEV_MAJOR,设备的名称MAP_DEV_NAME,还有它所链接的file_operations结构&mapdrvo_fops);
这里如果主设备号为零,该设备将自己分配一个主设备号,返回给result。如果返回值为0,表示分配成功;返回值为负表示设备注册失败。
接下来是申请内存,此处用的是vmalloc函数,vmalloc函数的特点是申请的内存区域在内核的线性地址是连续的,但物理地址不连续。
这里我们看到这里还有为申请到的页框的PageReserved标志位置位,这样做是告诉系统,该物理页框已经被我使用。
下面是模块的卸载函数。在模块的卸载函数中,要做的正相反。
首先是清理PageReserved标志位,接着是通过vfree释放我们申请的vmalloc线性区的线性地址,最后是通过unregister_chrdev注销掉这个设备。
接下来介绍的是file_operations结构体和mmap函数。如下为驱动程序中定义的file_operations结构体
驱动模块中只实现了三个功能,owner是用来指向该驱动module结构的指针
open函数在这里,我们用它来打印了调用该模块进程的pid
再就是mmap函数,具体分析过程在下面
include/linux/fs.h
内核源码中有关file_operations结构体的完整定义如下,设备的读取,写入,保持等等这些操作,都是由存储在file_operations结构体中的这些函数指针来处理的,这些函数指针所指向的函数都需要我们在驱动模块中将其实现,在这里我们可以看到file_operations结构体中提到了许多的函数指针,包括read,write,mmap,open等。但是我们可以不全使用它们。对于那些指向未实现函数的只是可以简单的设置为空。操作系统将负责实现该功能。
Linux关于地址空间和MMAP映射有何特点
Linux采用
虚拟
内存技术,系统中的所有进程之间以虚拟方式共享内存。对每个进程来说,它们好像都可以访问整个系统的所有物理内存。更重要的是,即使单独一个进程,它拥有的地址空间也可以远远大于系统物理内存。
进程地址空间由每个进程中的线性地址区组成,每个进程都有一个32位或64位的平坦(flat)空间,空间的具体大小取决于体系结构。“平坦”指地址空间范围是一个独立的连续区间。通常情况下,每个进程都有唯一的这种平坦空间,而且每个进程的地址空间之间彼此互不相干。两个不同的进程可以在它们各自地址空间的相同地址内存存放不同的数据。但是进程之间也可以选择共享地址空间,我们称这样的进程为线程。
在地址空间中,我们更为关心的是进程有权访问的虚拟内存地址区间,比如08048000~0804c000。这些可被访问的合法地址区间被成为内存区域(memory area),通过内核,进程可以给自己的地址空间动态地添加或减少内存区域。
进程只能访问有效范围内的内存地址。每个内存区域也具有相应进程必须遵循的特定访问属性,如只读、只写、可执行等属性。如果一个进程访问了不在有效范围中的地址,或以不正确的方式访问有效地址,那么内核就会终止该进程,并返回“段错误”信息。
?
内存区域可以包含各种内存对象,如下:
?
可执行文件代码的内存映射,成为代码段(text section)。
?
可执行文件的已初始化全局变量的内存映射,成为数据段(data section)。
?
包含未初始化全局变量的零页(也就是bss段)的内存映射。零页是指页面中的数据全部为0。
?
用于进程用户空间栈的零页的内存映射。
?
每一个诸如C库或动态链接程序等共享库的代码段、数据段和bss也会被载入进程的地址空间。
?
任何内存映射文件。
?
任何共享内存段。
?
任何匿名的内存映射,比如由malloc()分配的内存。
进程地址空间的任何有效地址都只能位于唯一的区域,这些内存区域不能相互覆盖。可以看到,在执行的进程中,每个不同的内存片断都对应一个独立的内存区域:栈、对象代码、全局变量、被映射的文件等等。
内核使用内存描述符表示进程的地址空间。内存描述符由mm_struct结构体表示,定义在文件中,该结构包含了和进程地址空间有关的全部信息。
VMA
内存区域由vm_area_struct结构体描述,定义在文件中,内存区域在内核中也经常被称作虚拟内存区域或者VMA。
VMA标志是一种位标志,它定义在vm_area_struct结构中(该结构中的vm_flags子域)。和物理页的访问权限不同,VMA标志反映了内核处理页面索需要遵守的行为准则,而不是硬件要求。VM_IO标志内存区域中包含对设备I/O空间的映射。该标志通常在设备驱动程序执行 mmap()函数进行I/O空间映射时才被设置,同时该标志也表示该内存区域不能被包含在任何进程的存放转存(core dump)中。VM_RESERVED标志内存区域不能被换出,它也是在设备驱动程序进行映射时被设置。
vm_area_struct结构体中的vm_ops域指向与指定内存区域相关的操作函数表,内核使用表中的方法操作VMA。
mmap()和do_mmap():创建地址区间
内核使用do_mmap()函数创建一个新的线性地址区间。但是说给函数创建一个新VMA并不非常准确,因为如果创建的地址区间和一个已经存在的地址区间相邻,并且它们具有相同的访问权限的话,那么两个区间将合并为一个。如果不能合并,那么就确实需要创建一个新的VMA了。但无论哪种情况,do_mmap()函数都会将一个地址区间加入到进程的地址空间中——无论是扩展已经存在的内存区域还是创建一个新的区域。
do_mmap()函数声明在文件中,原型如下:
unsigned long do_mmap(struct file*file, unsigned long addr,
unsigned long len, unsigned long prot,
unsigned long flag, unsigned long offset)
在用户空间可以通过mmap()函数调用获取内核函数do_mmap()的功能。mmap()系统调用原型如下:
void*mmap2(void*start, size_t length,
int prot, int flags,
int fd, off_t pgoff)
do_munmap()函数从特定的进程地址空间中删除指定地址区间,该函数在文件中声明:
int do_munmap(struct mm_struct*mm, unsigned long start, size_t len)
系统调用munmap()给用户空间程序提供了一种从自身地址空间中删除指定地址区间的方法,它和系统调用mmap()的作用相反:
int munmap(void*start, size_t length)
mmap设备操作
对于驱动程序来说,内存映射可以提供给用户程序直接访问设备内存的能力。映射一个设备,意味着使用户空间的一段地址关联到设备内存上。无论何时,只要程序在分配的地址范围内进行读取或者写入,实际上就是对设备的访问。
并不是所有的设备都能进行mmap抽象。例如,串口设备和其他面向流的设备就无法实现这种抽象。mmap的另一个限制是映射都是以 PAGE_SIZE为单位的。内核只能在页表一级处理虚拟地址;因此,被映射的区域必须是PAGE_SIZE的整数倍,而且必须位于起始于 PAGE_SIZE整数倍地址的物理内存内。如果区域的大小不是页大小的整数倍,内核就通过生成一个稍微大一些的区域来容纳它。
mmap方法是file_operations结构中的一员,并且在执行mmap系统调用时就会调用该方法。在调用实际方法之前,内核会完成很多工作,而且该方法的原型与系统调用的原型由很大区别。关于Linux命令的介绍,看看《linux就该这么学》,具体关于这一章地址3w(dot)linuxprobe/chapter-02(dot)html
文件操作声明如下:
int(*mmap)(struct file* filp, struct vm_area_struct*vma);
其中vma参数包含了用于访问设备的虚拟地址区间的信息。大部分工作已经由内核完成了,要实现mmap,驱动程序只要为这一地址范围构造合适的页表即可,如果需要的话,就用一个新的操作集替换vma->vm_ops。
有两种建立页表的方法:使用remap_page_range函数可一次建立所有的页表,或者通过nopage VMA方法每次建立一个页表。
构造用于映射一段物理地址的新页表的工作是由remap_page_range完