linux 内存机制,linux内存管理机制有哪些
大家好,今天小编来为大家解答linux 内存机制这个问题,linux内存管理机制有哪些很多人还不知道,现在让我们一起来看看吧!
linux内存管理之用户空间与内核空间详解
Linux操作系统运行在内核空间,应用程序运行在用户空间,两者之间不能简单地使用指针传递数据,因为Linux采用虚拟内存机制,用户空间的数据可能被换出。内核空间使用用户空间指针时,对应的数据可能不在内存中。
Linux内核使用段页式地址映射模型,进程代码中的地址为逻辑地址,通过段页式映射后,才能真正访问物理内存。
Linux内核地址空间通常分为用户空间和内核空间,其中用户空间范围为0~3G,内核空间范围为3~4G。注意,这里指的是32位Linux内核的地址空间划分,64位内核的地址空间划分有所不同。
内核空间的高端内存概念源于物理内存的限制。当内核模块代码或线程访问内存时,逻辑地址需要一对一映射到物理内存地址。例如,逻辑地址0xc0000003对应物理地址0×3,以此类推。如果内核逻辑地址空间为0xc0000000~0xffffffff,那么物理内存范围只能访问0×0~0×40000000,即最多访问1G物理内存。若安装8G物理内存,内核只能访问前1G内存,后面的7G内存无法访问。
为了解决这个问题,x86架构将内核地址空间划分为三部分:ZONE_DMA、ZONE_NORMAL和ZONE_HIGHMEM。ZONE_HIGHMEM即为高端内存区域,它是内存高端内存概念的来源。
在x86结构中,内核地址空间分为如下三部分:
ZONE_DMA:内存开始的16MB
ZONE_NORMAL:从16MB到896MB
ZONE_HIGHMEM:从896MB至结束
Linux内核高端内存的理解在于,通过将高端内存划分为特定区域,如ZONE_HIGHMEM,允许内核使用有限的地址空间访问所有物理内存。当内核想访问超过896MB的物理内存时,它可以在高端内存区域找到相应的空闲逻辑地址空间,借用一段时间来映射到物理内存,完成访问后释放这段逻辑地址空间,以便其他进程或代码使用。
例如,内核想要访问从物理地址0×80000000开始的1MB内存,可以找到一段1MB大小的空闲逻辑地址空间,借用一段时间进行映射,完成访问后释放这段逻辑地址空间。
高端内存的基本思想是借用一段地址空间建立临时映射,用完后释放,实现循环使用,从而访问所有物理内存。
在Linux内核管理中,高端内存的使用涉及到复杂的地址映射和释放机制。为了支持高端内存,内核将内存划分为VMALLOC_START~VMALLOC_END、KMAP_BASE~FIXADDR_START和FIXADDR_START~4G等部分。对于高端内存的映射,有三种方式:映射到“内核动态映射空间”、持久内核映射和临时映射。
通过使用这些映射方式,Linux内核能够高效地管理高端内存,实现访问所有物理内存的功能。当进程或内核模块占用逻辑地址空间不释放时,系统会提醒释放,以避免内存资源的浪费。
综上所述,Linux内存管理中的用户空间与内核空间通过虚拟内存机制和地址映射模型实现隔离,内核使用特定的地址空间和映射技术访问所有物理内存,而用户进程则受限于用户空间。这种设计保证了系统的稳定性和安全性,同时也支持了多进程和多任务的高效运行。
Linux用户空间与内核空间(理解高端内存)
理解Linux用户空间与内核空间的高端内存机制,首先需要知道操作系统和驱动程序运行在内核空间,而应用程序运行在用户空间,两者之间的数据传输受限于虚拟内存映射,用户空间数据可能被换出,导致内核使用用户空间指针时,对应的数据可能不在内存中。
Linux内核采用了x86的段页式地址映射模型,将进程的逻辑地址映射到物理内存。通常,32位内核地址空间划分为0~3G为用户空间,3~4G为内核空间。但这种简单的映射方式限制了内核对物理内存的访问范围,只能访问1GB,即使机器有更多内存。
为了解决这个问题,内核引入了高端内存(ZONE_HIGHMEM),通过动态分配逻辑地址空间,临时建立映射来访问超过预设范围的物理内存。例如,内核可能借入0xF8000000到0xFFFFFFFF的逻辑地址空间,对应物理内存的1024MB至8GB,实现对所有物理内存的访问,用完后归还。
需要注意的是,如果内核进程长时间占用高端内存不释放,会导致地址空间紧张,影响其他进程的内存映射。这个管理方式类似于写字楼的洗手间管理,需要用户合理使用并及时归还资源。
Linux内核将高端内存划分为不同的区域,包括VMALLOC_START~VMALLOC_END、KMAP_BASE~FIXADDR_START和FIXADDR_START~4G,通过不同的函数如alloc_page()来分配和映射这些内存。高端内存映射有三种方式,分别是内核动态映射、永久内核映射和临时映射,每种映射方式都有其特定的用途和释放机制。
最后,用户空间和内核空间的内存访问权限不同,用户进程只能访问3GB(32位系统)或超过512GB(64位系统)的物理内存,而内核可以访问所有物理内存。虚拟内存映射和物理地址之间的关系复杂,但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第一个功能一样!只不过不是普通文件而已,但都是物理内存。