`
netcome
  • 浏览: 464153 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

linux内存管理概述

阅读更多

Linux中的地址空间(一)
有这么一系列的问题,是否在困扰着你:用户程序编译连接形成的地址空间在什么范围内?内核编译后地址空间在什么范围内?要对外设进行访问,I/O的地址空间又是什么样的?
先回答第一个问题。Linux最常见的可执行文件格式为elf(Executable and Linkable Format)。在elf格式的可执行代码中,ld总是从0×8000000开始安排程序的“代码段”,对每个程序都是这样。至于程序执行时在物理内存中的实际地址,则由内核为其建立内存映射时临时分配,具体地址取决于当时所分配的物理内存页面。
我们可以用Linux的实用程序objdump对你的程序进行反汇编,从而知晓其地址范围。
例如:假定我们有一个简单的C程序Hello.c
  # include <stdio.h>
  greeting ( )  {
              printf(“Hello,world!\n”);
  }
  main()   {
         greeting();
 }
之所以把这样简单的程序写成两个函数,是为了说明指令的转移过程。我们用gcc和ld对其进行编译和连接,得到可执行代码hello。然后,用Linux的实用程序objdump对其进行反汇编:
$objdump –d hello
得到的主要片段为:
08048568 <greeting>:
   8048568:     pushl  %ebp
   8048569:     movl  %esp, %ebp
   804856b:     pushl  $0×809404
   8048570:     call    8048474  <_init+0×84>
   8048575:     addl   $0×4, %esp
   8048578:     leave
   8048579:     ret
   804857a:     movl  %esi, %esi
   0804857c <main>:
   804857c:     pushl  %ebp
   804857d:     movl  %esp, %ebp
   804857f:     call    8048568  <greeting>
   8048584:     leave
   8048585:     ret
   8048586:     nop
   8048587:     nop

其中,像08048568这样的地址,就是我们常说的虚地址(这个地址实实在在的存在,只不过因为物理地址的存在,显得它是“虚”的罢了)。

虚拟内存、内核空间和用户空间

   Linux虚拟内存的大小为2^32(在32位的x86机器上),内核将这4G字节的空间分为两部分。最高的1G字节(从虚地址0xC0000000到0xFFFFFFFF)供内核使用,称为“内核空间”。而较低的3G字节(从虚地址0×00000000到0xBFFFFFFF),供各个进程使用,称为“用户空间”。因为每个进程可以通过系统调用进入内核,因此,Linux内核空间由系统内的所有进程共享。于是,从具体进程的角度来看,每个进程可以拥有4G字节的虚拟地址空间(也叫虚拟内存)。
  
    每个进程有各自的私有用户空间(0~3G),这个空间对系统中的其他进程是不可见的。最高的1GB内核空间则为所有进程以及内核所共享。另外,进程的“用户空间”也叫“地址空间”,在后面的叙述中,我们对这两个术语不再区分。
用户空间不是进程共享的,而是进程隔离的。每个进程最大都可以有3GB的用户空间。一个进程对其中一个地址的访问,与其它进程对于同一地址的访问绝不冲突。比如,一个进程从其用户空间的地址0×1234ABCD处可以读出整数8,而另外一个进程从其用户空间的地址0×1234ABCD处可以读出整数20,这取决于进程自身的逻辑。
任意一个时刻,在一个CPU上只有一个进程在运行。所以对于此CPU来讲,在这一时刻,整个系统只存在一个4GB的虚拟地址空间,这个虚拟地址空间是面向此进程的。当进程发生切换的时候,虚拟地址空间也随着切换。由此可以看出,每个进程都有自己的虚拟地址空间,只有此进程运行的时候,其虚拟地址空间才被运行它的CPU所知。在其它时刻,其虚拟地址空间对于CPU来说,是不可知的。所以尽管每个进程都可以有4 GB的虚拟地址空间,但在CPU眼中,只有一个虚拟地址空间存在。虚拟地址空间的变化,随着进程切换而变化。

从上面我们知道,一个程序编译连接后形成的地址空间是一个虚拟地址空间,但是程序最终还是要运行在物理内存中。因此,应用程序所给出的任何虚地址最终必须被转化为物理地址,所以,虚拟地址空间必须被映射到物理内存空间中,这个映射关系需要通过硬件体系结构所规定的数据结构来建立。这就是我们所说的段描述符表和页表,Linux主要通过页表来进行映射。
于是,我们得出一个结论,如果给出的页表不同,那么CPU将某一虚拟地址空间中的地址转化成的物理地址就会不同。所以我们为每一个进程都建立其页表,将每个进程的虚拟地址空间根据自己的需要映射到物理地址空间上。既然某一时刻在某一CPU上只能有一个进程在运行,那么当进程发生切换的时候,将页表也更换为相应进程的页表,这就可以实现每个进程都有自己的虚拟地址空间而互不影响。所以,在任意时刻,对于一个CPU来说,只需要有当前进程的页表,就可以实现其虚拟地址到物理地址的转化。

Linux内核空间(二)

内核空间到物理内存的映射 

   内核空间对所有的进程都是共享的,其中存放的是内核代码和数据,而进程的用户空间中存放的是用户程序的代码和数据,不管是内核程序还是用户程序,它们被编译和连接以后,所形成的指令和符号地址都是虚地址(参见2.5节中的例子),而不是物理内存中的物理地址。
虽然内核空间占据了每个虚拟空间中的最高1GB字节,但映射到物理内存却总是从最低地址(0×00000000)开始的,如图4.2所示,之所以这么规定,是为了在内核空间与物理内存之间建立简单的线性映射关系。其中,3GB(0xC0000000)就是物理地址与虚拟地址之间的位移量,在Linux代码中就叫做PAGE_OFFSET。
                  
我们来看一下在include/asm/i386/page.h头文件中对内核空间中地址映射的说明及定义:

#define __PAGE_OFFSET           (0xC0000000)
……
#define PAGE_OFFSET             ((unsigned long)__PAGE_OFFSET)
#define __pa(x)                 ((unsigned long)(x)-PAGE_OFFSET)
#define __va(x)                 ((void *)((unsigned long)(x)+PAGE_OFFSET))
对于内核空间而言,给定一个虚地址x,其物理地址为“x- PAGE_OFFSET”,给定一个物理地址x,其虚地址为“x+ PAGE_OFFSET”。

这里再次说明,宏__pa()仅仅把一个内核空间的虚地址映射到物理地址,而决不适用于用户空间,用户空间的地址映射要复杂得多,它通过分页机制完成。
 内核空间为3GB~4GB,这1GB的空间分为如下几部分,如图1所示:

  linux内存管理概述(转) - tauruspdj - tauruspdj的博客

           图1  从PAGE_OFFSET开始的1GB地址空间 

 先说明图中符号的含义:

PAGE_OFFSET: 0XC0000000,即3GB

high_memory: 这个变量的字面含义是高端内存,到底什么是高端内存,Linux内核规定,RAM的前896为所谓的低端内存,而896~1GB共128MB为高端内存。如果你的内存是512M,那么high_memory是多少?是3GB+512M,也就是说,物理地址x<=896M,就有内核地址0xc0000000+x,否则,high_memory=0xc0000000+896M
或者说high_memory实际值为0xc0000000+x,但最大不能超过0xc0000000+896M

在源代码中函数mem_init中,有这样一行:
high_memory = (void *) __va(max_low_pfn * PAGE_SIZE);
其中,max_low_pfn为物理内存的最大页数。

所以在图中,PAGE_OFFSET到high_memory 之间就是所谓的物理内存映射。只有这一段之间物理地址与虚地址之间是简单的线性关系。
  还要说明的是,要在这段内存分配内存,则调用kmalloc()函数。反过来说,通过kmalloc()分配的内存,其物理页是连续的。
void *kmalloc(size_t size, gfp_t flags);
 * The @flags argument may be one of:
 * %GFP_USER – Allocate memory on behalf of user.  May sleep.
 * %GFP_KERNEL – Allocate normal kernel ram.  May sleep.
 * %GFP_ATOMIC – Allocation will not sleep.

VMALLOC_START:非连续区的的起始地址。
VMALLOC_END:非连续区的的末尾地址
在非连续区中,物理内存映射的末端与第一个VMalloc之间有一个8MB的安全区,目的是为了“捕获”对内存的越界访问。处于同样的理由,插入其他4KB的安全区来隔离非连续区。
非连续区的分配调用vmalloc()函数。
void *vmalloc(unsigned long size);

vmalloc()与 kmalloc()都是在内核代码中用来分配内存的函数,但二者有何区别?
   从前面的介绍已经看出,这两个函数所分配的内存都处于内核空间,即从3GB~4GB;但位置不同,kmalloc()分配的内存处于3GB~high_memory之间,这一段内核空间与物理内存的映射一一对应,而vmalloc()分配的内存在VMALLOC_START~4GB之间,这一段非连续内存区映射到物理内存也可能是非连续的。

   vmalloc()工作方式与kmalloc()类似, 其主要差别在于前者分配的物理地址无需连续,而后者确保页在物理上是连续的(虚地址自然也是连续的)。

尽管仅仅在某些情况下才需要物理上连续的内存块,但是,很多内核代码都调用kmalloc(),而不是用vmalloc()获得内存。这主要是出于性能的考虑。vmalloc()函数为了把物理上不连续的页面转换为虚拟地址空间上连续的页,必须专门建立页表项。还有,通过vmalloc()获得的页必须一个一个的进行映射(因为它们物理上不是连续的),这就会导致比直接内存映射大得多的缓冲区刷新。因为这些原因,vmalloc()仅在绝对必要时才会使用——典型的就是为了获得大块内存时,例如,当模块被动态插入到内核中时,就把模块装载到由vmalloc()分配的内存上。

vmalloc()函数用起来比较简单:
char *buf;
buf = vmalloc(16*PAGE_SIZE);  /*获得16页*/
if (!buf)
    /* 错误!不能分配内存*/

在使用完分配的内存之后,一定要释放它:
vfree(buf);

****************************************************************************************************************************************
下面的内容参考LDD3,十五章内存映射

在内核空间中,kmalloc分配的空间往往被称为内核逻辑地址(必须与物理地址的映射是线性的和一一对应的),vmalloc分配的空间被称为内核虚拟地址(不必和物理地址线性对应)。
所有的内核逻辑地址都是内核虚拟地址,但是许多内核虚拟地址不是逻辑地址
内核逻辑地址通常保存在unsigned long或者void*类型的变量中;内核虚拟地址通常保存在指针变量中。

物理内存中,低端内存存在于内核逻辑地址上,且与内核逻辑地址线性对应;而高端内存是不存在内核逻辑地址上的,只处于内核虚拟地址之上的。所有的物理内存都是通过page结构来描述的。
page结构中有个void *virtual域,表示如果页面被映射,则指向页的内核虚拟地址,如果未被映射则为NULL。低端内存页面总是被映射,而高端内存通常不被映射。

page结构和内核地址的转换
struct page *virt_to_page(void *kaddr); /* 内核逻辑地址->page结构指针 */
struct page *pfn_to_page(int pfn); /* 页帧号->page结构指针,应在传递前用pfn_valid检查页帧号的合法性 */
void *page_address(struct page *page); /* page结构指针-> 内核虚拟地址,大多数情况用kmap替代 */

#include <linux/highmem.h>
void *kmap(struct page *page);  /* page结构指针-> 内核虚拟地址 ,不同于page_address若是高端内存,则会创建映射,不要持有映射过长时间。会休眠*/
void kunmap(struct page *page); /* 释放kmap创建的映射 */
#include <linux/highmem.h>
#include <asm/kmap_types.h>
void *kmap_atomic(struct page *page, enum km_type type); /* kmap的原子版本,type指定使用的槽 */
void kunmap_atomic(void *addr, enum km_type type);
The only slots that make sense for drivers are KM_USER0 and KM_USER1 (for code running directly from a call from user space), and KM_IRQ0 and KM_IRQ1 (for interrupt handlers).

前面提到了每个进程都各自维护一个页表,来实现内存映射关系。2.6内核中删除了对页表直接操作的需求。
虚拟内存区(VMA)用于管理进程地址空间中不同区域的内核数据结构。查看/proc/<pid>/maps文件就能了解进程的内存区域。 /proc/self是一个特殊的文件,它始终指向当前进程。

# cat /proc/1/maps    look at init
        start-end                pern       offset       major:minor   inode          image
08048000-0804e000 r-xp 00000000   03:01      64652      /sbin/init  text
0804e000-0804f000 rw-p 00006000   03:01      64652      /sbin/init  data
0804f000-08053000 rwxp 00000000   00:00      0          zero-mapped BSS
40000000-40015000 r-xp 00000000   03:01      96278      /lib/ld-2.3.2.so  text
40015000-40016000 rw-p 00014000   03:01     96278      /lib/ld-2.3.2.so  data
40016000-40017000 rw-p 00000000   00:00     0          BSS for ld.so
42000000-4212e000 r-xp 00000000   03:01     80290      /lib/tls/libc-2.3.2.so  text
4212e000-42131000 rw-p 0012e000  03:01     80290      /lib/tls/libc-2.3.2.so  data
42131000-42133000 rw-p 00000000  00:00     0          BSS for libc
bffff000-c0000000     rwxp 00000000  00:00     0          Stack segment
ffffe000-fffff000          —p  00000000  00:00     0          vsyscall page
perm 内存区域的读写和执行权限的位掩码,p表示私有,s表示共享
上面这些字段和vm_area_struct结构<linux/mm.h>中的重要成员对应。
vm_operations_struct结构包含了处理进程的内存需求的操作。
mm_struct结构,包含了虚拟内存区域链表、页表以及其它大量内存管理信息等待。

mmap设备操作

在现代Unix操作系统中,内存映射是最吸引人的特征。对于驱动程序来说,内存映射可以提供给用户程序直接访问设备内存的能力。例如:X-windows服务的有关显卡的/dev/mem映射。
映射一个设备意味着将用户空间的一段内存与设备内存关联。
不是所有设备都能进行mmap抽象。如串口和其它面向流的设备就不能做这样的抽象。
mmap必须以PAGE_SIZE为单位进行映射,即只能在页表一级上对虚拟地址进行管理。
mmap方法是file_operations结构的一部分,
int (*mmap) (struct file *filp, struct vm_area_struct *vma);
为了执行mmap,驱动程序只需要为该地址范围建立合适的页表,并将vma->vm_ops替换为一系列的新操作即可。

有两种方法建立页表:
1使用remap_pfn_range函数一次全部建立,2或者通过nopage VMA方法每次建立一个页表。
int remap_pfn_range(struct vm_area_struct *vma, 
                                   unsigned long virt_addr,      /*重新映射时,起始用户虚拟地址*/
                                   unsigned long pfn,  /*页帧号,phys_addr>>PAGE_SHIFT,更多用vma->vm_pgoff的值*/
                                   unsigned long size,     /*以字节为单位*/
                                   pgprot_t prot);            /*保护属性,用vma->vm_page_prot的值*/

int io_remap_page_range(struct vm_area_struct *vma, 
                                      unsigned long virt_addr, 
                                      unsigned long phys_addr,
  
      /*指向IO内存*/
                                      unsigned long size, 
                                      pgprot_t prot);

简单的示例:
static int simple_remap_mmap(struct file *filp, struct vm_area_struct *vma)
{
    if (remap_pfn_range(vma, vma->vm_start, vm->vm_pgoff,
                                    vma->vm_end – vma->vm_start,
                                    vma->vm_page_prot))
         return -EAGAIN;
    vma->vm_ops = &simple_remap_vm_ops;
    simple_vma_open(vma);
    return 0;
}

然后便是为VMA添加新操作:
void simple_vma_open(struct vm_area_struct *vma)
{
    printk(KERN_NOTICE “Simple VMA open, virt %lx, phys %lx\n”,
            vma->vm_start, vma->vm_pgoff << PAGE_SHIFT);
}
void simple_vma_close(struct vm_area_struct *vma)
{
    printk(KERN_NOTICE “Simple VMA close.\n”);
}
static struct vm_operations_struct simple_remap_vm_ops = {
    .open =  simple_vma_open,
    .close = simple_vma_close,
};

最后是在mmap中将默认的vm_ops指针替换为自己的操作:
vma->vm_ops = &simple_remap_vm_ops;

如果要支持mremap系统调用,就必须实现nopage函数。
struct page *(*nopage)(struct vm_area_struct *vma, 
                                  unsigned long address,   /*包含引起错误的虚拟地址*/
                                  int *type);

当用户需要访问VMA,而该页又不在内存中时,将调用相关的nopage函数。
remap_pfn_range函数的一个限制是:它只能访问保留页和超出物理内存的物理地址。(Linux中,在内存映射时,物理地址页被标记为reserved,表示内存管理对其不起作用。如在PC中,640KB-1MB之间的内存被标记为保留的,因为这个范围位于内核自身代码的内部。)所以remap_pfn_range不允许重新映射常规地址。包括调用get_free_page函数所获得的地址。相反,他能映射零内存页。
nopage函数可用于重新映射RAM。

直接执行I/O访问
直接I/O开销巨大,又没有使用缓存I/O的优势。无论如何在字符设备中执行直接I/O是不可行的。
只有确定设置缓冲I/O的开销特别巨大,才使用直接I/O。2.6内核的一个例子是SCSI磁带机驱动程序,因为磁带机传输通常是面向记录的。
通过下面的函数实现直接I/O:
int get_user_pages(struct task_struct *tsk, 
                   struct mm_struct *mm, 
                   unsigned long start,
                   int len, 
                   int write, 
                   int force, 
                   struct page **pages, 
                   struct vm_area_struct **vmas);

如果调用成功,调用这就会拥有一个指向用户空间缓冲区的页数组,它将被锁在内存中。为了能够直接操作缓冲区,内核空间代码必须用kmap函数将每个page结构指针转换成内核虚拟地址。
一旦直接I/O操作完成,就必须释放用户内存页。

直接内存访问(DMA)
the software asks for data (via a function such as read), the steps involved can be summarized as follows:

  1. When a process calls read, the driver method allocates a DMA buffer and instructs the hardware to transfer its data into that buffer. The process is put to sleep.

  2. The hardware writes data to the DMA buffer and raises an interrupt when it’s done.

  3. The interrupt handler gets the input data, acknowledges the interrupt, and awakens the process, which is now able to read data.

hardware asynchronously pushes data to the system,The steps are slightly different:

  1. The hardware raises an interrupt to announce that new data has arrived.

  2. The interrupt handler allocates a buffer and tells the hardware where to transfer its data.

  3. The peripheral device writes the data to the buffer and raises another interrupt when it’s done.

  4. The handler dispatches the new data, wakes any relevant process, and takes care of housekeeping.

高效的DMA处理依赖于中断报告!

分配的DMA缓冲区须使用线性物理内存区,用kmalloc及get_free_page函数,使用GFP_DMA标志。

关于DMA的更多操作和介绍参看LDD3的DMA部分。
下面两篇翻译则是转自陈莉君老师的博客
****************************************************************************************************************************************
Linux执行以及虚拟内存之用(Linux Execution and Virtual Memory Utilization 

When Linux boots, it starts with the MMU disabled, so initially it deals only with physical 
memory. The kernel image is copied to physical address 0×8000 in DRAM and executed. First a master page table is created and placed just before the kernel, which describes all available DRAM. The MMU is then switched on, mapping all of DRAM to virtual address 0xC0000000. The kernel reserves 64K of virtual memory for the interrupt handler code (usually at the very top of virtual memory), and sets up all the mappings for the hardware registers (UART, USB, LCD, GPIO, etc). Once kernel space is established and all drivers are initialized, the linux kernel moves on to establishing user space. This involves reading in the file system and actually executing processes.
linux内存及IO管理概述(转) - tauruspdj - tauruspdj的博客
当Linux启动时,是以MMU禁用来开启它的旅途的,因此,它起初只关注物理内存。内核映像(kernel image)被拷贝到DRAM的物理地址 0×8000处,并得到执行权。第一个主页表( master page table)从而诞生,并紧挨着内核映像存放,这个页表是对全部可用DRAM的描述。此后,MMU被启用,从而把全部DRAM映射到从0xC0000000开始的虚地址。内核为中断处理程序保留64K虚拟内存(通常在虚拟内存的顶端),然后为所有的硬件寄存器(UART, USB, LCD, GPIO, etc)建立映射。一旦内核空间建立起来,并且所有的驱动程序都被初始化,则Linux内核转向建立用户空间。这涉及到在文件系统中读取并实际执行进程。

Each process that runs on the system does so in its own memory “context”. Each context 
has its virtual memory space established and maintained by the kernel using a separate page table. So each process can “see” the entire user space, but its underlying physical memory is different from the other processes. Multiple processes and their contexts are run in time slices. This means that each process executes for a time, then is halted, and execution is passed to the next process in a rotating queue. The act of passing execution is called “context switching” and its frequency varies, depending on the kernel configuration and the current system load.

运行在系统中的每个进程都的确在它自己的内存“上下文”中执行。每个上下文都有自己的虚拟内存空间,这一空间的建立和维护是内核通过独立的页表进行的。因此,每个进程可以“看到”整个用户空间,但是,其对应的物理内存互不重叠。多个进程及其上下文以时间片轮流执行。这就意味着,每个进程执行一段时间,然后停下来,从而把执行权传递给轮流队列中的下一进程。传递执行权的行为就是所谓的“上下文切换”,传递的频率随内核的配置及当前负载而变化。

Each context includes the mappings for both user and kernel space because each new 
page table is initialized with the master page table contents. So, code executing in kernel space has full access to the entire memory map for the current process. Switching back and forth between kernel and user space execution does not require a page table swap.

每个上下文既包含用户空间的映射,也包含内核空间的映射,这是因为,每个新的页表都是用主页表内容进行初始化的。因此,对当前进程而言,在内核空间执行的代码具有对整个内存映射访问的权利。在内核空间和用户空间之间来回切换根本无需切换页表

When a process is executed, the kernel creates a new context, and maps just enough 
physical memory to copy the executable image into DRAM (starting at least a page above virtual address 0, because 0 is reserved for faults). The executable is broken into the same sections as the kernel: code and data (uninitialized global data, zero-initialized global data). Once the kernel schedules the process for execution, it sets the stack to grow down from the top of user space, and the heap to grow up from the end of the executable image. This way the chances of the two spaces colliding is minimized. Any dynamic libraries the process pulls in are placed at virtual address 0×40000000.

当一个进程执行时,内核创建新的上下文,并为把可执行映像拷贝到DRAM仅映射足够用的物理内存(从虚地址0之上的某个地址开始一个页,因为0地址留作存放出错信息)。可执行映像如内核一样也被划分为两部分:代码和数据(未初始化数据和初始化为0的全局数据)。一旦内核调度进程执行,它就设置栈和堆,前者从用户空间的顶部向下增长,后者从可执行映像的尾部向上增长。以这种方式,两个空间冲突的机会就大大减少。进程调用的动态库都存放在 0×40000000开始的地址处。

The kernel reserves virtual memory whenever a process or driver requests it, but it doesn’t 
actually map in the underlying physical memory until the memory is accessed (copy-on-write). So, the physical DRAM is used only when it absolutely has to be, and the kernel controls the allocation through a global memory management scheme. Ideally, the kernel tries to allocate contiguous blocks of physical memory as contiguous memory is most likely to evenly fill physically tagged caches. Thus, the kernel organizes memory to grow inward from the ends of physical memory, maximizing contiguous memory in the middle. The physical memory map is described in detail in the next section.

只要进程或驱动程序请求内存,内核就留出虚拟内存来,但,并不实际映射到真正的物理内存,直到要访问相应的内存时才进行映射(写时复制)。因此,只有在万不得已时才用到物理DRAM,内核通过全局内存管理模式控制分配。理想的情况下,内核尽量分配连续的物理块,这是因为连续内存很可能均匀地填充物理上紧挨的高速缓存。因此,内核对内存的组织采用从物理内存的末端向内方向增长,以图在中间最大化连续内存。物理内存的映射在下一部分将详细给予描述。

嵌入式Linux物理内存映射
**********************************************************************************************************************
The physical memory map for Linux is completely independent from the virtual map and is designed to maximize contiguous space. Given that the kernel image will always be at the start of DRAM, the Linux kernel maximizes contiguous space by allocating runtime memory from the end of physical DRAM moving downward.

The kernel starts by breaking available memory out into large, contiguous blocks (typically 4MB or more).It then maintains memory using the buddy system, where physical memory is always allocated in combinations of blocks of 2^n pages (where n is the order, that is, 4K is a 0 order block, 8K is a 1st order block, 16K is a 2nd order block, etc).

    Linux物理内存的映射完全独立于虚拟内存的映射,而且尽可能映射到连续的空间。假定内核映像总是位于DRAM的开始处,Linux内核尽可能让空间连续,这是通过从物理DRAM的末端向下移动来分配运行时内存而达到的。
   内核一开始把可用内存分割为大而连续的块(通常为4MB 或更多)。此后,内核利用伙伴算法来管理内存,这里,物理内存的分配总是2^n个页所形成块的组合(这里,n是幂,4k就是幂为0的块,8K是幂为1块,16K为幂为2的块,如此等等)
<img style=

分享到:
评论

相关推荐

    glibc内存管理ptmalloc源代码分析

    3.2 Ptmalloc内存管理概述 3.2.1 简介 3.2.2 内存管理的设计假设 3.2.3 内存管理数据结构概述 3.2.4 内存分配概述 3.2.5 内存回收概述 3.2.6 配置选项概述 3.2.7 使用注意事项 4. 问题分析及解决 5. 源代码...

    内存管理内存管理内存管理

    程序员可以使用的内存管理技术进行概述,虽然关注的重点是 C 语言,但同样也适用于其他语言。文中将为您提供如何管理内存的细节,然后将进一步展示如何手工管理内存,如何使用引用计数或者内存池来半手工地管理...

    深入分析Linux内核源码.chm

    6.1 Linux的内存管理概述 6.2 Linux内存管理的初始化 6.3 内存的分配和回收 6.4 地址映射机制 6.5 请页机制 6.6 交换机制 6.7 缓存和刷新机制 6.8 进程的创建和执行 第七章 进程间通信 7.1 管道 7.2 信号(signal) ...

    操作系统(内存管理)

    文将对 Linux™ 程序员可以使用的内存管理技术进行概述,虽然关注的重点是 C 语言,但同样也适用于其他语言。文中将为您提供如何管理内存的细节,然后将进一步展示如何手工管理内存,如何使用引用计数或者内存池来半...

    探讨Android内存管理

    在看这篇文章之前,需要Linux内存管理基础,推荐先学习一些Linux内存管理基础知识点,这里我们在做一些应用层的优化工作,但从底层理解一些原理性的知识点,对我们有较大帮助。 主要讨论四点内容: Android 内存管理...

    linux 内核源代码分析

    2.1 Linux内存管理的基本框架 2.2 地址映射的全过程 2.3 几个重要的数据结构和函数 2.4 越界访问 2.5 用户堆栈的扩展 2.6 物理页面的使用和周转 2.7 物理页面的分配 2.8 页面的定期换出 2. 9 页面的...

    LINUX原理与结构

    全书包括Linux概述、平台与工具、引导与初始化、中断处理、时钟管理、物理内存管理、进程管理、虚拟内存管理、互斥与同步、进程间通信、虚拟文件系统、物理文件系统、设备管理、电源管理等十四章,其主要内容已在...

    LINUX系统管理白皮书

    第4章 内存管理 222 4.1 何谓虚拟内存 222 4.2 创建交换空间 222 4.3 交换空间的使用 223 4.4 与其他操作系统共享交换空间 224 4.5 交换空间的分配 224 4.6 缓冲区 225 第5章 引导和关机 227 5.1 概论 227 ...

    Linux内核源代码情景分析 (上下册 高清非扫描 )

    2.1 Linux内存管理的基本框架 2.2 地址映射的全过程 2.3 几个重要的数据结构和函数 2.4 越界访问 2.5 用户堆栈的扩展 2.6 物理页面的使用和周转 2.7 物理页面的分配 2.8 页面的定期换出 2.9 页面的换入 2.10 内核...

    嵌入式Linux应用开发完全手册.pdf

    第7章 内存管理单元MMU 第8章 NANDFlash控制器 第9章 中断体系结构 第10章 系统时钟和定时器 第11章 通用异步收发器UART 第12章 I*IC接口 第13章 LCD控制器 第14章 ADC和触摸屏接口  第3篇 嵌入式...

    嵌入式Linux应用开发完全手册

    第7章 内存管理单元MMU 第8章 NAND Flash控制器 第9章 中断体系结构 第10章 系统时钟和定时器 第11章 通用异步收发器UART 第12章 I*IC接口 第13章 LCD控制器 第14章 ADC和触摸屏接口 第3篇 嵌入式Linux系统移植篇 ...

    Linux 基础课程课件

    第1章 Linux系统概述 1.1 计算机基础知识 1.2 操作系统的功能 1.3 Linux系统的历史、现状和特点 1.4 Linux系统安装 1.5 安装工具软件和开发软件 1.6 Linux图形环境 第2章 Linux常用命令 ... 5.4 内存管理

    Linux编程--Linux内核

    第2章 内存管理 15 2.1 虚拟内存抽象模型 15 2.1.1 请求调页 17 2.1.2 交换 17 2.1.3 共享虚拟内存 18 2.1.4 物理寻址模式和虚拟寻址模式 18 2.1.5 访问控制 18 2.2 高速缓存 19 2.3 Linux页表 20 2.4 页分配和回收 ...

    边干边学——LINUX内核指导

    7. 2 Linux虚拟内存管理 7. 3 实例 第8章 进程的同步 8. 1 同步机制 8. 2 Linux中几种同步机制的实现 8. 3 设计我们自己的同步机制 第9章 进程调度 9. 1 进程调度简介 9. 2 进程调度的策略与算法 9. 3 进程调度的...

    linux内核源代码情景分析

    2.1 Linux内存管理的基本框架 2.2 地址映射的全过程 2.3 几个重要的数据结构和函数 2.4 越界访问 2.5 用户堆栈的扩展 2.6 物理页面的使用和周转 2.7 物理页面的分配 2.8 页面的定期换出 2.9 页面的换入 ...

Global site tag (gtag.js) - Google Analytics