- 论坛徽章:
- 0
|
翻译 :Liangvy JU6Linux联盟
E-mail : liangvy@bigfoot.com JU6Linux联盟
原著 :FreeBSD核心入门(日文版) 大木敦雄 JU6Linux联盟
JU6Linux联盟
1.1概述 JU6Linux联盟
FreeBSD可以在PC/AT兼容机器上运行。CPU是i386,i486,Pentium, JU6Linux联盟
Pentium Pro以及其兼容芯片等。 JU6Linux联盟
1.1.1(略) JU6Linux联盟
1,理论地址: 2个13 bit 长+32 bit 长 JU6Linux联盟
2,线形地址:32 bit 长的空间 JU6Linux联盟
3,物理地址:32 bit 长的空间 JU6Linux联盟
1.1.2进程的虚拟空间 JU6Linux联盟
1,text部分 JU6Linux联盟
这部分是执行文件的的text领域,也就是机器语言部分,对于这个 JU6Linux联盟
部分的空间在机器上的物理内存页是共有的,还有,这部分最后的变量 JU6Linux联盟
地址是etext。 JU6Linux联盟
2,data和bss部分 JU6Linux联盟
执行文件的data部分,也就是初始化的数据段和执行文件指定的内 JU6Linux联盟
存变量。内存变量在开始的时候以0填充。这一段空间可以读写。它的 JU6Linux联盟
边界也是以edata和end的地址做结尾。进程的malloc()等内存分配的 JU6Linux联盟
操作的时候,地址的增加方向向bss空间进行。 JU6Linux联盟
3,stack部分 JU6Linux联盟
也就是进程执行的时候的stack空间,这部分空间(从地址的最高位 JU6Linux联盟
开始可以伸缩),其对于物理内存,伸缩程度由核心自动执行。 JU6Linux联盟
1.2 kernel的configure JU6Linux联盟
freebsd的kernel构成文件在/usr/src/sys的目录下面。下面的子目录做一个 JU6Linux联盟
介绍。 JU6Linux联盟
compile 编译核心的目录。 JU6Linux联盟
conf configure的目录。 JU6Linux联盟
ddb 核心调试的sounre code的目录。 JU6Linux联盟
dev 一部分的drivers的source code的目录。 JU6Linux联盟
gnu 浮点运算的仿真以及ex2fs文件系统的source code目录。 JU6Linux联盟
i386 依赖于pc/at机器的目录,以下介绍它的字目录。 JU6Linux联盟
apm suspend一些节电程序。 JU6Linux联盟
boot 不是kernel本身的东西,只是一些怎么从开机到读入kernel JU6Linux联盟
的boot program的source code。 JU6Linux联盟
conf config的一些依赖data。 JU6Linux联盟
isa isa bus的驱动程序类的source code。 JU6Linux联盟
eisa eisa bus的驱动程序类的source code。 JU6Linux联盟
include 对pc/at的一些include files JU6Linux联盟
i386 对pc/at的一些核心code JU6Linux联盟
ibcs2,linux 使各类的os的执行文件在freebsd上执行的code JU6Linux联盟
isofs/cd9660 JU6Linux联盟
cd-rom在unix文件系统上操作的的有关code JU6Linux联盟
kern 核心code JU6Linux联盟
libkern 核心库的source code JU6Linux联盟
miscfs 实现unix文件系统的code JU6Linux联盟
msdosfs 在unix上操作ms-dos文件系统的有关code JU6Linux联盟
net 实现network功能的基本部分code JU6Linux联盟
netatalk JU6Linux联盟
实现appletalk network功能code JU6Linux联盟
netinet 实现internet network功能的code JU6Linux联盟
netipx 实现ipx功能的code JU6Linux联盟
netns 实现ns network的code JU6Linux联盟
netkey 实现网络加密部分的功能的code JU6Linux联盟
nfs 实现nfs服务 JU6Linux联盟
pc98 对于pc98的支持 JU6Linux联盟
pccard 对pcmcia的支持 JU6Linux联盟
pci 对pci bus的驱动程序的source code JU6Linux联盟
scsi 对cd-rom,hard disk,tape 等的scsi驱动程序的source code JU6Linux联盟
sys 独立于机器体系结构的一部分code JU6Linux联盟
ufs unix file system 的支持code JU6Linux联盟
vm 虚拟内存管理的部分 JU6Linux联盟
1.2.1配置的操作----config command JU6Linux联盟
在root权限下,config,make实行后,可以得到简单的kernel。 JU6Linux联盟
*configure file JU6Linux联盟
移动到/usr/src/sys/i386/config看看。 JU6Linux联盟
GENERIC 从cd-rom等安装freebsd的时候对应于defaule kernel JU6Linux联盟
的配置file JU6Linux联盟
LINT kernel组合功能的网罗的的配置file JU6Linux联盟
下面4个是对配置很有必要的的依赖data file JU6Linux联盟
Makefile.386 config生成的Makefile file的template. JU6Linux联盟
devices.i386 对于unix filesystem可能的block型的device JU6Linux联盟
名字和major号的对照表 JU6Linux联盟
files.i386 记录kernel功能组合的基础上,依赖于pc/at JU6Linux联盟
机器的功能名称和各种功能实现的source code JU6Linux联盟
file的名字表。 JU6Linux联盟
options.i386 记录配置项目的表。 JU6Linux联盟
还有,majors.i386是记录对应驱动器的I/O表和major号的一个文件。 JU6Linux联盟
于核心配置没关系。 JU6Linux联盟
对于新的i/o设备,如果要做device driver,对pc/at,要在files.i386(没 JU6Linux联盟
有的话在/usr/src/sys/conf/files)追加相应的行,不然就不能把它加入 JU6Linux联盟
到核心里面。 JU6Linux联盟
追加的格式为 JU6Linux联盟
相对path名 optional device-name device-driver JU6Linux联盟
JU6Linux联盟
对于配置文件,首先,要设置cpu,bus,i/o设备,多少用户等。例如对于GENERIC JU6Linux联盟
machine "i386" JU6Linux联盟
cpu "I386_CPU" JU6Linux联盟
cpu "I486_CPU" JU6Linux联盟
cpu "I586_CPU" JU6Linux联盟
cpu "I686_COU" JU6Linux联盟
ident GENERIC JU6Linux联盟
maxusers 10 JU6Linux联盟
当作为server时候,应该把最大user设置大一点,以提高系统性能。 JU6Linux联盟
下一步,指定options,对于GENERIC JU6Linux联盟
options MATH_EMULATE #support for x87 emulation JU6Linux联盟
options INET #interNETworing JU6Linux联盟
options FFS #Berkeley Fast Filesystem JU6Linux联盟
options NFS #Network Filesystem JU6Linux联盟
...... JU6Linux联盟
options指定的名字xxx等,如果在/usr/src/sys/conf/options或者在 JU6Linux联盟
/usr/src/sys/i386/conf/options.i386中记载的时候,应在对应的opt_XXX.h中写入 JU6Linux联盟
。没有的话,作为cc命令行的参数定义"-D"在Makefile里面追加。对于XXX的格式应该 JU6Linux联盟
是 JU6Linux联盟
相对path名 optional xxx JU6Linux联盟
下一步,对于config JU6Linux联盟
config kernel root on wd0 JU6Linux联盟
(略) JU6Linux联盟
配置文件剩下的部分应该是bus,i/o等一些硬件配置,一般有controller,device, JU6Linux联盟
disk,tape四类。例如 JU6Linux联盟
controller isa0 JU6Linux联盟
controller eisa0 JU6Linux联盟
controller pci0 JU6Linux联盟
等。 JU6Linux联盟
第二层的device和controller,记录了一些bus设备的连接。ISA的情况是 JU6Linux联盟
device device_name at isa? 参数 JU6Linux联盟
controller controller_name at isa? 参数 JU6Linux联盟
EISA和PCI就相对简单一点: JU6Linux联盟
device device_name JU6Linux联盟
controller controller_name JU6Linux联盟
device_name里指定的设备名是,串口,并口,网络等装置。 JU6Linux联盟
第三层的disk和tape为 JU6Linux联盟
disk disk_name at 控制设备名 drive 号 JU6Linux联盟
tape tape_name at 控制设备名 drive 号 JU6Linux联盟
SCSI接口卡作为第二层的控制装置记录的同时 JU6Linux联盟
controller scbus0 JU6Linux联盟
作为通用的scsi控制设备。因此,对于它的hard disk,tape,cd-rom,mo设备,有 JU6Linux联盟
device sd0 JU6Linux联盟
device st0 JU6Linux联盟
device cd0 JU6Linux联盟
device od0 JU6Linux联盟
等,它可以自动识别和分配号码。 JU6Linux联盟
对于其他的scsi设备,有 JU6Linux联盟
device pt0 at scbus? JU6Linux联盟
这些东西(bus,scsi,i/o),在生成的ioconf.c以及相应的include中有反映。 JU6Linux联盟
configure的最后,不是一些物理设备,而是kernel内部的一些软设置 JU6Linux联盟
pseudo-device 理论设备名 JU6Linux联盟
首先,要考虑以下两个设备: JU6Linux联盟
pseudo-device pty 16 #ttys - can go as high as 256 JU6Linux联盟
pseudo-device log #syslog interface (/dev/klog) JU6Linux联盟
network使用的场合,应该有下面两个 JU6Linux联盟
pseudo-device loop JU6Linux联盟
pseudo-device ether JU6Linux联盟
这种情况下,最好有 JU6Linux联盟
pseudo-device bpfilter 4 #berkeley packet filter JU6Linux联盟
pseudo-device tun 1 #Tunnel driver ( PPP) JU6Linux联盟
想做floppy的时候,要 JU6Linux联盟
pseudo-device vn #Vnode driver ( turns a file into a device) JU6Linux联盟
(代续) JU6Linux联盟
JU6Linux联盟
JU6Linux联盟
JU6Linux联盟
FreeBSD核心探讨(翻译)2 JU6Linux联盟
JU6Linux联盟
1.3 FreeBSD boot之前的工作 JU6Linux联盟
1.3.1pc/at机器的boot顺序 JU6Linux联盟
hard disk的最前面的一个block(512byte),叫做master boot recorder(MBR).这 JU6Linux联盟
里有启动限定的program和分区的信息。分区信息是指对于一个区是16byte长,最多 JU6Linux联盟
只能有4个区。16byte的内容是,分区哪里开始,哪里结束。哪种os,能否启动等。对 JU6Linux联盟
于freebsd,安装的时候向MBR写入了boot easy. JU6Linux联盟
磁盘的结构如下图表示: JU6Linux联盟
block Number JU6Linux联盟
#0 #1 #2 ... #14 #15 #16 #17 JU6Linux联盟
------------------------------------------------------------- JU6Linux联盟
disk no used JU6Linux联盟
label JU6Linux联盟
------------------------------------------------------------- JU6Linux联盟
;|;| |JU6Linux联盟
JU6Linux联盟
FreeBSD用的block#0--#14的15个block里面,含有读入freebsd的程序,bootease JU6Linux联盟
只在block#0里面,在15个block中并没有。它的作用 JU6Linux联盟
。读入mbr,找freebsd的分区 JU6Linux联盟
。读入最初的15个block,到物理内存中0x0001000 JU6Linux联盟
。跳转到相当于block#2的内存位置 JU6Linux联盟
然后,屏幕表示为: JU6Linux联盟
。。。 JU6Linux联盟
。。。 JU6Linux联盟
boot: JU6Linux联盟
(参数说明略) JU6Linux联盟
它的source是/usr/src/sys/i386/boot/biosboot,make之后,生成两个文件: JU6Linux联盟
boot1,boot2分别写入block#1,block#2--#14中。 JU6Linux联盟
一般,一个物理的unix分区理论上可以有8个,比如swap,unix system等。 JU6Linux联盟
boot2部分是boot program,它读入kernel的文件名和option。然后 JU6Linux联盟
。找boot label指定的分区。 JU6Linux联盟
。构造unix filesystem,找指定的kernel JU6Linux联盟
。从开始执行文件,text,data的顺序向物理内存读入。对bss清零。 JU6Linux联盟
。以option的选择,向开始位置跳转。 JU6Linux联盟
1.3.2 kernel的初始化动作 JU6Linux联盟
boot program执行之后,转向kernel的text段开始进行初始化,即先执行 JU6Linux联盟
locore.s的text段。因此是虚拟内存还没有发生作用,locore.s的开始部分必 JU6Linux联盟
须对offset进行补正。locore.s的作用是 JU6Linux联盟
。保存从boot program过来的option JU6Linux联盟
。设定虚拟的stacker JU6Linux联盟
。检测cpu的module JU6Linux联盟
。对自己的bss空间进行0初始化 JU6Linux联盟
。为使虚拟内存工作,要保证最少的管理信息。然后是虚拟空间动作。 JU6Linux联盟
也就是,调用cpu有强的依赖关系的过程init386()(@i386/i386/machdep.c), JU6Linux联盟
然后进行kernel内的管理信息初始化,i/o设备的登记,生成4个kernel process JU6Linux联盟
,再调用main()(@kern/init_main.c)。当main()返回locore.s时,应该有如下 JU6Linux联盟
5个进程: JU6Linux联盟
PID TT STAT TIME COMMAND JU6Linux联盟
0 ?? DLs 0:00.17 (swapper) JU6Linux联盟
1 ?? Is 0:00.19 /sbin/init -- JU6Linux联盟
2 ?? DL 0:56.60 (pagedaemon) JU6Linux联盟
3 ?? DL 0:00.06 (vmdaemon) JU6Linux联盟
4 ?? DL 6:07.65 (updata) JU6Linux联盟
从locore.s返回到process #1,/sbin/init开始动作,然后转向freebsd的普通 JU6Linux联盟
动作。 JU6Linux联盟
init386()和main()的处理大致如下: JU6Linux联盟
。init386() JU6Linux联盟
GDT和LDT,IDT,task stages处理的初始化,例外处理等locore.s没做的 JU6Linux联盟
事情,虚拟内存初始化。然后,根据boot program的参数,增加物理内 JU6Linux联盟
存page数。然后,作成process #0的雏形。 JU6Linux联盟
。main() JU6Linux联盟
逐步调用构成kernel模块的的初始化部分。 JU6Linux联盟
JU6Linux联盟
JU6Linux联盟
FreeBSD核心探讨(翻译)3 JU6Linux联盟
JU6Linux联盟
(续上,liangvy.icewolf.leon翻译) JU6Linux联盟
JU6Linux联盟
但是,kernel构成的各个模块的初始化子程序一个个的列举出来运行很显然是 JU6Linux联盟
不行的。通常是利用时间连表的技能来运行它(ld command)。也就是,程序 JU6Linux联盟
是以很多个source分开编译和联结。相同的模块名字就对应于相同的地址来进 JU6Linux联盟
行调用。它在时间链表里面自动调节执行。 JU6Linux联盟
初始化时候,main()函数要call的模块利用在sys/kernel.h里面定义的宏 JU6Linux联盟
SYSINIT()和SYSINIT_KT()进行登记。这样,kernel在link的时候,ld命令就 JU6Linux联盟
能够得到那些信息和进行配置列表。这个列表就是kernel的组成模块的初始化 JU6Linux联盟
routine的登记。检查source, JU6Linux联盟
就可以找到初始化routine的部分。 JU6Linux联盟
如表: JU6Linux联盟
print_caddr_t(copyright) kern/init_main.c JU6Linux联盟
vm_men_init(NULL) vm/vm_init.c JU6Linux联盟
syctl_order(&sysctl_) kern/kern_sysctl.c JU6Linux联盟
kmemnit(NULL) kern/kern_malloc.c JU6Linux联盟
fpu_init(NULL) i386/i386/math_emulate.c JU6Linux联盟
cpu_startup(NULL) i386/i386/machdep.c JU6Linux联盟
gnufpu_init(NULL) miscfs/devfs/devfs_tree.c JU6Linux联盟
... JU6Linux联盟
各个device的major号与处理routine的登记 (major循序号) JU6Linux联盟
... JU6Linux联盟
configure(NULL) i386/i386/autoconf.c JU6Linux联盟
proc0_init(NULL) kern/init_main.c JU6Linux联盟
rqinit(NULL) kern/kern_synch.c JU6Linux联盟
vm_init_limits(&proc0) vm/vm_glue.c JU6Linux联盟
vfsinit(NULL) kern/vfs_init.c JU6Linux联盟
elf_insert_brand_entry(&linux_brand) i386/linux/linux_sysvec.c JU6Linux联盟
initclocks(NULL) kern/kern_clock.c JU6Linux联盟
mbinit(NULL) kern/uipc_mbuf.c JU6Linux联盟
clst_init(NULL) kern/tty_subr.c JU6Linux联盟
shmnit(NULL) kern/sysv_shm.c JU6Linux联盟
seminit(NULL) kern/sysv_sem.c JU6Linux联盟
msginit(NULL) kern/sysc_msg.c JU6Linux联盟
kludge_splimp(&x_save_spl) kern/uipc_domain.c JU6Linux联盟
ifinit(NULL) net/if.c JU6Linux联盟
domaininit(NULL) kern/uipc_domain.c JU6Linux联盟
kludge_splx(&x_save_spl) kern/uipc_domain.c JU6Linux联盟
kmstartup(NULL) kern/subr_prof.c JU6Linux联盟
sched_setup(NULL) kern/init_main.c JU6Linux联盟
xxx_vfs_mountroot(NULL) kern/init_main.c JU6Linux联盟
xxx_vfs_root_fdtab(NULL) kern/init_main.c JU6Linux联盟
swapinit(NULL) kern/init_main.c JU6Linux联盟
proc0_post(NULL) kern/init_main.c JU6Linux联盟
kthread_init(NULL) kern/init_main.c|| JU6Linux联盟
kproc_start(&page_kp) vm/vm_pageout.c|| JU6Linux联盟
kproc_start(&vm_kp) vm/vm_pageout.c|| JU6Linux联盟
kproc_start(&up_kp) kern/vfs_bio.c|| JU6Linux联盟
scheduler(NULL) vm/vm_glue.c JU6Linux联盟
(||表示有多个程序) JU6Linux联盟
proc-post()被呼叫后,main()就是在对应process 0 的kernel的虚拟 JU6Linux联盟
内存里动作。kthread_init(),kproc_start(&page_kp),kproc_start(&vm_kp) JU6Linux联盟
,kproc_start(&up_kp)等这几个进程,在fork()后相继被调用。它就是相 JU6Linux联盟
应的进程1,2,3,4等。 JU6Linux联盟
除process 1 以外,其他的进程调用并不返回调用的地址。(也就是,main() JU6Linux联盟
的跟随执行后,并不返回locore.s)。对于process #1的kernel的虚拟内存, JU6Linux联盟
在kthread_init()返回后,main()的跟随就完了,回到locore.s后,process #1 JU6Linux联盟
的进程空间的配置文件/sbin/init就被执行。 JU6Linux联盟
main()在process #0对应的kernel虚拟内存运行后,进入时间链表scheduler()。 JU6Linux联盟
这个并不返回。那现在就有五个进程了。 JU6Linux联盟
然后,fork() 的调用在下面说明。 JU6Linux联盟
1,分配process ID,保证struct proc()用的空间。 JU6Linux联盟
2,复制父亲的process的虚拟内存空间,作成物理内存的变换表。对 JU6Linux联盟
应两个进程,采用相对应的物理内存表。 JU6Linux联盟
3,给回父亲的struct proc和struct user,然后对子进程的struct和 JU6Linux联盟
struct user进行初始化。 JU6Linux联盟
4,kernel的stacker也进行复制。 JU6Linux联盟
5,返回父进程后,标记生成的子进程。完成处理。 JU6Linux联盟
但是,process #0 -- 4 这五个进程的虚拟内存里面什么都没有。这些是核心 JU6Linux联盟
进程的特殊部分。进程0,2,3是调节系统存在的进程的执行优先级,监视物理 JU6Linux联盟
内存的不足,如果不够就使用swap区进行交换。进程4的作用就是定期调查核心 JU6Linux联盟
的unix文件系统的管理信息与驱动程序的管理信息的一致性,使它的信息一直 JU6Linux联盟
是最新的。 JU6Linux联盟
JU6Linux联盟
1.3.3 /sbin/init JU6Linux联盟
从kernel里面看,/sbin/init就是单一的进程空间里动作,与一般的 JU6Linux联盟
user program一样,提供user使用的unix文件系统的环境的服务。 JU6Linux联盟
核心启动后最初的动作就是/sbin/init。作用如下: JU6Linux联盟
。确保file system的一致性,进行mount。 JU6Linux联盟
。之后,network的设定和各种daemon的启动。 JU6Linux联盟
。监视终端的login的配置和动作状态。这个动作完了后(logout), JU6Linux联盟
修改和配置 login。 JU6Linux联盟
也就是说,如果没有它,用户就不能使用unix文件系统。还有就是,如果boot JU6Linux联盟
progam参数指定-s的话,它就过渡到单一的用户模式。相对来说,普通的用户 JU6Linux联盟
模式也就是multi模式。为了使普通用户能够使用系统,/sbin/init的参考文件 JU6Linux联盟
主要在/etc目录里放着。主要就是运行/etc/rc文件对系统进行初始化。 JU6Linux联盟
/etc/rc文件的主要内容和作用如下: JU6Linux联盟
。使系统能够使用swap区 JU6Linux联盟
。检查/etc/fstab,检查它的连贯性,如果有问题就转到单一的用户模式 JU6Linux联盟
。mount nfs以外的文件系统 JU6Linux联盟
。读入network 的设定和各种daemon进程的设定情况的记录文件 JU6Linux联盟
/etc/c.conf,这个内容作为shell script的变量设定,以下的就是 JU6Linux联盟
各个shell的动作调整 JU6Linux联盟
。serial的初始化(/etc/rc.serial) JU6Linux联盟
。运行PCMCIA卡的插拔监控守护进程(/etc/rc.pccard) JU6Linux联盟
。network的部分初始化(/etc/rc.network) JU6Linux联盟
。如果有nfs的时候就进行mount操作 JU6Linux联盟
。network的最终初始化(/etc/rc.network:启动和entwork有关的daemon) JU6Linux联盟
。共有库的有关信息的初始化 JU6Linux联盟
。intd,lpd,sendmail的启动 JU6Linux联盟
。依赖系统的一些初始化进程 JU6Linux联盟
/etc/rc的处理完了后,/sbin/init就对/etc/ttys等记述的一些终端的用户login进行 JU6Linux联盟
监视。对于这个,/etc/ttys里指定的终端,fork()后的进程里: JU6Linux联盟
。exec()指定的程序(普通的情况是/usr/libexec/getty) JU6Linux联盟
。/usr/libexec/getty进行终端速度等的设定。提示login:,等待用户输入 JU6Linux联盟
。用户输入后,名字作为参数exec() /etc/bin/login JU6Linux联盟
。/usr/bin/login就提示出passwd:,等待用户的输入 JU6Linux联盟
。准备user名和passwd,对输入的用户名进行确定,正确的话就exec()用户 JU6Linux联盟
shell JU6Linux联盟
JU6Linux联盟
下图就是/sbin/init的监视进程图: JU6Linux联盟
JU6Linux联盟
process #1 JU6Linux联盟
-------------------------------------------------------->; JU6Linux联盟
/sbin/init | ^ \ JU6Linux联盟
| fork() | | fork() JU6Linux联盟
+ exec() exec() exec() | | exec() JU6Linux联盟
process #n |---------->;+--------->;+------------------*+-------- JU6Linux联盟
getty login user的login shell process #m JU6Linux联盟
JU6Linux联盟
(第一章完,下一章介绍文件系统和驱动程序,liangvy) JU6Linux联盟
JU6Linux联盟
JU6Linux联盟
JU6Linux联盟
FreeBSD核心探讨.4.驱动程序篇 JU6Linux联盟
JU6Linux联盟
翻译:liangvy liangvy@bigfoot.com icewolf.leon :-) JU6Linux联盟
版权所有,可以转贴 JU6Linux联盟
JU6Linux联盟
第二章 文件系统和设备驱动程序 JU6Linux联盟
JU6Linux联盟
这章主要介绍文件系统和特殊的设备文件以及它们的对应关系。 JU6Linux联盟
JU6Linux联盟
2..1 disk上的 unix file system 的基本知识 JU6Linux联盟
首先介绍一下经典的unix file system的思维方法。 JU6Linux联盟
disk 的 partition就是从0到512byte的连续长度的block的东西。这里有 JU6Linux联盟
1.file/directory有关的固定长度的信息,i-node JU6Linux联盟
2.file/directory的本体,data block JU6Linux联盟
的两样不同的东西。partition的前面的附近块(block#16 ,1--15用于boot JU6Linux联盟
program :-))就是i-node,data block用的领域等等的开始位置(block号) JU6Linux联盟
和长度(block数量)等的记录,叫做super-block。一个block可能的容量只 JU6Linux联盟
能有固定数目的i-node,所以如果分配了固定的i-node,收录了节点号和节 JU6Linux联盟
点的块号和块的位置就可以计算出来。 JU6Linux联盟
i-node就是 JU6Linux联盟
。表明i-node的种类(file ,direstory,device等) JU6Linux联盟
。这个节点参考的次数(目录数) JU6Linux联盟
。参考,作成,变化的时间 JU6Linux联盟
。权限 JU6Linux联盟
。所有者的user id / group id JU6Linux联盟
。本体的长度 JU6Linux联盟
。收集本体的data block的block号码的固定长度的对应表 JU6Linux联盟
的一些记录。因为data block的对应表是固定的关系,比 JU6Linux联盟
如10个,最长就能够作出512*10=5k为止的file。 JU6Linux联盟
JU6Linux联盟
当文件比块大的时候,unix就采用成组联结的方式对它们进行管理。就是 JU6Linux联盟
把所有的空闲块以一定数目为一组的方法作成单向空闲块stacker。 JU6Linux联盟
特别地,文件的从先头的byte位置开始和i-node内的对应表有着密切的关系。 JU6Linux联盟
而且,对于i-node的输入输出,可以对应指定位置的数据块进行读写。重要 JU6Linux联盟
的是,核心可以依照这个管理表对io装置进行管理。 JU6Linux联盟
unix对io设备的操作也是作为(特殊)文件进行的。对于用i-node进行 JU6Linux联盟
描述的io设备,data block数据块的对应表就没必要了。这个部分的io设备 JU6Linux联盟
的识别就通过device号码来进行。向这些对i-node进行输入输出处理的, JU6Linux联盟
就又设备驱动号区别,来进行device driver驱动。 JU6Linux联盟
那么,节点怎么的进行查找呢?partition的最初的目录(根目录)就是, JU6Linux联盟
从第二个i-node开始,一个一个顺着节点进行查找。 JU6Linux联盟
比如,对于目录/uuu/vvv/.../yyy/zzz的查找方式,有这种关系: JU6Linux联盟
。i-node #2 所存放的是root directory。读入它的本体,就可以找到 JU6Linux联盟
相应的uuu所对应的i-node。 JU6Linux联盟
。读入这个i-node所存放的directory的i-node本体,找到相应的vvv节点。 JU6Linux联盟
...... JU6Linux联盟
查找对应yyy的节点 JU6Linux联盟
。读入这个节点的本体信息,这里包含目录本题的内容,这样就可以找到 JU6Linux联盟
zzz所对应的i-node。 JU6Linux联盟
目录里面由于记录了对应文件名的节点号,所以,也有可能同一个节点号 JU6Linux联盟
根据文件名不一样,就可以找到不同的目录名。这就是硬连接(hard link). JU6Linux联盟
但是,节点号有只存在于节点所在的分区的含义,所以,不同的分区, JU6Linux联盟
这种硬连接就不具有存在的可能性。为了解决这个矛盾,就有了符号连接 JU6Linux联盟
(symble link)的说法。当节点是输入符号连接的时候,符号连接就包含 JU6Linux联盟
了这个节点的data block所指定的路径名。但是,空连接和loop连接这种 JU6Linux联盟
情况也是允许的,所以核心要指定循环连接的最大次数。具体由参数 JU6Linux联盟
MAXSYMLINKS(@sys/param.h)指定。 JU6Linux联盟
这样,多个分区建立一个文件系统就有可能了。启动核心的分区作为一个 JU6Linux联盟
已存的文件系统,其他的分区就嫁接到目录层上面。这个操作过程就是mount。 JU6Linux联盟
利用mount指令,就可以实现上面的操作。但是,mount之前的目录,在mount后 JU6Linux联盟
就给屏蔽了,直到mount结束,那些目录就可以再现。 JU6Linux联盟
以上就是经典的unix文件系统理论。但是,对于读入了i-node,就去读 JU6Linux联盟
data block ,这种情况,对于一个比较大的分区,硬盘磁头向disk head的距 JU6Linux联盟
离就太大了。总的来说,访问时间就会变长。在这里有一些指导思想: JU6Linux联盟
。分区要比较小,多分小区 JU6Linux联盟
。了解超级块的地位,超级块记录了分区的信息,考虑由于介质的原因而 JU6Linux联盟
使这个超级块造成损害,所以,在分区内部就必须为它准备多几个拷贝。 JU6Linux联盟
。目录和它下层的文件,要在相同的领域内放置。 JU6Linux联盟
。确保单位data block要比磁盘的block大。 JU6Linux联盟
考虑了一些东西后,经过改良标准,freebsd就采用一个叫做FFS的文件系 JU6Linux联盟
统(Fast File System),但这只是i-node领域/data领域的配置方法的变化,基 JU6Linux联盟
本的考虑方法并没有变。对磁盘分区进行文件系统的构造的初始化由命令newfs JU6Linux联盟
提供。看看它的source就知道怎么配置的了。其他的构造(......)对应于kernel JU6Linux联盟
的source,对于构成boot program的文件disk.c和sys.c(@i386/boot/biosboot) JU6Linux联盟
比较简单易懂(单纯性)。 JU6Linux联盟
上面讲述的i-node对disk的partition的记录形式,详细的(source)在 JU6Linux联盟
struct dinode(@ufs/ufs/dinode.h)里面有。在核心内部使用的,包含这个东西 JU6Linux联盟
的是struct inode(@ufs/ufs.inode.h)。 JU6Linux联盟
JU6Linux联盟
描述io设备的文件叫特殊文件(special file),他对应的i-node有两个种 JU6Linux联盟
类: JU6Linux联盟
。块型(block) JU6Linux联盟
和装置的固有的数据记录的单位(大多数的情况是512byte)无关。读写 JU6Linux联盟
的最小单位是1byte,可以在任意的场所里任意长度的data。核心对各个 JU6Linux联盟
block型的特殊文件进行固定的记录单位长度(倍数)进行缓冲(buffer) JU6Linux联盟
管理,这样就可以处理任意长度的读写了. JU6Linux联盟
。文字型(char) JU6Linux联盟
读写的基本单位是,受到装置固有的date记录单位长的限定。没有block JU6Linux联盟
型的缓冲管理,对应于装置的物理特性,读写属于专用。或者说,是读写 JU6Linux联盟
两用。 JU6Linux联盟
除了网络接口之外,io装置可以全部分为文字型和块型两个大类。总的来说, JU6Linux联盟
磁盘操作的两样都用,但其他的io装置只有文字型。还有就是一些没对应物理设 JU6Linux联盟
备的kernel modules提供的虚拟设备也有,它们对应着文字型的特殊文件。特殊 JU6Linux联盟
文件习惯放在目录/dev里面。 JU6Linux联盟
JU6Linux联盟
对于特殊设备文件的i-node有block和chat两个类,设备通过驱动号进行记录 JU6Linux联盟
。通过这些,就可以识别device driver。device 号就是major号(8bit)(主设备 JU6Linux联盟
号)和minor号(24bit)(辅助设备号),device driver的识别就是由major的不 JU6Linux联盟
一样而区别。而且呢,block型,char型的等等可能存在最大数目是256种类。一般的 JU6Linux联盟
情况,同种类的设备不同数目的区别就是通过辅助设备号进行识别。实际上,对于 JU6Linux联盟
disk的特殊文件,有disk/slide/partition表示法,而且,文字型,块型等的特殊 JU6Linux联盟
设备文件也存在。以下就是一个ide硬盘的的文字型特殊设备文件的例子: JU6Linux联盟
/dev/rwd0 1台ide的硬盘 JU6Linux联盟
/dev/rwd0s1 1台ide的硬盘的slide #1 JU6Linux联盟
/dev/rwd0s2 1台ide的硬盘的slide #2 JU6Linux联盟
/dev/rwd0s2a slide #2的partition a JU6Linux联盟
/dev/rwd0s2b slide #2的partition b JU6Linux联盟
... JU6Linux联盟
/dev/rwd0s3 1台的ide的硬盘的slide #3 JU6Linux联盟
JU6Linux联盟
如果把rwd换成wd,对应的就是block型的特殊设备文件了。 JU6Linux联盟
对于磁盘,有如下的使用方法: JU6Linux联盟
。对于slide的文字型特殊文件 JU6Linux联盟
读写disk label时候使用(disklabel command) JU6Linux联盟
。对于对应的partition的文字型特殊文件 JU6Linux联盟
在分区上建立unix文件系统时候(newfs command),文件系统修复, JU6Linux联盟
检查(fsck)时候使用 JU6Linux联盟
。对于partition的block型的特殊文件 JU6Linux联盟
作为mount命令的参数使用 JU6Linux联盟
(下一节介绍虚拟文件系统和v-node,要休息了:-) ) JU6Linux联盟
JU6Linux联盟
JU6Linux联盟
JU6Linux联盟
FreeBSD核心探讨.5.驱动程序篇 JU6Linux联盟
JU6Linux联盟
2.2 虚拟文件系统和v-node JU6Linux联盟
FreeBSD在disk上的除了ffs以外还可以操作各种各样的文件系统。主要的如 JU6Linux联盟
下: JU6Linux联盟
。cd9660 JU6Linux联盟
可以对ISO9660形式的cd-rom的目录/文件构造的文件系统进行mount, JU6Linux联盟
locate等目录层的操作 JU6Linux联盟
。ms-dos JU6Linux联盟
对ms-dos文件系统进行目录层次的mount,定位等操作 JU6Linux联盟
。mfs JU6Linux联盟
通过使用虚拟内存对swap区的一部分进行unix文件系统的构造,定位 JU6Linux联盟
作为目录的一部分进行读写 JU6Linux联盟
。nfs JU6Linux联盟
由nfs server提供的remote目录级进行mount,定位的目录层操作。 JU6Linux联盟
。null JU6Linux联盟
对已经存在的目录层的使用别名 JU6Linux联盟
。union JU6Linux联盟
对已有的目录A(上层)在下层目录B上进行重叠 (不大理解这的意思 JU6Linux联盟
,大概是在下层目录里面又嫁接了上层目录的意思:译者)。文件名的查 JU6Linux联盟
找由上层优先进行。没有的话就转道下层。如果对下层的文件进行写操作 JU6Linux联盟
,它的拷贝就在上层上进行。举例说明,作业目录在上层,但cd-rom的源 JU6Linux联盟
在下层,两个目录重叠,那么编译source的时候,就相当方便了。 JU6Linux联盟
。procfs JU6Linux联盟
对于进程号的目录作成mount point。通过文件名对各个目录的进程进行 JU6Linux联盟
控制。 JU6Linux联盟
。kernfs JU6Linux联盟
为了对动作中的kernel有关的信息进行参考,而作成的mount point JU6Linux联盟
。fdesc JU6Linux联盟
对于各个进程,用它所打开的文件柄对应的文件作成的mount point JU6Linux联盟
JU6Linux联盟
实际上,在核心内部,为了对它们进行统一操作,就对文件系统和v-node JU6Linux联盟
进行抽象化,实际的处理过程就是调用各类的文件系统的模块进行处理。 JU6Linux联盟
JU6Linux联盟
2.2.1对虚拟文件系统的操作 JU6Linux联盟
各个文件系统可以提供的操作的一览如下,它在struct vfsops JU6Linux联盟
(@sys/mount.h)里面定义: JU6Linux联盟
。对文件系统进行mount的操作 JU6Linux联盟
。本文件系统的开始动作的操作 JU6Linux联盟
。本文件系统的umount操作 JU6Linux联盟
。表达文件系统的根的v-node的查找操作 JU6Linux联盟
。对一般用户的权限控制 JU6Linux联盟
。取得文件系统的状态 JU6Linux联盟
。内存内的管理信息写入介质中 JU6Linux联盟
。从i-node到v-node的取得操作 JU6Linux联盟
。v-node和nfs的文件柄的相互变换的操作 JU6Linux联盟
。文件系统实际的模块的初始化 JU6Linux联盟
JU6Linux联盟
对于文件系统,各个实际的操作routine在vfsops的形式提供准备工作。各个文件系 JU6Linux联盟
统的vfsops,在以下的表里的source进行定义: JU6Linux联盟
JU6Linux联盟
-------------------------------------------------------------- JU6Linux联盟
file system vfsops的定义 source JU6Linux联盟
-------------------------------------------------------------- JU6Linux联盟
ufs ufs_vfsops ufs/ffs/ffs_vfsops.c JU6Linux联盟
cd9660 cd9660_vfsops isofs/cd9660/cd9660_vfsops.c JU6Linux联盟
msdos msdosfs_vfsops msdosfs/msdosfs_vfsops.c JU6Linux联盟
mfs mfs_vfsops ufs/mfs/mfs_vfops.c JU6Linux联盟
nfs nfs_vfsops nfs/nfs_vfsops.c JU6Linux联盟
null null_vfsops miscfs/nullfs/null_vfsops.c JU6Linux联盟
nuion union_vfsops miscfs/union/union_vfsops.c JU6Linux联盟
procfs procfs_vfsops miscfs/procfs/procfs_vfsops.c JU6Linux联盟
kernfs kernfs_vfsops miscfs/kernfs/kernfs_vfsops.c JU6Linux联盟
fdesc fdesc_vfsops miscfs/fdesc/fdesc_vfsops.c JU6Linux联盟
devfs devfs_vfsops miscfs/devfs/devfs_vfsops.c JU6Linux联盟
ext2fs ext2fs_vfsops gnu/ext2fs/ext2_vfsops.c JU6Linux联盟
lfs lfs_vfsops ufs/lfs/lfs_vfsops.c JU6Linux联盟
portal portal_vfsops miscfs/portal.portal_vfsops.c JU6Linux联盟
umap umap_vfsops miscfs/umapfs/umap_vfsops.c JU6Linux联盟
--------------------------------------------------------------- JU6Linux联盟
这些就是文件系统的实际模块(*_vfsops.c),文件系统名称,文件系统号等等 JU6Linux联盟
在struct vfsconf(@sys/mount.h)里面汇总,各个模块里用宏VFS_SET()进入核 JU6Linux联盟
心。 JU6Linux联盟
根据main()(@kern/init_main.c),在kernel初始化的过程中,vfsinit() JU6Linux联盟
(@kern/vfs_init.c)里面有 JU6Linux联盟
struct vfsconf *vfsconf[MOUNT_MAXTYPE+1]; JU6Linux联盟
struct vfsops *vfssw[MOUNT_MAXTYPE+1]; JU6Linux联盟
各种东西的设定,这些是,管理mount信息的struct mount(@sys/mount.h)的成员 JU6Linux联盟
mnt_vfc和mnt_op要指定所对应的文件系统的vfsconf,vfssw。还有宏VFS_操作名 JU6Linux联盟
(struct mount *,..)里,可以各个操作的调用。 JU6Linux联盟
JU6Linux联盟
2.2.2对v-node的操作 JU6Linux联盟
虚拟文件系统就是通过对i-node的抽象化之后的v-node的文件/目录进行io处理。 JU6Linux联盟
为了这个目的,作为对v-node的适用处理,有 JU6Linux联盟
。从v-node到文件名的查找,返回v-node JU6Linux联盟
。打开/关闭v-node JU6Linux联盟
。检查是否可能访问v-node JU6Linux联盟
。得到-v-node的属性 JU6Linux联盟
。设定v-node的属性 JU6Linux联盟
。对v-node的输入/输出 JU6Linux联盟
。扩展v-node的硬连接和符号连接 JU6Linux联盟
。对v-node进行目录的作成和删除 JU6Linux联盟
。。。。 JU6Linux联盟
由这里开始,一共定义了41个。 JU6Linux联盟
v-node由struct vnode(@sys/vnode.h)里定义,作为类别在enum vtype JU6Linux联盟
里面表示出来,一共是9种类。它包含着在各个文件系统上对各个的文件/目录(包 JU6Linux联盟
括特殊)文件进行统一识别的信息。为了实现这样,v-node一连串的操作就是在各 JU6Linux联盟
模块里通过宏VNODEOP_SET()和核心通讯。这些操作名和实现的routine只需要必要 JU6Linux联盟
的几个对应。在核心初始化里,vfs_opv_init()(@kern/vfs_init.c)就使从数据得 JU6Linux联盟
到的号码一一对应,收集了routine的地址的同一size的配列再进行组合。各个 JU6Linux联盟
v-node就一个一个指向这些配列。对v-node的操作在vnode_if.h里定义: JU6Linux联盟
它以 JU6Linux联盟
VOP_操作名(v-node,...) JU6Linux联盟
的统一形式记述。 JU6Linux联盟
JU6Linux联盟
下面是对v-node的操作的定义source: JU6Linux联盟
------------------------------------------------------------------------ JU6Linux联盟
各个v-node操作(vnodeopv) source JU6Linux联盟
------------------------------------------------------------------------ JU6Linux联盟
cd9660_fifoop_opv_desc isofs/cd9660/cd9660_vnops.c JU6Linux联盟
cd9660_specop_opv_desc isofs/cd9660/cd9660_vnops.c JU6Linux联盟
cd9660_vnodeop_opv_desc isofs/cd9660/cd9660_vnops.c JU6Linux联盟
dead_vnodop_opv_desc miscfs/deadfs/dead_devfs_vnops.c JU6Linux联盟
devfs_vnodeop_desc miscfs/devfs/devfs_vnops.c JU6Linux联盟
ext2fs_fifoop_opv_desc gnu/ext2fs/ext2fs_vnops.c JU6Linux联盟
ext2fs_specop_opv_desc gnu/ext2fs/ext2fs_vnops.c JU6Linux联盟
ext2fs_vnodeop_opv_desc gnu/ext2fs/ext2fs_vnops.c JU6Linux联盟
fdesc_vnodeop_opv_desc miscfs/fdesc/fdesc_vnops.c JU6Linux联盟
ffs_fifoop_opv_desc ufs/ffs/ffs_vnops.c JU6Linux联盟
ffs_specop_opv_desc ufs/ffs/ffs_vnops.c JU6Linux联盟
ffs_vnodeop_opv_desc ufs/ffs/ffs_vnops.c JU6Linux联盟
fifo_nfsv2nodeop_opv_desc nfs/nfs_vnops.c JU6Linux联盟
fifo_vnodeop_opv_desc miscfs/fifofs/fifo_vnops.c JU6Linux联盟
kernfs_vnodeop_opv_desc miscfs/kernfs/kernfs_vnops.c JU6Linux联盟
lfs_fifoop_opv_desc ufs/lfs/lfs_vnops.c JU6Linux联盟
lfs_specop_opv_desc ufs/lfs/lfs_vnops.c JU6Linux联盟
lfs_vnodeop_opv_desc ufs/lfs/lfs_vnops.c JU6Linux联盟
mfs_vnodeop_opv_desc ufs/mfs/mfs_vnops.c JU6Linux联盟
msdosfs_vnodeop_opv_desc msdosfs/msdosfs_vnops.c JU6Linux联盟
nfsv2_vnodeop_opv_desc nfs/nfs_vnops.c JU6Linux联盟
null_vnodeop_opv_desc miscfs/nullfs/null_vnops.c JU6Linux联盟
portal_vnodeop_opv_desc miscfs/portal/portal_vnops.c JU6Linux联盟
procfs_vnodeop_opv_desc miscfs/procfs/procfs_vnops.c JU6Linux联盟
spec_nfsv2nodeop_opv_desc nfs/nfs_vnops.c JU6Linux联盟
spec_vnodeop_opv_desc miscfs/specfs/spec_vnops.c JU6Linux联盟
umap_vnodeop_opv_desc miscfs/umapfs/umap_vnops.c JU6Linux联盟
union_vnodeop_opv_desc miscfs/union/union_vnops.c JU6Linux联盟
------------------------------------------------------------------------ JU6Linux联盟
这个基础上,spec_vnodeop_opv_spec里描述的操作群就是device driver JU6Linux联盟
interface的东西!! JU6Linux联盟
JU6Linux联盟
( 本小节完,待本岛主有空再继续:-) ) JU6Linux联盟
JU6Linux联盟
JU6Linux联盟
JU6Linux联盟
FreeBSD核心探讨.6.驱动程序篇 JU6Linux联盟
JU6Linux联盟
2.3 mount根目录之前的处理概要 JU6Linux联盟
mount根目录的时候,main()(@kern/init_main.c)的初始化的过程从xxx_vfs_mountroot() JU6Linux联盟
(@kern/init_mail.c)被调用开始。如果处理过程正常,就对rootvp设定包含了root的 JU6Linux联盟
v-node。 JU6Linux联盟
。main()的初始化过程中,configure()(@autoconf.c)被调用。在这个,io设备 JU6Linux联盟
初始化完了后,就转移到如下两个变量的地址:一个是mountroot,是处理mount的routine, JU6Linux联盟
另一个是mountrootvfsops,是处理虚拟文件系统的routine。在本机磁盘中,就进入变量 JU6Linux联盟
rootdev所指定的disk号中。这里就是,假定本机磁盘 JU6Linux联盟
mountroot vfs_mountroot JU6Linux联盟
mountrootvfsop &ufs_vfsops JU6Linux联盟
rootdev boot disk number JU6Linux联盟
JU6Linux联盟
。xxx_vfs_mountroot()(@kern/init_main.c) JU6Linux联盟
运行(*mountroot)(mountrootvfsops)后,就指明了root file system的mount. JU6Linux联盟
。vfs_mountroot()(@kern/vfs_conf.c) JU6Linux联盟
管理mount的了文件系统的信息的struct mount(@sys/mount.h),对它进行确认 JU6Linux联盟
,然后设定传递过来的对虚拟文件系统的操作群(&ufs_vfsops),才进行"root" JU6Linux联盟
标记。根据VFS_MOUNT(mp,...)进行mount这个虚拟文件系统。mount成功后,就 JU6Linux联盟
追加file system的list。这里,由于传递了&ufs_vfsops,就可以调用 JU6Linux联盟
ffs_mount()(@ufs/ffs/ffs_vfsops.c) JU6Linux联盟
。ffs_mount() JU6Linux联盟
首先调用bdevvp()(@kern/vfs_subr.c),进行VBLK类别,spec_vnodeop_p JU6Linux联盟
(@misc/specfs/spec_vnops.c) v-node操作,保证设定了驱动号的rootdev的 JU6Linux联盟
v-node的最新信息,然后设定rootvp。最后,通过ffs_mountfs()调用进行实际 JU6Linux联盟
的mount rootvp操作。 JU6Linux联盟
。ffs_mountfs() JU6Linux联盟
各种各样的检查完了后,调用VOP_OPEN(),打开rootvp的v-node。在这里,如果 JU6Linux联盟
v-node的v_op成员在spec_vnodeop_p存在的话,就调用spec_open()(@misc/ JU6Linux联盟
specfs/spec_vnops.c)。 JU6Linux联盟
.spec_open JU6Linux联盟
由于VBLK里包含v-node的种类,从v-node指定的device号取得major的 JU6Linux联盟
号,调用对应driver的XXopen() routine JU6Linux联盟
JU6Linux联盟
续上,由VOP_IOCTL()(还是的通过spec_ioctl()(@misc/specfs/spec_vnops.c)) JU6Linux联盟
可以得到partition信息,然后该检查super block的内容。正确的话,就在struct JU6Linux联盟
ufsmount(@ufs/ufs/ufsmount.h)设定unix file system,这样处理过程就完了。 JU6Linux联盟
JU6Linux联盟
2.2.4 struct buf 和block的输入输出routine JU6Linux联盟
前节的ffs_mountfs()提到使用bread()(@kern/vfs_bio.c)读出partition的 JU6Linux联盟
super block。这个接口函数很快就会解释。它主要用于读取block型的device到 JU6Linux联盟
kernel内部的buffer中。 JU6Linux联盟
bread(struct vnode *vp, /*(in)输入对象的v-node*/ JU6Linux联盟
daddr_t blkno, /*(in)block号*/ JU6Linux联盟
int size, /*(in)读出的byte数量,block长的倍数*/ JU6Linux联盟
struct ucred * cred,/*(in)权限信息*/ JU6Linux联盟
struct buf ** bpp)/*(out)存储读来的data*/ JU6Linux联盟
同样的buffer link后的block输出的子程序是bwrite()。 JU6Linux联盟
bwrite(struct buf *bp) /*(out)可以输出的struct buf*/ JU6Linux联盟
两者之间共同的地方就是struct buf(@/sys/buf.h),它用于io处理中给device driver JU6Linux联盟
做桥梁作用的数据结构。它记录了v-node,io的区别,可以io的block位置/byte数,存 JU6Linux联盟
储实际data buffer的address,io处理的进展情况等。 JU6Linux联盟
JU6Linux联盟
bread则通过getblk()对block输入的结构struct buf进行操作。getblk()调用在核心 JU6Linux联盟
管理buffer link和返回指定大小的block的struct buf。这个(缓冲区)内容在目的 JU6Linux联盟
block是否存在与指定v-node的指定位置block是否已经构成缓冲环有关。struct buf JU6Linux联盟
里面有一个标志位,当缓冲环内容变化是,这个标志位就会改变。bread()根据这个 JU6Linux联盟
flag判断block是否已经缓冲,如果已经完成,它就终止退出。如果不是这样,则在 JU6Linux联盟
struct buf的mark里面标志,然后调用VOP_STRATEGY()。在v-node登记的strategy JU6Linux联盟
routine记录了io处理的过程,所以bread()当实际的处理完了后,就调用biowait() JU6Linux联盟
进入等待状态。然后,就转移到别的进程A。io处理完了后,调用biodone(),进程A JU6Linux联盟
也可以继续进行。还有,调用bread()的一边,当完成操作后,就调用brelse(),在 JU6Linux联盟
里面对struct buf的flag重新设置,让它对别的程序开放。 JU6Linux联盟
JU6Linux联盟
bwrite也是同样的通过VOP_STRATEGY()对io处理要求进行登记,同时也调用biowait() JU6Linux联盟
进入等待状态,同样,当实际操作完了后,也设置flag进行复位,使得其他程序可以 JU6Linux联盟
使用io,当空闲的时候,io就挂起,转向其他进程处理。 JU6Linux联盟
进程等待进入的时候,当然不限于只是调用biowait()。在bread()或者bwrite()之前, JU6Linux联盟
系统必须分配足够的资源供它使用,比如一些缓冲区等。当进行实际io时候,1个block JU6Linux联盟
也可以,多个block也可以,而且这样可以获得更高的效率,这样看起来,就象实际上 JU6Linux联盟
是连续操作了。 JU6Linux联盟
(代续) JU6Linux联盟
JU6Linux联盟
JU6Linux联盟
JU6Linux联盟
FreeBSD核心探讨.7.驱动程序篇 JU6Linux联盟
JU6Linux联盟
2.2.5系统调用open()的处理概要 JU6Linux联盟
进程通过系统调用read()/write()进行io处理,它由文件描述符指定对哪里进 JU6Linux联盟
行i/o,文件描述符是0以上的整数,它在各个进程的struct proc的成员 JU6Linux联盟
struct filedesc *p_fd(struct filedesc(@sys/filedesc.h))保留的struct file JU6Linux联盟
((@sys/file.h)进行选择添加。对struct file,它含有从文件的头的输入输出的byte JU6Linux联盟
位置,输入操作,输出操作,输入输出控制,输入输出的准备状态的检查,执行close JU6Linux联盟
的routine,以及描述io处理对象的信息(v-node,socket,pipe) 。系统调用open() JU6Linux联盟
(@kern/vfs_syscalls.c)就是把包含路径信息的v-node找寻出来,为了对它进行io处理, JU6Linux联盟
先要对struct file进行初始化,然后返回文件描述符。 JU6Linux联盟
从路径名查找v-nodehe和io准备操作由vn_open()(@kern/vfs_vnops.c)承担。 JU6Linux联盟
vn_open()通过namei()(@kern/vfs_lookup.c)查找路径对应的v-node名,由VOP_OPEN() JU6Linux联盟
调用不同的v-node定义的准备过程routine。例如,有如下的处理方法。 JU6Linux联盟
。普通的file/directory JU6Linux联盟
调用ufs_open()(@ufs/ufs/ufs_vnops.c),检查open的mode JU6Linux联盟
。特殊设备文件 JU6Linux联盟
调用spec_open()(@miscfs/specfs/spec_vnops.c) JU6Linux联盟
文字型 调用device driver的open routine JU6Linux联盟
快型 mount的时候出错。如果不是这样,就调用device driver的 JU6Linux联盟
open routine。 JU6Linux联盟
JU6Linux联盟
回过头来,namei()的任务是就是,对于指定的路径名,对应于跟目录或者当 JU6Linux联盟
前目录的v-node作为起点,通过lookup()(@kern/vfs_lookup.c)进行v-node查找。 JU6Linux联盟
lookup()从路径名开始的v-node(VDIR)开始查找。找到了的v-node作为新的起点继续进行 JU6Linux联盟
查找下一步的要素名,然后得到目的的v-node。这个时候,根据v-node的不同,目录的检 JU6Linux联盟
索方法也就不同。各个要素的实际检索由VOP_LOOKUP()来做。 JU6Linux联盟
JU6Linux联盟
2.2.6系统调用read()的处理概要 JU6Linux联盟
open()取得文件描述符后,对它的输入处理,有如下的流程。指定的文件描述符 JU6Linux联盟
的struct file内登记的处理routine有vn_read()(@kern/vfs_vnops.c),vn_write(), JU6Linux联盟
vn_ioctl(),vn_select(),vn_closefile(),v_node JU6Linux联盟
登记的操作routine不能分开使用。vn_*()里,只有在合适的前缀操作下,才能正确调用。 JU6Linux联盟
read()首先在struct uio(@sys/uio.h)登记进程指定的buffer的位置和长度。 JU6Linux联盟
执行read()后,vn_read()向struct file设定登记的文件的读写位置,然后调用VOP_READ()。 JU6Linux联盟
根据读出来的byte数,读写位置相应增加。 JU6Linux联盟
VOP_READ()的call routine则是与v-node有关,就象下图一样。 JU6Linux联盟
JU6Linux联盟
vn_read() JU6Linux联盟
文字型/块型 | JU6Linux联盟
/------------------ JU6Linux联盟
| | file/directory JU6Linux联盟
spec_read() ---------ffs_read()-------VOP_READ() JU6Linux联盟
block型 | | JU6Linux联盟
/---------------|char型 | JU6Linux联盟
bread() device driver bread() JU6Linux联盟
| | JU6Linux联盟
spec_strategy() ---------------ufs_strategy() --VOP_STRATEGY() JU6Linux联盟
| | | JU6Linux联盟
| | | JU6Linux联盟
device driver spec_strategy() -------------/ JU6Linux联盟
| JU6Linux联盟
| JU6Linux联盟
device driver JU6Linux联盟
JU6Linux联盟
JU6Linux联盟
。普通的file/directory JU6Linux联盟
调用ffs_read()(@ufs/ufs/ufs_readwrite.c)。对应指定的读写位置,计算block JU6Linux联盟
的位置,然后用bread()读出来。读出来的数据送到进程所准备的缓冲区。从bread() JU6Linux联盟
传递过来的block并不是物理block的位置,而是把file作为block列的一个理论值。 JU6Linux联盟
从理论块到物理块的变换由VOP_STARATEGY()完成。也就是说,ufs_strategy()先把 JU6Linux联盟
文件内位置转化为物理block位置,然后从v-node记录的i-node把表示物理设备的 JU6Linux联盟
v-node 去出来,这个VOP_STRATEGY就调用spec()(@miscfs/specfs/spec_vnops.c) JU6Linux联盟
让它进行输入要求。 JU6Linux联盟
。特殊设备文件 JU6Linux联盟
通过调用spec_read()(@miscfs/specfs/spec_vnops.c),把它分为文字型和块型两类。 JU6Linux联盟
文字型 调用device driver的输入routine JU6Linux联盟
块型 通过bread()进行输入处理 JU6Linux联盟
JU6Linux联盟
对文件的系统调用write()的场合也是类似的处理流程(ufs_write()->;bwrite()), JU6Linux联盟
ufs_write()则要考虑到文件大小的延伸。 JU6Linux联盟
JU6Linux联盟
JU6Linux联盟
JU6Linux联盟
JU6Linux联盟
FreeBSD核心探讨.8.驱动程序篇 JU6Linux联盟
JU6Linux联盟
JU6Linux联盟
2.3 Device Driver JU6Linux联盟
进程的io要求到这里说的差不多了。上面也解说了对于文字型,块型的驱动程序接口,就 JU6Linux联盟
是dev_spec_vnodeop_opv_desc里定义的子函数那些。参考设备驱动程序,在sys/conf.h JU6Linux联盟
里定义的结构体。block型是 JU6Linux联盟
struct bdevsw{ JU6Linux联盟
d_open_t *d_open; JU6Linux联盟
d_close_t *d_close; JU6Linux联盟
d_strategy_t *d_strategy; JU6Linux联盟
d_ioctl_t *d_ioctl; JU6Linux联盟
d_dump_t *d_dump; JU6Linux联盟
d_psize_t *d_psize; /*得到容量*/ JU6Linux联盟
int *d_flags; JU6Linux联盟
char *d_name; /*device 名*/ JU6Linux联盟
struct cdesw *d_cdev; /*对应的文字型*/ JU6Linux联盟
int d_maj; /*major号*/ JU6Linux联盟
} JU6Linux联盟
文字型的则是 JU6Linux联盟
struct cdevsw{ JU6Linux联盟
d_open_t *d_open; JU6Linux联盟
d_close_t *d_close; JU6Linux联盟
d_read_t *d_read; /* rawread() */ JU6Linux联盟
d_write_t *d_write; /* rawwrite()*/ JU6Linux联盟
d_ioctl_t *d_ioctl; JU6Linux联盟
d_stop_t *d_stop; /* nostop()*/ JU6Linux联盟
d_reset_t *d_reset; /* nullreset()*/ JU6Linux联盟
d_devtotty_t *d_devtotty; /* nodevtotty*/ JU6Linux联盟
d_select_t *d_select; /* deltrue*/ JU6Linux联盟
d_mmap_t *d_mmap; /* nommap*/ JU6Linux联盟
d_strategy_t *d_strategy JU6Linux联盟
char *d_name; /*device名*/ JU6Linux联盟
struct bdevsw *d_bdev; /*对应block型*/ JU6Linux联盟
int d_may; /*major号*/ JU6Linux联盟
} JU6Linux联盟
JU6Linux联盟
JU6Linux联盟
两方面共同的部分有 JU6Linux联盟
xx_open(dev_t dev,int oflags,int devtype,struct proc *p) JU6Linux联盟
xx_close(dev_t dev,int fflag,int devtype,struct proc *p) JU6Linux联盟
xx_ioctl(dev_t dev,int cmd,caddr_t data,int fflag,struct proc *p) JU6Linux联盟
xx_open()用于打开device号的设备。xx_close()则用于关闭它。xx_ioctl()则对设备的 JU6Linux联盟
动作状态,机能的取得,设置等进行控制,它通过int cmd命令和参数caddr_t data对之 JU6Linux联盟
进行处理。xx_open()的oflags则是系统调用open()里指定的标志。xx_close()和 JU6Linux联盟
xx_ioctl()的fflag是每个文件描述符设定的标志。int devtype用来区别设备类型是文 JU6Linux联盟
字型的还是块型的。struct proc *p是本次要求的进程号。 JU6Linux联盟
JU6Linux联盟
在文字型的操作里,有这三个函数 JU6Linux联盟
xx_read(dev_t dev,struct uio *uio,int ioflag) JU6Linux联盟
xx_write(dev_t dev,struct uio *uio,int ioflag) JU6Linux联盟
xx_select(dev_t dev,int which, struct proc *p) JU6Linux联盟
xx_read()/xx_write()是对device号的io,struct uio *uio 是io的buffer,int ioflag JU6Linux联盟
标志io动作的option。例如,输入data没准备好的场合不用进入等待状态也可以。 JU6Linux联盟
xx_select()检查是否可以进行io要求。 JU6Linux联盟
在块设备的操作中,有一个函数 JU6Linux联盟
xx_strategy(struct buf *bp) JU6Linux联盟
它处理io要求。struct buf *bp里面包含着device号,输入还是输出,io的buffer等。 JU6Linux联盟
JU6Linux联盟
device号中的major号,对文字型的struct cdevsw *cdevsw[],对块型的struct JU6Linux联盟
bdevsw *bdevsw[],作为配列的添加字使用。向这些配列登记,就可以调出device driver JU6Linux联盟
的登记routine。 JU6Linux联盟
对cdevsw[]登记的过程在kern/kern_conf.c,它使用 JU6Linux联盟
int cdevsw_add( JU6Linux联盟
dev_t *descrip, /*收集device号的变量的指针*/ JU6Linux联盟
struct cdevsw *newentry,/*设置struct cdevsw的指针*/ JU6Linux联盟
struct cdevsw **oldentry,/*旧的设定内容的返回领域*/ JU6Linux联盟
) JU6Linux联盟
另一方面,对bdevsw[]的登记过程则使用 JU6Linux联盟
int bdevsw_add_generic( JU6Linux联盟
int bdev, /*block型的major号*/ JU6Linux联盟
int cdev, /*文字型的major浩*/ JU6Linux联盟
struct bdevsw *bdevsw, /*设定struct bdevsw的指针,对应d_cdev*/ JU6Linux联盟
) JU6Linux联盟
block型的device和char型的device有着一定的对应关系。这些结构体相互参考。 JU6Linux联盟
bdevsw_add_generic()从block的结构体开始,对作为char型的device的结构体进行初始化。 JU6Linux联盟
还有,network interface的devive driver,并没有向cdevsw[]和bdevsw[]登记。而且也没有 JU6Linux联盟
device号。网络间的package流,和进程间与网络间的package流也没有特别指明。 JU6Linux联盟
调用登记routine的时候,可以把文件系统的modules作为特殊设备文件参考。登记 JU6Linux联盟
routine在什么地方都可以调用。 JU6Linux联盟
。main()(@kern/init_mail.c)的初始化过程中登记的routine调用的时候,各个 JU6Linux联盟
device driver的modules里由宏SYSINIT()准备进行。 JU6Linux联盟
。确认device driver里的io设备的存在的时候,调用登记routine。 JU6Linux联盟
当调用登记程序段的时候,如果major号和/dev/MAKEDEV的major号有冲突的时候, JU6Linux联盟
就调用全部无关性device file的处理routine,也可能没有预期的的灾难事情。还有别的 JU6Linux联盟
以外事情,就是当/dev里没有对应的特殊设备文件的时候,也就不能从进程进行参考。 JU6Linux联盟
JU6Linux联盟
JU6Linux联盟
JU6Linux联盟
FreeBSD核心探讨.9.驱动程序篇 JU6Linux联盟
JU6Linux联盟
JU6Linux联盟
2.3.1驱动程序初始化 JU6Linux联盟
从文件系统的模块可以看出来,如果要对驱动程序的物理设备进行io,必须 JU6Linux联盟
先对它们进行初始化,否则不能处理process的io。核心初始化的过程里,一共登记 JU6Linux联盟
了两个基本的操作过程。 JU6Linux联盟
1.probe 确认io设备 JU6Linux联盟
2.attach 设置device driver内部的数据结构,使它能够对io设备 JU6Linux联盟
进行操作。登记中断子程序。 JU6Linux联盟
在device driver中的处理过程有: JU6Linux联盟
1.i/o地址 JU6Linux联盟
i/o命令使用的地址,使io设备的控制硬件和数据交换。 JU6Linux联盟
2.中断号 JU6Linux联盟
io设备的状态变化的时候,向cpu发出通知。 JU6Linux联盟
3.共有内存地址 JU6Linux联盟
根据设备的不同,使用一部分内存空间进行cpu和数据的交换。 JU6Linux联盟
4.DMA通道 JU6Linux联盟
不用通过cpu做中介,设备和内存直接交换数据时候采用的通道的识别号。 JU6Linux联盟
cpu可以在数据传送的时候同时执行它的机器语言。 JU6Linux联盟
前两种是必须有的。设备根据他连接的总线设备不一样,处理过程也就不同。 JU6Linux联盟
这个在核心的configure中反映出来。 JU6Linux联盟
JU6Linux联盟
各种总线设备的device driver的初始化 JU6Linux联盟
驱动程序的初始化在main()初始化的过程中调用configure() JU6Linux联盟
(@i386/i386autoconf.c). JU6Linux联盟
JU6Linux联盟
EISA bus JU6Linux联盟
连接EISA bus的io设备用的device driver的初始化在eisa_configure() JU6Linux联盟
(@i386/eisa/eisaconf.c)。各个device driver在module里对struct eisa_driver JU6Linux联盟
XXX(@i386/eisa/eisaconf.h)进行probe,attach等的设置,准备在宏DATA_SET JU6Linux联盟
(eisadriver_set,XXX)进行登记。 JU6Linux联盟
eisa_configure()(@i386/eisa/eisaconf.c)对连接EISA bus的全部io设备 JU6Linux联盟
标志和i/o地址进行检测。之后便调用登记的probe子程序。在probe子程序中,通过 JU6Linux联盟
eisa_match_dev()(@i386/eisa/eisaconf.c)对自身检测,查找io设备,检测i/o中断 JU6Linux联盟
号,然后进行使用预定,之后用eisa_registerdev()(@i386/eisa/eisaconf.c)在 JU6Linux联盟
struct eisa_driver XXX对这个设备操作,作为device driver登记。全部的io设备 JU6Linux联盟
的控制device driver登记完毕后,eisa_configure()就调用device driver的attach JU6Linux联盟
子程序。attach子程序则进行中断处理程序的登记和device driver的数据的初始化。 JU6Linux联盟
JU6Linux联盟
核心的configure文件登记了以下的一些device driver: JU6Linux联盟
-------------------------------------------------------------------------- JU6Linux联盟
device device driver的情报 source 参考 JU6Linux联盟
-------------------------------------------------------------------------- JU6Linux联盟
mainboard_drv i386/eisa/eisaconf.c JU6Linux联盟
ahb ahb_eisa_driver i386/eisa/aha1742.c scsi adapt JU6Linux联盟
ahc ahc_eisa_driver i386/eisa/aic7770.c scsi adapt JU6Linux联盟
bt bt_eisa_driver i386/eisa/bt74x.c scsi adapt JU6Linux联盟
ep ep_eisa_driver i386/eisa/3c5x9.c network interface JU6Linux联盟
fea pdq_eisa_driver i386/eisa/if_fea.c network interface JU6Linux联盟
vx vx_eisa_driver i386/eida/if_vx_eisa.c network interface JU6Linux联盟
-------------------------------------------------------------------------- JU6Linux联盟
JU6Linux联盟
PCI bus JU6Linux联盟
连接pci bus的设备的初始化在pci_configure()(@pci/pci.c)进行。各个 JU6Linux联盟
device driver在module内的struct pci_device XXX(@pci/pcivar.h)设置probe和 JU6Linux联盟
attach,在通过宏DATA_SET(pcidevice_est,XXX)进行登记。 JU6Linux联盟
DATA_SET(pcibus_set,i386pci)(@i386/isa/pcibus.c)登记的子程序可以 JU6Linux联盟
得到有关pci bus的一些信息。之后和eisa bus处理过程一样进行各种各样的调用。 JU6Linux联盟
核心的configure文件登记了以下的一些device driver: JU6Linux联盟
-------------------------------------------------------------------------- JU6Linux联盟
device device driver的情报 source 参考 JU6Linux联盟
-------------------------------------------------------------------------- JU6Linux联盟
ahc ahc_pci_driver pci/aic7870.c scsi adapt JU6Linux联盟
bt bt_pci_driver pci/bt9xx.c scsi adapt JU6Linux联盟
ncr ncr_device pci/ncr.c scsi adapt JU6Linux联盟
amd trmamd_device pci/tek390.c scsi adapt JU6Linux联盟
cy cy_device pci/cy_pci.c serial port JU6Linux联盟
meteor met_device pci/meteor.c meteor通道 JU6Linux联盟
stl stlpcidriver i386/isa/stallion.c serial port JU6Linux联盟
wdc wdc_pci_driver pci/wdc_p.c ide control JU6Linux联盟
de dedevice pci/if_de.c network interface JU6Linux联盟
ed ed_pci_driver pci/if_ed_p.c network interface JU6Linux联盟
fpa pfadevice pci/if_pfa.c network interface JU6Linux联盟
fxp fxp_device pci/if_pxp.c network interface JU6Linux联盟
lnc lnc_pci_driver pci/if_lnc_p.c network interface JU6Linux联盟
sr sr_pci_driver pci/if_sr_p.c network interface JU6Linux联盟
vx vxdevice pci/if_vx_pci.c network interface JU6Linux联盟
------------------------------------------------------------------------- JU6Linux联盟
JU6Linux联盟
ISA bus JU6Linux联盟
连接ISA bus的io设备的device driver的初始化在isa_configure()(@i386/ JU6Linux联盟
isa/isa.c)进行。和EISA,PCI很大的一个区别就是,在核心的配置文件中,要指定所 JU6Linux联盟
有的io地址等。 JU6Linux联盟
configure文件中,有象如下的记录 JU6Linux联盟
controller 控制设备名 at isa?... JU6Linux联盟
device device名 at isa?... JU6Linux联盟
这些内容在编译核心的目录下作为ioconf.c的struct isa_device JU6Linux联盟
isa_devtab_XXX[]的初始值由config命令写进去。在struct isa_device(@i386/isa JU6Linux联盟
/isa_device.h)的上,其次的成员变量由configure文件的记录内容进行设定。但是 JU6Linux联盟
,“名字”是控制设备名/device名的数字除外的部分。 JU6Linux联盟
------------------------------------------------------------------------- JU6Linux联盟
member名 configure的记述内容 JU6Linux联盟
------------------------------------------------------------------------- JU6Linux联盟
id_driver 名字drvier JU6Linux联盟
id_iobase prot I/O address JU6Linux联盟
id_irq irq号 JU6Linux联盟
id_drq drq DMA通道号 JU6Linux联盟
id_maddr iomem共有memory address JU6Linux联盟
id_msize iosiz共有memory长度 JU6Linux联盟
id_intr vector device driver的中断处理程序名 JU6Linux联盟
id_unit 名字的后的数字(?) JU6Linux联盟
id_flags flags JU6Linux联盟
------------------------------------------------------------------------- JU6Linux联盟
JU6Linux联盟
但是,和控制设备/device名有关的一些东西如bio,net,tty出现的场合,这 JU6Linux联盟
些一般成为isa_devtab_bio[],isa_devtab_net[],isa_devtab_tty[]数组的初始值。 JU6Linux联盟
没有的情况,则成为isa_tab_null[]的初始值。还有一个就是名字driver,它是各个 JU6Linux联盟
device driver的module内部的struct isa_driver(@i386/isa/isa_device.h)一个东 JU6Linux联盟
西。对isa bus设备的device driver,这个是一个固定值。 JU6Linux联盟
象这样的记录: JU6Linux联盟
------------------------------------------------------------ JU6Linux联盟
disk device名 at 控制设备名 driver 数字 JU6Linux联盟
tape device名 at 控制设备名 driver 数字 JU6Linux联盟
------------------------------------------------------------ JU6Linux联盟
每个数字除外控制设备名(wdc或者fdc),总结起来就是写进一个叫做 JU6Linux联盟
isa_biotab_控制设备名[]的数组的某个元素的初始设定值。但对unit成员填入数字 JU6Linux联盟
外,其他的也就和isa_devtab_bio[]的内容一样。 JU6Linux联盟
isa_configure()依照isa_devtab_bio[],isa_devtab_net[], JU6Linux联盟
isa_devtab_tty[]的设定值调用probe子程序对设备的有无进行确认。有的话就继续 JU6Linux联盟
调用attach子程序。 JU6Linux联盟
probe子程序对设备进行确认,不同的probe子程序也有可能对同样的io地址 JU6Linux联盟
进行操作。所以为了防止这个问题,isa_configure()对已经确认过的的io地址不再 JU6Linux联盟
给别的probe进行动作。 JU6Linux联盟
同样,错认的可能性也有的。必要的时候没连接的设备的probe要禁止使用, JU6Linux联盟
(在boot的参数的时候)。
本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u2/63316/showart_654840.html |
|