FreeBSD学习总结

来源:互联网 发布:牛津大学古典学数据库 编辑:程序博客网 时间:2024/06/05 05:34

FreeBSD完全入门手册

http://www.jb51.net/os/Unix/1525.html


虽然非常基础,但是对于初学者非常有用的,相信不管是初学者还是非初学者,都是有用的。 引用:送


给FreeBSD和UNIX的初学者 Annelise Anderson August 15, 1997 引用: 1.登录和退出引用:       


登录时(当看到login:时)你必 
虽然非常基础,但是对于初学者非常有用的,相信不管是初学者还是非初学者,都是有用的。
引用:送给FreeBSD和UNIX的初学者 
Annelise 
Anderson 
August 15, 1997
引用: 
1.登录和退出引用: 
      登录时(当看到login:时)你必须是在系统安装时创建的用户或root超级用户.(在FreeBSD系统安装


时就已经创建了root用户了 ,root用户遍历到系统的任何一个目录并且可以做任何事情,包括删除系统文


件,所以一定要千万小心!)符号%和符号# 代表提示符(你的可能不一样),%表示普通用户,#表示超级用户


root要退出系统(并且回到login:提示符),打命令: 
      # exit 
      对,打完命令后按回车,记住UNIX对命令是区分大小写的--也就是说,是exit,而不是EXIT. 
      要关闭机器,打命令: 
      # /sbin/shutdown -h now 
      要重启机器,打命令: 
      # /sbin/shutdown -r now 
      或者 
      # /sbin/reboot 
      你也可以按Ctrl-Alt-Delete来重启机器. 
      花少许时间练习一下.在最近发行的FreeBSD版本中这和/sbin/reboot是相等的,而且这比按reset


按钮要好多了.你也不想重装东西,难道不是吗?
2.用Root权限添加用户引用: 
      当你第一次运行adduser时,它也许会创建一些缺省设置.在它建议你把sh作为缺省shell的时候,你


可能想把csh作为缺省shell 而不是sh.否则直接按回车接受默认值.这些默认设置保存


在/etc/adduser.conf中,一个可编辑文件. 
      # adduser 
      假如你创建了一个新用户jack全名为Jack Benimble.出于安全因素,给jack一个口令(即使周围的


孩子也可能敲击键盘).当它问你是否想jack成为某个组的成员时,回答wheel 
      Login group is ``jack''. Invite jack into other groups: wheel 
      这样就可以用户jack登录系统,再用su命令成为root超级用户.然后你就再也不会因为以root超级


用户登录而受到责备了.
      你可以在adduser中,通过按Ctrl-C退出随时退出.在创建结束时你可以批准该用户的生成或打n来


取消创建该用户.你也许想创建第二个用户(jill?)这样当你编辑jack的登录文件时,就有一个热备份以免


出错.一旦创建完用户,exit用exit回到login:提示符以jack登录.通常情况下,最好不要用root用户而是


用普通用户完成大部分的工作.如果你已经创建了一个用户而且想使该用户能够用su命令成为root用户,


你可以root登录然后编辑文件/etc/group,把jack加入第一行(wheel组),但是你首先要练习使用vi,文本


编辑器--或简单些的编辑器,安装在最近发行的FreeBSD中的ee.
      要删除一个用户使用rmuser命令.
3. 环顾四周引用: 
      以普通用户登录,四处浏览一下再使用一些命令试着访问帮助资源和FreeBSD的别的信息.以下是一


些命令和它们的功能: 
      id 告诉你你是谁! 
      pwd 显示你在哪个目录--当前工作目录. 
      ls 显示当前目录的文件. 
      ls -F 显示当前目录的文件.执行文件的文件名后加*,目录名后加/,符号链接后加@. 
      ls -l 以长格式显示文件. 
      ls -a 列出隐藏点文件和其它文件.如果你是root用户,无须加-a选项,点文件将自动显示. 
      cd 改变目录. 
      cd .. 回到上级目录;注意cd后的空格. 
      cd /usr/local 到/usr/local目录下. 
      cd ~ 到以登录用户的主目录--例如/usr/home/jack. 
      试试cd /cdrom,然后ls, 看看你的CDROM是否mounted并且正常工作. 
      view filename 
      让你看一个文件(文件名为filename 不改变文件名).试试 
      view /etc/fstab. 
      :q :q退出. 
      cat filename 
      在屏幕上显示filename.如果文件太长你只能看到文件的最末部分,按ScrollLock 然后用上下键往


回移;             
      ScrollLock键在看手册的时候也用的上.再按一下ScrollLock将退出屏幕滚动.你可以试一下cat你


主目录的点文件cat .cshrc , cat .login , cat .profile. 
      你也许注意到了.cshrc文件中有关ls命令的别名(它们用起来很方便).你可以编辑.cshrc文件来创


建一些别的别名.为了使系统的所有用户都能使用这些别名,把它们放到csh的系统配置文


件/etc/csh.cshrc中.
4. 获得帮助信息引用: 
      这里有一些关于帮助的有用的资源.text表示你从键盘打入的东西--通常是一条命令或文件名. 
      apropos text 在whatis数据库中所有包含text的有关信息. 
      man text 
      text的手册.是Un*x系统文档的主要来源.man ls会告诉你使用ls命令的所有方法.按回车键在文本


中移动,Ctrl-b往前翻一页,Ctrl-f往后翻一页,q或Ctrl-c退出. 
      which text 告诉你text命令所在的路径. 
      locate text 所有找到text字符串的路径. 
      whatis text 
      告诉你text命令主要用来干什么和它的手册页.打入whatis *将告诉你当前目录中的所有命令的有


关信息. 
      whereis text 寻找text文件,并给出它的完全路径. 
      你可能想对一些常用命令如cat, more,grep,mv,find,tar,chmod,chown,date,和script使用


whatis命令.more命令可以让你以DOS的方式一次只阅读一页内容. 例如: ls -l | more 或 more 


filename.符号*代表通配符--例如:ls w*将显示所有以w开头的文件名.
      这些命令是不是并不太有效?locate和whatis命令都取决于每星期重建一次的数据库.如果你不准


备让你的机器在周末也运行的话(运行FreeBSD),你也许希望不时地每天、每星期、每月运行这些命令?


现在,以root身份运行这些命令然后在运行下条命令之前指定一个结束时间. 
      # /etc/daily 输出省略 
      # /etc/weekly 输出省略 
      # /etc/monthly 输出省略 
      如果你等的不耐烦了,按Alt-F2到另一个虚拟控制台上并登录进去.实际上FreeBSD是一个多用户、


多任务系统.不过这些命令在运行时出现的信息大概会在你的屏幕上显示一下;你可以打clear命令清除


屏幕.一旦它们开始运行了,你可以看看/var/mai l/root和/var/log/messages着两个目录.
      你是你自己系统的管理员,作为一个系统管理员或Unix系统的单用户,运行这些命令是最基本的.实


质上,所有你须要已root身份做的事情就是系统管理.这种职责在那些又大又厚的Unix书里并没有得到很


好的概括,这些书籍似乎花了很大空间用来说明w indows管理器的下拉菜单.系统管理方面的书你应该看


看以下两本,
      红封面的Evi Nemeth et.al.'s UNIX System Administration Handbook(Prentice-Hall, 1995, 


ISBN 0-13-15051-7)--the second edition,另外一本为aleen Frisch's Essential System 


Administration (O'Reilly & Associates, 1993,ISBN 0-937175-80-3).我使用的是Nemeth的书.
5. 文本编辑引用: 
      配置系统的时候,一般都要编辑文本文件.大多数要编辑的都在/etc目录下,你需要使用su成为root


才能更改这些文件.你可以使用方便的ee命令,但是长远考虑,vi值得学


习./src/contrib/nvi/docs/tutorial目录下有一个出色的教程,如果你安装了的话;否则你可以从


ftp.cdrom.com的 FreeBSD/FreeBSD-current/src/contrib/nvi/docs/tutorial目录下ftp过来.
      在编辑文件之前,你应该先做个备份.假设你要编辑文件/etc/rc.conf.你只要用cd/etc到达/etc目


录然后打命令: 
      # cp rc.conf rc.conf.orig 
      这样就把rc.conf拷贝为rc.conf.orig,稍后你可以把rc.conf.orig拷贝回rc.conf来覆盖源文件.


但最好是先移动(重命名)然后拷贝回去: 
      # mv rc.conf rc.conf.orig 
      # cp rc.conf.orig rc.conf 
      因为mv命令保留了文件的原先日期和拥有者.你现在可以编辑rc.conf了.如果你要源备份,你只要


mv rc.conf rc.conf.myedit 
      (假设你要保留你的编辑版本)然后用 
      # mv rc.conf.orig rc.conf 
      来恢复成以前的样子. 
      要编辑文件,打命令 
      # vi filename 
      用方向键在文本中来回移动.Esc(escape键)使vi进入命令模式.这儿有些命令: 
      x 删除光标所在处的字符 
      dd 删除整个一行(即使在屏幕上是折行) 
      i 在光标所在处插入文本 
      a 在光标所在处之后插入文本 
      一旦你打了i或a,你就可以键入文本了.Esc使你进入命令模式,然后你可以打: 
      :w 存盘然后继续编辑 
      :wq 存盘并退出 
      :q! 放弃存盘并退出 
      /text 移动光标到text处;/Enter (回车键)用来寻找下一个text 
      G 移动到文件最末 
      nG 到文件的第n行,n代表一个数字 
      Ctrl-L 刷新屏幕 
      Ctrl-b and Ctrl-f 朝前或朝后翻一屏,就象more和view一样
      在你的主目录下用vi filename创建一个文件来练习一下如何添加、删除文本,保存文件,然后继续


编辑.vi会带来一些惊奇,因为它的确相当复杂,有时候你会不经意的发出一条命令取得一些意想不到的效


果.(一些人竟然会喜欢vi--vi比DOS EDIT强多了--去查查:r命令.)当你遇到麻烦时,多按几下Esc键


以确保你在命令模式然后从那里继续下去,经常用:w存盘,还有在需要的时候用:q!退出重来一次.
      现在你可以cd到/etc目录下,su成为root,用vi编辑文件/etc/group,加一个用户到wheel组这样这


个用户就有享有root特权了. 在文件的第一行的末尾加一个逗号和用户名,按Esc,然后用:wq存盘退出.立


即生效.(你没有在逗号之后加空格,对吗?)
6. 在DOS下打印文件引用: 
      在这一点上,你也许没有打印机,有个办法可以先从手册页建一个文件然后再把这个文件移到软盘,


然后在DOS下打印.假设你想仔细阅读一下有关文件权限的文档(非常重要).你可以打命令man chmod.命令


: 
      # man chmod | col -b > chmod.txt 
      会删掉格式代码然后把手册页发送到chmod.txt文件,而不是显示在屏幕上.现在你可以插入一张


dos格式化好的软盘,su成为ro ot,然后输入命令: 
      # /sbin/mount -t msdos /dev/fd0 /mnt 
      这样就把软盘mount到了/etc目录下了. 
      现在(你没必要再用root了,输入exit回到刚才的用户jack)可以到刚才创建chmod.txt的那个目录


然后把文件拷贝到软盘: 
      % cp chmod.txt /mnt 
      再用ls /mnt查看一下/mnt目录下的文件列表,应该能显示chmod.txt. 也许你很想将/sbin/dmesg


输出到一个文件,可以输入命令: 
      % /sbin/dmesg > dmesg.txt 
      然后拷贝dmesg.txt到软盘./sbin/dmesg是系统的启动日志,最好能了解它的内容,因为它能告诉你


系统在启动的时候都找到了什么硬件.如果你想向freebsd-questions@freebsd.org或一个USENET组--


象FreeBSD找不到我的磁带机,我该怎么办?--回答问题的人总是想先知道dmesg的内容. 
      现在你可以将软驱从FreeBSD卸掉(用root)并取出软盘: 
      # /sbin/umount /mnt 
      然后重启机器到DOS.把这些文件copy到一个DOS目录,用诸如DOS EDIT,Windows Notepad或


Wordpad,或一个字处理器打开上述文件,做一些小小的改动,然后存盘,再象平常一样在DOS或Windows下打


印出来.希望一切顺利!用dos的print命令打印手册文档效果最好.(把文件从FreeBSD拷贝到mount好的


dos风区要冒一些风险.)
      要从FreeBSD下打印首先要牵涉到在/etc/printcap目录下创建合适的条目和在/var/spool/output


目录下创建相应的脱机目录 .如果你的打印机在lpt0上(即dos下的LPT1),你只须到/var/spool/output目


录然后用mkdir lpd命令(用root)建一个lpd目录(如果该目录原先不存在的话).接着当系统启动的时候如


果打印机电源接通的话,打印机会有反应,并且lp或lpr命令应该能够向打印机发送一个文件.无论文件是


否打印取决于它的配置,在FreeBSD手册里有详细介绍.
7. 其他一些常用命令引用: 
      df 显示磁盘空间和安装了的文件系统. 
      ps aux 显示正在运行的进程.ps ax是窄格式. 
      rm filename 删除filename. 
      rm -R dir 删除一个目录,包括所有子目录--小心! 
      ls -R 
      显示当前目录和所有其子目录的文件;我用的不大一样,用ls -AFR > where.txt,可以在我找到更


好的查找文件的方法之前得到/目录和(分别的)/usr目录下所有文件的列表. 
      passwd 修改一个用户(或root)的口令 
      man hierUnix 文件系统上的手册 
      在/usr或任何别的目录下使用find命令来查找文件: 
      % find /usr -name filename 
      你可以用在filename里使用*通配符(应该加在引号里). 如果你告诉find到/目录而不是/usr目录


下查找文件,它会到所有安装好的文件系统包括CDROM和dos分区中去查找文件.
      关于Unix命令和使用工具,一本比较好的书是Abrahams & Larson, Unix for the Impatient (2nd 


ed., Addison-Wesley, 1996).在Internet上也有很多Unix信息.看看Unix Reference Desk.
8. 下一步引用: 
      现在你有了编辑工具并且知道如何遍历系统,因此你可以运行任何你想要的东西.在FreeBSD的站点


上和FreeBSD手册上(很可能在你的硬盘上)都可以找到大量的相关信息.同时在Walnut Creek CDROM和它


的站点上也可以找到大量的应用程序包.使用手册已经告诉你如何添加这些程序包(添加程序包的时候该


包必须存在 ,然后用pkg_add/cdrom/packages/All/packagename,packagename表示包的文件名).在下列


目录及文件中可以找到光盘上有关每个程序包简要说明的一张列表:


cdrom/packages/index,cdrom/packages/index.txt,和cdrom/ports/index.更详细的说明可以


在/cdrom/ports/*/*/pkg/DESCR文件里找到,前后两个*分别代表程序所属的类别和程序名.
      如果你不能理解手册上所说的关于如何从cdrom上安装程序包的话,下列步骤通常可以行的通:
      找一个你要的包,比如kermit.存放在光盘上的某个目录.用下列命令把该子目录拷贝到/usr/local


目录下(供所有用户使用的软件最好都放在这): 
      # cp -R /cdrom/ports/comm/kermit /usr/local 
      这样/usr/local/kermit子目录就包含了是光盘上kermit子目录下的所有文件.下一步,如


果/usr/ports/distfiles不存在的话就用mkdir把这个目录建起来.现在到/cdrom/ports/distfiles文件


里查找一下你所要的程序包的名字.然后包相关的安装文件拷贝到/usr/ports/distfiles目录下;在最近


的新版本中你可以跳过这一步,FreeBSD会自动完成这一步.对kermit来说,没有d istfile.然后cd到包含


Makefile的/usr/local/kermit子目录下,打命令: 
      # make all install 
      在这一过程中,如果系统在/usr/ports/distfiles目录中找不到相关的压缩文件的话,它就会ftp去


下载该文件.如果你没有联网并且该文件不在上述目录下的话,你得用另外一台机器来获得该文件,然后再


把它从软盘或dos分区拷贝到/usr/ports/distf iles下.阅读一下Makefile(用cat或more命令)看看应该


到哪里(master distribution site)下载、文件名是什么.下载到了dos下后文件名会缩短,你把它放


到/usr/ports/distfiles下后得把文件名改为原来的(用 mv命令),这样在安装的时候,系统就能找到这个


文件了.(用二进制模式下载!)然后回到/usr/local/kermit目录,到有Makefil e的目录,打make all 


install.
      安装程序包的时候可能碰到的另外一件事就是需要事先安装一些别的应用程序.如果安装过程停下


来说找不到unzip或别的什么的话,你就必须先安装unzip的程序包然后才能继续. 
      一旦安装完毕,打rehash命令让FreeBSD重新读一下该路径下的文件,这样它就知道目录下都有些什


么.(如果当你用whereis或w hich命令时出现很多路径找不到的信息的话,你得在你的主目录下的.cshrc


文件中path描述后添加一部分.出于安全考虑,Unix中的path除了当前目录不在path中(默认),别的和DOS


下的path都是一样的,要运行不在path中的当前目录的命令,要在命令前打./才能使命令执行,斜杠后不能


有空格.) 
< br> 也许你想从Netscape的ftp站点上得到最新版的Netscape.(Netscape必须运行在X Windows系统


上.)现在有FreeBSD版本了,仔细找找.只要运行 gunzip filename和tar xvf filename,把执行文件移


到/usr/local/bin或其它一些存放执行文件的地方,rehash,然后把下列行加到每个用户主目录下的.c 


shrc文件中或者(更简单)干脆到系统启动配置文件/etc/csh.cshrc中: 
      setenv XKEYSYMDB /usr/X11R6/lib/X11/XKeysymDB 
      setenv XNLSPATH /usr/X11R6/lib/X11/nls 
      前提是假设文件XkeysymDB和目录nls在目录/usr/X11R6/lib/X11下;如果不在的话,查找一下然后


放到上述目录下.
      如果你以前就已经用CDROM(或ftp)安装了Netscape,不要把用新的Netscape执行文件覆


盖/usr/local/bin/netscape,老的文件只是一个设置环境变量的shell脚本.相反,把新的执行文件更名为


netscape.bin,然后覆盖老的执行文件/usr/local/lib/nets cape/netscape.bin.
9. 你的工作环境引用: 
      你的shell是你的工作环境中最重要的部分.在DOS下,shell通常为command.com.对你所打入的命令


进行解释的程序就是shell, 这样来与操作系统通信.你也可以写你自己的shell脚本,就象DOS批处理文件


:一组在运行时不需要你的干涉的命令.
      FreeBSD装好后有2个shell:csh和sh.Csh比较适合命令行处理,而脚本应该用sh(或bash)来写.可


以打命令echo $SHELL来查看你用的是什么shell.
      Csh shell是不错的,但是tcsh能做的要比csh能做的要多.它允许你用方向键调用历史命令而且还


能对历史命令进行编辑.tcsh还允许你用tab键补齐文件名(csh用esc键),用 cd -命令可以切换到上一次


你所在的目录.除此而外,用tcsh切换你的提示符也极其方便.Tcsh是你感到更轻松.
      安装一个新的shell分以下3步:
      I. 把shell作为一个程序包安装,就象安装别的程序包一样.用rehash和which tcsh(假设你正在安


装tcsh)来确保安装正确. 
      II. 成为root,编辑/etc/shells,为新的shell添加一行,就本例而言,应该


为/usr/local/bin/tcsh, 存盘.(有的程序包也许会自动完成.) 
      III. 用chsh命令把你的shell永久的改为tcsh,或者在提示符下打tcsh更改你的shell,不用重新登


录. 
注意:对于FreeBSD较早的版本和大部分的Unix,把root的shell改为除sh或csh之外别的什么shell可能回


带来危险;因为当系统进入单用户模式后,你的shell可能运行不起来.解决办法是用su -m成为root,这样


tcsh就成为你作为root时候的shell了,因为shell是环境的一部分.把下面的别名加到你的.tcshrc文件中


后上述情况就一直如此了: 
      alias su su -m. 
      当tcsh启动时,它会和csh一样样去读取/etc/csh.cshrc和/etc/csh.login.还会读取你主目录中的


.login和.cshrc文件,除非你的目录中有.tcshrc文件.你可以干脆把.cshrc拷贝到.tcshrc.
      这样你就把tcsh安装好了,可以修改提示符了.你可以查看手册中有关tcsh的细节.把下面这行放到


你的.tcshrc中后,你就可以知道你打了多少命令、现在几点钟和当前所在目录.如果你是普通用户,还有


一个>;如果你是root,就是#.Tcsh在任何情况下都能做到: 
      set prompt = %h %t %~ %# 
      如果原来有这句话应该放在原来设置提示符的地方;如果找不到的话,就应该在if($?prompt) 


then后面.注释掉原来的那一行;这样你就总是可以在喜欢的时候切换到老的提示符.别忘记空格和引号.


打source .tcshrc来让系统重新读一下.tcshrc文件.你可以用env命令列出已经设置好的别的环境变量.


它会显示你默认的编辑器、翻屏、终端类型和其它一些变量.如果你远程登录后不能运行程序,这是因为


终端没配置好,可以运行命令setenv TERM vt100.
10. 其它引用: 
      作为root,用 /sbin/umount /cdrom卸装CDROM,从光驱中取出CDROM,放入另外一张,然后假设cd0a


是你光驱的设备名,用 /sbin/mount_cd9660 /dev/cd0a /cdrom安装光驱点.在最近的版本中,要安装光驱


点的话,只要打/sbin/mount /cdrom.
      如果你的空间不够,可以使用可启动文件系统-第二张FreeBSD CDROM.可启动文件系统CDROM上的内


容应版本的不同而有所变化.使用可启动文件系统将牵涉到使用lndir,lndir随X Window系统一起安


装,lndir告诉程序到哪里找所需要的文件,因为这些文件在/cdrom目录下而不是通常情况下的/usr目录.


阅读一下man lndir.
11. 欢迎提建议引用: 
      如果你使用这篇教程的话,我想知道哪里说的不清楚、还有哪些你认为应该包括进去的却被遗漏的


、还有本教程是否对你有帮助.感谢Eugene W. Stark,计算机科学系的教授,和John Fieber和他具有帮助


的注释.
Annelise Anderson, andrsn@andrsn.stanford.edu 
freebsd-questions@freebsd.org 
----------------------------- 
(C)1999 by China FreeBSD User Group. All rights reserved. 
URL:www.cn.freebsd.org 
E-Mail:webmaster@cn.freebsd.org 
$Date: 1999/12/28 05:40:56 $ 
========

FreeBSD核心入门

http://blog.chinaunix.net/uid-210143-id-2408936.html
 
翻译 :Liangvy 
原著 :FreeBSD核心入门(日文版) 大木敦雄
1.1概述 
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.com icewolf.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,要休息了 )
 
FreeBSD核心探讨.5.驱动程序篇
2.2 虚拟文件系统和v-node 
FreeBSD在disk上的除了ffs以外还可以操作各种各样的文件系统。主要的如 
下: 
。cd9660 
可以对ISO9660形式的cd-rom的目录/文件构造的文件系统进行mount, 
locate等目录层的操作 
。ms-dos 
对ms-dos文件系统进行目录层次的mount,定位等操作 
。mfs 
通过使用虚拟内存对swap区的一部分进行unix文件系统的构造,定位 
作为目录的一部分进行读写 
。nfs 
由nfs server提供的remote目录级进行mount,定位的目录层操作。 
。null 
对已经存在的目录层的使用别名 
。union 
对已有的目录A(上层)在下层目录B上进行重叠 (不大理解这的意思 
,大概是在下层目录里面又嫁接了上层目录的意思:译者)。文件名的查 
找由上层优先进行。没有的话就转道下层。如果对下层的文件进行写操作 
,它的拷贝就在上层上进行。举例说明,作业目录在上层,但cd-rom的源 
在下层,两个目录重叠,那么编译source的时候,就相当方便了。 
。procfs 
对于进程号的目录作成mount point。通过文件名对各个目录的进程进行 
控制。 
。kernfs 
为了对动作中的kernel有关的信息进行参考,而作成的mount point 
。fdesc 
对于各个进程,用它所打开的文件柄对应的文件作成的mount point
实际上,在核心内部,为了对它们进行统一操作,就对文件系统和v-node 
进行抽象化,实际的处理过程就是调用各类的文件系统的模块进行处理。
2.2.1对虚拟文件系统的操作 
各个文件系统可以提供的操作的一览如下,它在struct vfsops 
(@sys/mount.h)里面定义: 
。对文件系统进行mount的操作 
。本文件系统的开始动作的操作 
。本文件系统的umount操作 
。表达文件系统的根的v-node的查找操作 
。对一般用户的权限控制 
。取得文件系统的状态 
。内存内的管理信息写入介质中 
。从i-node到v-node的取得操作 
。v-node和nfs的文件柄的相互变换的操作 
。文件系统实际的模块的初始化
对于文件系统,各个实际的操作routine在vfsops的形式提供准备工作。各个文件系 
统的vfsops,在以下的表里的source进行定义:
-------------------------------------------------------------- 
file system vfsops的定义 source 
-------------------------------------------------------------- 
ufs ufs_vfsops ufs/ffs/ffs_vfsops.c 
cd9660 cd9660_vfsops isofs/cd9660/cd9660_vfsops.c 
msdos msdosfs_vfsops msdosfs/msdosfs_vfsops.c 
mfs mfs_vfsops ufs/mfs/mfs_vfops.c 
nfs nfs_vfsops nfs/nfs_vfsops.c 
null null_vfsops miscfs/nullfs/null_vfsops.c 
nuion union_vfsops miscfs/union/union_vfsops.c 
procfs procfs_vfsops miscfs/procfs/procfs_vfsops.c 
kernfs kernfs_vfsops miscfs/kernfs/kernfs_vfsops.c 
fdesc fdesc_vfsops miscfs/fdesc/fdesc_vfsops.c 
devfs devfs_vfsops miscfs/devfs/devfs_vfsops.c 
ext2fs ext2fs_vfsops gnu/ext2fs/ext2_vfsops.c 
lfs lfs_vfsops ufs/lfs/lfs_vfsops.c 
portal portal_vfsops miscfs/portal.portal_vfsops.c 
umap umap_vfsops miscfs/umapfs/umap_vfsops.c 
--------------------------------------------------------------- 
这些就是文件系统的实际模块(*_vfsops.c),文件系统名称,文件系统号等等 
在struct vfsconf(@sys/mount.h)里面汇总,各个模块里用宏VFS_SET()进入核 
心。 
根据main()(@kern/init_main.c),在kernel初始化的过程中,vfsinit() 
(@kern/vfs_init.c)里面有 
struct vfsconf *vfsconf[MOUNT_MAXTYPE+1]; 
struct vfsops *vfssw[MOUNT_MAXTYPE+1]; 
各种东西的设定,这些是,管理mount信息的struct mount(@sys/mount.h)的成员 
mnt_vfc和mnt_op要指定所对应的文件系统的vfsconf,vfssw。还有宏VFS_操作名 
(struct mount *,..)里,可以各个操作的调用。
2.2.2对v-node的操作 
虚拟文件系统就是通过对i-node的抽象化之后的v-node的文件/目录进行io处理。 
为了这个目的,作为对v-node的适用处理,有 
。从v-node到文件名的查找,返回v-node 
。打开/关闭v-node 
。检查是否可能访问v-node 
。得到-v-node的属性 
。设定v-node的属性 
。对v-node的输入/输出 
。扩展v-node的硬连接和符号连接 
。对v-node进行目录的作成和删除 
。。。。 
由这里开始,一共定义了41个。 
v-node由struct vnode(@sys/vnode.h)里定义,作为类别在enum vtype 
里面表示出来,一共是9种类。它包含着在各个文件系统上对各个的文件/目录(包 
括特殊)文件进行统一识别的信息。为了实现这样,v-node一连串的操作就是在各 
模块里通过宏VNODEOP_SET()和核心通讯。这些操作名和实现的routine只需要必要 
的几个对应。在核心初始化里,vfs_opv_init()(@kern/vfs_init.c)就使从数据得 
到的号码一一对应,收集了routine的地址的同一size的配列再进行组合。各个 
v-node就一个一个指向这些配列。对v-node的操作在vnode_if.h里定义: 
它以 
VOP_操作名(v-node,...) 
的统一形式记述。
下面是对v-node的操作的定义source: 
------------------------------------------------------------------------ 
各个v-node操作(vnodeopv) source 
------------------------------------------------------------------------ 
cd9660_fifoop_opv_desc isofs/cd9660/cd9660_vnops.c 
cd9660_specop_opv_desc isofs/cd9660/cd9660_vnops.c 
cd9660_vnodeop_opv_desc isofs/cd9660/cd9660_vnops.c 
dead_vnodop_opv_desc miscfs/deadfs/dead_devfs_vnops.c 
devfs_vnodeop_desc miscfs/devfs/devfs_vnops.c 
ext2fs_fifoop_opv_desc gnu/ext2fs/ext2fs_vnops.c 
ext2fs_specop_opv_desc gnu/ext2fs/ext2fs_vnops.c 
ext2fs_vnodeop_opv_desc gnu/ext2fs/ext2fs_vnops.c 
fdesc_vnodeop_opv_desc miscfs/fdesc/fdesc_vnops.c 
ffs_fifoop_opv_desc ufs/ffs/ffs_vnops.c 
ffs_specop_opv_desc ufs/ffs/ffs_vnops.c 
ffs_vnodeop_opv_desc ufs/ffs/ffs_vnops.c 
fifo_nfsv2nodeop_opv_desc nfs/nfs_vnops.c 
fifo_vnodeop_opv_desc miscfs/fifofs/fifo_vnops.c 
kernfs_vnodeop_opv_desc miscfs/kernfs/kernfs_vnops.c 
lfs_fifoop_opv_desc ufs/lfs/lfs_vnops.c 
lfs_specop_opv_desc ufs/lfs/lfs_vnops.c 
lfs_vnodeop_opv_desc ufs/lfs/lfs_vnops.c 
mfs_vnodeop_opv_desc ufs/mfs/mfs_vnops.c 
msdosfs_vnodeop_opv_desc msdosfs/msdosfs_vnops.c 
nfsv2_vnodeop_opv_desc nfs/nfs_vnops.c 
null_vnodeop_opv_desc miscfs/nullfs/null_vnops.c 
portal_vnodeop_opv_desc miscfs/portal/portal_vnops.c 
procfs_vnodeop_opv_desc miscfs/procfs/procfs_vnops.c 
spec_nfsv2nodeop_opv_desc nfs/nfs_vnops.c 
spec_vnodeop_opv_desc miscfs/specfs/spec_vnops.c 
umap_vnodeop_opv_desc miscfs/umapfs/umap_vnops.c 
union_vnodeop_opv_desc miscfs/union/union_vnops.c 
------------------------------------------------------------------------ 
这个基础上,spec_vnodeop_opv_spec里描述的操作群就是device driver 
interface的东西!!
( 本小节完,待本岛主有空再继续 )
 
FreeBSD核心探讨.6.驱动程序篇
2.3 mount根目录之前的处理概要 
mount根目录的时候,main()(@kern/init_main.c)的初始化的过程从xxx_vfs_mountroot() 
(@kern/init_mail.c)被调用开始。如果处理过程正常,就对rootvp设定包含了root的 
v-node。 
。main()的初始化过程中,configure()(@autoconf.c)被调用。在这个,io设备 
初始化完了后,就转移到如下两个变量的地址:一个是mountroot,是处理mount的routine, 
另一个是mountrootvfsops,是处理虚拟文件系统的routine。在本机磁盘中,就进入变量 
rootdev所指定的disk号中。这里就是,假定本机磁盘 
mountroot vfs_mountroot 
mountrootvfsop &ufs_vfsops 
rootdev boot disk number
。xxx_vfs_mountroot()(@kern/init_main.c) 
运行(*mountroot)(mountrootvfsops)后,就指明了root file system的mount. 
。vfs_mountroot()(@kern/vfs_conf.c) 
管理mount的了文件系统的信息的struct mount(@sys/mount.h),对它进行确认 
,然后设定传递过来的对虚拟文件系统的操作群(&ufs_vfsops),才进行"root" 
标记。根据VFS_MOUNT(mp,...)进行mount这个虚拟文件系统。mount成功后,就 
追加file system的list。这里,由于传递了&ufs_vfsops,就可以调用 
ffs_mount()(@ufs/ffs/ffs_vfsops.c) 
。ffs_mount() 
首先调用bdevvp()(@kern/vfs_subr.c),进行VBLK类别,spec_vnodeop_p 
(@misc/specfs/spec_vnops.c) v-node操作,保证设定了驱动号的rootdev的 
v-node的最新信息,然后设定rootvp。最后,通过ffs_mountfs()调用进行实际 
的mount rootvp操作。 
。ffs_mountfs() 
各种各样的检查完了后,调用VOP_OPEN(),打开rootvp的v-node。在这里,如果 
v-node的v_op成员在spec_vnodeop_p存在的话,就调用spec_open()(@misc/ 
specfs/spec_vnops.c)。 
.spec_open 
由于VBLK里包含v-node的种类,从v-node指定的device号取得major的 
号,调用对应driver的XXopen() routine
续上,由VOP_IOCTL()(还是的通过spec_ioctl()(@misc/specfs/spec_vnops.c)) 
可以得到partition信息,然后该检查super block的内容。正确的话,就在struct 
ufsmount(@ufs/ufs/ufsmount.h)设定unix file system,这样处理过程就完了。
2.2.4 struct buf 和block的输入输出routine 
前节的ffs_mountfs()提到使用bread()(@kern/vfs_bio.c)读出partition的 
super block。这个接口函数很快就会解释。它主要用于读取block型的device到 
kernel内部的buffer中。 
bread(struct vnode *vp, /*(in)输入对象的v-node*/ 
daddr_t blkno, /*(in)block号*/ 
int size, /*(in)读出的byte数量,block长的倍数*/ 
struct ucred * cred,/*(in)权限信息*/ 
struct buf ** bpp)/*(out)存储读来的data*/ 
同样的buffer link后的block输出的子程序是bwrite()。 
bwrite(struct buf *bp) /*(out)可以输出的struct buf*/ 
两者之间共同的地方就是struct buf(@/sys/buf.h),它用于io处理中给device driver 
做桥梁作用的数据结构。它记录了v-node,io的区别,可以io的block位置/byte数,存 
储实际data buffer的address,io处理的进展情况等。
bread则通过getblk()对block输入的结构struct buf进行操作。getblk()调用在核心 
管理buffer link和返回指定大小的block的struct buf。这个(缓冲区)内容在目的 
block是否存在与指定v-node的指定位置block是否已经构成缓冲环有关。struct buf 
里面有一个标志位,当缓冲环内容变化是,这个标志位就会改变。bread()根据这个 
flag判断block是否已经缓冲,如果已经完成,它就终止退出。如果不是这样,则在 
struct buf的mark里面标志,然后调用VOP_STRATEGY()。在v-node登记的strategy 
routine记录了io处理的过程,所以bread()当实际的处理完了后,就调用biowait() 
进入等待状态。然后,就转移到别的进程A。io处理完了后,调用biodone(),进程A 
也可以继续进行。还有,调用bread()的一边,当完成操作后,就调用brelse(),在 
里面对struct buf的flag重新设置,让它对别的程序开放。
bwrite也是同样的通过VOP_STRATEGY()对io处理要求进行登记,同时也调用biowait() 
进入等待状态,同样,当实际操作完了后,也设置flag进行复位,使得其他程序可以 
使用io,当空闲的时候,io就挂起,转向其他进程处理。 
进程等待进入的时候,当然不限于只是调用biowait()。在bread()或者bwrite()之前, 
系统必须分配足够的资源供它使用,比如一些缓冲区等。当进行实际io时候,1个block 
也可以,多个block也可以,而且这样可以获得更高的效率,这样看起来,就象实际上 
是连续操作了。 
(代续)
 
FreeBSD核心探讨.7.驱动程序篇
2.2.5系统调用open()的处理概要 
进程通过系统调用read()/write()进行io处理,它由文件描述符指定对哪里进 
行i/o,文件描述符是0以上的整数,它在各个进程的struct proc的成员 
struct filedesc *p_fd(struct filedesc(@sys/filedesc.h))保留的struct file 
((@sys/file.h)进行选择添加。对struct file,它含有从文件的头的输入输出的byte 
位置,输入操作,输出操作,输入输出控制,输入输出的准备状态的检查,执行close 
的routine,以及描述io处理对象的信息(v-node,socket,pipe) 。系统调用open() 
(@kern/vfs_syscalls.c)就是把包含路径信息的v-node找寻出来,为了对它进行io处理, 
先要对struct file进行初始化,然后返回文件描述符。 
从路径名查找v-nodehe和io准备操作由vn_open()(@kern/vfs_vnops.c)承担。 
vn_open()通过namei()(@kern/vfs_lookup.c)查找路径对应的v-node名,由VOP_OPEN() 
调用不同的v-node定义的准备过程routine。例如,有如下的处理方法。 
。普通的file/directory 
调用ufs_open()(@ufs/ufs/ufs_vnops.c),检查open的mode 
。特殊设备文件 
调用spec_open()(@miscfs/specfs/spec_vnops.c) 
文字型 调用device driver的open routine 
快型 mount的时候出错。如果不是这样,就调用device driver的 
open routine。
回过头来,namei()的任务是就是,对于指定的路径名,对应于跟目录或者当 
前目录的v-node作为起点,通过lookup()(@kern/vfs_lookup.c)进行v-node查找。 
lookup()从路径名开始的v-node(VDIR)开始查找。找到了的v-node作为新的起点继续进行 
查找下一步的要素名,然后得到目的的v-node。这个时候,根据v-node的不同,目录的检 
索方法也就不同。各个要素的实际检索由VOP_LOOKUP()来做。
2.2.6系统调用read()的处理概要 
open()取得文件描述符后,对它的输入处理,有如下的流程。指定的文件描述符 
的struct file内登记的处理routine有vn_read()(@kern/vfs_vnops.c),vn_write(), 
vn_ioctl(),vn_select(),vn_closefile(),v_node 
登记的操作routine不能分开使用。vn_*()里,只有在合适的前缀操作下,才能正确调用。 
read()首先在struct uio(@sys/uio.h)登记进程指定的buffer的位置和长度。 
执行read()后,vn_read()向struct file设定登记的文件的读写位置,然后调用VOP_READ()。 
根据读出来的byte数,读写位置相应增加。 
VOP_READ()的call routine则是与v-node有关,就象下图一样。
vn_read() 
文字型/块型 | 
/------------------ 
| | file/directory 
spec_read() ---------ffs_read()-------VOP_READ() 
block型 | | 
/---------------|char型 | 
bread() device driver bread() 
| | 
spec_strategy() ---------------ufs_strategy() --VOP_STRATEGY() 
| | | 
| | | 
device driver spec_strategy() -------------/ 


device driver


。普通的file/directory 
调用ffs_read()(@ufs/ufs/ufs_readwrite.c)。对应指定的读写位置,计算block 
的位置,然后用bread()读出来。读出来的数据送到进程所准备的缓冲区。从bread() 
传递过来的block并不是物理block的位置,而是把file作为block列的一个理论值。 
从理论块到物理块的变换由VOP_STARATEGY()完成。也就是说,ufs_strategy()先把 
文件内位置转化为物理block位置,然后从v-node记录的i-node把表示物理设备的 
v-node 去出来,这个VOP_STRATEGY就调用spec()(@miscfs/specfs/spec_vnops.c) 
让它进行输入要求。 
。特殊设备文件 
通过调用spec_read()(@miscfs/specfs/spec_vnops.c),把它分为文字型和块型两类。 
文字型 调用device driver的输入routine 
块型 通过bread()进行输入处理
对文件的系统调用write()的场合也是类似的处理流程(ufs_write()->bwrite()), 
ufs_write()则要考虑到文件大小的延伸。
 


FreeBSD核心探讨.8.驱动程序篇


2.3 Device Driver 
进程的io要求到这里说的差不多了。上面也解说了对于文字型,块型的驱动程序接口,就 
是dev_spec_vnodeop_opv_desc里定义的子函数那些。参考设备驱动程序,在sys/conf.h 
里定义的结构体。block型是 
struct bdevsw{ 
d_open_t *d_open; 
d_close_t *d_close; 
d_strategy_t *d_strategy; 
d_ioctl_t *d_ioctl; 
d_dump_t *d_dump; 
d_psize_t *d_psize; /*得到容量*/ 
int *d_flags; 
char *d_name; /*device 名*/ 
struct cdesw *d_cdev; /*对应的文字型*/ 
int d_maj; /*major号*/ 

文字型的则是 
struct cdevsw{ 
d_open_t *d_open; 
d_close_t *d_close; 
d_read_t *d_read; /* rawread() */ 
d_write_t *d_write; /* rawwrite()*/ 
d_ioctl_t *d_ioctl; 
d_stop_t *d_stop; /* nostop()*/ 
d_reset_t *d_reset; /* nullreset()*/ 
d_devtotty_t *d_devtotty; /* nodevtotty*/ 
d_select_t *d_select; /* deltrue*/ 
d_mmap_t *d_mmap; /* nommap*/ 
d_strategy_t *d_strategy 
char *d_name; /*device名*/ 
struct bdevsw *d_bdev; /*对应block型*/ 
int d_may; /*major号*/ 
}


两方面共同的部分有 
xx_open(dev_t dev,int oflags,int devtype,struct proc *p) 
xx_close(dev_t dev,int fflag,int devtype,struct proc *p) 
xx_ioctl(dev_t dev,int cmd,caddr_t data,int fflag,struct proc *p) 
xx_open()用于打开device号的设备。xx_close()则用于关闭它。xx_ioctl()则对设备的 
动作状态,机能的取得,设置等进行控制,它通过int cmd命令和参数caddr_t data对之 
进行处理。xx_open()的oflags则是系统调用open()里指定的标志。xx_close()和 
xx_ioctl()的fflag是每个文件描述符设定的标志。int devtype用来区别设备类型是文 
字型的还是块型的。struct proc *p是本次要求的进程号。
在文字型的操作里,有这三个函数 
xx_read(dev_t dev,struct uio *uio,int ioflag) 
xx_write(dev_t dev,struct uio *uio,int ioflag) 
xx_select(dev_t dev,int which, struct proc *p) 
xx_read()/xx_write()是对device号的io,struct uio *uio 是io的buffer,int ioflag 
标志io动作的option。例如,输入data没准备好的场合不用进入等待状态也可以。 
xx_select()检查是否可以进行io要求。 
在块设备的操作中,有一个函数 
xx_strategy(struct buf *bp) 
它处理io要求。struct buf *bp里面包含着device号,输入还是输出,io的buffer等。
device号中的major号,对文字型的struct cdevsw *cdevsw[],对块型的struct 
bdevsw *bdevsw[],作为配列的添加字使用。向这些配列登记,就可以调出device driver 
的登记routine。 
对cdevsw[]登记的过程在kern/kern_conf.c,它使用 
int cdevsw_add( 
dev_t *descrip, /*收集device号的变量的指针*/ 
struct cdevsw *newentry,/*设置struct cdevsw的指针*/ 
struct cdevsw **oldentry,/*旧的设定内容的返回领域*/ 

另一方面,对bdevsw[]的登记过程则使用 
int bdevsw_add_generic( 
int bdev, /*block型的major号*/ 
int cdev, /*文字型的major浩*/ 
struct bdevsw *bdevsw, /*设定struct bdevsw的指针,对应d_cdev*/ 

block型的device和char型的device有着一定的对应关系。这些结构体相互参考。 
bdevsw_add_generic()从block的结构体开始,对作为char型的device的结构体进行初始化。 
还有,network interface的devive driver,并没有向cdevsw[]和bdevsw[]登记。而且也没有 
device号。网络间的package流,和进程间与网络间的package流也没有特别指明。 
调用登记routine的时候,可以把文件系统的modules作为特殊设备文件参考。登记 
routine在什么地方都可以调用。 
。main()(@kern/init_mail.c)的初始化过程中登记的routine调用的时候,各个 
device driver的modules里由宏SYSINIT()准备进行。 
。确认device driver里的io设备的存在的时候,调用登记routine。 
当调用登记程序段的时候,如果major号和/dev/MAKEDEV的major号有冲突的时候, 
就调用全部无关性device file的处理routine,也可能没有预期的的灾难事情。还有别的 
以外事情,就是当/dev里没有对应的特殊设备文件的时候,也就不能从进程进行参考。
 
FreeBSD核心探讨.9.驱动程序篇


2.3.1驱动程序初始化 
从文件系统的模块可以看出来,如果要对驱动程序的物理设备进行io,必须 
先对它们进行初始化,否则不能处理process的io。核心初始化的过程里,一共登记 
了两个基本的操作过程。 
1.probe 确认io设备 
2.attach 设置device driver内部的数据结构,使它能够对io设备 
进行操作。登记中断子程序。 
在device driver中的处理过程有: 
1.i/o地址 
i/o命令使用的地址,使io设备的控制硬件和数据交换。 
2.中断号 
io设备的状态变化的时候,向cpu发出通知。 
3.共有内存地址 
根据设备的不同,使用一部分内存空间进行cpu和数据的交换。 
4.DMA通道 
不用通过cpu做中介,设备和内存直接交换数据时候采用的通道的识别号。 
cpu可以在数据传送的时候同时执行它的机器语言。 
前两种是必须有的。设备根据他连接的总线设备不一样,处理过程也就不同。 
这个在核心的configure中反映出来。
各种总线设备的device driver的初始化 
驱动程序的初始化在main()初始化的过程中调用configure() 
(@i386/i386autoconf.c).
EISA bus 
连接EISA bus的io设备用的device driver的初始化在eisa_configure() 
(@i386/eisa/eisaconf.c)。各个device driver在module里对struct eisa_driver 
XXX(@i386/eisa/eisaconf.h)进行probe,attach等的设置,准备在宏DATA_SET 
(eisadriver_set,XXX)进行登记。 
eisa_configure()(@i386/eisa/eisaconf.c)对连接EISA bus的全部io设备 
标志和i/o地址进行检测。之后便调用登记的probe子程序。在probe子程序中,通过 
eisa_match_dev()(@i386/eisa/eisaconf.c)对自身检测,查找io设备,检测i/o中断 
号,然后进行使用预定,之后用eisa_registerdev()(@i386/eisa/eisaconf.c)在 
struct eisa_driver XXX对这个设备操作,作为device driver登记。全部的io设备 
的控制device driver登记完毕后,eisa_configure()就调用device driver的attach 
子程序。attach子程序则进行中断处理程序的登记和device driver的数据的初始化。
核心的configure文件登记了以下的一些device driver: 
-------------------------------------------------------------------------- 
device device driver的情报 source 参考 
-------------------------------------------------------------------------- 
mainboard_drv i386/eisa/eisaconf.c 
ahb ahb_eisa_driver i386/eisa/aha1742.c scsi adapt 
ahc ahc_eisa_driver i386/eisa/aic7770.c scsi adapt 
bt bt_eisa_driver i386/eisa/bt74x.c scsi adapt 
ep ep_eisa_driver i386/eisa/3c5x9.c network interface 
fea pdq_eisa_driver i386/eisa/if_fea.c network interface 
vx vx_eisa_driver i386/eida/if_vx_eisa.c network interface 
--------------------------------------------------------------------------
PCI bus 
连接pci bus的设备的初始化在pci_configure()(@pci/pci.c)进行。各个 
device driver在module内的struct pci_device XXX(@pci/pcivar.h)设置probe和 
attach,在通过宏DATA_SET(pcidevice_est,XXX)进行登记。 
DATA_SET(pcibus_set,i386pci)(@i386/isa/pcibus.c)登记的子程序可以 
得到有关pci bus的一些信息。之后和eisa bus处理过程一样进行各种各样的调用。 
核心的configure文件登记了以下的一些device driver: 
-------------------------------------------------------------------------- 
device device driver的情报 source 参考 
-------------------------------------------------------------------------- 
ahc ahc_pci_driver pci/aic7870.c scsi adapt 
bt bt_pci_driver pci/bt9xx.c scsi adapt 
ncr ncr_device pci/ncr.c scsi adapt 
amd trmamd_device pci/tek390.c scsi adapt 
cy cy_device pci/cy_pci.c serial port 
meteor met_device pci/meteor.c meteor通道 
stl stlpcidriver i386/isa/stallion.c serial port 
wdc wdc_pci_driver pci/wdc_p.c ide control 
de dedevice pci/if_de.c network interface 
ed ed_pci_driver pci/if_ed_p.c network interface 
fpa pfadevice pci/if_pfa.c network interface 
fxp fxp_device pci/if_pxp.c network interface 
lnc lnc_pci_driver pci/if_lnc_p.c network interface 
sr sr_pci_driver pci/if_sr_p.c network interface 
vx vxdevice pci/if_vx_pci.c network interface 
-------------------------------------------------------------------------
ISA bus 
连接ISA bus的io设备的device driver的初始化在isa_configure()(@i386/ 
isa/isa.c)进行。和EISA,PCI很大的一个区别就是,在核心的配置文件中,要指定所 
有的io地址等。 
configure文件中,有象如下的记录 
controller 控制设备名 at isa?... 
device device名 at isa?... 
这些内容在编译核心的目录下作为ioconf.c的struct isa_device 
isa_devtab_XXX[]的初始值由config命令写进去。在struct isa_device(@i386/isa 
/isa_device.h)的上,其次的成员变量由configure文件的记录内容进行设定。但是 
,“名字”是控制设备名/device名的数字除外的部分。 
------------------------------------------------------------------------- 
member名 configure的记述内容 
------------------------------------------------------------------------- 
id_driver 名字drvier 
id_iobase prot I/O address 
id_irq irq号 
id_drq drq DMA通道号 
id_maddr iomem共有memory address 
id_msize iosiz共有memory长度 
id_intr vector device driver的中断处理程序名 
id_unit 名字的后的数字(?) 
id_flags flags 
-------------------------------------------------------------------------
但是,和控制设备/device名有关的一些东西如bio,net,tty出现的场合,这 
些一般成为isa_devtab_bio[],isa_devtab_net[],isa_devtab_tty[]数组的初始值。 
没有的情况,则成为isa_tab_null[]的初始值。还有一个就是名字driver,它是各个 
device driver的module内部的struct isa_driver(@i386/isa/isa_device.h)一个东 
西。对isa bus设备的device driver,这个是一个固定值。 
象这样的记录: 
------------------------------------------------------------ 
disk device名 at 控制设备名 driver 数字 
tape device名 at 控制设备名 driver 数字 
------------------------------------------------------------ 
每个数字除外控制设备名(wdc或者fdc),总结起来就是写进一个叫做 
isa_biotab_控制设备名[]的数组的某个元素的初始设定值。但对unit成员填入数字 
外,其他的也就和isa_devtab_bio[]的内容一样。 
isa_configure()依照isa_devtab_bio[],isa_devtab_net[], 
isa_devtab_tty[]的设定值调用probe子程序对设备的有无进行确认。有的话就继续 
调用attach子程序。 
probe子程序对设备进行确认,不同的probe子程序也有可能对同样的io地址 
进行操作。所以为了防止这个问题,isa_configure()对已经确认过的的io地址不再 
给别的probe进行动作。 
同样,错认的可能性也有的。必要的时候没连接的设备的probe要禁止使用, 
(在boot的参数的时候)。
========

freebsd完全攻略

http://www4.it168.com/jtzt/shenlan/server/freebsd/freebsd.htm
0 0