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;
}