barebox学习笔记之三 -- 文件系统

本文深入探讨了Barebox文件系统实现机制,包括文件系统的注册、挂载过程及核心‘系统调用’接口函数集。分析了devfs设备文件系统如何简化传统Linux文件系统实现,特别关注了fs_driver_d结构、fs_device_d实例及其注册过程,并以open系统调用为例阐述了文件操作的实现方式。最后,详细解释了Barebox中devfs的设计思想和其实现细节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. 文件系统的注册
文件系统是以总线的形式实现的.
某种具体的文件系统作为设备,而这些设备都挂在fs_bus这条总线上.
struct bus_type fs_bus = {
        .name = "fs",
        .match = fs_match,
        .probe = fs_probe,
        .remove = fs_remove,
};

int register_fs_driver(struct fs_driver_d *fsdrv)
{
        fsdrv->drv.bus = &fs_bus; //fs总线
        register_driver(&fsdrv->drv);

        return 0;
}
EXPORT_SYMBOL(register_fs_driver);


//每个fs_driver_d的实例定义了一种具体的文件系统相关的接口函数,会通过初始化的方式注册到fs_bus总线和driver_list上去.
//而fs_device_d的实例则用于保存文件系统相关的数据信息.并会在mount的时候被创建,然后注册到device_list上去,注册时引发
//fs_bus的match()和probe() (因为所有文家系统设备和驱动所关联的总线都是fs_bus,进而具体文件系统对应的fs_driver_d
//的fs_driver_d::drv->probe()被调用.以cramfs为例:

fs_bus->probe(struct device_d dev)
+-- dev->driver->probe(dev);       //dev已经在match()的时候拿到了driver_d的指针存在dev->driver里面了.
    =-- cramfs_probe()
+-- list_add_tail(&fsdev->list, &fs_device_list);   //所有的fs_device_d最终都会被注册到fs_device_list中.


@cramfs.c
static struct fs_driver_d cramfs_driver = {
        .open           = cramfs_open,
        .close          = cramfs_close,
        .read           = cramfs_read,
        .lseek          = cramfs_lseek,
        .opendir        = cramfs_opendir,
        .readdir        = cramfs_readdir,
        .closedir       = cramfs_closedir,
        .stat           = cramfs_stat,
        .drv = {
                .probe = cramfs_probe,      //dev->driver->probe()实际上就是cramfs_probe
                .remove = cramfs_remove,      
                .name = "cramfs",
        }
};
 

struct fs_device_d {
        char * backingstore; /* the device we are associated with */
        struct device_d dev; /* our own device */
        struct fs_driver_d *driver;
        struct cdev *cdev;
        char        *path;
        struct device_d *parent_device;
        struct list_head list;
};

struct fs_driver_d {
        char *name;
        int (*probe) (struct device_d *dev);
        ...
        int (*open)(struct device_d *dev, FILE *f, const char *pathname);
        int (*close)(struct device_d *dev, FILE *f);
        int (*read)(struct device_d *dev, FILE *f, void *buf, size_t size);
        int (*write)(struct device_d *dev, FILE *f, const void *buf, size_t size);
        ...
        int (*ioctl)(struct device_d *dev, FILE *f, int request, void *buf);
        ...
        int (*memmap)(struct device_d *dev, FILE *f, void **map, int flags);

        struct driver_d drv;
        unsigned long flags;
};

//值得注意的是:各种文件系统的fs_driver_d实例注册的实际是系统初始化阶段,此时还没有fs_device_d实例被注册.
device_initcall(cramfs_init);
+-- static int cramfs_init(void)
    +-- register_fs_driver(&cramfs_driver);

coredevice_initcall(devfs_init);
+-- static int devfs_init(void)
    +-- register_fs_driver(&devfs_driver);

coredevice_initcall(fat_init);
+-- static int fat_init(void)
    +-- register_fs_driver(&fat_driver);

coredevice_initcall(nfs_init);
+-- static int nfs_init(void)
    +-- register_fs_driver(&nfs_driver);

coredevice_initcall(ramfs_init);
+-- static int ramfs_init(void)
    +-- register_fs_driver(&ramfs_driver);

coredevice_initcall(tftp_init);
+-- static int tftp_init(void)
    +-- register_fs_driver(&tftp_driver);


//fs_device_d实例注册的时机是在mount某一具体文件系统的时候.

// Mount a device to a directory.
// We do this by registering a new device on which the filesystem
// driver will match.
int mount(const char *device, const char *fsname, const char *_path)
{
        struct fs_device_d *fsdev;
        int ret;
        char *path = normalise_path(_path);

        debug("mount: %s on %s type %s\n", device, path, fsname);

        if (fs_dev_root) {
                fsdev = get_fsdevice_by_path(path);
                if (fsdev != fs_dev_root) {
                        printf("sorry, no nested mounts\n");
                        ret = -EBUSY;
                        goto err_free_path;
                }
                ret = path_check_prereq(path, S_IFDIR);
                if (ret)
                        goto err_free_path;
        } else {
                /* no mtab, so we only allow to mount on '/' */
                if (*path != '/' || *(path + 1)) {
                        ret = -ENOTDIR;
                        goto err_free_path;
                }
        }

        //创建并初始化fs_device_d设备
        fsdev = xzalloc(sizeof(struct fs_device_d));    
        fsdev->backingstore = xstrdup(device);
        safe_strncpy(fsdev->dev.name, fsname, MAX_DRIVER_NAME);
        fsdev->dev.id = get_free_deviceid(fsdev->dev.name);
        fsdev->path = xstrdup(path);             //path的地址保存在fsdev->path中,open()的时候会用到.
        fsdev->dev.bus = &fs_bus;            //fs_bus总线

        if (!strncmp(device, "/dev/", 5))
                fsdev->cdev = cdev_by_name(device + 5);

        ret = register_device(&fsdev->dev);  //注册设备
        if (ret)
                goto err_register;

        if (!fsdev->dev.driver) {
                //Driver didn't accept the device or no driver for this
                //device. Bail out
                ret = -EINVAL;
                goto err_no_driver;
        }

        return 0;

err_no_driver:
        unregister_device(&fsdev->dev);
err_register:
        fs_remove(&fsdev->dev);
err_free_path:
        free(path);

        errno = -ret;

        return ret;
}
EXPORT_SYMBOL(mount);

2. 文件系统的挂载
int mount(const char *device, const char *fsname, const char *_path)
+-- char *path = normalise_path(_path);
    //首先检查path指定的文件系统是否已经挂载,如果已经挂载
+-- if (fs_dev_root) {
        fsdev = get_fsdevice_by_path(path);
        //如果get_fsdevice_by_path()没有找到path指定的fs_device_d则将会返回fs_dev_root
          //因此如果返回的fsdev不是fs_dev_root,说明没有找到path指定的fs_device_d.
        
        //如果能在fs_device_list中找到fs_device_d,说明文件系统已经挂载,则认为重复挂载,返回-EBUSY.
        if (fsdev != fs_dev_root) {
            printf("sorry, no nested mounts\n");
            ret = -EBUSY;
            goto err_free_path;
        }
        ret = path_check_prereq(path, S_IFDIR);
        if (ret)
            goto err_free_path;
    } else {
        //如果path不是指向根目录,并且根文件系统尚未挂载,则返回-ENOTDIR.
        //即在根文件系统挂载之前不能挂载任何文件系统.
        /* no mtab, so we only allow to mount on '/' */
        if (*path != '/' || *(path + 1)) {
             ret = -ENOTDIR;
             goto err_free_path;
        }
    }
    //走到这,说名path指向的目录并没有文件系统挂载.因此直接创建一个fs_device_d设备,并注册.
+-- fsdev = xzalloc(sizeof(struct fs_device_d));
+-- fsdev->backingstore = xstrdup(device);
+-- safe_strncpy(fsdev->dev.name, fsname, MAX_DRIVER_NAME);
+-- fsdev->dev.id = get_free_deviceid(fsdev->dev.name);
+-- fsdev->path = xstrdup(path);
+-- fsdev->dev.bus = &fs_bus;
+-- if (!strncmp(device, "/dev/", 5))
         fsdev->cdev = cdev_by_name(device + 5);
    //将fs_device_d注册到全局device_list中,这个注册的动作会导致dev->bus->probe()也就是fs_bus->probe()
    //进而导致dev->driver->probe()也就是fs_driver_d::drv->probe()的调用.而在fs_driver_d::drv->probe()
    //调用之前会将fs_device_d实例添加到fs_device_list里面.
+-- ret = register_device(&fsdev->dev); 



//根据path指定的路径找到fs_device_d实例
static struct fs_device_d *get_fsdevice_by_path(const char *path)
{
        struct fs_device_d *fsdev = NULL;
        //遍历fs_device_list,找到fsdev->path成员和path匹配的fsdev.
        for_each_fs_device(fsdev) {
                int len = strlen(fsdev->path);
                if (!strncmp(path, fsdev->path, len) &&
                                (path[len] == '/' || path[len] == 0))
                        return fsdev;
        }
        //如果没找到,将fs_dev_root返回.
        return fs_dev_root;
}

3. 相关“系统调用”的实现
barebox没有利用到不同的CPU特权级,因此也就没有真正实现像Linux kernel那样的系统调用,而是在fs.c中定义了
一系列的接口函数供外部调用,以此实现对系统调用的模拟,实现一个“系统调用”接口函数集.
Fs.c (source_tree\barebox\fs):EXPORT_SYMBOL(read_file);
Fs.c (source_tree\barebox\fs):EXPORT_SYMBOL(mkmodestr);
Fs.c (source_tree\barebox\fs):EXPORT_SYMBOL(normalise_path);
Fs.c (source_tree\barebox\fs):EXPORT_SYMBOL(automount_remove);
Fs.c (source_tree\barebox\fs):EXPORT_SYMBOL(automount_add);
Fs.c (source_tree\barebox\fs):EXPORT_SYMBOL(automount_print);
Fs.c (source_tree\barebox\fs):EXPORT_SYMBOL(getcwd);
Fs.c (source_tree\barebox\fs):EXPORT_SYMBOL(chdir);
Fs.c (source_tree\barebox\fs):EXPORT_SYMBOL(unlink);
Fs.c (source_tree\barebox\fs):EXPORT_SYMBOL(open);
Fs.c (source_tree\barebox\fs):EXPORT_SYMBOL(creat);
Fs.c (source_tree\barebox\fs):EXPORT_SYMBOL(read);
Fs.c (source_tree\barebox\fs):EXPORT_SYMBOL(write);
Fs.c (source_tree\barebox\fs):EXPORT_SYMBOL(lseek);
Fs.c (source_tree\barebox\fs):EXPORT_SYMBOL(erase);
Fs.c (source_tree\barebox\fs):EXPORT_SYMBOL(protect);
Fs.c (source_tree\barebox\fs):EXPORT_SYMBOL(memmap);
Fs.c (source_tree\barebox\fs):EXPORT_SYMBOL(close);
Fs.c (source_tree\barebox\fs):EXPORT_SYMBOL(register_fs_driver);
Fs.c (source_tree\barebox\fs):EXPORT_SYMBOL(mount);
Fs.c (source_tree\barebox\fs):EXPORT_SYMBOL(umount);
Fs.c (source_tree\barebox\fs):EXPORT_SYMBOL(opendir);
Fs.c (source_tree\barebox\fs):EXPORT_SYMBOL(readdir);
Fs.c (source_tree\barebox\fs):EXPORT_SYMBOL(closedir);
Fs.c (source_tree\barebox\fs):EXPORT_SYMBOL(stat);
Fs.c (source_tree\barebox\fs):EXPORT_SYMBOL(mkdir);
Fs.c (source_tree\barebox\fs):EXPORT_SYMBOL(rmdir);
Fs.c (source_tree\barebox\fs):EXPORT_SYMBOL(mem_read);
Fs.c (source_tree\barebox\fs):EXPORT_SYMBOL(mem_write);
以open()函数为例:
int open(const char *pathname, int flags, ...)
+-- struct fs_device_d *fsdev;
+-- struct fs_driver_d *fsdrv;
+-- FILE *f;
+-- exist_err = stat(path, &s);

    //从files[MAX_FILES]数组中找到一个没有被使用的FILE结构.
+-- f = get_file();

    //系统初始化完成后,相关文件系统将会被加入到fs_device_list中去
    //因此,可以通过path 在 fs_device_list 中找到对应的fs_device_d实例,然后根据fs_device_d找到fs_driver_d实例.
+-- fsdev = get_fs_device_and_root_path(&path);
+-- fsdrv = fsdev->driver;

+-- f->dev = &fsdev->dev;
+-- f->flags = flags;
    //如果文件不存在,则调用相应的fsdrv->create()
+-- if (exist_err) 
        if (NULL != fsdrv->create)
	    ret = fsdrv->create(&fsdev->dev, path, S_IFREG | S_IRWXU | S_IRWXG | S_IRWXO);

    //调用fs_driver_d实例的open()函数......
+-- ret = fsdrv->open(&fsdev->dev, f, path);

+-- if (flags & O_TRUNC) {
        ret = fsdrv->truncate(&fsdev->dev, f, 0);
    }
    //如果flags设置了O_APPEND标志,则将f->pos置为f->size,即文件末尾.
+-- if (flags & O_APPEND)
        f->pos = f->size;


static struct fs_driver_d cramfs_driver = {
        .open           = cramfs_open,
        ...
}

//barebox不是一个OS,没有进程/线程的概念,因此打开的文件是用一个静态全局数组来维护的.
//#define MAX_FILES       128
//因此,整个系统里面最多可以打开的文件的数目为128.
static FILE files[MAX_FILES];
static FILE *get_file(void)
{
        int i;
        //
        for (i = 3; i < MAX_FILES; i++) {
                //如果FILE结构没有被使用则对其进行处理,并将其返回.
                if (!files[i].in_use) { 
                        memset(&files[i], 0, sizeof(FILE));
                        files[i].in_use = 1;
                        files[i].no = i;
                        return &files[i];
                }
        }
        return NULL;
}

FILE结构的定义如下:
typedef struct filep {
        struct device_d *dev;  /* The device this FILE belongs to              */
        loff_t pos;            /* current position in stream                   */

#define FILE_SIZE_STREAM        ((loff_t) -1)

        loff_t size;           /* The size of this inode                       */
        ulong flags;           /* the O_* flags from open                      */

        void *inode;           /* private to the filesystem driver             */

        /* private fields. Mapping between FILE and filedescriptor number      */
        int no;
        char in_use;
} FILE;

FILE::dev、FILE::flags、FILE::size都是在open() "系统调用"接口函数中实现的.

4. "设备文件系统" -- devfs
    按照目前的理解,在barebox里,要实现一个文件系统,只要实现对应的fs_driver_d结构就可以了,因此对devfs的分析也将围绕
struct fs_driver_d devfs_driver这个结构展开.因为barebox的对外接口实际上也是类似linux的一堆文件操作,对应到底层也
就是这些具体文件系统的接口函数.
    另外,这里所谓的"设备文件系统",并非真正意义上的设备文件系统,而是模仿并高度简化了linux对设备文件系统的实现.这里没有
VFS,也没有对应文件操作的系统调用(如上文所述)和复杂的inode/dentry结构来维护文件相关的信息.而是采用了另一种更加简化的实现.
static struct fs_driver_d devfs_driver = {
        .read      = devfs_read,
        .write     = devfs_write,
        .lseek     = devfs_lseek,
        .open      = devfs_open,
        .close     = devfs_close,
        .flush     = devfs_flush,
        .ioctl     = devfs_ioctl,
        .opendir   = devfs_opendir,
        .readdir   = devfs_readdir,
        .truncate  = devfs_truncate,
        .closedir  = devfs_closedir,
        .stat      = devfs_stat,
        .erase     = devfs_erase,
        .protect   = devfs_protect,
        .memmap    = devfs_memmap,
        .flags     = FS_DRIVER_NO_DEV,
        .drv = {
                .probe  = devfs_probe,
                .remove = devfs_delete,
                .name = "devfs",
        }
};

//在cdev_list中找到filename对应的cdev,并将其保存到f->inode中去,此后
//devfs_read()、devfs_write()
//...
//devfs_ioctl()、devfs_close()...
//等都可以通过f->inode拿到cdev,进而拿到cdev->ops指向的接口函数集.
//cdev->ops是在各个设备的probe()函数中赋值的.

static int devfs_open(struct device_d *_dev, FILE *f, const char *filename)
{
        struct cdev *cdev;
        int ret;

        //根据filename在cdev_list中找到cdev,如果找不到,说明设备相关的设备文件还未创建
          //或者进一步,说明设备还未和驱动进行匹配.
        cdev = cdev_by_name(filename + 1);

        if (!cdev)
                return -ENOENT;

        f->size = cdev->flags & DEVFS_IS_CHARACTER_DEV ?
                        FILE_SIZE_STREAM : cdev->size;

        //将找到的cdev放到f->inode里面去,之后就可以从f->inode拿到cdev以及cdev->ops
        f->inode = cdev;

        if (cdev->ops->open) {
                ret = cdev->ops->open(cdev, f->flags);
                if (ret)
                        return ret;
        }

        cdev->open++;

        return 0;
}

static int devfs_read(struct device_d *_dev, FILE *f, void *buf, size_t size)
+-- struct cdev *cdev = f->inode;
+-- cdev_read(cdev, buf, size, f->pos, f->flags);
    +-- cdev->ops->read(cdev, buf, count, cdev->offset +offset, flags);

通常情况下,barebox里面的具体的设备结构的定义会包含两个基本结构,一个是device_d结构,用于承载设备相关的基本信息;
一个是cdev结构,用于容纳和设备文件系统相关的将一些基本信息.
//cdev在linux里面是字符设备的结构体,但是在barebox里,块设备也将其内嵌到各个实际设备的描述结构中,用以实现将其
//在设备文件系统"/dev/"下以文件的形式将其信息暴露给用户,因此这里将cdev理解为一个使用目的更加泛化的(非特指字符设备)
//结构.为所有的设备描述结构所使用.

struct cdev {
        struct file_operations *ops;
        void *priv;
        struct device_d *dev;
        struct list_head list;
        struct list_head devices_list;
        char *name;
        loff_t offset;
        loff_t size;
        unsigned int flags;
        int open;
        struct mtd_info *mtd;
};

//cdev结构一般都在具体设备驱动的driver_d::xxx_probe()函数中创建,
//也就是在device_d和driver_d匹配的时候
//亦即系统初始化,注册设备和驱动完成的时候创建的.
//同时,也正式在这些probe()函数完成了对cdev的各个成员尤其是cdev->ops的初始化.例如:
static int mem_probe(struct device_d *dev)
{
        struct cdev *cdev;

        cdev = xzalloc(sizeof (*cdev));
        dev->priv = cdev;

        cdev->name = (char*)dev->resource[0].name;
        cdev->size = (unsigned long)resource_size(&dev->resource[0]);

        cdev->ops = &memops;   //cdev->ops就是在这些probe()函数里面赋值的
        cdev->dev = dev;

        devfs_create(cdev);

        return 0;
}

//devfs_create()会将cdev加入到cdev_list链表中.
int devfs_create(struct cdev *new)
{
        struct cdev *cdev;
        //首先在cdev_list中查找,看cdev是否在cdev_list中,如果在,返回-EEXIST,表明cdev已经存在在链表当中,
        //否则,说明不在链表中,则将其加入cdev_list中.
        cdev = cdev_by_name(new->name);
        if (cdev)
                return -EEXIST;
        //cdev被加入到cdev_list中之后,就可以根据cdev->name找到对应的cdev,
        //进而找到cdev->ops指向的接口函数集.
        list_add_tail(&new->list, &cdev_list);
        if (new->dev)
                list_add_tail(&new->devices_list, &new->dev->cdevs);

        return 0;
}


资源下载链接为: https://pan.quark.cn/s/d9ef5828b597 在本文中,我们将探讨如何通过 Vue.js 实现一个带有动画效果的“回到顶部”功能。Vue.js 是一款用于构建用户界面的流行 JavaScript 框架,其组件化和响应式设计让实现这种交互功能变得十分便捷。 首先,我们来分析 HTML 代码。在这个示例中,存在一个 ID 为 back-to-top 的 div 元素,其中包含两个 span 标签,分别显示“回到”和“顶部”文字。该 div 元素绑定了 Vue.js 的 @click 事件处理器 backToTop,用于处理点击事件,同时还绑定了 v-show 指令来控制按钮的显示与隐藏。v-cloak 指令的作用是在 Vue 实例渲染完成之前隐藏该元素,避免出现闪烁现象。 CSS 部分(backTop.css)主要负责样式设计。它首先清除了一些默认的边距和填充,对 html 和 body 进行了全屏布局,并设置了相对定位。.back-to-top 类则定义了“回到顶部”按钮的样式,包括其位置、圆角、阴影、填充以及悬停时背景颜色的变化。此外,与 v-cloak 相关的 CSS 确保在 Vue 实例加载过程中隐藏该元素。每个 .page 类代表一个页面,每个页面的高度设置为 400px,用于模拟多页面的滚动效果。 接下来是 JavaScript 部分(backTop.js)。在这里,我们创建了一个 Vue 实例。实例的 el 属性指定 Vue 将挂载到的 DOM 元素(#back-to-top)。data 对象中包含三个属性:backTopShow 用于控制按钮的显示状态;backTopAllow 用于防止用户快速连续点击;backSeconds 定义了回到顶部所需的时间;showPx 则规定了滚动多少像素后显示“回到顶部”按钮。 在 V
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值