399 void mem_init(l start_mem, l end_mem)

400 {

401 int i;

402

403 high_memory = end_mem;

404 for (i=0 ; i405 mem_map[i] = used;

406 i = map_nr(start_mem);

407 end_mem -= start_mem;

408 end_mem >>= 12;

409 while (end_mem-->0)

410 mem_map[i++]=0;

411 }

其实前面所有的申请内存的程序里都最终使用了一个函数get_free_page(),不管申请多少的内存,最终还是要按页面来申请:

63 unsigned l get_free_page(void)

64 {

65 register unsigned l __res asm("ax");

66

67 __asm__("std ; repne ; scasb\n\t"

68 "jne 1f\n\t"

69 "movb ,1(%%edi)\n\t"

70 "sall ,%%ecx\n\t"

71 "addl %2,%%ecx\n\t"

72 "movl %%ecx,%%edx\n\t"

73 "movl 24,%%ecx\n\t"

74 "leal 4092(%%edx),%%edi\n\t"

75 "rep ; stosl\n\t"

76 "movl %%edx,%%eax\n"

77 "1:"

78 :"=a" (__res)

79 :"" (0),"i" (low_mem),"c" (paging_pages),

80 "d" (mem_map+paging_pages-1)

81 :"di","cx","dx");

82 return __res;

83 }

这个函数就是在物理内存中找一张没有使用的页面并返回其物理地址。这是一段gcc内联汇编,它在mem_map数组中的最后一项一直向前找,只要找一项的值不为0,则用这个数组下标计算出物理地址返回,并把那一项的值设为1。用下标计算物理地址的方法我想是这样的:index*4096+low_men (std;repne;scasb,这三句是依次检查mem_map里的每一项的值,如果全部不为0,也即没有物理内存可以用,立即返回0。movb ,1(%%edi)这句就是把mem_map数组里找到的可用的一项的标志设为1。此时ecx里的值就是数组下标,因此sall ,%%ecx就是index*4096,addl %2,%%ecx即把刚才的index*4096+low_mem。73,74,5三句是把相应的物理内存空间内容全部清0。movl %%edx,%%eax显然是返回值了)。

有件事一定要做,那就是在返回之前把那个物理页面的内容全部清0。清0的事情让get_free_page做了,回收就简单了,只要把mem_map数组的相应项置为0就可以了,从下面可以看出来,free_page确实只做了这件事:

89 void free_page(unsigned l addr)

90 {

91 if (addr < low_mem) return;

92 if (addr >= high_memory)

93 panic("trying to free n page");

94 addr -= low_mem;

95 addr >>= 12;

96 if (mem_map[addr]--) return;

97 mem_map[addr]=0;

98 panic("trying to free free page");

99 }

进程退出时,会调用sys_exit,sys_exit只是调用了一下do_exit,回收内存的工作就在这里完成的。

106 free_page_tables(get_base(current->ldt[1]),get_limit(0x0f));

107 free_page_tables(get_base(current->ldt[2]),get_limit(0x17));

free_page_tables释放进程的代码段和数据段占用的内存,它内部使用循环,调用free_page完成最终的工作。

由于新进程和其父进程共享物理内存页面,因此把这些物理页面重新都设成只读是必要的。上面这句是放在copy_page_tables函数里面的循环中的。copy_mem主要是靠调用这个程序来完成工作的。分析到这里,我终于可以小舒一口气了。不如回顾一下:系统初始化的时候在内存起始处建一张页目录(page_dir),以后所有的进程都使用这张页目录。并为系统建了4张页表。以后每有新进程产生,便为之分配空间存放pcb(即struct task_struct),然后为之通过复制父进程的页表来创建自己的页表,并创建相应的页目录项。

程序运行了,问题又来了。终于读到了“写时复制”和请求调页的部分。当程序访问的线性地址没有被映射到一个物理页面,或欲写操作的线性地址映射的物理页面仅是只读,都会产生一个页异常,然后就会转去页异常中断处理程序(int 14)执行,页异常中断处理程序(page.s)如下:

14 _page_fault:

15 xchgl %eax,(%esp)

16 pushl %ecx

17 pushl %edx

18 push %ds

19 push %es

20 push %fs

21 movl x10,%edx

22 mov %dx,%ds

23 mov %dx,%es

24 mov %dx,%fs

25 movl %cr2,%edx

26 pushl %edx

27 pushl %eax

28 testl ,%eax

29 jne 1f

30 call _do_no_page

31 jmp 2f

32 1: call _do_wp_page

33 2: addl ,%esp

34 pop %fs

35 pop %es

36 pop %ds

37 popl %edx

38 popl %ecx

39 popl %eax

40 iret

根据error_code判断是缺页还是写保护引起的异常,然后去执行相应的处理程序段,先看写保护的处理吧。

247 void do_wp_page(unsigned l error_code,unsigned l address)

248 {

249 #if 0

250 /* we cannot do this yet: the estdio library writes to code space */

251 /* stupid, stupid. i really want the libc.a from gnu */

252 if (code_space(address))

253 do_exit(sigsegv);

254 #endif

255 un_wp_page((unsigned l *)

256 (((address>>10) & 0xffc) + (0xfffff000 &

257 *((unsigned l *) ((address>>20) &0xffc)))));

258

259 }

程序就一个函数调用,很少有这么简单的函数,哈哈!address很显然是程序想要访问但引起出错的线性地址了。(0xfffff000&*((unsigned l *)((address>>20)&0xffc))计算出32位线性地址对应页表的地址,再加上一个((address>>10) & 0xffc),就是加上页表内的偏移量,即得到页表内的一个页表项。看un_wp_page()就更明白了。

221 void un_wp_page(unsigned l * table_entry)

222 {

223 unsigned l old_page,new_page;

224

225 old_page = 0xfffff000 & *table_entry;

226 if (old_page >= low_mem && mem_map[map_nr(old_page)]==1) {

227 *table_entry |= 2;

228 invalidate();

229 return;

230 }

231 &nb

232 oom();

233 if (old_page >= low_mem)

234 mem_map[map_nr(old_page)]--;

235 *table_entry = new_page | 7;

236 invalidate();

237 copy_page(old_page,new_page);

238 }

225-229做了个判断,如果此物理页面没有被共享,则只要将可写位置1(227)。不然就进入231行去。

在物理内存中分配一页空间,把原页面的内容copy到新页面里(copy_page),再把那个引起出错的address映射到这个新页面的物理地址上去(235行)。至此,写保护出错的处理完成了,可以返回去执行原进程里引起出错的那条指令了。

上面所述,就是所谓的“写时复制(copy on write)”。如果是缺页异常的话,则执行do_no_page,最简单的办法就是直接申请一张物理页面,对应到这个引起出错的address,如下:

372 address &= 0xfffff000;

373 tmp = address - current->start_code;

374 if (!current->executable || tmp >= current->end_data) {

375 get_empty_page(address);

376 return;

377 }

如果这样了之,那也太不负责任了,只是在!current->executable || tmp >= current->end_data的情况下,才这样做。这是怎样的情况呢?!current->executable有待阅读,tmp >= current->end_data很简单,在程序体已全部读入内存后,这可能是动态内存分配所要求的内存空间。否则就尝试去和别的进程共享一下,如下:

378 if (share_page(tmp))

379 return;

如果共享不成,那也只好自己申请一张页面了,如下:

380 if (!(page = get_free_page()))

381 oom();

Linux创建页表内存代码,Linux内存管理的源码实现相关推荐

  1. 【Linux 内核 内存管理】虚拟地址空间布局架构 ② ( 用户虚拟地址空间组成 | 内存描述符 mm_struct 结构体源码 )

    文章目录 一.用户虚拟地址空间组成 二.内存描述符 mm_struct 结构体源码 一.用户虚拟地址空间组成 " 用户虚拟地址空间 " 包括以下区域 : ① 代码段 ② 数据段 ③ ...

  2. Linux虚拟化KVM-Qemu分析(三)之KVM源码(1)kvm_init

    Table of Contents 1. 概述 2. KVM初始化 2.1 kvm_arch_init 2.1.1 init_hyp_mode 2.1.2 init_subsystems 2.2 mi ...

  3. c语言比较函数memcmp,c语言函数memcmp()如何比较内存前n个字节实例源码介绍

    c语言函数memcmp()如何比较内存前n个字节实例源码介绍.引入头文件:#include 定义memcmp()函数:int memcmp (const void *s1, const void *s ...

  4. Eatting外卖基于瑞吉外卖代码全功能优化含源码

    Eatting外卖基于瑞吉外卖代码全功能优化含源码 文章目录 项目的最终部署 源码地址 github:[源码地址](https://github.com/yangxingyue0623/Eating_ ...

  5. Qt物联网综合管理平台源码

    Qt物联网综合管理平台源码 0.2.1 软件模块 设备监控模块,包括数据监控(表格形式展示).设备面板(面板形式展示).地图监控(地图形式展示).曲线监控(曲线形式展示). 数据查询模块,包括报警记录 ...

  6. 我的CSDN笔记总索引(阅读量降序,代码自动遍历生成HTML5源码)

    Python代码用"命令容器"方法os.system(),调用Linux命令行工具crul获取CSDN博文页面源码,Python内置re正则解析出博文笔记信息,按阅读量降序模块输出 ...

  7. Qt编写安防楼宇对讲管理平台源码

    Qt编写安防楼宇对讲管理平台源码 同时集成了楼宇对讲.住户报警.门禁控制.公共报警.视频监控等模块. 系统管理部分包括系统配置.对讲配置.住户配置.公共配置.监控配置.地图管理.视频联动.用户管理.区 ...

  8. 单文件图片管理php,PHP照片图片管理器源码,单文件PHP照片/图片文件管理源码FileManager...

    PHP照片图片管理器源码,单文件PHP照片/图片文件管理源码FileManager. 如果你有NAS,想要远程管理自己的照片,那本文非常适合你.只有一个单文件PHP管理你自己的照片,还可以设置个性登录 ...

  9. react相关代码库以及框架的源码解析

    持续更新中react相关库源码浅析, react ts3 项目 ???对react相关代码库以及框架的源码进行了一定的分析 ?react16.6 View contents 源码实例分析:可见runl ...

  10. 企业网上下单订货管理软件源码搭建功能介绍|移讯云订货通订单管理系统

    网上下单订货管理软件源码搭建功能介绍|移讯云订货通订单管理系统 一:系统概述和用途 系统基于网络,实现厂家和代理商批发商通过网络下单订货功能. 什么是移讯云订货通.什么是企业订货管理系统. 是一款针对 ...

最新文章

  1. Linux 开机网络无法自动连接配置、网络开机自动连接
  2. 【深度学习】深度学习的三个主要步骤!
  3. new Date('2014/04/30') 和 new Date('2014-04-30') 的区别
  4. Nacos源码ServiceManager
  5. 深入浅出SQL(2)——select、update…
  6. 第一次作业-李纯锐201731084433
  7. python面试100讲
  8. mac上安装webpack报错解决方法Hit error EACCES: permission denied, mkdir ‘/usr/local/lib/node_modules/webpack
  9. Sql Server系列:数据表操作
  10. paypal java sdk_PayPal-Java SDK /信用卡付款问题
  11. mysql proxy maxscale_MaxScale: 一个用于解决MySQL扩展性的新工具(译)
  12. 深海迷航坐标传送代码_深海迷航全控制台代码一览
  13. 从word中提取图片的三种方法
  14. ios游戏源码下载网站推荐
  15. JavaScript 时间范围
  16. 大数据时代,数据实时同步解决方案的思考—最全的数据同步总结
  17. 9月第2周业务风控关注 |国家计算机病毒应急处理中心:这十款App存在危险行为代码
  18. 为什么判断一个数X是不是素数时只需判断其能否在2~根号X被整除即可?
  19. c语言e怎么表示_如何一个月学完c语言
  20. 每日简报 5月11日简报新鲜事 每天一分钟 了解新鲜事

热门文章

  1. win10无法装载iso文件_win10镜像文件不能安装怎么办?win10镜像文件无法安装的解决教程...
  2. python解释器有多大_python解释器到底是什么?
  3. 安卓平台OpenGL ES的调用
  4. 如何写出一个较好的快速排序程序
  5. C++:求区间内最大的因子和
  6. UE4 iOS设备的兼容性
  7. 书籍:Python游戏开发 Game Development Using Python - 2019.pdf
  8. Web开发秘方(WEB DEVELOPMENT RECIPES)[47.5MB] PDF扫描版
  9. 不会点SQLite,都不好意思说自己是开发的
  10. 英特尔吃iPhone 7 Modem芯片订单可能逾5成 挹注15亿美元营收