今天我们不讲具体的内存管理算法,而是来看看操作系统是用什么样的表来达到内存管理的效果。以Linux0.11的源代码为例,我们发现在进入内核的main函数后不久,就有这么一堆代码。voidmain(void){...memory_end=(1<<20)+(EXT_MEM_K<<10);memory_end&=0xfffff000;if(memory_end>16*1024*1024)memory_end=16*1024*1024;if(memory_end>12*1024*1024)buffer_memory_end=4*1024*1024;elseif(memory_end>6*1024*1024)buffer_memory_end=2*1024*1024;elsebuffer_memory_end=1*1024*1024;;...}除了最后一行,前面的大疙瘩很简单。其实就是针对不同的内存大小设置不同的边界值而已。为了理解它,我们不需要考虑得那么透彻。假设总内存为8M。那么如果内存大小是8M,memory_end是8*1024*1024,只会走到倒数第二个分支,那么buffer_memory_end是2*1024*1024,那么main_memory_start也是2*1024*1024。仔细看代码逻辑,看看是不是这样?当然,如果你不想去想也没关系。上面的代码执行后,会产生如下效果。你看,其实是三个边界变量定义了三个箭头指向的地址。具体的主内存区域如何管理和分配取决于mem_init中做了什么。voidmain(void){...mem_init(main_memory_start,memory_end);...}如何管理和分配缓冲区取决于后面的buffer_init中做了什么。voidmain(void){...buffer_init(buffer_memory_end);...}不过今天我们只看主内存是怎么管理的,很简单,放轻松。进入mem_init函数。#defineLOW_MEM0x100000#definePAGING_MEMORY(15*1024*1024)#definePAGING_PAGES(PAGING_MEMORY>>12)#defineMAP_NR(addr)(((addr)-LOW_MEM)>>12)#defineUSED100staticlongHIGH_MEMORY=0;charstatic_map[;//start_mem=2*1024*1024//end_mem=8*1024*1024voidmem_init(longstart_mem,longend_mem){inti;HIGH_MEMORY=end_mem;for(i=0;i>=12;while(end_mem-->0)mem_map[i++]=0;}找到的行不多,也没有更深入的方法调用,它看来这是欺负人的好方法。仔细看这个方法,其实折腾折腾就是给一个mem_map数组的每个位置赋值,而且显示全部赋值都是USED,也就是100,然后有的赋值为0。赋值100的部分为USED,表示内存被占用。如果更具体的占用100次,这个以后再说。其余部分赋值0表示未使用,即使用次数为零。是不是很简单?就是准备一张表,记录哪些内存被占用,哪些内存没有被占用。这就是所谓的“管理”,并没有那么神奇。那么自然有两个问题。每个元素代表占用和未占用。该代表的范围是什么?初始化时哪些地方被占用,哪些地方没有被占用?还是看一张图就明白了。我们仍然假设总内存只有8M。可以看到,初始化完成后,mem_map数组的每个元素代表一块4K内存是否空闲(准确的说是使用次数)。4K内存通常被称为1-page内存,这种管理方式称为分页管理,就是将内存分成一个页面一个页面(4K)为单位进行管理。1M以下的内存数组根本不记录。这里的内存不需要管理,或者说是没有管理权,也就是没有申请和释放的权利,因为这个区域是内核代码所在的地方,不能被“污染”“。1M到2M的范围是缓冲区,2M是缓冲区的末尾。稍后我们将讨论缓冲区从哪里开始。这些地方不是主内存区,所以直接标记为USED,效果就是不能再分配了。2M以上的空间是主存区,目前没有程序申请主存,所以初始化时全为0,等待应用程序在这里申请和释放内存资源将来。应用程序如何申请内存?我们先不说,先简单期待一下mem_map结构在申请内存的过程中是如何使用的。memory.c文件中有一个函数get_free_page(),用于在主内存区申请一个空闲内存页,并返回物理内存页的起始地址。比如我们fork一个子进程的时候,会调用copy_process函数来复制进程的结构信息。其中一个步骤是申请一页内存来存放进程结构信息task_struct。intcopy_process(...){structtask_struct*p;...p=(structtask_struct*)get_free_page();...}我们看一下get_free_page的具体实现,是行内汇编代码,你会不会看不懂,关注一下里面有mem_map结构的使用。unsignedlongget_free_page(void){registerunsignedlong__resasm("ax");__asm__("std;repne;scasb\n\t""jne1f\n\t""movb$1,1(%%edi)\n\t""sall$12,%%ecx\n\t""addl%2,%%ecx\n\t""movl%%ecx,%%edx\n\t""movl$1024,%%ecx\n\t""leal4092(%%edx),%%edi\n\t""rep;stosl\n\t""movl%%edx,%%eax\n""1:":"=a"(__res):"0"(0),"i"(LOW_MEM),"c"(PAGING_PAGES),"D"(mem_map+PAGING_PAGES-1):"di","cx","dx");return__res;}是选择mem_map第一个空闲页面被标记为已使用。好了,本讲就这些了,填个大表就行了,简单吧?后面的内存申请和释放操作等等,都是在和大表mem_map打交道,一定要记住。