http://blog.sina.com.cn/bytex[订阅]
个人资料
README

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

                 - bytex

 

COPYING

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

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

Email:bytex@163.com

Msn:bytex@yahoo.com

 

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

hyl的blog

国学之窗

李守力博客专栏

音乐播放器
评论
读取中...
博文


另外,对于Linux,可以将多个文件系统 mount 到同一个目录上,这样的话, 新 mount 的文件系统会覆盖原来mount的文件系统。
比如我们再把一个 '/dve/hdb2' 的设备 mount 到 '/home/xpl'  目录下,
这样,如果我们访问 '/home/xpl' 的时候,就会访问到 '/dev/hdb2'
当新mount的文件系统被 unmount 之后,原来被覆盖的文件系统就会再次显露出来了。

这个过程的之所以是这样的,是因为在路径查找的时候,如果发现要查找的目录上mount了 文件系统(其dentry的d_mounted 不为0),会切换文件系统。
比如我们上面的例子,当路径查找 '/home/xpl' 的时候,当查找到根文件系统的目录 'xpl' 的dentry时,发现有一个文件系统已经mount到这个目录上,就会切换文件系统,进而找到 '/dev/hdb1' 的根目录的 dentry
这样,路径查找'/home/xpl' 返回的时候,返回的是 '/dve/hdb1' 根目录的 dentry

这样,在我们 mount 第二个文件系统 '/dev/hdb2' 的时候,其mountpoint 就是 '/dev/hdb1' 的根目录的 dentry。
这个过程如下图所示:

             

 

找到了要 mount 的目录,下面就开始实际的mount过程
mount的过程就是把设备的文件系统加入到 vfs 框架中
1. 首先,要mount一个新的设备,需要创建一个新的 super block。
 这通过要mount的文件系统的 file_system_type, 调用其 get_sb 方法来创建一个新的 super block

2. 对于任何一个 mount 的文件系统,都要有一个 vfsmount, 创建这个vfsmount, 并设置好其属性(就是 vfsmount 中的各个成员)

3. 将创建好的 vfsmount 加入到系统中。

整个过程如下所示:
                                                                             
      


mount 文件系统

主要数据结构:

struct namespace {
 atomic_t  count;  
 struct vfsmount * root; 
 struct list_head list; 
 struct rw_semaphore sem; 
};

 

struct vfsmount
{
 struct list_head mnt_hash;  
 struct vfsmount *mnt_parent; 
 struct dentry *mnt_mountpoint; 
 struct dentry *mnt_root;  
 struct super_block *mnt_sb;  
 struct list_head mnt_mounts; 
 struct list_head mnt_child;  
 atomic_t mnt_count;    
 int mnt_flags;     
 int mnt_expiry_mark;   
 char *mnt_devname;    
 struct list_head mnt_list;  
 struct list_head mnt_fslink; 
 

 

前面我们介绍了高速缓存的基本概念,下面我们将介绍它和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):