本文的例子是指 \rtems-4.10.2\testsuites\samples\fileio 的文件系统访问的例子RAMDISK部分
/*
* RAM disk driver so you can create a RAM disk from the shell prompt.
*/
/**
* The RAM Disk configuration.
*/
rtems_ramdisk_config rtems_ramdisk_configuration[] =
{
{
block_size: 512,
block_num: 1024,
location: NULL
}
};
/**
* The number of RAM Disk configurations.
*/
size_t rtems_ramdisk_configuration_size = 1;
RAMDISK 有2个全局变量需要用户去指定的,也就是有多少个RAMdisk,还有每个RAMdisk多大,一个块多大
block_size: 512,
block_num: 1024,
location: NULL
一般一个块大小不会少于512字节,否则就无法格式化为FAT格式了,多大需要用户自己去指定。
假设使用 shell ,
rtems_shell_add_cmd ("mkrd", "files",
"Create a RAM disk driver", create_ramdisk);
配置了一个命令 mkrd,具体是执行 create_ramdisk 是用来创建一个 ramdisk的。
可以不带参数,块大小和数量就是指前面设定了。
sc = rtems_io_register_driver (RTEMS_DRIVER_AUTO_MAJOR,
&rtems_ramdisk_io_ops,
&major);
/**
* Create the RAM Disk Driver entry.
*/
rtems_driver_address_table rtems_ramdisk_io_ops = {
initialization_entry: ramdisk_initialize,
open_entry: rtems_blkdev_generic_open,
close_entry: rtems_blkdev_generic_close,
read_entry: rtems_blkdev_generic_read,
write_entry: rtems_blkdev_generic_write,
control_entry: rtems_blkdev_generic_ioctl
};
注册一个驱动,注意,注册一个驱动,不会在 /dev上建立一个节点,而是由初始化函数自己去
创建的。
在 rtems_io_register_driver函数中
当分配到一个 major 后,跟着执行 rtems_io_initialize,实际上是调用 rtems_ramdisk_io_ops
传入的初始化函数,也就是 ramdisk_initialize
在 ramdisk_initialize 函数中首先初始化disk IO,如果已经初始化则会立刻返回的
rc = rtems_disk_io_initialize();
if (rc != RTEMS_SUCCESSFUL)
return rc;
注意这时的入口参数,major 是之前已经分配好的,minor为0,参数 arg 为 NULL
接着根据 rtems_ramdisk_configuration_size 知道系统到底有多少个ramdisk需要初始化。
这里设置为1,所以
从 dev_t dev = rtems_filesystem_make_dev_t(major, i); 可以看出,如果有多个ramdisk,则每个
ramdisk的minor 从 0~N ,用来区分到底是哪个盘。
#define RAMDISK_DEVICE_BASE_NAME "/dev/rd"
char name [] = RAMDISK_DEVICE_BASE_NAME "a";
name [sizeof(RAMDISK_DEVICE_BASE_NAME)] += i;
从这里看出,这个盘的名字为 /dev/rda ,如果有多个,则为 /dev/rdb ,/dev/rdc .... 等等。
接着需要生成一个或者多个物理盘,
rc = rtems_disk_create_phys(dev, c->block_size, c->block_num,
ramdisk_ioctl, r, name);
参数为 major。minor的组合 dev,块大小数量,注册一个回调函数 ramdisk_ioctl ,让盘自己提供一些
处理的方法,r 为这个驱动的信息,用来绑定到盘上面的,最后name就是刚才说的 /dev/rda 等
在 rtems_disk_create_phys 里面会调用这个真正的生产函数
sc = create_disk(dev, name, &dd);
分配内存等,最后会发现一个系统调用 mknod,将这个节点真正的注册到 /dev 上面。
if (mknod(alloc_name, 0777 | S_IFBLK, dev) < 0)
到这里为止,系统里面注册了一个驱动,根据major来区分是否ram盘,然后通过minor区分具体哪个
在文件系统的 /dev目录就存在了 rda 等的具体一个盘的节点了。
但是,这个盘还是不能用的。就好像新的硬盘买回来什么都没有,必须进行,分区,格式化,最后才能存储数据。
下面说怎么在 fileio 例子中做测试
p -> part_table_initialize
f -> mount all disks in fs_table
l -> list file
r -> read file
w -> write file
s -> start shell
先执行 w 写入文件,不用怕,因为默认是使用了 IMFS 来启动的,所以这个文件系统一定能用。
输入文件名,例如 /test,然后输入大小,输入 100 字节,接着输入一个缓冲大小,输入 512 就行了。
Enter your selection ==>w
=========================
WRITE FILE ...
=========================
--- unused dynamic memory: 65709332 bytes ---
Enter path/filename ==>/test
use suffix K for Kbytes, M for Mbytes or no suffix for bytes:
Enter filesize to write ==>100
use suffix K for Kbytes, M for Mbytes or no suffix for bytes:
Enter block size to use for write calls ==>512
... allocating 512 bytes of buffer for write data
... filling buffer with write data
... creating file "/test"
... writing to file
time elapsed for write: 0 seconds
write data rate: inf KBytes/second
... closing file
... deallocating buffer
******** End of file write
--- unused dynamic memory: 65709332 bytes ---
接着按 s 进入shell,ls一下根目录,应该出现了 test文件,然后cat一下文件内容
RTEMS SHELL (Ver.1.0-FRC):/dev/console. Dec 13 2013. 'help' to list commands.
[/] # ls
dev etc scripts test
[/] # cat test
The quick brown fox jumps over the lazy dog
The quick brown fox jumps over the lazy dog
表面文件已经存在了。可以 exit命令退出刚才的菜单,选择读取和list来测试。
接着看看怎么测试 ramdisk
上面的分析看到,这个demo上面添加了 mkrd,用来生成一个
RTEMS SHELL (Ver.1.0-FRC):/dev/console. Dec 13 2013. 'help' to list commands.
[/] # mkrd
Register RAM Disk Driver [blocks=1024 block-size=512]:successful
[/] # ls /dev/
console rda
[/] #
执行返回成功,一个块是 512字节,一共有 1024个,在 /dev上生成了 rda 的节点,这个和上面的分析
是完全吻合的。
接着执行格式化,生成一个加载点,ramdisk,然后加载到上面。
[/] # mkdir ramdisk
[/] # mkdos -t 16 /dev/rda
msdos format: /dev/rda
msdos format successful
[/] # mount -t dosfs /dev/rda /ramdisk
mounted /dev/rda -> /ramdisk
[/] #
可以看到,操作都成功了。怎么验证呢?
现在已经加载成功了,表示对 /ramdisk 的操作实际上是写进去 ramdisk里面的。
先回去刚才的菜单执行 w ,在 /ramdisk 目录生成一个 test文件,操作和刚才一样
又回到shell,查看 /ramdisk 目录下确实有个这么一个文件
[/] # ls /ramdisk/
test
[/] # cat /ramdisk/test
The quick brown fox jumps over the lazy dog
The quick brown fox jumps over the lazy dog
[/] #
接着 unmount 盘
[/] # unmount /ramdisk
unmounted /ramdisk
[/] # ls /ramdisk
[/] #
可以看到,unmount之后,这个目录本来有的文件就没有了。因为文件是保存在 ramdisk里面的
[/] # mount -t dosfs /dev/rda /ramdisk
mounted /dev/rda -> /ramdisk
[/] # ls /ramdisk
test
[/] # ls /ramdisk
test
[/] # cat /ramdisk/test
The quick brown fox jumps over the lazy dog
The quick brown fox jumps over the lazy dog
[/]
重新加载之后发现文件还在,内容也是一样的,这样就验证了,ramdisk确实是已经正常运行了。在ramdisk直接可以用cp
等命令复制文件到根目录,等待操作都是可以的。
更多的细节,读写是怎么实现的呢?
rc = rtems_disk_create_phys(dev, c->block_size, c->block_num,
ramdisk_ioctl, r, name);
看看这个处理句柄
int
ramdisk_ioctl(rtems_disk_device *dd, uint32_t req, void *argp)
case RTEMS_BLKIO_REQUEST:
{
rtems_blkdev_request *r = argp;
struct ramdisk *rd = rtems_disk_get_driver_data(dd);
switch (r->req)
{
case RTEMS_BLKDEV_REQ_READ:
return ramdisk_read(rd, r);
case RTEMS_BLKDEV_REQ_WRITE:
return ramdisk_write(rd, r);
default:
errno = EINVAL;
return -1;
}
break;
}
可以看到,具体的读写是通过调用 ioctl来实现的,反向跟踪能找到根源
rtems_driver_address_table rtems_ramdisk_io_ops = {
.....
read_entry: rtems_blkdev_generic_read,
.....
};
ramdisk的设备驱动注册的时候指定了 rtems_blkdev_generic_read 作为个驱动的读方法,也就是说,ramdisk作为一个
block device 块设备,用其通用的操作
rtems_device_driver
rtems_blkdev_generic_read(
rtems_device_major_number major __attribute__((unused)),
rtems_device_minor_number minor __attribute__((unused)),
void * arg)
{
----》
rc = rtems_bdbuf_read(dev, block, &diskbuf);
生成一个读请求
rtems_bdbuf_create_read_request (dd, media_block, bds_per_group, req, &bd);
--》req->req = RTEMS_BLKDEV_REQ_READ;
sc = rtems_bdbuf_execute_transfer_request (dd, req, true);
--》result = dd->ioctl (dd->phys_dev, RTEMS_BLKIO_REQUEST, req);
这里的 ioctl 就是之前创建disk的时候传入的ioctl回调函数,也就是 ramdisk_ioctl 了。
而两个参数 RTEMS_BLKIO_REQUEST 和 RTEMS_BLKDEV_REQ_READ就是这么传进来的。而具体的读写
是有 ramdisk_ioctl 自己去实现的。
这是基本的设计思想了,块设备是一个非相关的设计,具体怎么读写磁盘是由各个驱动自己去实现的。
另外RTEMS的函数名字很长很完整,感觉看起来实在太舒服了。
分析到这里为止了。基本能解释架构了。
接着需要分析的是文件系统的实现,和 bdpart 相关的,目前还没有看懂,慢慢来。
Etual
2013-12-19
/*
* RAM disk driver so you can create a RAM disk from the shell prompt.
*/
/**
* The RAM Disk configuration.
*/
rtems_ramdisk_config rtems_ramdisk_configuration[] =
{
{
block_size: 512,
block_num: 1024,
location: NULL
}
};
/**
* The number of RAM Disk configurations.
*/
size_t rtems_ramdisk_configuration_size = 1;
RAMDISK 有2个全局变量需要用户去指定的,也就是有多少个RAMdisk,还有每个RAMdisk多大,一个块多大
block_size: 512,
block_num: 1024,
location: NULL
一般一个块大小不会少于512字节,否则就无法格式化为FAT格式了,多大需要用户自己去指定。
假设使用 shell ,
rtems_shell_add_cmd ("mkrd", "files",
"Create a RAM disk driver", create_ramdisk);
配置了一个命令 mkrd,具体是执行 create_ramdisk 是用来创建一个 ramdisk的。
可以不带参数,块大小和数量就是指前面设定了。
sc = rtems_io_register_driver (RTEMS_DRIVER_AUTO_MAJOR,
&rtems_ramdisk_io_ops,
&major);
/**
* Create the RAM Disk Driver entry.
*/
rtems_driver_address_table rtems_ramdisk_io_ops = {
initialization_entry: ramdisk_initialize,
open_entry: rtems_blkdev_generic_open,
close_entry: rtems_blkdev_generic_close,
read_entry: rtems_blkdev_generic_read,
write_entry: rtems_blkdev_generic_write,
control_entry: rtems_blkdev_generic_ioctl
};
注册一个驱动,注意,注册一个驱动,不会在 /dev上建立一个节点,而是由初始化函数自己去
创建的。
在 rtems_io_register_driver函数中
当分配到一个 major 后,跟着执行 rtems_io_initialize,实际上是调用 rtems_ramdisk_io_ops
传入的初始化函数,也就是 ramdisk_initialize
在 ramdisk_initialize 函数中首先初始化disk IO,如果已经初始化则会立刻返回的
rc = rtems_disk_io_initialize();
if (rc != RTEMS_SUCCESSFUL)
return rc;
注意这时的入口参数,major 是之前已经分配好的,minor为0,参数 arg 为 NULL
接着根据 rtems_ramdisk_configuration_size 知道系统到底有多少个ramdisk需要初始化。
这里设置为1,所以
从 dev_t dev = rtems_filesystem_make_dev_t(major, i); 可以看出,如果有多个ramdisk,则每个
ramdisk的minor 从 0~N ,用来区分到底是哪个盘。
#define RAMDISK_DEVICE_BASE_NAME "/dev/rd"
char name [] = RAMDISK_DEVICE_BASE_NAME "a";
name [sizeof(RAMDISK_DEVICE_BASE_NAME)] += i;
从这里看出,这个盘的名字为 /dev/rda ,如果有多个,则为 /dev/rdb ,/dev/rdc .... 等等。
接着需要生成一个或者多个物理盘,
rc = rtems_disk_create_phys(dev, c->block_size, c->block_num,
ramdisk_ioctl, r, name);
参数为 major。minor的组合 dev,块大小数量,注册一个回调函数 ramdisk_ioctl ,让盘自己提供一些
处理的方法,r 为这个驱动的信息,用来绑定到盘上面的,最后name就是刚才说的 /dev/rda 等
在 rtems_disk_create_phys 里面会调用这个真正的生产函数
sc = create_disk(dev, name, &dd);
分配内存等,最后会发现一个系统调用 mknod,将这个节点真正的注册到 /dev 上面。
if (mknod(alloc_name, 0777 | S_IFBLK, dev) < 0)
到这里为止,系统里面注册了一个驱动,根据major来区分是否ram盘,然后通过minor区分具体哪个
在文件系统的 /dev目录就存在了 rda 等的具体一个盘的节点了。
但是,这个盘还是不能用的。就好像新的硬盘买回来什么都没有,必须进行,分区,格式化,最后才能存储数据。
下面说怎么在 fileio 例子中做测试
p -> part_table_initialize
f -> mount all disks in fs_table
l -> list file
r -> read file
w -> write file
s -> start shell
先执行 w 写入文件,不用怕,因为默认是使用了 IMFS 来启动的,所以这个文件系统一定能用。
输入文件名,例如 /test,然后输入大小,输入 100 字节,接着输入一个缓冲大小,输入 512 就行了。
Enter your selection ==>w
=========================
WRITE FILE ...
=========================
--- unused dynamic memory: 65709332 bytes ---
Enter path/filename ==>/test
use suffix K for Kbytes, M for Mbytes or no suffix for bytes:
Enter filesize to write ==>100
use suffix K for Kbytes, M for Mbytes or no suffix for bytes:
Enter block size to use for write calls ==>512
... allocating 512 bytes of buffer for write data
... filling buffer with write data
... creating file "/test"
... writing to file
time elapsed for write: 0 seconds
write data rate: inf KBytes/second
... closing file
... deallocating buffer
******** End of file write
--- unused dynamic memory: 65709332 bytes ---
接着按 s 进入shell,ls一下根目录,应该出现了 test文件,然后cat一下文件内容
RTEMS SHELL (Ver.1.0-FRC):/dev/console. Dec 13 2013. 'help' to list commands.
[/] # ls
dev etc scripts test
[/] # cat test
The quick brown fox jumps over the lazy dog
The quick brown fox jumps over the lazy dog
表面文件已经存在了。可以 exit命令退出刚才的菜单,选择读取和list来测试。
接着看看怎么测试 ramdisk
上面的分析看到,这个demo上面添加了 mkrd,用来生成一个
RTEMS SHELL (Ver.1.0-FRC):/dev/console. Dec 13 2013. 'help' to list commands.
[/] # mkrd
Register RAM Disk Driver [blocks=1024 block-size=512]:successful
[/] # ls /dev/
console rda
[/] #
执行返回成功,一个块是 512字节,一共有 1024个,在 /dev上生成了 rda 的节点,这个和上面的分析
是完全吻合的。
接着执行格式化,生成一个加载点,ramdisk,然后加载到上面。
[/] # mkdir ramdisk
[/] # mkdos -t 16 /dev/rda
msdos format: /dev/rda
msdos format successful
[/] # mount -t dosfs /dev/rda /ramdisk
mounted /dev/rda -> /ramdisk
[/] #
可以看到,操作都成功了。怎么验证呢?
现在已经加载成功了,表示对 /ramdisk 的操作实际上是写进去 ramdisk里面的。
先回去刚才的菜单执行 w ,在 /ramdisk 目录生成一个 test文件,操作和刚才一样
又回到shell,查看 /ramdisk 目录下确实有个这么一个文件
[/] # ls /ramdisk/
test
[/] # cat /ramdisk/test
The quick brown fox jumps over the lazy dog
The quick brown fox jumps over the lazy dog
[/] #
接着 unmount 盘
[/] # unmount /ramdisk
unmounted /ramdisk
[/] # ls /ramdisk
[/] #
可以看到,unmount之后,这个目录本来有的文件就没有了。因为文件是保存在 ramdisk里面的
[/] # mount -t dosfs /dev/rda /ramdisk
mounted /dev/rda -> /ramdisk
[/] # ls /ramdisk
test
[/] # ls /ramdisk
test
[/] # cat /ramdisk/test
The quick brown fox jumps over the lazy dog
The quick brown fox jumps over the lazy dog
[/]
重新加载之后发现文件还在,内容也是一样的,这样就验证了,ramdisk确实是已经正常运行了。在ramdisk直接可以用cp
等命令复制文件到根目录,等待操作都是可以的。
更多的细节,读写是怎么实现的呢?
rc = rtems_disk_create_phys(dev, c->block_size, c->block_num,
ramdisk_ioctl, r, name);
看看这个处理句柄
int
ramdisk_ioctl(rtems_disk_device *dd, uint32_t req, void *argp)
case RTEMS_BLKIO_REQUEST:
{
rtems_blkdev_request *r = argp;
struct ramdisk *rd = rtems_disk_get_driver_data(dd);
switch (r->req)
{
case RTEMS_BLKDEV_REQ_READ:
return ramdisk_read(rd, r);
case RTEMS_BLKDEV_REQ_WRITE:
return ramdisk_write(rd, r);
default:
errno = EINVAL;
return -1;
}
break;
}
可以看到,具体的读写是通过调用 ioctl来实现的,反向跟踪能找到根源
rtems_driver_address_table rtems_ramdisk_io_ops = {
.....
read_entry: rtems_blkdev_generic_read,
.....
};
ramdisk的设备驱动注册的时候指定了 rtems_blkdev_generic_read 作为个驱动的读方法,也就是说,ramdisk作为一个
block device 块设备,用其通用的操作
rtems_device_driver
rtems_blkdev_generic_read(
rtems_device_major_number major __attribute__((unused)),
rtems_device_minor_number minor __attribute__((unused)),
void * arg)
{
----》
rc = rtems_bdbuf_read(dev, block, &diskbuf);
生成一个读请求
rtems_bdbuf_create_read_request (dd, media_block, bds_per_group, req, &bd);
--》req->req = RTEMS_BLKDEV_REQ_READ;
sc = rtems_bdbuf_execute_transfer_request (dd, req, true);
--》result = dd->ioctl (dd->phys_dev, RTEMS_BLKIO_REQUEST, req);
这里的 ioctl 就是之前创建disk的时候传入的ioctl回调函数,也就是 ramdisk_ioctl 了。
而两个参数 RTEMS_BLKIO_REQUEST 和 RTEMS_BLKDEV_REQ_READ就是这么传进来的。而具体的读写
是有 ramdisk_ioctl 自己去实现的。
这是基本的设计思想了,块设备是一个非相关的设计,具体怎么读写磁盘是由各个驱动自己去实现的。
另外RTEMS的函数名字很长很完整,感觉看起来实在太舒服了。
分析到这里为止了。基本能解释架构了。
接着需要分析的是文件系统的实现,和 bdpart 相关的,目前还没有看懂,慢慢来。
Etual
2013-12-19