bytex的博客 订阅
README

这里记录了我学习Linux kernel的历程和心得.是对我的学习的一个总结,也是对我的一种鞭策.

                  - bytex

 

COPYING

本博客所有文章均为原创.

如要转载,请先与本人联系.

Email: bytex@163.com

Msn: bytex@yahoo.com

 

分类
内容读取中…
推荐blog
hyl

hyl的blog

国学之窗

李守力博客专栏

音乐
留言
内容读取中…
访客
内容读取中…
博文

 

前面我们介绍了高速缓存的基本概念,下面我们将介绍它和memory中的页框是如何关联到一起的.

slab alloctor是通过buddy alloctor来申请页框的.
这在 kmem_cache_alloc() 中,通过调用kmem_getpages();而kmem_getpages()又会相应的调用alloc_pages().

当一个页框被分配给slab之后,page->flag 中的 PG_slab会被置位.同时,该page描述符的lru会分别指向相应的cache和slab, 如下图所示:

                
 
                                                           
                    &n

 

主要数据结构:

struct kmem_cache_s {

 struct array_cache *array[NR_CPUS];
 unsigned int  batchcount;
 unsigned int  limit;

 struct kmem_list3 lists;
 
 unsigned int  objsize;
 unsigned int   flags; 
 unsigned int  num; 
 unsigned int  free_limit;
 spinlock_t  spinlock;


 
 unsigned int  gfporder;

 
 unsigned int  gfpflags;

 size_t   colour;  
 unsigned int  colour_off; 
 unsigned int  colour_next; 
 kmem_cache_t  *slabp_cache;
 unsigned int  slab_size;
 unsigned int  dflags;  

 
 void (*ctor)(voi

Char Device Driver

 

相关数据结构:

 

struct cdev {
 struct kobject kobj;
 struct module *owner;
 const struct file_operations *ops;
 struct list_head list;
 dev_t dev;
 unsigned int count;
};


struct kobj_map {
 struct probe {
  struct probe *next;
  dev_t dev;
  unsigned long range;
  struct module *owner;
  kobj_probe_t *get;
  int (*lock)(dev_t, void *);
  void *data;
 } *probes[255];
 struct mutex *lock;
};


static struct char_device_struct {
 struct char_device_struct *next;
 unsigned int major;
 unsigned int baseminor;
 int minorct;
 char name[64];
 struct file_operations *fops;
 struct cdev *cdev;  
}

 

6. 切换数据

        在 arch/arm/kernel/head-common.S 中:

00014:  .type __switch_data, %object
00015: __switch_data:
00016:  .long __mmap_switched
00017:  .long __data_loc   @ r4
00018:  .long __data_start   @ r5
00019:  .long __bss_start   @ r6
00020:  .long _end    @ r7
00021:  .long processor_id   @ r4
00022:  .long __machine_arch_type  @ r5
00023:  .long cr_alignment   @ r6
00024:  .long init_thread_union + THREAD_START_SP @ sp
00025:
00026:
00034:  .type __mmap_switched, %function
00035: __mmap_switched:
00036:  adr r3, __switch_data + 4
00037:
00038:  ldmia r3!, {r4, r5, r6, r7}

 

5. 开启mmu
        开启mmu是又函数 __enable_mmu 实现的.
       
        在进入 __enable_mmu 的时候, r0中已经存放了控制寄存器c1的一些配置(在上一步中进行的设置), 但是并没有真正的打开mmu,
        在 __enable_mmu 中,我们将打开mmu.
       
        此时,一些特定寄存器的值如下所示:
r0 = c1 parameters      (用来配置控制寄存器的参数)       
r4 = pgtbl              (page table 的物理基地址)
r8 = machine info       (struct machine_desc的基地址)
r9 = cpu id             (通过cp15协处理器获得的cpu id)

 

4. 调用平台特定的 __cpu_flush 函数

当 __create_page_tables 返回之后

此时,一些特定寄存器的值如下所示:
r4 = pgtbl              (page table 的物理基地址)
r8 = machine info       (struct machine_desc的基地址)
r9 = cpu id             (通过cp15协处理器获得的cpu id)
r10 = procinfo          (struct proc_info_list的基地址)


在我们需要在开启mmu之前,做一些必须的工作:清除ICache, 清除 DCache, 清除 Writebuffer, 清除TLB等.
这些一般是通过cp15协处理器来实现的,并且是平台相关的. 这就是 __cpu_flush 需要做的工作.
       
        在 arch/arm/kernel/head.S中
00091:  ldr r13, __switch_data  @ address to jump to after
00092: &nb

 

3. 创建页表

通过前面的两步,我们已经确定了processor type 和 machine type.
此时,一些特定寄存器的值如下所示:
r8 = machine info       (struct machine_desc的基地址)
r9 = cpu id             (通过cp15协处理器获得的cpu id)
r10 = procinfo          (struct proc_info_list的基地址)

创建页表是通过函数 __create_page_tables 来实现的.
这里,我们使用的是arm的L1主页表,L1主页表也称为段页表(section page table)
L1 主页表将4 GB 的地址空间分成若干个1 MB的段(section),因此L1页表包含4096个页表项(section entry). 每个页表项是32 bits(4 bytes)
因而L1主页表占用 4096 *4 = 16k的内存空间.

        对于ARM926,其L1 section entry的格式为:(可参考arm926EJS TRM):
                   

2. 确定 machine type

    arch/arm/kernel/head.S中:
00079:  bl __lookup_machine_type  @ r5=machinfo             
00080:  movs r8, r5    @ invalid machine (r5=0)? 
00081:  beq __error_a   @ yes, error 'a' 

79行: 跳转到__lookup_machine_type函数,在__lookup_machine_type中,会把struct machine_desc的基地址(machine type)存储在r5中
80,81行: 将r5中的 machine_desc的基地址存储到r8中,并判断r5是否是0,如果是0,说明是无效的machine type,跳转到__error_a(出错)

__lookup_machine_type 函数
下面我们分析__lookup_machine_type 函数:

        arch/arm/kernel/head-common.S中:
       
00176:  .long __proc_info_begin
00177:  .long __proc_info_end
00178: 3: .long .
0

 

1. 确定 processor type
 


    arch/arm/kernel/head.S中:
00075:  mrc p15, 0, r9, c0, c0  @ get processor id        
00076:  bl __lookup_processor_type  @ r5=procinfo r9=cpuid    
00077:  movs r10, r5    @ invalid processor (r5=0)?
00078:  beq __error_p   @ yes, error 'p'          

75行: 通过cp15协处理器的c0寄存器来获得processor id的指令. 关于cp15的详细内容可参考相关的arm手册

76行: 跳转到__lookup_processor_type.在__lookup_processor_type中,会把processor type 存储在r5中
77,78行: 判断r5中的processor type是否是0,如果是0,说明是无效的processor type,跳转到__error_p(出错)

__lookup_processor_type 函数主要是根据从cpu中获得的processor id和系统中的proc_info进行匹配,将匹配到的proc_info_list的

 

本文针对arm linux, 从kernel的第一条指令开始分析,一直分析到进入start_kernel()函数.
我们当前以linux-2.6.19内核版本作为范例来分析,本文中所有的代码,前面都会加上行号以便于和源码进行对照.
例:
在文件init/main.c中:
00478: asmlinkage void __init start_kernel(void)
前面的'00478:' 表示478行,冒号后面的内容就是源码了.

在分析代码的过程中,我们使用缩进来表示各个代码的调用层次.

由于启动部分有一些代码是平台特定的,虽然大部分的平台所实现的功能都比较类似,但是为了更好的对code进行说明,对于平台相关的代码,我们选择at91(ARM926EJS)平台进行分析.

另外,本文是以uncompressed kernel开始讲解的.对于内核解压缩部分的code,在 arch/arm/boot/compressed中,本文不做讨论.


一. 启动条件
 通常从系统上电到执行到linux kenel这部分的任务是由boot loader来完成.
 关于boot loader的内容,本文就不做过多介绍.
 这里只讨论进入到linux kernel的时候的一些限制条件,这一般是boot loader在最后跳转到kernel之前要完成的:
 1. CPU必须

新浪BLOG意见反馈留言板 不良信息反馈 电话:95105670 提示音后按2键(按当地市话标准计费) 欢迎批评指正

新浪简介 | About Sina | 广告服务 | 联系我们 | 招聘信息 | 网站律师 | SINA English | 会员注册 | 产品答疑

新浪公司 版权所有