概述
FreeBSD可以在PC/AT兼容机器上运行 。CPU是i386,i486,Pentium,
Pentium Pro以及其兼容芯片等 。
1.1.1(略)
1 , 理论地址: 2个13 bit 长 32 bit 长
2 , 线形地址:32 bit 长的空间
3 , 物理地址:32 bit 长的空间
1.1.2进程的虚拟空间
1 , text部分
这部分是执行文件的的text领域 , 也就是机器语言部分 , 对于这个
部分的空间在机器上的物理内存页是共有的 , 还有 , 这部分最后的变量
地址是etext 。
2 , data和bss部分
执行文件的data部分 , 也就是初始化的数据段和执行文件指定的内
存变量 。内存变量在开始的时候以0填充 。这一段空间可以读写 。它的
边界也是以edata和end的地址做结尾 。进程的malloc()等内存分配的
操作的时候 , 地址的增加方向向bss空间进行 。
3 , stack部分
也就是进程执行的时候的stack空间 , 这部分空间(从地址的最高位
开始可以伸缩) , 其对于物理内存 , 伸缩程度由核心自动执行 。
1.2 kernel的configure
freebsd的kernel构成文件在/usr/src/sys的目录下面 。下面的子目录做一个
介绍 。
compile 编译核心的目录 。
conf configure的目录 。
ddb 核心调试的sounre code的目录 。
dev 一部分的drivers的source code的目录 。
gnu 浮点运算的仿真以及ex2fs文件系统的source code目录 。
i386 依赖于pc/at机器的目录 , 以下介绍它的字目录 。
apm suspend一些节电程序 。
boot 不是kernel本身的东西 , 只是一些怎么从开机到读入kernel
的boot program的source code 。
conf config的一些依赖data 。
isa isa bus的驱动程序类的source code 。
eisa eisa bus的驱动程序类的source code 。
include 对pc/at的一些include files
i386 对pc/at的一些核心code
ibcs2,Linux 使各类的os的执行文件在freebsd上执行的code
isofs/cd9660
cd-rom在Unix文件系统上操作的的有关code
kern 核心code
libkern 核心库的source code
miscfs 实现unix文件系统的code
msDosfs 在unix上操作ms-dos文件系统的有关code
net 实现network功能的基本部分code
netatalk
实现appletalk network功能code
netinet 实现internet network功能的code
netipx 实现ipx功能的code
netns 实现ns network的code
netkey 实现网络加密部分的功能的code
nfs 实现nfs服务
pc98 对于pc98的支持
pccard 对pcmcia的支持
pci 对pci bus的驱动程序的source code
scsi 对cd-rom , hard disk,tape 等的scsi驱动程序的source code
sys 独立于机器体系结构的一部分code
ufs unix file system 的支持code
vm 虚拟内存管理的部分
1.2.1配置的操作----config command
在root权限下 , config , make实行后 , 可以得到简单的kernel 。
*configure file
移动到/usr/src/sys/i386/config看看 。
GENERIC 从cd-rom等安装freebsd的时候对应于defaule kernel
的配置file
LINT kernel组合功能的网罗的的配置file
下面4个是对配置很有必要的的依赖data file
Makefile.386 config生成的Makefile file的template.
devices.i386 对于unix filesystem可能的block型的device
名字和major号的对照表
files.i386 记录kernel功能组合的基础上 , 依赖于pc/at
机器的功能名称和各种功能实现的source code
file的名字表 。
options.i386 记录配置项目的表 。
还有 , majors.i386是记录对应驱动器的I/O表和major号的一个文件 。
于核心配置没关系 。
对于新的i/o设备 , 如果要做device driver,对pc/at,要在files.i386(没
有的话在/usr/src/sys/conf/files)追加相应的行 , 不然就不能把它加入
到核心里面 。
追加的格式为
相对path名 optional device-name device-driver
对于配置文件 , 首先 , 要设置cpu , bus , i/o设备,多少用户等 。例如对于GENERIC
Machine "i386"
cpu "I386_CPU"
cpu "I486_CPU"
cpu "I586_CPU"
cpu "I686_COU"
ident GENERIC
maxusers 10
当作为server时候 , 应该把最大user设置大一点 , 以提高系统性能 。
下一步 , 指定options , 对于GENERIC
options MATH_EMULATE #support for x87 emulation
options INET #interNETworing
options FFS #Berkeley Fast Filesystem
options NFS #Network Filesystem
......
options指定的名字xxx等 , 如果在/usr/src/sys/conf/options或者在
/usr/src/sys/i386/conf/options.i386中记载的时候 , 应在对应的opt_XXX.h中写入
。没有的话 , 作为cc命令行的参数定义"-D"在Makefile里面追加 。对于XXX的格式应该
是
相对path名 optional xxx
下一步 , 对于config
config kernel root on wd0
(略)
配置文件剩下的部分应该是bus,i/o等一些硬件配置 , 一般有controller,device,
disk,tape四类 。例如
controller isa0
controller eisa0
controller pci0
等 。
第二层的device和controller , 记录了一些bus设备的连接 。ISA的情况是
device device_name at isa? 参数
controller controller_name at isa? 参数
EISA和PCI就相对简单一点:
device device_name
controller controller_name
device_name里指定的设备名是 , 串口 , 并口 , 网络等装置 。
第三层的disk和tape为
disk disk_name at 控制设备名 drive 号
tape tape_name at 控制设备名 drive 号
SCSI接口卡作为第二层的控制装置记录的同时
controller scbus0
作为通用的scsi控制设备 。因此 , 对于它的hard disk,tape,cd-rom,mo设备 , 有
device sd0
device st0
device cd0
device od0
等 , 它可以自动识别和分配号码 。
对于其他的scsi设备 , 有
device pt0 at scbus?
这些东西(bus,scsi,i/o) , 在生成的ioconf.c以及相应的include中有反映 。
configure的最后 , 不是一些物理设备 , 而是kernel内部的一些软设置
pseudo-device 理论设备名
首先 , 要考虑以下两个设备:
pseudo-device pty 16 #ttys - can go as high as 256
pseudo-device log #syslog interface (/dev/klog)
network使用的场合 , 应该有下面两个
pseudo-device loop
pseudo-device ether
这种情况下 , 最好有
pseudo-device bpfilter 4 #berkeley packet filter
pseudo-device tun 1 #Tunnel driver ( PPP)
想做floppy的时候 , 要
pseudo-device vn #Vnode driver ( turns a file into a device)
(代续)
FreeBSD核心探讨(翻译)2
1.3 FreeBSD boot之前的工作
1.3.1pc/at机器的boot顺序
hard disk的最前面的一个block(512byte),叫做master boot recorder(MBR).这
里有启动限定的program和分区的信息 。分区信息是指对于一个区是16byte长 , 最多
只能有4个区 。16byte的内容是 , 分区哪里开始 , 哪里结束 。哪种os , 能否启动等 。对
于freebsd , 安装的时候向MBR写入了boot easy.
磁盘的结构如下图表示:
block Number
#0 #1 #2 ... #14 #15 #16 #17
-------------------------------------------------------------
disk no used
label
-------------------------------------------------------------
<-boot->|<---------boot2-------------->| |<--unix file system--
FreeBSD用的block#0--#14的15个block里面 , 含有读入freebsd的程序 , bootease
只在block#0里面 , 在15个block中并没有 。它的作用
。读入mbr , 找freebsd的分区
。读入最初的15个block , 到物理内存中0x0001000
。跳转到相当于block#2的内存位置
然后 , 屏幕表示为:
。。。
。。。
boot:
(参数说明略)
它的source是/usr/src/sys/i386/boot/biosboot,make之后 , 生成两个文件:
boot1,boot2分别写入block#1 , block#2--#14中 。
一般 , 一个物理的unix分区理论上可以有8个 , 比如swap , unix system等 。
boot2部分是boot program , 它读入kernel的文件名和option 。然后
。找boot label指定的分区 。
。构造unix filesystem , 找指定的kernel
。从开始执行文件 , text,data的顺序向物理内存读入 。对bss清零 。
。以option的选择 , 向开始位置跳转 。
1.3.2 kernel的初始化动作
boot program执行之后 , 转向kernel的text段开始进行初始化 , 即先执行
locore.s的text段 。因此是虚拟内存还没有发生作用 , locore.s的开始部分必
须对offset进行补正 。locore.s的作用是
。保存从boot program过来的option
。设定虚拟的stacker
。检测cpu的module
。对自己的bss空间进行0初始化
。为使虚拟内存工作 , 要保证最少的管理信息 。然后是虚拟空间动作 。
也就是 , 调用cpu有强的依赖关系的过程init386()(@i386/i386/machdep.c) ,
然后进行kernel内的管理信息初始化 , i/o设备的登记 , 生成4个kernel process
, 再调用main()(@kern/init_main.c) 。当main()返回locore.s时 , 应该有如下
5个进程:
PID TT STAT TIME COMMAND
0 ?? DLs 0:00.17 (swapper)
1 ?? Is 0:00.19 /sbin/init --
2 ?? DL 0:56.60 (pagedaemon)
3 ?? DL 0:00.06 (vmdaemon)
4 ?? DL 6:07.65 (updata)
从locore.s返回到process #1 , /sbin/init开始动作 , 然后转向freebsd的普通
动作 。
init386()和main()的处理大致如下:
。init386()
GDT和LDT,IDT,task stages处理的初始化 , 例外处理等locore.s没做的
事情 , 虚拟内存初始化 。然后 , 根据boot program的参数 , 增加物理内
存page数 。然后 , 作成process #0的雏形 。
。main()
逐步调用构成kernel模块的的初始化部分 。
FreeBSD核心探讨(翻译)3
(续上 , liangvy.icewolf.leon翻译)
但是 , kernel构成的各个模块的初始化子程序一个个的列举出来运行很显然是
不行的 。通常是利用时间连表的技能来运行它(ld command) 。也就是 , 程序
是以很多个source分开编译和联结 。相同的模块名字就对应于相同的地址来进
行调用 。它在时间链表里面自动调节执行 。
初始化时候 , main()函数要call的模块利用在sys/kernel.h里面定义的宏
SYSINIT()和SYSINIT_KT()进行登记 。这样 , kernel在link的时候 , ld命令就
能够得到那些信息和进行配置列表 。这个列表就是kernel的组成模块的初始化
routine的登记 。检查source ,
就可以找到初始化routine的部分 。
如表:
print_CADdr_t(copyright) kern/init_main.c
vm_men_init(NULL) vm/vm_init.c
syctl_order(&sysctl_) kern/kern_sysctl.c
kmemnit(NULL) kern/kern_malloc.c
fpu_init(NULL) i386/i386/math_emulate.c
cpu_startup(NULL) i386/i386/machdep.c
gnufpu_init(NULL) miscfs/devfs/devfs_tree.c
...
各个device的major号与处理routine的登记 (major循序号)
...
configure(NULL) i386/i386/autoconf.c
proc0_init(NULL) kern/init_main.c
rqinit(NULL) kern/kern_synch.c
vm_init_limits(&proc0) vm/vm_glue.c
vfsinit(NULL) kern/vfs_init.c
elf_insert_brand_entry(&linux_brand) i386/linux/linux_sysvec.c
initclocks(NULL) kern/kern_clock.c
mbinit(NULL) kern/uipc_mbuf.c
clst_init(NULL) kern/tty_subr.c
shmnit(NULL) kern/sysv_shm.c
seminit(NULL) kern/sysv_sem.c
msginit(NULL) kern/sysc_msg.c
kludge_splimp(&x_save_spl) kern/uipc_domain.c
ifinit(NULL) net/if.c
domaininit(NULL) kern/uipc_domain.c
kludge_splx(&x_save_spl) kern/uipc_domain.c
kmstartup(NULL) kern/subr_prof.c
sched_setup(NULL) kern/init_main.c
xxx_vfs_mountroot(NULL) kern/init_main.c
xxx_vfs_root_fdtab(NULL) kern/init_main.c
swapinit(NULL) kern/init_main.c
proc0_post(NULL) kern/init_main.c
kthread_init(NULL) kern/init_main.c||
kproc_start(&page_kp) vm/vm_pageout.c||
kproc_start(&vm_kp) vm/vm_pageout.c||
kproc_start(&up_kp) kern/vfs_bio.c||
scheduler(NULL) vm/vm_glue.c
(||表示有多个程序)
proc-post()被呼叫后 , main()就是在对应process 0 的kernel的虚拟
内存里动作 。kthread_init(),kproc_start(&page_kp),kproc_start(&vm_kp)
,kproc_start(&up_kp)等这几个进程 , 在fork()后相继被调用 。它就是相
应的进程1 , 2 , 3 , 4等 。
除process 1 以外 , 其他的进程调用并不返回调用的地址 。(也就是 , main()
的跟随执行后 , 并不返回locore.s) 。对于process #1的kernel的虚拟内存 ,
在kthread_init()返回后 , main()的跟随就完了 , 回到locore.s后,process #1
的进程空间的配置文件/sbin/init就被执行 。
main()在process #0对应的kernel虚拟内存运行后 , 进入时间链表scheduler() 。
这个并不返回 。那现在就有五个进程了 。
然后 , fork() 的调用在下面说明 。
1 , 分配process ID , 保证struct proc()用的空间 。
2 , 复制父亲的process的虚拟内存空间 , 作成物理内存的变换表 。对
应两个进程 , 采用相对应的物理内存表 。
3 , 给回父亲的struct proc和struct user , 然后对子进程的struct和
struct user进行初始化 。
4 , kernel的stacker也进行复制 。
5 , 返回父进程后 , 标记生成的子进程 。完成处理 。
但是 , process #0 -- 4 这五个进程的虚拟内存里面什么都没有 。这些是核心
进程的特殊部分 。进程0 , 2 , 3是调节系统存在的进程的执行优先级 , 监视物理
内存的不足 , 如果不够就使用swap区进行交换 。进程4的作用就是定期调查核心
的unix文件系统的管理信息与驱动程序的管理信息的一致性 , 使它的信息一直
是最新的 。
1.3.3 /sbin/init
从kernel里面看 , /sbin/init就是单一的进程空间里动作 , 与一般的
user program一样 , 提供user使用的unix文件系统的环境的服务 。
核心启动后最初的动作就是/sbin/init 。作用如下:
。确保file system的一致性 , 进行mount 。
。之后 , network的设定和各种daemon的启动 。
。监视终端的login的配置和动作状态 。这个动作完了后(logout) ,
修改和配置 login 。
也就是说 , 如果没有它 , 用户就不能使用unix文件系统 。还有就是 , 如果boot
progam参数指定-s的话 , 它就过渡到单一的用户模式 。相对来说 , 普通的用户
模式也就是multi模式 。为了使普通用户能够使用系统 , /sbin/init的参考文件
主要在/etc目录里放着 。主要就是运行/etc/rc文件对系统进行初始化 。
/etc/rc文件的主要内容和作用如下:
。使系统能够使用swap区
。检查/etc/fstab,检查它的连贯性 , 如果有问题就转到单一的用户模式
。mount nfs以外的文件系统
。读入network 的设定和各种daemon进程的设定情况的记录文件
/etc/c.conf , 这个内容作为shell script的变量设定 , 以下的就是
各个shell的动作调整
。serial的初始化(/etc/rc.serial)
。运行PCMCIA卡的插拔监控守护进程(/etc/rc.pccard)
。network的部分初始化(/etc/rc.network)
。如果有nfs的时候就进行mount操作
。network的最终初始化(/etc/rc.network:启动和entwork有关的daemon)
。共有库的有关信息的初始化
。intd,lpd,sendmail的启动
。依赖系统的一些初始化进程
/etc/rc的处理完了后 , /sbin/init就对/etc/ttys等记述的一些终端的用户login进行
监视 。对于这个 , /etc/ttys里指定的终端 , fork()后的进程里:
。exec()指定的程序(普通的情况是/usr/libexec/getty)
。/usr/libexec/getty进行终端速度等的设定 。提示login:,等待用户输入
。用户输入后 , 名字作为参数exec() /etc/bin/login
。/usr/bin/login就提示出passwd: , 等待用户的输入
。准备user名和passwd , 对输入的用户名进行确定 , 正确的话就exec()用户
shell
下图就是/sbin/init的监视进程图:
process #1
-------------------------------------------------------->
/sbin/init | ^
| fork() | | fork()
exec() exec() exec() | | exec()
process #n |----------> ---------> ------------------* --------
getty login user的login shell process #m
(第一章完 , 下一章介绍文件系统和驱动程序 , liangvy)
FreeBSD核心探讨.4.驱动程序篇
翻译:liangvy liangvy@bigfoot.comicewolf.leon
版权所有 , 可以转贴
第二章 文件系统和设备驱动程序
这章主要介绍文件系统和特殊的设备文件以及它们的对应关系 。
2..1 disk上的 unix file system 的基本知识
首先介绍一下经典的unix file system的思维方法 。
disk 的 partition就是从0到512byte的连续长度的block的东西 。这里有
1.file/Directory有关的固定长度的信息,i-node
2.file/directory的本体 , data block
的两样不同的东西 。partition的前面的附近块(block#16 ,1--15用于boot
program )就是i-node,data block用的领域等等的开始位置(block号)
和长度(block数量)等的记录 , 叫做super-block 。一个block可能的容量只
能有固定数目的i-node , 所以如果分配了固定的i-node,收录了节点号和节
点的块号和块的位置就可以计算出来 。
i-node就是
。表明i-node的种类(file ,direstory,device等)
。这个节点参考的次数(目录数)
。参考 , 作成 , 变化的时间
。权限
。所有者的user id / group id
。本体的长度
。收集本体的data block的block号码的固定长度的对应表
的一些记录 。因为data block的对应表是固定的关系 , 比
如10个 , 最长就能够作出512*10=5k为止的file 。
当文件比块大的时候 , unix就采用成组联结的方式对它们进行管理 。就是
把所有的空闲块以一定数目为一组的方法作成单向空闲块stacker 。
特别地 , 文件的从先头的byte位置开始和i-node内的对应表有着密切的关系 。
而且 , 对于i-node的输入输出 , 可以对应指定位置的数据块进行读写 。重要
的是 , 核心可以依照这个管理表对io装置进行管理 。
unix对io设备的操作也是作为(特殊)文件进行的 。对于用i-node进行
描述的io设备 , data block数据块的对应表就没必要了 。这个部分的io设备
的识别就通过device号码来进行 。向这些对i-node进行输入输出处理的 ,
就又设备驱动号区别 , 来进行device driver驱动 。
那么 , 节点怎么的进行查找呢?partition的最初的目录(根目录)就是 ,
从第二个i-node开始 , 一个一个顺着节点进行查找 。
比如 , 对于目录/uuu/vvv/.../yyy/zzz的查找方式 , 有这种关系:
。i-node #2 所存放的是root directory 。读入它的本体 , 就可以找到
相应的uuu所对应的i-node 。
。读入这个i-node所存放的directory的i-node本体 , 找到相应的vvv节点 。
......
查找对应yyy的节点
。读入这个节点的本体信息 , 这里包含目录本题的内容 , 这样就可以找到
zzz所对应的i-node 。
目录里面由于记录了对应文件名的节点号 , 所以 , 也有可能同一个节点号
根据文件名不一样 , 就可以找到不同的目录名 。这就是硬连接(hard link).
但是 , 节点号有只存在于节点所在的分区的含义 , 所以 , 不同的分区 ,
这种硬连接就不具有存在的可能性 。为了解决这个矛盾 , 就有了符号连接
(symble link)的说法 。当节点是输入符号连接的时候 , 符号连接就包含
了这个节点的data block所指定的路径名 。但是 , 空连接和loop连接这种
情况也是允许的 , 所以核心要指定循环连接的最大次数 。具体由参数
MAXSYMLINKS(@sys/param.h)指定 。
这样 , 多个分区建立一个文件系统就有可能了 。启动核心的分区作为一个
已存的文件系统 , 其他的分区就嫁接到目录层上面 。这个操作过程就是mount 。
利用mount指令 , 就可以实现上面的操作 。但是 , mount之前的目录 , 在mount后
就给屏蔽了 , 直到mount结束 , 那些目录就可以再现 。
以上就是经典的unix文件系统理论 。但是 , 对于读入了i-node , 就去读
data block , 这种情况 , 对于一个比较大的分区 , 硬盘磁头向disk head的距
离就太大了 。总的来说 , 访问时间就会变长 。在这里有一些指导思想:
。分区要比较小 , 多分小区
。了解超级块的地位 , 超级块记录了分区的信息 , 考虑由于介质的原因而
使这个超级块造成损害 , 所以 , 在分区内部就必须为它准备多几个拷贝 。
。目录和它下层的文件 , 要在相同的领域内放置 。
。确保单位data block要比磁盘的block大 。
考虑了一些东西后 , 经过改良标准 , freebsd就采用一个叫做FFS的文件系
统(Fast File System),但这只是i-node领域/data领域的配置方法的变化 , 基
本的考虑方法并没有变 。对磁盘分区进行文件系统的构造的初始化由命令newfs
提供 。看看它的source就知道怎么配置的了 。其他的构造(......)对应于kernel
的source , 对于构成boot program的文件disk.c和sys.c(@i386/boot/biosboot)
比较简单易懂(单纯性) 。
上面讲述的i-node对disk的partition的记录形式 , 详细的(source)在
struct dinode(@ufs/ufs/dinode.h)里面有 。在核心内部使用的 , 包含这个东西
的是struct inode(@ufs/ufs.inode.h) 。
描述io设备的文件叫特殊文件(special file) , 他对应的i-node有两个种
类:
。块型(block)
和装置的固有的数据记录的单位(大多数的情况是512byte)无关 。读写
的最小单位是1byte , 可以在任意的场所里任意长度的data 。核心对各个
block型的特殊文件进行固定的记录单位长度(倍数)进行缓冲(buffer)
管理 , 这样就可以处理任意长度的读写了.
。文字型(char)
读写的基本单位是 , 受到装置固有的date记录单位长的限定 。没有block
型的缓冲管理 , 对应于装置的物理特性 , 读写属于专用 。或者说 , 是读写
两用 。
除了网络接口之外 , io装置可以全部分为文字型和块型两个大类 。总的来说 ,
磁盘操作的两样都用 , 但其他的io装置只有文字型 。还有就是一些没对应物理设
备的kernel modules提供的虚拟设备也有 , 它们对应着文字型的特殊文件 。特殊
文件习惯放在目录/dev里面 。
对于特殊设备文件的i-node有block和chat两个类 , 设备通过驱动号进行记录
。通过这些 , 就可以识别device driver 。device 号就是major号(8bit)(主设备
号)和minor号(24bit)(辅助设备号) , device driver的识别就是由major的不
一样而区别 。而且呢 , block型 , char型的等等可能存在最大数目是256种类 。一般的
情况 , 同种类的设备不同数目的区别就是通过辅助设备号进行识别 。实际上 , 对于
disk的特殊文件 , 有disk/slide/partition表示法 , 而且 , 文字型 , 块型等的特殊
设备文件也存在 。以下就是一个ide硬盘的的文字型特殊设备文件的例子:
/dev/rwd0 1台ide的硬盘
/dev/rwd0s1 1台ide的硬盘的slide #1
/dev/rwd0s2 1台ide的硬盘的slide #2
/dev/rwd0s2a slide #2的partition a
/dev/rwd0s2b slide #2的partition b
...
/dev/rwd0s3 1台的ide的硬盘的slide #3
如果把rwd换成wd , 对应的就是block型的特殊设备文件了 。
对于磁盘 , 有如下的使用方法:
。对于slide的文字型特殊文件
读写disk label时候使用(disklabel command)
。对于对应的partition的文字型特殊文件
在分区上建立unix文件系统时候(newfs command),文件系统修复 ,
检查(fsck)时候使用
。对于partition的block型的特殊文件
作为mount命令的参数使用
(下一节介绍虚拟文件系统和v-node , 要休息了 )
- 海藻的功效与作用 海藻的好处
- 白寇的功效与作用 白寇的禁忌
- 黑藜麦的功效与作用黑藜麦的禁忌人群
- 茯苓的功效与作用 茯苓的功效与作用有哪些
- 梨子的功效与作用 梨子的功效与作用是什么
- 石榴根的功效与作用 石榴根的功效与作用及禁忌
- 舒肤佳香皂的功效与作用 舒肤佳香皂的功效
- 女生吃莲子的功效与作用 女生吃莲子的禁忌
- 板栗烧鸡的功效与作用 板栗烧鸡的功效与作用禁忌
- 一枝蒿的功效与作用 一枝蒿的功效与作用是什么
