注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

Tsecer的回音岛

Tsecer的博客

 
 
 

日志

 
 

Linux内核启动原始代码  

2011-03-23 20:50:42|  分类: Linux内核 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

一、内核启动参数

一般内核启动的时候都会通过某种方式将一些内核启动参数传递给内核。例如对于PowerPC,可能通过r4寄存器来传递内核启动参数,而对于i386系统来说,这个系统启动的时候将会通过ESI寄存器来传递这个启动参数指针,这个参数由loader来传递

linux-2.6.37.1\arch\x86\kernel\head_32.S

__HEAD
ENTRY(startup_32)
 /* test KEEP_SEGMENTS flag to see if the bootloader is asking
  us to not reload segments */
 testb $(1<<6), BP_loadflags(%esi) 
 jnz 2f
二、页表的初始化

page_pde_offset = (__PAGE_OFFSET >> 20);

 movl $pa(__brk_base), %edi 这个__brk_base表示的是text结束的地方。
 movl $pa(initial_page_table), %edx 从这里可以看到,其中的initial_page_table中包含的整个内核的根页目录,它指向的内容中每项的大小为1M,这一点从上面的page_pde_offset中可以证明。
 movl $PTE_IDENT_ATTR, %eax
10:
 leal PDE_IDENT_ATTR(%edi),%ecx  /* Create PDE entry */
 movl %ecx,(%edx)   /* Store identity PDE entry */
 movl %ecx,page_pde_offset(%edx)  /* Store kernel PDE entry */
 addl $4,%edx
 movl $1024, %ecx
11:
 stosl 注意,这里没有加rep前缀,所以这里的这个指令只会被执行一次。但是edi将会随着这条指令的执行而不断的增加。
 addl $0x1000,%eax
 loop 11b
 /*
  * End condition: we must map up to the end + MAPPING_BEYOND_END.
  */
 movl $pa(_end) + MAPPING_BEYOND_END + PTE_IDENT_ATTR, %ebp
 cmpl %ebp,%eax
 jb 10b
 addl $__PAGE_OFFSET, %edi
 movl %edi, pa(_brk_end)
 shrl $12, %eax
 movl %eax, pa(max_pfn_mapped)

 /* Do early initialization of the fixmap area */
 movl $pa(initial_pg_fixmap)+PDE_IDENT_ATTR,%eax
 movl %eax,pa(initial_page_table+0xffc)

linux-2.6.37.1\arch\x86\kernel\ head_32.S

#ifdef CONFIG_X86_PAE
ENTRY(initial_pg_pmd)
 .fill 1024*KPMDS,4,0
#else
ENTRY(initial_page_table)
 .fill 1024,4,0 这里总共大小为4KB,所以4K×1M= 4G,为整个系统的页目录,这个是足够的。
#endif
linux-2.6.37.1\arch\x86\kernel\vmlinux.lds.S

 .brk : AT(ADDR(.brk) - LOAD_OFFSET) {
  __brk_base = .;
  . += 64 * 1024;  /* 64k alignment slop space */
  *(.brk_reservation) /* areas brk users have reserved */
  __brk_limit = .;
 }

 _end = .;

/* Number of possible pages in the lowmem region */
LOWMEM_PAGES = (((1<<32) - __PAGE_OFFSET) >> PAGE_SHIFT)
 
/* Enough space to fit pagetables for the low memory linear map */
MAPPING_BEYOND_END = PAGE_TABLE_SIZE(LOWMEM_PAGES) << PAGE_SHIFT
也就是内核中4G地址空间中除了用户态的地址之外的地址

 

然后在linux-2.6.37.1\arch\x86\kernel\setup.c

void __init setup_arch(char **cmdline_p) 函数

#ifdef CONFIG_X86_32
 memcpy(&boot_cpu_data, &new_cpu_data, sizeof(new_cpu_data));
 visws_early_detect();

 /*
  * copy kernel address range established so far and switch
  * to the proper swapper page table
  */
 clone_pgd_range(swapper_pg_dir     + KERNEL_PGD_BOUNDARY,
   initial_page_table + KERNEL_PGD_BOUNDARY,
   KERNEL_PGD_PTRS  这里将所有的initial_page_table复制给了我们常见的swapper_pg_dir中,这个正是我们0号系统任务是用的整个系统的页表。

 load_cr3(swapper_pg_dir);
 __flush_tlb_all();
#else
 printk(KERN_INFO "Command line: %s\n", boot_command_line);
#endif
static inline void clone_pgd_range(pgd_t *dst, pgd_t *src, int count)
{
       memcpy(dst, src, count * sizeof(pgd_t));
}

2.1.5中对页面的描述为

The base physical address of the page directory is contained in control register CR3. An entry
in a page directory contains the physical address of the base of a page table, access rights and
memory management information. An entry in a page table contains the physical address of a
page frame, access rights and memory management information.

这也就是说,线性地址转换过程中使用的都是物理地址,这一点非常重要

三、页目录和页表的初始化

这个代码还是比较重要的。这里涉及到 两个内容 一个是 页目录,一个是页表,在内核的headS文件中,这两者分别用 swap_pg_dir和p0表示,前者为页目录,后者为页表。

然后在汇编中,这里需要四个寄存器:

1、页表地址, 2、页表内容

3、页目录地址 4、页目录内容

这四个都是会不断变化的。这里的限制是页目录项需要4KB也就是一个页面也就足够了。

#else /* Not PAE */

page_pde_offset = (__PAGE_OFFSET >> 20);

 movl $pa(__brk_base), %edi   这里特殊寄存器的意义: edi保存页表项数组的地址。
 movl $pa(initial_page_table), %edx edx 保存页目录项数组的地址。
 movl $PTE_IDENT_ATTR, %eax  eax 保存页表项的值。
10:
 leal PDE_IDENT_ATTR(%edi),%ecx  /* Create PDE entry */  ecx保存页目录项数组的内容。
 movl %ecx,(%edx)   /* Store identity PDE entry */  这里的开始从0--968M之间的逻辑地址。
 movl %ecx,page_pde_offset(%edx)  /* Store kernel PDE entry */ 这里为从3G到3G+968M之间的逻辑地址。这里的映射也就是无论内容通过逻辑地址的0--768还是通过3G--3G+768M之间的逻辑地址来访问,都可以访问到相同的物理地址。这一点在《Linux内核源代码情景分析》的最后一章中也有说明在,这里就是为了进行平稳的过渡,在真正的内核初始化完成之后,这里的低端逻辑地址00--768M之间的空间就不会被内核映射,完全归还给用户。
 addl $4,%edx  外层循环为页目录项的地址。当页表增加1024项之后,这个地址增加4个字节。
 movl $1024, %ecx loop的循环次数为1024,每次一个页表项,共占用1KB×4=4KB
11:
 stosl
 addl $0x1000,%eax  每次增加一个页面大小,低12位为一些状态设置位。
 loop 11b
 /*
  * End condition: we must map up to the end + MAPPING_BEYOND_END.
  */
 movl $pa(_end) + MAPPING_BEYOND_END + PTE_IDENT_ATTR, %ebp  这里的ebp保存页目录项的终止。
 cmpl %ebp,%eax
 jb 10b
 addl $__PAGE_OFFSET, %edi
 movl %edi, pa(_brk_end) 这里增加brk的大小,从brk_start到brk_end之间的内容扩充为内核的页表项的大小。
 shrl $12, %eax
 movl %eax, pa(max_pfn_mapped)

 /* Do early initialization of the fixmap area */
 movl $pa(initial_pg_fixmap)+PDE_IDENT_ATTR,%eax
 movl %eax,pa(initial_page_table+0xffc)
#endif

  评论这张
 
阅读(1299)| 评论(0)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017