1.设备分类:
转载自字符设备与块设备的区别
(1)字符设备:
字符设备按照字符流的方式被有序访问,像串口和键盘就都属于字符设备。如果一个硬件设备是以字符流的方式被访问的话,那就应该将它归于字符设备;反过来,如果一个设备是随机(无序的)访问的,那么它就属于块设备。
驱动函数核心的file_operations结构体:open read write
(2)块设备:
系统中能够随机(不需要按顺序)访问固定大小数据片(chunks)的设备被称作块设备,这些数据片就称作块。例如:flash还需要按照sector来进行操作。
最常见的块设备是硬盘,除此以外,还有软盘驱动器、CD-ROM驱动器和闪存等等许多其他块设备。注意,它们都是以安装文件系统的方式使用的——这也是块设备的一般访问方式。
驱动函数核心的block_operations结构体:《OpenharmonyFor6ull\third_party\NuttX\include\nuttx\fs\fs.h》
其中,geometry结构体定义如下《OpenharmonyFor6ull\kernel\liteos_a\fs\include\fs\fs.h》
struct geometry
{
bool geo_available; /* true: The device is available */
bool geo_mediachanged; /* true: The media has changed since last query eg:U盘是否插拔过*/
bool geo_writeenabled; /* true: It is okay to write to this device */
unsigned long long geo_nsectors; /* Number of sectors on the device */
size_t geo_sectorsize; /* Size of one sector */
};
(3)区别:
字符设备、块设备主要区别是:在对字符设备发出读/写请求时,实际的硬件I/O一般就紧接着发生了;而块设备则不然,它利用一块系统内存作为缓冲区,当用户进程对设备请求能满足用户的要求时,就返回请求的数据,如果不能就调用请求函数来进行实际的I/O操作,因此,块设备主要是针对磁盘等慢速设备设计的,以免消耗过多的CPU时间来等待~~~
这两种类型的设备的根本区别在于它们是否可以被随机访问——换句话说就是,能否在访问设备时随意地从一个位置跳转到另一个位置。
举个例子:键盘这种设备提供的就是一个数据流,当你敲入"fox" 这个字符串时,键盘驱动程序会按照和输入完全相同的顺序返回这个由三个字符组成的数据流。如果让键盘驱动程序打乱顺序来读字符串,或读取其他字符,都是没有意义的。所以键盘就是一种典型的字符设备,它提供的就是用户从键盘输入的字符流。对键盘进行读操作会得到一个字符流,首先是"f",然后是"o",最后是"x",最终是文件的结束(EOF)。当没人敲键盘时,字符流就是空的。硬盘设备的情况就不大一样了。硬盘设备的驱动可能要求读取磁盘上任意块的内容,然后又转去读取别的块的内容,而被读取的块在磁盘上位置不一定要连续,所以说硬盘可以被随机访问,而不是以流的方式被访问,显然它是一个块设备。
内核管理块设备要比管理字符设备细致得多,需要考虑的问题和完成的工作相比字符设备来说要复杂许多。这是因为字符设备仅仅需要控制一个位置—当前位置—而块设备访问的位置必须能够在介质的不同区间前后移动。所以事实上内核不必提供一个专门的子系统来管理字符设备,但是对块设备的管理却必须要有一个专门的提供服务的子系统。不仅仅是因为块设备的复杂性远远高于字符设备,更重要的原因是块设备对执行性能的要求很高;对硬盘每多一分利用都会对整个系统的性能带来提升,其效果要远远比键盘吞吐速度成倍的提高大得多。另外,我们将会看到,块设备的复杂性会为这种优化留下很大的施展空间。
例如:linux中如果几个程序都要对硬盘进行写操作,实际上是先调用ll_rw_block()这个函数,凑够一定的字节数的时候再操作硬件。
块设备通过系统缓存进行读取,不是直接和物理磁盘读取。字符设备可以直接物理磁盘读取,不经过系统缓存。(如键盘,直接相应中断)
字符设备是裸设备 通过查看 ll /dev/vg00/ 下的内容 若开头带c字符的则为字符设备
块设备是文件设备 通过查看 ll /dev/vg00/ 下的内容 若开头带b字符的则为块符设备
2.定义block_operations结构体
核心在于实现上面提到的block_operations结构体,对于结构体的每一个函数都需要实现
(1)实现open函数
(2)实现close函数
(3)实现read函数
自行定义了用于模拟flash的内存的基地址/扇区大小/总大小
从flash中读取数据,起始地址:基地址+第几个扇区的偏移地址;
(4)实现write函数
(5)实现geometry函数:设置块结构体的信息,是否可用,是否可写,共有多少个扇区,扇区大小
3.注册block_operations结构体
(1)使用register_blockdriver函数注册结构体
注意:应当使用if0里面的代码初始化块设备,即函数los_disk_init(),该函数又调用了register_blockdriver函数。
关于函数的输入参数的说明
* Input Parameters:
* path - The path to the inode to create 创建节点的路径
* bops - The block driver operations structure 等待注册block_operations结构体的地址
* mode - Access privileges (not used) 用不到
* priv - Private, user data that will be associated with the inode.
*
* Returned Value:
* Zero on success (with the inode point in 'inode'); A negated errno 0表示成功,负数表示失败
* value is returned on a failure (all error values returned by
* inode_reserve):
*
(2)代码调用
《OpenharmonyFor6ull\vendor\nxp\imx6ull\board\board.c》
void SystemInit()函数
----调用1------->static void imx6ull_driver_init()函数
------调用---->my_ramdisk_init()函数
----调用2----->static void imx6ull_mount_rootfs()函数
-----调用---->mount("/dev/ramdisk", "/", "vfat", 0, NULL)函数。其中,"/dev/ramdisk"就是上面的路径,挂载到根目录下,vfat是文件系统格式,
也就是说根文件系统的设备节点就是上面的设备"/dev/ramdisk",这就是挂载根文件系统
4.实际编译
(1)编译内核源代码:make -j 16
(2)编译根文件系统:make rootfs FSTYPE=VFAT 或者 make rootfs FSTYPE=jffs2
关于文件系统:
(1)内核启动会挂载虚拟的文件系统procfs,在该虚拟文件系统上可以注册各种虚拟设备:例如:/dev/ramdisk /dev/uart
(2)挂载真实设备:mount(“/dev/ramdisk”,/)一旦挂载真实的设备,那么procfs就不存在了。
5.答疑记录
(1)liteOS-a的文件系统结构:
【1】init可执行文件对应的源代码为:《OpenharmonyFor6ull\kernel\liteos_a\apps\init\src\init.c》
【2】shell可执行文件:内核态实现的,例如kill命令 是由文件《OpenharmonyFor6ull\kernel\liteos_a\kernel\base\misc\kill_shellcmd.c》实现的。shell中输入kill命令的时候会调用OsShellCmdKill函数
#ifdef LOSCFG_SHELL
SHELLCMD_ENTRY(kill_shellcmd, CMD_TYPE_EX, "kill", 2, (CmdCallBackFunc)OsShellCmdKill);
#endif
(2)rootfs中没有dev目录,但是启动之后又dev目录,为什么?
/dev目录是虚拟的目录,dev下面挂载的这些文件都是虚拟的文件,这些文件对应的驱动程序