一、linux设备驱动
我们在linux驱动基础概念以及驱动程序框架搭建中已经介绍过,linux 将所有的外设分为 3 类:字符设备、块设备、网络设备。
1.1 字符设备
字符设备是能够像字节流(比如文件)一样被访问的设备,就是说对它的读写是以字节为单位的。 比如串口在进行收发数据时就是一个字节一个字节的进行的,我们可以在驱动程序内部使用缓冲区来存放数据以提高效率,但是串口本身对这并没有要求。字符设备的驱动程序中实现了 open、close、read、write 等系统调用,应用程序可以通过设备文件(比如/dev/ttySAC0 等)来访问字符设备。
字符设备一般包括:led、按键、usb鼠标、usb键盘等等。
1.2 块设备
块设备上的数据以块的形式存放,比如 NAND FLASH上的数据就是以页为单位存放的。块设备驱动程序向用户层提供的接口与字符设备一样, 应用程序也可以通过相应的设备文件(比如/dev/mtdblock0、/dev/hda1 等)来调用 open、close、read、write 等系统调用,与块设备传送任意字节的数据。对用户而言,字符设备和块设备的访问方式没有差别。块设备驱动程序的特别之处如下。
- 操作硬件的接口实现方式不一样:块设备驱动程序先将用户发来的数据组织成块,再写入设备;或从设备中读出若干块数据,再从中挑出用户需要的;
- 数据块上的数据可以有一定的格式:通常在块设备中按照一定的格式存放数据,不同的文件系统类型就是用来定义这些格式的。内核中,文件系统的层次位于块设备驱动程序上面,这意味着块设备驱动程序除了向用户层提供与字符设备一样的接口外,还要向内核其他部件提供一些接口,这些接口用户是看不到的。这些接口使得可以在块设备上存放文件系统,挂载块设备。
块设备一般包括SD卡、TF卡、eMMC、Nand Flash、Nor Flash:
- Nor Flash:可以随机读写,以“字”为基本单位、有单独的地址线和数据线,可以随机存储,可以运行程序;
- Nand Flash:以“页面”为基本单位,通常是采用8个I/O引脚来传送控制、地址、数据信息;
- eMMC:相当于Nand Flash + 主控IC,对外的接口协议与SD、TF卡一样,主要是针对手机或平板电脑等产品的内嵌式存储器标准规格;
针对不同的存储设备实现了不同的I/O调度算法;
块设备结构的不同其I/O算法也会不用,比如对于eMMC、SD卡、Nand Flash这里没有任何机械设备的存储设备就可以任意读写任意的扇区;
但是对于机械硬盘这种带有刺头的设备,读取不同的盘面或者磁道里面的数据,磁头都需要进行移动,因此对于机械硬盘而言,将那些杂乱的访问按照一定的顺序进行排列可以有效提高磁盘性能;
1.3 网络设备
网络设备同时具有字符设备、块设备的部分特点,无法将它归入这两类中:
- 如果说它是字符设备,它的输入/输出却是有结构的、成块的(报文、包、帧);
- 如果说它是块设备,它的“块”又不是固定大小的,大到数百甚至数千字节,小到几字节;
UNIX 式的操作系统访问网络接口的方法是给它们分配一个唯一的名字(比如 eth0),但这个名字在文件系统中(比如/dev 目录下)不存在对应的节点项。应用程序、内核和网络驱动程序间的通信完全不同于字符设备、块设备,库、内核提供了一套和数据包传输相关的函数,而不是 open、read、write 等。
二、块设备基本知识
2.1 块设备结构
段:由若干个块组成,段的大小只与块有关,必须是块的整数倍,段在内核中由结构struct bio_vec来描述;
块:块是虚拟文件系统传输数据的基本单位,通常由1个或多个扇区组成。在Linux中,块的大小必须是2的幂,但是不能超过一个页的大小(4k)。(在X86平台,一个页的大小是4094个字节,所以块大小可以是512,1024,2048,4096);
扇区:扇区是硬件传输数据的基本单位,硬件一次传输一个扇区的数据到内存中。通常在512字节到32768字节之间,默认512字节;
2.2 块设备流程
比如当我们要写一个很小的数据到txt文件某个位置时,由于块设备写的数据是按扇区为单位,但又不能破坏txt文件里其它位置,那么就引入了一个“缓存区”,将所有数据读到缓存区里,然后修改缓存数据,再将整个数据放入txt文件对应的某个扇区中,当我们对txt文件多次写入很小的数据的话,那么就会重复不断地对扇区读出,写入,这样会浪费很多时间在读/写硬盘上,所以内核提供了一个队列的机制,在没有关闭txt文件之前,会将读写请求进行优化,排序,合并等操作,从而提高访问硬盘的效率。
其具体流程如下:
- 当一个用户程序要向块设备写数据时,将发出一个write()系统调用给内核;
- 内核将调用虚拟文件系统中一个适当的函数,将需要写入的文件描述符和文件内容指针传递给这个函数;
- 经过磁盘文件系统(vfat,ext2,ext3,yaffs)将把文件的读写转换为对扇区的读写,根据磁盘文件格式调用不同文件格式的写入函数,将数据发送到通用块层;
- 数据到达通用块层(ll_rw_block是通用层的入口),就会对块设备发出写请求;
- 在通用层发出写请求时,会先执行”I/O”调度器,调用器的作用就是把物理上相邻的读写磁盘请求合并为一个请求,提高效率;
- 最后,块设备驱动程序向块设备发送命令和数据,将数据写入磁盘,这样一次write()操作就完成了;
2.1 内核块设备目录结构
linux内核将块设备驱动相关的代码放在目录drivers/block下:
root@zhengyang:/work/sambashare/linux-5.2.8/drivers# ls block/ amiflop.c built-in.a loop.c nbd.c pktcdvd.c rsxx swim_asm.S virtio_blk.c zram aoe cryptoloop.c loop.h null_blk.h ps3disk.c skd_main.c swim.c xen-blkback ataflop.c drbd loop.o null_blk_main.c ps3vram.c skd_s1120.h sx8.c xen-blkfront.c brd.c floppy.c Makefile null_blk_zoned.c rbd.c sunvdc.c umem.c xsysace.c brd.o Kconfig mtip32xx paride rbd_types.h swim3.c umem.h z2ram.c
参考文章:
[4]Linux块设备驱动详解
原创文章,作者:ItWorker,如若转载,请注明出处:https://blog.ytso.com/288820.html