服务器 频道

FreeBSD 核心 (2)

    【IT168 服务器学院】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里由宏SYSIN99v()准备进行。
    。确认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的参数的时候)。  

0
相关文章