📄 memory.c
字号:
oom (); }}/** try_to_share() checks the page at address "address" in the task "p",* to see if it exists, and if it is clean. If so, share it with the current* task.** NOTE! This assumes we have checked that p != current, and that they* share the same executable.*//** try_to_share()在任务"p"中检查位于地址"address"处的页面,看页面是否存在,是否干净。* 如果是干净的话,就与当前任务共享。** 注意!这里我们已假定p !=当前任务,并且它们共享同一个执行程序。*///// 尝试对进程指定地址处的页面进行共享操作。// 同时还验证指定的地址处是否已经申请了页面,若是则出错,死机。// 返回1-成功,0-失败。static inttry_to_share (unsigned long address, struct task_struct *p){ unsigned long from; unsigned long to; unsigned long from_page; unsigned long to_page; unsigned long phys_addr;// 求指定内存地址的页目录项。 from_page = to_page = ((address >> 20) & 0xffc);// 计算进程p 的代码起始地址所对应的页目录项。 from_page += ((p->start_code >> 20) & 0xffc);// 计算当前进程中代码起始地址所对应的页目录项。 to_page += ((current->start_code >> 20) & 0xffc);/* is there a page-directory at from? *//* 在from 处是否存在页目录?*/// *** 对p 进程页面进行操作。// 取页目录项内容。如果该目录项无效(P=0),则返回。否则取该目录项对应页表地址??from。 from = *(unsigned long *) from_page; if (!(from & 1)) return 0; from &= 0xfffff000;// 计算地址对应的页表项指针值,并取出该页表项内容??phys_addr。 from_page = from + ((address >> 10) & 0xffc); phys_addr = *(unsigned long *) from_page;/* is the page clean and present? *//* 页面干净并且存在吗?*/// 0x41 对应页表项中的Dirty 和Present 标志。如果页面不干净或无效则返回。 if ((phys_addr & 0x41) != 0x01) return 0;// 取页面的地址??phys_addr。如果该页面地址不存在或小于内存低端(1M)也返回退出。 phys_addr &= 0xfffff000; if (phys_addr >= HIGH_MEMORY || phys_addr < LOW_MEM) return 0;// *** 对当前进程页面进行操作。// 取页目录项内容??to。如果该目录项无效(P=0),则取空闲页面,并更新to_page 所指的目录项。 to = *(unsigned long *) to_page; if (!(to & 1)) if (to = get_free_page ()) *(unsigned long *) to_page = to | 7; else oom ();// 取对应页表地址??to,页表项地址??to_page。如果对应的页面已经存在,则出错,死机。 to &= 0xfffff000; to_page = to + ((address >> 10) & 0xffc); if (1 & *(unsigned long *) to_page) panic ("try_to_share: to_page already exists");/* share them: write-protect *//* 对它们进行共享处理:写保护 */// 对p 进程中页面置写保护标志(置R/W=0 只读)。并且当前进程中的对应页表项指向它。 *(unsigned long *) from_page &= ~2; *(unsigned long *) to_page = *(unsigned long *) from_page;// 刷新页变换高速缓冲。 invalidate ();// 计算所操作页面的页面号,并将对应页面映射数组项中的引用递增1。 phys_addr -= LOW_MEM; phys_addr >>= 12; mem_map[phys_addr]++; return 1;}/** share_page() tries to find a process that could share a page with* the current one. Address is the address of the wanted page relative* to the current data space.** We first check if it is at all feasible by checking executable->i_count.* It should be >1 if there are other tasks sharing this inode.*//** share_page()试图找到一个进程,它可以与当前进程共享页面。参数address 是* 当前数据空间中期望共享的某页面地址。** 首先我们通过检测executable->i_count 来查证是否可行。如果有其它任务已共享* 该inode,则它应该大于1。*///// 共享页面。在缺页处理时看看能否共享页面。// 返回1 - 成功,0 - 失败。static intshare_page (unsigned long address){ struct task_struct **p;// 如果是不可执行的,则返回。excutable 是执行进程的内存i 节点结构。 if (!current->executable) return 0;// 如果只能单独执行(executable->i_count=1),也退出。 if (current->executable->i_count < 2) return 0;// 搜索任务数组中所有任务。寻找与当前进程可共享页面的进程,并尝试对指定地址的页面进行共享。 for (p = &LAST_TASK; p > &FIRST_TASK; --p) { if (!*p) // 如果该任务项空闲,则继续寻找。 continue; if (current == *p) // 如果就是当前任务,也继续寻找。 continue; if ((*p)->executable != current->executable) // 如果executable 不等,也继续。 continue; if (try_to_share (address, *p)) // 尝试共享页面。 return 1; } return 0;}//// 页异常中断处理调用的函数。处理缺页异常情况。在page.s 程序中被调用。// 参数error_code 是由CPU 自动产生,address 是页面线性地址。voiddo_no_page (unsigned long error_code, unsigned long address){ int nr[4]; unsigned long tmp; unsigned long page; int block, i; address &= 0xfffff000; // 页面地址。// 首先算出指定线性地址在进程空间中相对于进程基址的偏移长度值。 tmp = address - current->start_code;// 若当前进程的executable 空,或者指定地址超出代码+数据长度,则申请一页物理内存,并映射// 影射到指定的线性地址处。executable 是进程的i 节点结构。该值为0,表明进程刚开始设置,// 需要内存;而指定的线性地址超出代码加数据长度,表明进程在申请新的内存空间,也需要给予。// 因此就直接调用get_empty_page()函数,申请一页物理内存并映射到指定线性地址处即可。// start_code 是进程代码段地址,end_data 是代码加数据长度。对于linux 内核,它的代码段和// 数据段是起始基址是相同的。 if (!current->executable || tmp >= current->end_data) { get_empty_page (address); return; }// 如果尝试共享页面成功,则退出。 if (share_page (tmp)) return;// 取空闲页面,如果内存不够了,则显示内存不够,终止进程。 if (!(page = get_free_page ())) oom ();/* remember that 1 block is used for header *//* 记住,(程序)头要使用1 个数据块 */// 首先计算缺页所在的数据块项。BLOCK_SIZE = 1024 字节,因此一页内存需要4 个数据块。 block = 1 + tmp / BLOCK_SIZE;// 根据i 节点信息,取数据块在设备上的对应的逻辑块号。 for (i = 0; i < 4; block++, i++) nr[i] = bmap (current->executable, block);// 读设备上一个页面的数据(4 个逻辑块)到指定物理地址page 处。 bread_page (page, current->executable->i_dev, nr);// 在增加了一页内存后,该页内存的部分可能会超过进程的end_data 位置。下面的循环即是对物理// 页面超出的部分进行清零处理。 i = tmp + 4096 - current->end_data; tmp = page + 4096; while (i-- > 0) { tmp--; *(char *) tmp = 0; }// 如果把物理页面映射到指定线性地址的操作成功,就返回。否则就释放内存页,显示内存不够。 if (put_page (page, address)) return; free_page (page); oom ();}//// 物理内存初始化。// 参数:start_mem - 可用作分页处理的物理内存起始位置(已去除RAMDISK 所占内存空间等)。// end_mem - 实际物理内存最大地址。// 在该版的linux 内核中,最多能使用16Mb 的内存,大于16Mb 的内存将不于考虑,弃置不用。// 0 - 1Mb 内存空间用于内核系统(其实是0-640Kb)。voidmem_init (long start_mem, long end_mem){ int i; HIGH_MEMORY = end_mem; // 设置内存最高端。 for (i = 0; i < PAGING_PAGES; i++) // 首先置所有页面为已占用(USED=100)状态, mem_map[i] = USED; // 即将页面映射数组全置成USED。 i = MAP_NR (start_mem); // 然后计算可使用起始内存的页面号。 end_mem -= start_mem; // 再计算可分页处理的内存块大小。 end_mem >>= 12; // 从而计算出可用于分页处理的页面数。 while (end_mem-- > 0) // 最后将这些可用页面对应的页面映射数组清零。 mem_map[i++] = 0;}// 计算内存空闲页面数并显示。voidcalc_mem (void){ int i, j, k, free = 0; long *pg_tbl;// 扫描内存页面映射数组mem_map[],获取空闲页面数并显示。 for (i = 0; i < PAGING_PAGES; i++) if (!mem_map[i]) free++; printk ("%d pages free (of %d)\n\r", free, PAGING_PAGES);// 扫描所有页目录项(除0,1 项),如果页目录项有效,则统计对应页表中有效页面数,并显示。 for (i = 2; i < 1024; i++) { if (1 & pg_dir[i]) { pg_tbl = (long *) (0xfffff000 & pg_dir[i]); 10.5 page.s
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -