我们以前多次讲过到,以主设备号/次设备号为基础的设备文件管理方式是有根本性的缺点的。这种从Unix早期一直沿用下来的方案一方面给设备号的管理带来了麻烦,一方面也破坏了/dev目录结构。Unix/Linux系统中的所有目录的结构都是层次的,惟独/dev目录是"平面"的。这不光是风格的问题,也直接影响着访问的效率和管理的方便与否。
那么理想中的/dev目录应该是什么样的呢?首先,它应该是层次的、树状的。其次,它的规模应该是可伸缩的,而且不受数量的限制(例如256个主设备号)。还有,/dev目录中的内容应该反映系统当前在设备驱动方面的实际情形。例如,这样一套方案就是比较理想的:
1、系统加电之初/dev目录为空。
2、系统在初始化阶段扫描并枚举所有连接着的设备,就像对PCI总线的扫描枚举一样。每找到一项设备就分门别类地在/dev目录下创建起子目录,然后以设备的序号作为最底层的节点名,例如"/dev/ide/hd/1",”/dev/ide/floppy/1“等等。
3、以后,每插入一个设备,或安装一个可安装模块,就由内核在/dev子树中增加一个或几个节点。
4、反之,如果关闭或拆除一个设备,或拆除一个可安装模块,就由内核在/dev子树中删去相应的节点。
5、还得与原来的方案兼容。
其实设备文件系统devfs,和特殊文件系统/proc思路基本一样。
一、文件系统类型devfs_fs_type的定义如下:
static DECLARE_FSTYPE (devfs_fs_type, DEVFS_NAME, devfs_read_super, FS_SINGLE);
经过gcc的编译预处理以后,就会成为如下的定义:
struct file_system_type devfs_fs_type = {
name: "devfs",
read_super: devfs_read_super,
fs_flags: FS_SINGLE,
owner: THIS_MODULE,
}
系统在初始化时会调用init_devfs_fs进行对devfs特殊文件系统的初始化,代码如下:
int __init init_devfs_fs (void)
{
int err;
printk ("%s: v%s Richard Gooch (rgooch@atnf.csiro.au)\n",
DEVFS_NAME, DEVFS_VERSION);
#ifdef CONFIG_DEVFS_DEBUG
devfs_debug = devfs_debug_init;
printk ("%s: devfs_debug: 0x%0x\n", DEVFS_NAME, devfs_debug);
#endif
printk ("%s: boot_options: 0x%0x\n", DEVFS_NAME, boot_options);
err = register_filesystem (&devfs_fs_type);//向系统登记文件系统类型devfs_fs_type
if (!err)
{
struct vfsmount *devfs_mnt = kern_mount (&devfs_fs_type);//初始安装特殊文件系统devfs
err = PTR_ERR (devfs_mnt);
if ( !IS_ERR (devfs_mnt) ) err = 0;
}
return err;
}
struct vfsmount *kern_mount(struct file_system_type *type)
{
kdev_t dev = get_unnamed_dev();
struct super_block *sb;
struct vfsmount *mnt;
if (!dev)
return ERR_PTR(-EMFILE);
sb = read_super(dev, NULL, type, 0, NULL, 0);
if (!sb) {
put_unnamed_dev(dev);
return ERR_PTR(-EINVAL);
}
mnt = add_vfsmnt(NULL, sb->s_root, NULL);
if (!mnt) {
kill_super(sb, 0);
return ERR_PTR(-ENOMEM);
}
type->kern_mnt = mnt;//重点
return mnt;
}
static struct super_block * read_super(kdev_t dev, struct block_device *bdev,
struct file_system_type *type, int flags,
void *data, int silent)
{
struct super_block * s;
s = get_empty_super();
if (!s)
goto out;
s->s_dev = dev;
s->s_bdev = bdev;
s->s_flags = flags;
s->s_dirt = 0;
sema_init(&s->s_vfs_rename_sem,1);
sema_init(&s->s_nfsd_free_path_sem,1);
s->s_type = type;
sema