linux 0.11 源码学习(十一)
memory.c
在X86的保护模式中,线性地址由页目录表(10位)+页表(10位)+ 偏移(12位)组成,因此对线性地址而言可以寻址4G的地址空间。而实际中linux支持16M的内存,因此在memory.c或者说linux的内存管理模块中,维护了线性地址和实际物理地址的映射。本篇博客主要记录内存管理的几个主要函数学习。
下面几个宏定义可以看出物理页面的分配数:
#define USED 100 //mem_map中的映射值,UNUSED是初始化值0,在mem_init中完成
#define PAGING_MEMORY (15*1024*1024) //实际的主内存15M,最低端的1M分配给内核 #define PAGING_PAGES (PAGING_MEMORY>>12) //实际的物理页,每页表项是12位偏移地址,因此长度为4096
- get_free_page(void)代码,完成功能是获取一个可用的页:
/* * Get physical address of first (actually last :-) free page, and mark it * used. If no free pages left, return 0. */ unsigned long get_free_page(void) { register unsigned long __res asm("ax"); //返回值是寄存器ax __asm__("std ; repne ; scasb\n\t"//edi指向的值与al(0)比较 "jne 1f\n\t" "movb $1,1(%%edi)\n\t" //edi+1的值=1 "sall $12,%%ecx\n\t" //ecx的值左移12位,即ecx * 4KB "addl %2,%%ecx\n\t" //ecx的值加2 "movl %%ecx,%%edx\n\t" //赋值给edx "movl $1024,%%ecx\n\t" //ecx = 1024 "leal 4092(%%edx),%%edi\n\t" //edx + 4092的值读进edi "rep ; stosl\n\t" //将eax的值拷贝到edi中,重复ecx次,完成页表项清零 "movl %%edx,%%eax\n" //edx赋值给eax,返回eax "1:" :"=a" (__res) :"0" (0),"i" (LOW_MEM),"c" (PAGING_PAGES), //ax = 0, ecx = PAGIN_PAGES 物理内存页数 "D" (mem_map+PAGING_PAGES-1)//edi = mem_map + PAGING_PAGES - 1 ); return __res; }
- free_page完成释放指定物理地址对应的物理页,代码如下:
addr -= LOW_MEM; addr >>= 12; //上述两行代码,将addr由实际物理地址转换为mem_map中的索引 if (mem_map[addr]--) return; mem_map[addr]=0;//对应的addr的mem_map设置为未使用
- copy_page_tables在进程fork时被调用,如下:
if (copy_page_tables(old_data_base,new_data_base,data_limit)) { printk("free_page_tables: from copy_mem\n"); free_page_tables(new_data_base,data_limit); return -ENOMEM; }
注: 此处的from/to都是线性地址,如上述描述由10+10+12表示,但要注意的是这里的10\10\都是在页目录表和页表中的索引,因此对于实际的物理地址要*4B。
int copy_page_tables(unsigned long from,unsigned long to,long size) { unsigned long * from_page_table; unsigned long * to_page_table; unsigned long this_page; unsigned long * from_dir, * to_dir; unsigned long nr; if ((from&0x3fffff) || (to&0x3fffff)) panic("copy_page_tables called with wrong alignment"); from_dir = (unsigned long *) ((from>>20) & 0xffc); //左移24位 * 4B,即from线性地址对应的目录表实际地址 to_dir = (unsigned long *) ((to>>20) & 0xffc); //同上 size = ((unsigned) (size+0x3fffff)) >> 22; for( ; size-->0 ; from_dir++,to_dir++) { if (1 & *to_dir) panic("copy_page_tables: already exist"); if (!(1 & *from_dir)) continue; from_page_table = (unsigned long *) (0xfffff000 & *from_dir); if (!(to_page_table = (unsigned long *) get_free_page()))//获取一个物理页,存放其页目录表 return -1; /* Out of memory, see freeing */ *to_dir = ((unsigned long) to_page_table) | 7; nr = (from==0)?0xA0:1024; for ( ; nr-- > 0 ; from_page_table++,to_page_table++) {//每个页目录表有1024个页表项 this_page = *from_page_table; if (!(1 & this_page)) continue; this_page &= ~2; *to_page_table = this_page; if (this_page > LOW_MEM) { *from_page_table = this_page; this_page -= LOW_MEM; this_page >>= 12; mem_map[this_page]++;//该物理页(即是1024*4大小) } } } invalidate(); return 0; }
在linux中采用了写时复制技术,也就是当某个线性地址被写时,触发相应的缺页错误。该缺页错误会导致分配物理页面,实现的代码是page.s,
- 具体的业务逻辑在函数do_no_page中,如下:
void do_no_page(unsigned long error_code,unsigned long address)//address是产生异常页面的线性地址 { int nr[4]; unsigned long tmp; unsigned long page; int block,i; address &= 0xfffff000;//低12位是页面内的偏移,此处要复制的是整个页的起始地址,因此低12位取0 tmp = address - current->start_code;//current->start_code是进程线性地址的起始地址 if (!current->executable || tmp >= current->end_data) { get_empty_page(address); return; } if (share_page(tmp))//申请共享内存,如果有其他进程已执行了一样的文件 return; if (!(page = get_free_page()))//获取一个page oom(); /* remember that 1 block is used for header */ block = 1 + tmp/BLOCK_SIZE; for (i=0 ; i<4 ; block++,i++) nr[i] = bmap(current->executable,block);//寻求进程相应地址在文件系统中的逻辑号 bread_page(page,current->executable->i_dev,nr);//将文件中的内容读入到分配的内存页中 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(); }
分类:
linux
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)