开发者社区> nothingfinal> 正文
阿里云
为了无法计算的价值
打开APP
阿里云APP内打开

0.11 内核 在内核态上如何实现‘写时复制’ verify_area write_verify

简介: 如果大家学习了内核中进程的部分就会听说过‘写时复制’ 。 但是从来没有想过在用户态和内核态上‘写时复制’判断的前提条件都不一样。 在进程创建之初,父子进程的数据段和代码段共享并且设置为只读,直到他们之一要将代码和数据段进行修改时才会进行复制即写时复制。
+关注继续查看
如果大家学习了内核中进程的部分就会听说过‘写时复制’ 。
但是从来没有想过在用户态和内核态上‘写时复制’判断的前提条件都不一样。

在进程创建之初,父子进程的数据段和代码段共享并且设置为只读,直到他们之一要将代码和数据段进行修改时才会进行复制即写时复制。但是,这种判断条件只能用于用户态,因为8086cpu, 在执行特权0代码时不会理会用户空间中页面是否为有保护,用户空间中数据页面保护标志不起任何作用的。这样将违背了进程的独立性。
用户态的写时复制:
          在对页面进程修改时会受到用户空间页面标志的影响。在用户态上的写时复制是由硬件支持的,写时当你把页表项是的属性设为只读的话,如果对页表所指向的这段内存空间执行了写操作(具本说就是写数据的指令,比如说 mov ) ,CPU 就会自动发现,然后进行一个陷阱中,去执行你事先设定好的处理程序,在这个处理程序中你自己把数据拷一份给写数据的进程,给这个进程分配真正的物理空间, 然后再改页表,让内存可写,这时候重新执行这条写指令就行了~~~~~~~~这里纯粹是硬件的机制问题,只是软件利用了这种机制而已

内核态的写时复制:
        为了保证进程的独立性,在内核态时,需要执行写前检测。[(verify_area(void &*addr, int size)) ],由于在实行写前验证是通过调用write_verify() 实现的,而对于该函数是以页为单位的,所以在verify_area需要得到addr所在的页面的首地址。

  页表项:
             页框地址   |     可见 |  0 0 | D | A  | 0 0 | u/s | r/w |p
             31--------12                                 
  p : 是否在内存中
  r/w 表示可读/可写
  verify_area:
  1. verify_area(void * addr, int size)
  2. {
  3. unsigned long start = (unsigned long)addr; #对于页面来讲是以4k为单位的
  4. size+= start&0xfff;   #获取addr的后12位,即业内偏移,再加上大小.
  5. start& = 0xfffff000;#获得页面的首地址
  6. start+=get_base(current->ldt[2]);#get_base()获得描述符中的线性基址,start加上之后变成线性地址  
  7. while(size>0)  
  8. {
  9.   size-=4096;
  10.   write_verify(start);
  11.   start+=4096;
  12. }
 write_verify:
  
  1. void write_verify(unsigned long address)  
  2. {
  3.      unsigned long page;
  4.      /*判断指定地址所对应的页面目录项的页表是否存在*/
  5.       if (!( (page = *((unsigned long *) ((address>>20) & 0xffc)) )&1))
  6.              return;
  7.       page &= 0xfffff000;
  8.       page += ((address>>10) & 0xffc);/*页号,page此时表示的是页表的地址*/
  9.       if ((3 & *(unsigned long *) page) == 1) /* non-writeable, present 页面不可写*/
  10.             un_wp_page((unsigned long *) page);//写实复制
  11.      return;
  12. }
  在该函数中传递的参数地址是线性地址,而且在linux OS中使用的页机制,因此我们需要从线性地址中获得页表 的索引值。而线性地址是针对4G空间,而每个页面为4k , 则可以有2^20个页面则:(对于页机制的线性地址分为: 页号(20位) 和 页内偏移量(12)构成
                        xxx...xx.xxx     yy.....yy
                             20                  12
      因为页面有2^20个,而且每个页表项是4k,则需要 2^22k空间。如果将所有的页表项存储在一起十分麻烦,为了解决该问题,可以将表项进行分页存储-----》多级页表。在80386以上,intel都采用两级页表。
     32位被分为三部分。
               Directory(目录)   最高10位
               Table      (页表)   中间10位
               Offset      (偏移量)  最低12位
  1. //将描述符的基址存储于edx中,在地址描述符中关于基址的存储有3部分,24-31, 16-23,0-1
  2. #define _get_base(addr) ({\
  3. unsigned long __base; \
  4. __asm__("movb %3,%%dh\n\t" \//24-31
  5. "movb %2,%%dl\n\t" \
  6. "shll $16,%%edx\n\t" \ /* 基地址高16 位移到edx 中高16 位处。*/
  7. "movw %1,%%dx" \
  8. :"=d" (__base) \
  9. :"m" (*((addr)+2)), \
  10. "m" (*((addr)+4)), \
  11. "m" (*((addr)+7))); \
  12. __base;})
分析:对于段描述符来讲,其实将基地址分成了4部分。
                       7              6                 5                 4                 3                  2                  1              0
字节7                                                             段基址31-24
字节6              G             X                 0               AVX                                    段长19-16
字节5              P              DPL            S                                 段类型和保护                                      A                  字节4                                                              段基址23-16
字节3                                                              段基址15-8
字节2                                                              段基址7-0
字节1                                                              段长 15-8  
字节0                                                              段长 7-0

其中:(addr)+7 表示的意思是: addr 表示描述的首地址,(addr)+7 表示以addr为基址,向高位偏移7个字节。其他的同理。

注: dx 16位  edx 32位
set_base与之相对应,将基址赋值给对应的描述符。
  1. //将地址描述符的基址存储于edx中,对于描述符的基址分为了3部分,
  2. #define _set_base(addr,base) \
  3. __asm__("movw %%dx,%0\n\t" \
  4. "rorl $16,%%edx\n\t" \
  5. "movb %%dl,%1\n\t" \
  6. "movb %%dh,%2" \
  7. ::"m" (*((addr)+2)), \
  8. "m" (*((addr)+4)), \
  9. "m" (*((addr)+7)), \
  10. "d" (base) \
  11. :"dx")



版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
SAP ABAP 写时拷贝(Copy on Write)策略的一个具体例子
SAP ABAP 写时拷贝(Copy on Write)策略的一个具体例子
5 0
Linux【入门篇】—— 概述、虚拟机创建、环境配置、vmtools的安装使用、共享文件夹的使用(2)
Linux【入门篇】—— 概述、虚拟机创建、环境配置、vmtools的安装使用、共享文件夹的使用(2)
51 0
Linux【入门篇】—— 概述、虚拟机创建、环境配置、vmtools的安装使用、共享文件夹的使用
Linux【入门篇】—— 概述、虚拟机创建、环境配置、vmtools的安装使用、共享文件夹的使用
42 0
AIX 6.1 Oracle11g 11.2.0.4 RAC 安装心得
在RAC安装过程中涉及多个用户,多种工具,这里列出常见命令提示符: #  UNIX的SHELL提示符,表示root用户的登录 $  UNIX的SHELL提示符,表示oracl用户或grid用户的登录 安装RAC是一个比较耗时,并且容易出错的过程。
1006 0
AIX 6.1 Oracle11g 11.2.0.4 RAC 安装心得
在RAC安装过程中涉及多个用户,多种工具,这里列出常见命令提示符: #  UNIX的SHELL提示符,表示root用户的登录 $  UNIX的SHELL提示符,表示oracl用户或grid用户的登录 安装RAC是一个比较耗时,并且容易出错的过程。
864 0
apache2.2.x和tomcat6.0.32集群并实现session复制功能
我测试是在windows7上测测试的, 需要准备的软件列表如: a. Apache2.2b. apache-tomcat-6.0.32-1我要集群的第一台tomcat服务器 c.
984 0
+关注
nothingfinal
软件开发,安全加密
1068
文章
341
问答
文章排行榜
最热
最新
相关电子书
更多
低代码开发师(初级)实战教程
立即下载
阿里巴巴DevOps 最佳实践手册
立即下载
冬季实战营第三期:MySQL数据库进阶实战
立即下载