- 博客(30)
- 资源 (14)
- 收藏
- 关注
原创 请求队列描述符
1.4.4 请求队列描述符<br />make_request_fn方法属于块设备I/O调度层的内容,要继续往下走,需要介绍一下通用块层的体系架构,这里需要从磁盘和磁盘分区开始说起。磁盘是一个由通用块层处理的逻辑块设备,是块设备驱动中最重要的一个概念。通常一个磁盘对应一个硬件块设备,例如硬盘、软盘或光盘。但是,磁盘也可以是一个虚拟设备,可以建立在几个物理磁盘分区之上或一些RAM专用页中的内存区上。在任何情形中,借助通用块层提供的服务,上层内核组件可以以同样的方式工作在所有的磁盘上。<br /> <br />
2011-01-31 23:59:00 3735 2
原创 提交I/O传输请求
1.4.3 提交I/O传输请求<br />好了,bio这个数据我们建立好了,随后调用generic_make_request 函数。这个函数是通用块层的入口点,该层只有这一个函数处理请求:<br /> <br />3020void generic_make_request(struct bio *bio)<br />3021{<br />3022 request_queue_t *q;<br />3023 sector_t maxsector;<br />3024
2011-01-31 23:53:00 3447
原创 通用块层相关数据结构
1.4.2 通用块层相关数据结构<br />好了,通用块层的一些比较重要的基础知识大家都知道了,下面我们着重来看bio这个东西。大家在前面“创建一个bio请求”一节中已经见过这个结构了,但是,由于它太重要了,所以我们这里有必要对它进行进一步的介绍。<br /> <br />每个bio结构都包含一个磁盘存储区标识符(存储区中的起始扇区号和扇区数目)和一个或多个描述与I/O操作相关的内存区的段。bio由bio数据结构描述:<br /> <br />struct bio {<br /> sector_
2011-01-31 23:47:00 4079
原创 块设备的基础知识
1.4 通用块层的处理<br />在前面普通文件和块设备文件的readpage方法中介绍到,对于一个普通文件,要读取相对于文件头的ppos处开始size个连续的字节,就必须计算成对应的页面缓存在内存中;如果存在不连续的情况,如“文件的洞”,就调用块设备的readpage方法建立块设备页高速缓存存放不来连续的块。<br /> <br />不管怎样,最终都将封装一个bio结构,并把请求传递给函数 generic_make_request ,并由 generic_make_request 函数将请求提交给通用块层
2011-01-31 23:43:00 6214 2
原创 文件的预读
1.3.5 文件的预读<br />文件预读的内容我是把ULK-3一书中的内容全盘拷贝下来了。如果大家感兴趣可以根据源代码深入了解一下。<br /> <br />很多磁盘的访问都是顺序的。普通文件以相邻扇区成组存放在磁盘上,因此很少移动磁头就可以快速检索到文件。当程序读或拷贝一个文件时,它通常从第一个字节到最后一个字节顺序地访问文件。因此,在处理进程对同一文件的一系列读请求时,可以从磁盘上很多相邻的扇区读取。<br /> <br />预读(read-ahead)是一种技术,这种技术在于在实际请求前读普通文件或
2011-01-31 23:32:00 3514
原创 文件的readpage方法
1.3.3 普通文件的readpage方法<br />回到do_mpage_readpage中,从mpage_readpage传进来的buffer_head类型的map_bh参数的b_bdev、b_blocknr和b_size字段就被赋上值了,然后245~271行对这个map_bh进行一系列的检查,检查可能发生的异常条件。具体有这几种情况:当一些块在磁盘上不相邻时,或某块落如“文件洞”内时,或一个块缓冲区已经由get_block函数写入时。那么跳到confused标号处,用一次读一块的方式读该页。有关“文件
2011-01-31 23:15:00 6138
原创 得到文件的逻辑块号
1.3.2 得到文件的逻辑块号<br />继续走,233行,设置map_bh的b_page字段为当前page。随后进入循环,对于页中的每一块,调用ext2文件系统的get_block函数,作为参数传递page的inode、相对于文件起始位置的块索引block_in_file、map_bh进去,最后返回相对于磁盘分区开始位置的逻辑块号,即相对于磁盘或分区开始位置的块索引,存放在结果参数map_bh的b_blocknr字段中。所以这里我们重点关注get_block的原型,ext2_get_block函数,来自f
2011-01-31 22:08:00 7382 2
原创 创建一个bio请求
1.3 页高速缓存层的处理<br />从上文得知:ext2_readpage 函数是该层的入口点,传给它的参数是文件的file,以及需要读入页高速缓存的页面。这个页面不是别的,正是刚才page_cache_alloc_cold分配的那个空白页面,其现在位于文件的基树中,基树中索引为index:<br /> <br />static int ext2_readpage(struct file *file, struct page *page)<br />{<br /> return mpage_r
2011-01-31 21:58:00 5274
原创 Ext2层读文件入口函数
1.2.5 Ext2层读文件入口函数<br />好了,我们知道了Ext2文件系统的磁盘布局,以及始终缓存的磁盘超级拷贝块结构ext2_super_block和动态缓存的已分配磁盘索引节点结构ext2_inode这些预备知识。接下来就假设一个文件的inode已经分配好,并且包含该文件所有块号的对应宿主ext2_inode_info结构也在内存中初始化好了。那么如何读这个文件?<br /> <br />前面讲了,ext2层,也就是第二扩展文件系统的入口函数 generic_file_read,下面我们就从它开始
2011-01-31 21:54:00 3074
原创 Ext2索引节点对象的读取
1.2.4 Ext2索引节点对象的读取<br />上一节我们提到了当open("file", O_CREAT)创建一个文件时,其对应的Ext2磁盘索引节点是如何建立的,又是如何与系统中的其他数据结构联系的。现在还是从一个普通文件的角度来分析,当我门在根目录下调用fd = open("file", O_RDONLY)打开一个已经存在文件时,同样也会启动do_sys_open系统调用,并根据路径“file”去触发do_filp_open函数返回一个file结构。<br /> <br />而这个时候,do_fil
2011-01-31 21:48:00 3539
原创 Ext2索引节点对象的创建
1.2.3 Ext2索引节点对象的创建<br />现在还是从一个普通文件的角度来分析上面的过程,比如说,当我门在根目录下调用fd = open("file", O_CREAT)打开(创建)一个文件时,会启动do_sys_open系统调用,并根据路径“file”去触发do_filp_open函数返回一个file结构。do_filp_open主要调用的两个函数(详细的过程请参考博客“VFS系统调用的实现”<br />http://blog.csdn.net/yunsongice/archive/2010/06/
2011-01-31 21:42:00 3673
原创 Ext2的超级块对象
1.2.2 Ext2的超级块对象<br />当安装Ext2文件系统时(执行诸如mount -t ext2 /dev/sda2 /mnt/test的命令),存放在Ext2分区的磁盘数据结构中的大部分信息将被拷贝到RAM中,从而使内核避免了后来的很多读操作。那么一些数据结构如何经常更新呢?因为所有的Ext2磁盘数据结构都存放在Ext2磁盘分区的块中,因此,内核利用页高速缓存来保持它们最新。<br /> <br />前面谈到,安装ext2文件系统时,最终会调用ext2_fill_super()函数来为数据结构分配
2011-01-31 21:37:00 4371 1
原创 块设备驱动应用之文件读写
1 读文件<br />其实Linux驱动程序最重要,也是难点就是那个块设备驱动程序。要全面研究这个问题不是那么容易,从本博开始,我们独辟蹊径,从一个文件读写的角度把这个问题阐述干净。<br /><br />大部分程序员可能会有这样的疑问:当我们在应用程序中调用库函数 read 时,这个请求是经过哪些处理最终到达磁盘的呢,数据又是怎么被拷贝到用户缓存区的呢?我们就从 read 系统调用发出到结束处理的全过程,来解密整个内核块设备驱动的内幕。<br /> 1.1 系统调用VFS层的处理<br />用户要读写文件
2011-01-31 21:32:00 4283
原创 设备驱动程序共性
<br />设备驱动程序是一组内核例程的集合,它使得硬件设备响应控制设备的编程接口,最关键的是该接口是一组规范的VFS函数集(open, read, lseek, ioctl等等)。这些函数的实际实现由设备驱动程序全权负责。由于每个设备都有一个唯一的I/O控制器,因此就有唯一的命令和唯一的状态信息,所以大部分I/O设备都有自己的驱动程序。<br /> <br />设备驱动程序的种类有很多。它们在对用户态应用程序提供支持的级别上有很大的不同,也对来自硬件设备的数据采集有不同的缓冲策略。这些选择极大地影响了设备
2011-01-31 16:39:00 3679
转载 linux设备模型 —— sysfs
1 sysfs初探<br />"sysfs is a ram-based filesystem initially based on ramfs. It provides a means to export kernel data structures, their attributes, and the linkages between them to userspace.” --- documentation/filesystems/sysfs.txt<br />可以先把documentation/fi
2011-01-31 16:34:00 3467 1
原创 添砖加瓦
5.2.6 添砖加瓦<br />回到setup_arch,来到1007,调用paging_init()进行页面初始化。<br /> <br />825void __init paging_init(void)<br /> 826{<br /> 827 pagetable_init();<br /> 828<br /> 829 __flush_tlb_all();<br /> 830<br /> 831 kmap_init();<br /> 832<br /> 833
2011-01-12 00:23:00 3558
原创 建立内存管理架构
5.2.5 建立内存管理架构回到setup_arch函数的中,第995行调用initmem_init来启用初始化期间的内存管理器early。这个函数在两个文件中有定义,arch/x86/mm/init_32.c和arch/x86/mm/numa_32.c,取决于是否启动了编译选项CONFIG_NEED_MULTIPLE_NODES。这个编译选项是什么意思?这得从NUMA说起。NUMA翻译成中文就叫“非对称内存访问体系”,其目的是为多CPU,或大型计算机集群提供一个分布式内存访问环境,而每一个分布式节点就叫做
2011-01-11 23:13:00 3038
原创 第二次启动分页管理
5.2.4 第二次启动分页管理<br />回到init_memory_mapping()函数,之后nr_range次调用kernel_physical_mapping_init(),nr_range为map_range数组的总的元素数,前面总共执行了两次save_mr,所以nr_range为2,分别是0~1<<9和2MB~end>>12的map_range结构,代表4k和2MB两种形式的内存页表。不过为了方便起见,我们暂时忽略2MB页面大小的体系,只分析传统4k页面体系。<br /> <br />238un
2011-01-11 23:09:00 2724 1
原创 着手建立内核永久页表
5.2.3 着手建立内核永久页表<br />得到了总的页面数max_pfn和高端页面数highmem_pages之后,来到setup_arch的947行,调用init_memory_mapping()函数来建立系统初始化阶段的临时分页体系,传入的参数意义代表从0~max_low_pfn对应的32位物理地址(低12位全为0,也就是页面对齐),在函数init_memory_mapping函数中先后调用下面的几个函数来设置内存相关数据(因为bootmem此时没有初始化):<br />find_early_tabl
2011-01-11 23:07:00 4057 2
原创 获得总页面数
5.2.2 获得总页面数<br />回到setup_arch()中,接下来,继续走,891行调用e820_end_of_ram_pfn()函数根据e820的数据来获得32位可用物理内存地址的最大值并右移PAGE_SHIFT,也就是12位,最后由函数e820_end_pfn返回这个20位的值,保存在内部变量max_pfn中,作为总的页面数量:<br /> <br />855static unsigned long __init e820_end_pfn(unsigned long limit_pfn, uns
2011-01-11 23:06:00 2242
原创 拷贝可用内存区信息
5.2.1 拷贝可用内存区信息首先setup_arch第一步要做的就是保存arch/x86/kernel/head_32.S初始化的new_cpu_data数据到boot_cpu_data中。new_cpu_data主要是保存CPU的相关信息,在哪儿初始化的?还记得我们在arch/x86/kernel/head_32.S中忽略过的checkCPUtype吗?就在那里,感兴趣的同学可以去探究一下。784行,setup_memory_map()函数,进入start_kernel内核初始化函数中第一个内存管理函数
2011-01-11 23:05:00 2685
原创 执行setup_arch()函数
5.2 执行setup_arch()函数<br />回到start_kernel当中,562行,调用setup_arch函数,传给他的参数是那个未被初始化的内部变量command_line。这个setup_arch()函数是start_kernel阶段最重要的一个函数,每个体系都有自己的setup_arch()函数,是体系结构相关的,具体编译哪个体系的setup_arch()函数,由顶层Makefile中的ARCH变量决定:<br /> <br />724void __init setup_arch(cha
2011-01-11 23:02:00 4060
原创 打印版本信息
5.1.6 打印版本信息<br />我们来简单的看看在内核中,在屏幕上打印一条信息的原理是怎么实现的。由于我们配置了CONFIG_PRINTK编译选项,所以调用位于kernel/printk.c中的printk函数:<br /> <br />584 asmlinkage int printk(const char *fmt, ...)<br />585 {<br />586 va_list args;<br />587 int r;<br />588 <br />589
2011-01-11 23:01:00 2070
原创 初始化地址散列表
5.1.5 初始化地址散列表<br />560行,page_address_init()函数,来自mm/highmem.c:<br /> <br />409void __init page_address_init(void)<br /> 410{<br /> 411 int i;<br /> 412<br /> 413 INIT_LIST_HEAD(&page_address_pool);<br /> 414 for (i = 0; i < ARRAY_SIZE(
2011-01-11 23:00:00 3551
原创 激活第一个CPU
<br />回到start_kernel,559行,boot_cpu_init函数,跟start_kernel位于同一文件:<br /> <br />494static void __init boot_cpu_init(void)<br /> 495{<br /> 496 int cpu = smp_processor_id();<br /> 497 /* Mark the boot cpu "present", "online" etc for SMP and UP case
2011-01-11 22:57:00 7786 9
原创 注册时钟事件监听器
5.1.3 注册时钟事件监听器<br />由于我们在.config文件中设置了CONFIG_GENERIC_CLOCKEVENTS,所以,start_kernel函数的第558行调用kernel/time/tick-common.c中的tick_init函数,来注册一个时钟事件监听器。tick_init只调用一个clockevents_register_notifier函数:<br /> <br />static struct notifier_block tick_notifier = {<br />
2011-01-11 22:53:00 2919
原创 启动大内核锁
5.1.2 启动大内核锁<br />回到start_kernel,557行,lock_kernel(),实际的代码来了,大内核锁。我们在.config文件中配置了CONFIG_LOCK_KERNEL的,所以这个函数是start_kernel中继local_irq_disable之后实际执行到的第二个函数。<br /> <br />有关大内核的知识,这里又简单的介绍一下。在早期的Linux内核版本中,大内核锁(big kernel block,也叫全局内核锁或BKL)被广泛使用。在2.0版本中,这个锁是相对粗
2011-01-11 22:52:00 2287
原创 初始化同步与互斥环境
5.1 初始化同步与互斥环境<br />要了解本节的内容,需要补充一下“疯狂内核之同步与互斥”的预备知识。<br /><br /> 5.1.1 屏蔽中断<br /> <br />void lockdep_init(void)<br />{<br /> int i;<br /> <br /> /*<br /> * Some architectures have their own start_kernel()<br /> * code which calls
2011-01-11 22:49:00 5133
原创 走向现代:start_kernel函数
5 走向现代:start_kernel函数<br />这是内核初始化至此,终于跑出arch/x86目录了,它在linux/init/main.c,以后的代码除了少数特例,其余的都在linux/init/目录中。<br /> <br />528asmlinkage void __init start_kernel(void)<br /> 529{<br /> 530 char * command_line;<br /> 531 extern struct kernel_param
2011-01-11 22:40:00 2799
原创 初始化0号进程
<br />arch/x86/kernel/head_32.S的336行,进入分页后的内核代码段,执行lss stack_start,%esp指令,立即为进程0建立内核态堆栈。stack_start定义在657行:<br />657 ENTRY(stack_start)<br />658 .long init_thread_union+THREAD_SIZE<br />659 .long __BOOT_DS<br /> <br />我们看到内核态堆栈由init_thread_un
2011-01-04 10:41:00 3871
NFS文件系统
2012-04-08
数据结构与算法——面向对象C++设计模式
2011-11-27
高性能分布式监控系统Ganglia详解
2011-07-10
疯狂内核之——内核初始化
2011-05-30
疯狂内核之——Linux虚拟内存
2011-05-30
疯狂内核之——进程管理子系统
2011-05-30
疯狂内核之——Linux预备知识.pdf
2011-05-30
基于C++语言的GoF23种设计模式
2011-05-29
从8086到Pentium Ⅲ微型计算机及接口技术3
2010-09-24
Linux sysfs 文件系统机制详解
2009-12-14
空空如也
TA创建的收藏夹 TA关注的收藏夹
TA关注的人