男和男做那个视频网站,wordpress怎么加菜单,设计师网址导航官网入口,安全联盟可信网站认证作者 | 闪客来源 | 低并发编程这里讲的是 Linux 内核里的写时复制原理。写时复制的原理网上讲述的文章很多#xff0c;今天来一篇很直接的文章#xff0c;通过看看 Linux 0.11 这个最简单的操作系统#xff0c;从源码层面把写时复制的原理搞清楚。很简单哦#xff0c;你可… 作者 | 闪客来源 | 低并发编程这里讲的是 Linux 内核里的写时复制原理。写时复制的原理网上讲述的文章很多今天来一篇很直接的文章通过看看 Linux 0.11 这个最简单的操作系统从源码层面把写时复制的原理搞清楚。很简单哦你可别中途就放弃了。直接干哦不行干之前先来点储备知识如果你已经有了这一 pa 可以略过不过我估计你没有...储备知识坚持看完这部分写时复制用到的这里的知识点只有其中一个位的值而已但我把周边也给你讲讲。32 位模式下Intel 设计了页目录表和页表两种结构用来给程序员们提供分页机制。在 Intel Volume-3 Chapter 4.3 Figure 4-4 中给出了页表和页目录表的数据结构PDE 就是页目录表PTE 就是页表。大部分的操作系统使用的都是 4KB 的页框大小Linux 0.11 也是所以我们只看 4KB 页大小时的情况即可。一个由程序员给出的逻辑地址要先经过分段机制的转化变成线性地址再经过分页机制的转化变成物理地址。Figure 4-2 给出了线性地址到物理地址也就是分页机制的转化过程。这里的 PDE 就是页目录表PTE 就是页表刚刚说过了。在手册接下来的 Table 4-5 和 Table 4-6 中详细解释了页目录表和页表数据结构各字段的含义。Table 4-5 是页目录表。Table 4-6 是页表。他们几乎都是一样的含义我们就只看页表就好了看一些比较重要的位。31:12 表示页的起始物理地址加上线性地址的后 12 位偏移地址就构成了最终要访问的内存的物理地址这个就不说了。第 0 位是 P表示 Present存在位。第 1 位是 RW表示读写权限0 表示只读那么此时往这个页表示的内存范围内写数据则不允许。第 2 位是 US表示用户态还是内核态0 表示内核态那么此时用户态的程序往这个内存范围内写数据则不允许。在 Linux 0.11 的 head.s 里初次为页表设置的值如下。setup_paging:...movl $pg07,_pg_dir /* set present bit/user r/w */movl $pg17,_pg_dir4 /* --------- --------- */movl $pg27,_pg_dir8 /* --------- --------- */movl $pg37,_pg_dir12 /* --------- --------- */movl $pg34092,%edimovl $0xfff007,%eax /* 16Mb - 4096 7 (r/w user,p) */std
1: stosl...后三位是 7用二进制表示就是 111即初始设置的 4 个页目录表和 1024 个页表都是存在1可读写1用户态1好了储备知识就到这里。如果你前面没读懂你只需要知道页表当中有一位是表示读\写的而 Linux 0.11 初始化时把它设置为了 1表示可读写。写时复制的本质在调用 fork() 生成新进程时新进程与原进程会共享同一内存区。只有当其中一个进程进行写操作时系统才会为其另外分配内存页面。不过我们考虑写时复制并不用这么复杂去掉些细节就是。原来的进程通过自己的页表占用了一定范围的物理内存空间。调用 fork 创建新进程时原本页表和物理地址空间里的内容都要进行复制因为进程的内存空间是要隔离的嘛。但 fork 函数认为复制物理地址空间里的内容比较费时所以姑且先只复制页表物理地址空间的内容先不复制。如果只有读操作那就完全没有影响复不复制物理地址空间里的内容就无所谓了这就很赚。但如果有写操作那就不得不把物理地址空间里的值复制一份保证进程间的内存隔离。有写操作时再复制物理内存就叫写时复制。看看代码咋写的有上述的现象必然是在 fork 时对页表做了手脚这回知道为啥储备知识里讲页表结构了吧 同时只要有写操作就会触发写时复制这个逻辑这是咋做到的呢答案是通过中断具体是缺页中断。好的首先来看 fork。这里只看其中关键的复制页表的代码。int copy_page_tables(...) {...// 源页表和新页表一样this_page *from_page_table;...// 源页表和新页表均置为只读this_page ~2;*from_page_table this_page;...
}还记得知识储备当中的页表结构吧就是把 R/W 位置 0 了。用刚刚的 fork 图表示就是。那么此时再次对这块物理地址空间进行写操作时就不允许了。但不允许并不是真的不允许Intel 会触发一个缺页中断具体是 0x14 号中断中断处理程序里边怎么处理那就由 Linux 源码自由发挥了。Linux 0.11 的缺页中断处理函数的开头是用汇编写的看着太闹心了这里我选 Linux 1.0 的代码给大家看逻辑是一样的。void do_page_fault(..., unsigned long error_code) {... if (error_code 1)do_wp_page(error_code, address, current, user_esp);elsedo_no_page(error_code, address, current, user_esp);...
}可以看出根据中断异常码 error_code 的不同有不同的逻辑。那触发缺页中断的异常码都有哪些呢在 Intel Volume-3 Chapter 4.7 Figure 4-12 中给出。可以看出当 error_code 的第 0 位也就是存在位为 0 时会走 do_no_page 逻辑其余情况均走 do_wp_page 逻辑。我们 fork 的时候只是将读写位变成了只读存在位仍然是 1 没有动所以会走 do_wp_page 逻辑。void do_wp_page(unsigned long error_code,unsigned long address) {// 后面这一大坨计算了 address 在页表项的指针un_wp_page((unsigned long *)(((address10) 0xffc) (0xfffff000 *((unsigned long *) ((address20) 0xffc)))));
}void un_wp_page(unsigned long * table_entry) {unsigned long old_page,new_page;old_page 0xfffff000 *table_entry;// 只被引用一次说明没有被共享那只改下读写属性就行了if (mem_map[MAP_NR(old_page)]1) {*table_entry | 2;invalidate();return;}// 被引用多次就需要复制页表了new_pageget_free_page()mem_map[MAP_NR(old_page)]--;*table_entry new_page | 7;invalidate();copy_page(old_page,new_page);
}// 刷新页变换高速缓冲宏函数
#define invalidate() \
__asm__(movl %%eax,%%cr3::a (0))我用图直接说明这段代码的细节。刚刚 fork 完一个进程是这个样子的对吧这是我们对着这个物理空间范围写一个值就会触发上述函数。假如是进程 2 写的。显然此时这个物理空间被引用了大于 1 次所以要复制页面。new_pageget_free_page()并且更改页面只读属性为可读写。*table_entry new_page | 7;图示就是这样。是不是很简单。那此时如果进程 1 再写呢那么引用次数就等于 1 了只需要更改下页属性即可不用进行页面复制操作。if (mem_map[MAP_NR(old_page)]1) ...图示就是这样。就这么简单。是不是从细节上看和你原来理解的写时复制还有点不同。缺页中断的处理过程中除了写时复制原理的 do_wp_page还有个 do_no_page是在页表项的存在位 P 为 0 时触发的。这个和进程按需加载内存有关如果还没加载到内存会通过这个函数将磁盘中的数据复制到内存来~往期推荐如果让你来设计网络这种本机网络 IO 方法性能可以翻倍留不住客户该从你的系统上找找原因了明明还有大量内存为啥报错“无法分配内存”点分享点收藏点点赞点在看