1 //// 把一物理内存页面映射到线性地址空间指定处。
2 // 或者说是把线性地址空间中指定地址address处的页面映射到主内存区页面page上。主
3 // 要工作是在相关页面目录项和页表项中设置指定页面的信息。若成功则返回物理页面地
4 // 址。在处理缺页异常的C函数do_no_page()中会调用此函数。对于缺页引起的异常,由于
5 // 任何缺页缘故而对页表作修改时,并不需要刷新CPU的页变换缓冲(或称Translation Lookaside
6 // Buffer - TLB),即使页表中标志P被从0修改成1.因为无效页表项不会被缓冲,因此当修改
7 // 了一个无效的页表项时不需要刷新。再次就表现为不用调用Invalidate()函数。
8 // 参数page是分配的主内存区中某一页面(页帧,页框)的指针;address是线性地址。
9 unsigned long put_page(unsigned long page,unsigned long address)
10 {
11 unsigned long tmp, *page_table;
12
13 /* NOTE !!! This uses the fact that _pg_dir=0 */
14
15 // 首先判断参数给定物理内存页面page的有效性。如果该页面位置低于LOW_MEM(1MB)
16 // 或超出系统实际含有内存高端HIGH_MEMORY,则发出警告。LOW_MEM是主内存区可能
17 // 有的最小起始位置。当系统物理内存小于或等于6MB时,主内存区起始于LOW_MEM处。
18 // 再查看一下该page页面是否已经申请的页面,即判断其在内存页面映射字节图mem_map[]
19 // 中相应字节是否已经置位。若没有则需发出警告。
20 if (page < LOW_MEM || page >= HIGH_MEMORY)
21 printk("Trying to put page %p at %p\n",page,address);
22 if (mem_map[(page-LOW_MEM)>>12] != 1)
23 printk("mem_map disagrees with %p at %p\n",page,address);
24 // 然后根据参数指定的线性地址address计算其在也目录表中对应的目录项指针,并
25 // 从中取得二级页表地址。如果该目录项有效(P=1),即指定的页表在内存中,则从中
26 // 取得指定页表地址放到page_table 变量中。否则就申请一空闲页面给页表使用,并
27 // 在对应目录项中置相应标志(7 - User、U/S、R/W).然后将该页表地址放到page_table
28 // 变量中。
29 page_table = (unsigned long *) ((address>>20) & 0xffc);
30 if ((*page_table)&1)
31 page_table = (unsigned long *) (0xfffff000 & *page_table);
32 else {
33 if (!(tmp=get_free_page()))
34 return 0;
35 *page_table = tmp|7;
36 page_table = (unsigned long *) tmp;
37 }
38 // 最后在找到的页表page_table中设置相关页表内容,即把物理页面page的地址填入
39 // 表项同时置位3个标志(U/S、W/R、P)。该页表项在页表中索引值等于线性地址位21
40 // -- 位12组成的10bit的值。每个页表共可有1024项(0 -- 0x3ff)。
41 page_table[(address>>12) & 0x3ff] = page | 7;
42 /* no need for invalidate */
43 return page;
44 }