(Linux内核探秘(高剑林著 )读书笔记)
Linux 文件系统探秘
一 文件系统基本概念
1.1 VFS
Linux内核通过虚拟文件系统(VFS)管理文件系统。
对每个具体文件系统的访问要通过VFS定义的接口来实现。
VFS定义了几个重要的结构,dentry, inode, super_block,通过这些结构将一个真实的硬盘文件系统抽象到内存,通过管理这几个对象就可以完成对文件系统的一些操作。
1.2 超级块super_block
超级块代表了整个文件系统本身。
文件系统内所有的inode也都要链接到超级块的链表头。
超级块是对应文件系统自身的控制块结构。
超级块结构给出了文件系统的全局信息:文件系统的块大小,文件系统中最大文件的尺寸,指向file_system_type结构的指针,魔术数字,指向文件系统根dentry的指针,
超级块对象还定义了一些链表头,用来链接文件系统内的重要成员:s_inodes指向文件系统内所有的inode, s_dirty指向所有dirty的inode对象,s_bdev指向文件系统存在的块设备指针;
超级块结构包含一些函数指针,super_operations提供了最重要的超级块操作。
1.3 目录项dentry
在VFS里,目录本身也是一个文件;每个文件都有一个dentry,这个dentry链接到上级目录的dentry。层层链接,形成一颗dentry树。
为了加快对dentry的查找,内核使用了hash表来缓存dentry,称为dentry cache。
dentry结构的成员:d_inode指向一个inode结构;d_subdirs是子项的链表头;d_child是dentry自身的链表头;d_parent指向父dentry结构;d_hash; d_name保存文件或目录的名字;d_mounted用来指示dentry是否是一个挂载点。
1.4 索引节点inode
inode代表一个文件。
dentry和inode代表一个文件?事实基本如此。
1.5 文件
文件对象的作用是描述进程和文件交互的关系。硬盘上并在存在一个文件结构;进程打开一个文件,内核就动态创建一个文件对象。
设备的概念和总体架构
一 设备的配置表
以PCI设备为准说明。
PCI设备本身就包含一个配置表。
配置表包含设备制造商填充的厂商信息、设备属性等通用配置信息。设备厂商还应该提供设备的控制寄存器信息。设备还可能配备了内存。
设备本身有一些配置信息,如设备ID、制造商ID等。
设备寄存器基址,可以将其看作一个地址,对这个地址写指令就可以控制设备。
PCI总线规范定义的PCI设备配置空间总长度为256字节,配置信息按一定的顺序和大小依次存放。配置空间的前64字节称为配置头。配置头的主要功能是用来识别设备、定义主机访问PCI卡的方式。其余192字节称为本地配置空间,主要定义卡上局部总线的特性、本地空间基地址及范围等。
为设备服务的特殊文件系统sysfs
kobject结构
sysfs创建目录时,传递的参数就是一个kobject结构。可以认为kobject代表sysfs文件系统的一个目录。
1 kobject和kset的关系
kset结构里封装了一个kobject结构,同时包括一个链表头,属于这个kset的所有kobject都要链接到kset的链表头。
2 总线的注册
总线的注册使用platform_bus_init函数。
bus_register的作用是把总线对象注册到内核。
bus对象内含两个kset,一个是devices,另一个是drivers。
serio总线
驱动之间可以嵌套。一种类型的总线可以架构在另一种类型的总线之上。
一 总线适配器
网卡、声卡、显卡、SCSI卡等设备很多都是以PCI卡的形式出现,并插入计算机的PCI插槽。声卡显卡加载驱动后,就可以直接读写操作。SCSI卡本身又可以连接
SCSI硬盘,因此加载SCSI卡的PCI驱动后,必须进行SCSI总线扫描,发现SCSI硬盘设备,才能正确地读写硬盘。这里,SCSI卡就担任了总线桥的任务,它提供了总线
之间的协议转换和互操作。像SCSI卡这样的设备,称为主机总线适配器(HBA),它一方面是PCI设备,另一方面它又管理SCSI总线的设备。
二 向serio总线注册设备
serio总线建筑在platform总线之上,它们分工合作,共同提供了完整的驱动功能。
从架构的角度看,serio总线这种总线嵌套使用模式类似于总线适配器的模式。
1 注册端口登记事件
serio_register_port函数的作用是注册serio总线。
serio_register_port函数的输入参数serio设置了端口类型。
serio_queue_event函数首先遍历内核的serio_event_list链表。同一个端口只能注册一次。然后创建一个serio_event结构,设置这个serio_event结构的类型为端口注册,唤醒处理这个事件的任务。
2 遍历总线的驱动
serio_add_port函数的关键部分是device_add函数。
serio总线的match函数定义在serio.c文件。serio_bus_match函数在设备注册时多次调用,它的输入参数是serio总线上注册的每一个驱动,需要逐个检查端口设备serio和驱动的匹配情况。
3 注册input设备
调用input_register_device注册input设备。
三 虚拟键盘驱动
PCI总线
一 深入理解PCI总线
当PCI总线扫描到PCI设备后,已经为设备设置了DMA信息、中断信息和I/O端口、I/O内存信息。
1.1 PCI设备工作原理
PCI设备具有自己的设备配置信息,也具备I/O端口和I/O内存,这些端口和内存构成一个独立的地址空间,就是PCI总线地址空间。
CPU要通过主桥才能访问PCI地址空间,PCI设备也要通过主桥才能访问主存。
主桥可以直接产生一条PCI总线,这条总线也是主桥管理的第一条总线,也是0号PCI总线。在内核代码中,会直接使用这条0号总线。
从该总线还可以扩展出一系列的PCI总线,称为PCI桥,以主桥为根节点,这些桥和设备形成了一颗PCI树。
一条PCI总线上,最多只能挂载256个PCI设备。
1.2 PCI总线域
PCI设备具有一个8 bit的总线号,一个5bit的设备编号以及一个3bit的功能编号。
一个主桥下最多拥有256个总线,对大型系统而言这是不够的,为此Linux引入PCI域的概念。
1.3 PCI资源管理
为了管理PCI设备的I/O端口和I/O内存,内核定义了一个resource结构。