文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

操作系统就用一张大表管理内存?

2024-12-02 11:02

关注

今天我们不聊具体内存管理的算法,我们就来看看,操作系统用什么样的一张表,达到了管理内存的效果。

我们以 Linux 0.11 源码为例,发现进入内核的 main 函数后不久,有这样一坨代码。

  1. void main(void) { 
  2.     ... 
  3.     memory_end = (1<<20) + (EXT_MEM_K<<10); 
  4.     memory_end &= 0xfffff000; 
  5.     if (memory_end > 16*1024*1024) 
  6.         memory_end = 16*1024*1024; 
  7.     if (memory_end > 12*1024*1024)  
  8.         buffer_memory_end = 4*1024*1024; 
  9.     else if (memory_end > 6*1024*1024) 
  10.         buffer_memory_end = 2*1024*1024; 
  11.     else 
  12.         buffer_memory_end = 1*1024*1024; 
  13.     main_memory_start = buffer_memory_end; 
  14.  
  15.     mem_init(main_memory_start,memory_end); 
  16.     ... 

除了最后一行外,前面的那一大坨的作用很简单。

其实就只是针对不同的内存大小,设置不同的边界值罢了,为了理解它,我们完全没必要考虑这么周全,就假设总内存一共就 8M 大小吧。

那么如果内存为 8M 大小,memory_end 就是

8 * 1024 * 1024

也就只会走倒数第二个分支,那么 buffer_memory_end 就为

2 * 1024 * 1024

那么 main_memory_start 也为

2 * 1024 * 1024

你仔细看看代码逻辑,看是不是这样?

当然,你不愿意细想也没关系,上述代码执行后,就是如下效果而已。

你看,其实就是定了三个箭头所指向的地址的三个边界变量。具体主内存区是如何管理和分配的,要看 mem_init 里做了什么。

  1. void main(void) { 
  2.     ... 
  3.     mem_init(main_memory_start, memory_end); 
  4.     ... 

而缓冲区是如何管理和分配的,就要看再后面的 buffer_init 里干了什么。

  1. void main(void) { 
  2.     ... 
  3.     buffer_init(buffer_memory_end); 
  4.     ... 

不过我们今天只看,主内存是如何管理的,很简单,放轻松。

进入 mem_init 函数。

  1. #define LOW_MEM 0x100000 
  2. #define PAGING_MEMORY (15*1024*1024) 
  3. #define PAGING_PAGES (PAGING_MEMORY>>12) 
  4. #define MAP_NR(addr) (((addr)-LOW_MEM)>>12) 
  5. #define USED 100 
  6.  
  7. static long HIGH_MEMORY = 0; 
  8. static unsigned char mem_map[PAGING_PAGES] = { 0, }; 
  9.  
  10. // start_mem = 2 * 1024 * 1024 
  11. // end_mem = 8 * 1024 * 1024 
  12. void mem_init(long start_mem, long end_mem) 
  13.     int i; 
  14.     HIGH_MEMORY = end_mem; 
  15.     for (i=0 ; i
  16.         mem_map[i] = USED; 
  17.     i = MAP_NR(start_mem); 
  18.     end_mem -= start_mem; 
  19.     end_mem >>= 12; 
  20.     while (end_mem-->0) 
  21.         mem_map[i++]=0; 

发现也没几行,而且并没有更深的方法调用,看来是个好欺负的方法。

仔细一看这个方法,其实折腾来折腾去,就是给一个 mem_map 数组的各个位置上赋了值,而且显示全部赋值为 USED 也就是 100,然后对其中一部分又赋值为了 0。

赋值为 100 的部分就是 USED,也就表示内存被占用,如果再具体说是占用了 100 次,这个之后再说。剩下赋值为 0 的部分就表示未被使用,也即使用次数为零。

是不是很简单?就是准备了一个表,记录了哪些内存被占用了,哪些内存没被占用。这就是所谓的“管理”,并没有那么神乎其神。

那接下来自然有两个问题,每个元素表示占用和未占用,这个表示的范围是多大?初始化时哪些地方是占用的,哪些地方又是未占用的?

还是一张图就看明白了,我们仍然假设内存总共只有 8M。

可以看出,初始化完成后,其实就是 mem_map 这个数组的每个元素都代表一个 4K 内存是否空闲(准确说是使用次数)。

4K 内存通常叫做 1 页内存,而这种管理方式叫分页管理,就是把内存分成一页一页(4K)的单位去管理。

1M 以下的内存这个数组干脆没有记录,这里的内存是无需管理的,或者换个说法是无权管理的,也就是没有权利申请和释放,因为这个区域是内核代码所在的地方,不能被“污染”。

1M 到 2M 这个区间是缓冲区,2M 是缓冲区的末端,缓冲区的开始在哪里之后再说,这些地方不是主内存区域,因此直接标记为 USED,产生的效果就是无法再被分配了。

2M 以上的空间是主内存区域,而主内存目前没有任何程序申请,所以初始化时统统都是零,未来等着应用程序去申请和释放这里的内存资源。

那应用程序如何申请内存呢?我们本讲不展开,不过我们简单展望一下,看看申请内存的过程中,是如何使用 mem_map 这个结构的。

在 memory.c 文件中有个函数 get_free_page(),用于在主内存区中申请一页空闲内存页,并返回物理内存页的起始地址。

比如我们在 fork 子进程的时候,会调用 copy_process 函数来复制进程的结构信息,其中有一个步骤就是要申请一页内存,用于存放进程结构信息 task_struct。

  1. int copy_process(...) { 
  2.     struct task_struct *p; 
  3.     ... 
  4.     p = (struct task_struct *) get_free_page(); 
  5.     ... 

我们看 get_free_page 的具体实现,是内联汇编代码,看不懂不要紧,注意它里面就有 mem_map 结构的使用。

  1. unsigned long get_free_page(void) { 
  2.     register unsigned long __res asm("ax"); 
  3.     __asm__( 
  4.         "std ; repne ; scasb\n\t" 
  5.         "jne 1f\n\t" 
  6.         "movb $1,1(%%edi)\n\t" 
  7.         "sall $12,%%ecx\n\t" 
  8.         "addl %2,%%ecx\n\t" 
  9.         "movl %%ecx,%%edx\n\t" 
  10.         "movl $1024,%%ecx\n\t" 
  11.         "leal 4092(%%edx),%%edi\n\t" 
  12.         "rep ; stosl\n\t" 
  13.         "movl %%edx,%%eax\n" 
  14.         "1:" 
  15.         :"=a" (__res) 
  16.         :"0" (0),"i" (LOW_MEM),"c" (PAGING_PAGES), 
  17.         "D" (mem_map + PAGING_PAGES-1) 
  18.         :"di","cx","dx"); 
  19.     return __res; 

就是选择 mem_map 中首个空闲页面,并标记为已使用。

好了,本讲就这么多,只是填写了一张大表而已,简单吧?之后的内存申请与释放等骚操作,统统是跟着张大表 mem_map 打交道而已,你一定要记住它哦。

 

来源: 低并发编程内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯