Linux设备文件分为两大类:block device driver和character device drive两类。
character device drive又被称为字符设备或裸设备,
raw devices; block device driver通常成为块设备。
传输方式:
block device driver是以固定大小长度来传送转移资料 ;
character device driver是以不定长度的字元传送资料 。
所连接的devices也有所不同:
block device大致是可以随机存取(random access)资料的设备,如硬碟机或光碟机;
而character device刚好相反,依循先後顺序存取资料的设备,如印表机 、终端机等皆是。
/dev/dsk对应的为块设备,文件系统的操作用到它,如mount。
/dev/rdsk对应的为字符设备(裸设备,rdsk的r即为 raw),fsck newfs等会涉及到。
一般我们的操作系统和各种软件都是以块方式读写硬盘,这里的块是逻辑块,创建文件系统时可以选择,windows里叫簇。
可看 newfs or mkfs的manual。oracle是比较常见的字符方式读写硬盘。
无论字符设备还是块设备的定义都属于操作系统的设备访问层,与实际物理设备的特性无必然联系。设备访问层下面是驱动程序,所以只要驱动程序提供的方式,都可以。
也就是说驱动程序支持stream方式,那么就可以用这种方式访问,驱动程序如果还支持block方式,那么你想用哪种方式访问都可以;
典型的比如硬盘式的裸设备,两种都支持块设备(block device):是一种具有一定结构的随机存取设备,
对这种设备的读写是按块进行的,他使用缓冲区来存放暂时的数据,待条件成熟后,从缓存一次性写入设备或从设备中一次性读出放入到缓冲区,
如磁盘和文件系统等字符设备(character device):这是一个顺序的数据流设备,对这种设备的读写是按字符进行的,而且这些字符是连续地形成一个数据流。
他不具备缓冲区,所以对这种设备的读 写是实时的,如终端、磁带机等。
系统中能够随机(不需要按顺序)访问固定大小数据片(chunks)的设备被称作块设备,
这些数据片就称作块。最常见的块设备是硬盘,除此以外,还有软盘驱 动器、cd-rom驱动器和闪存等等许多其他块设备。
注意,它们都是以安装文件系统的方式使用的——这也是块设备一般的访问方式。
另一种基本的设备类型是字符设备。
字符设备按照字符流的方式被有序访问,像串口和键盘就都属于字符设备。
如果一个硬件设备是以字符流的方式被访问的话,那就应该将它归于字符设备;
反过来,如果一个设备是随机(无序的)访问的,那么它就属于块设备。
这 两种类型的设备的根本区别在于它们是否可以被随机访问——换句话说就是,能否在访问设备时随意地从一个位置跳转到另一个位置。
举个例子,键盘这种设备提供 的就是一个数据流,当你敲入“fox”这个字符串时,键盘驱动程序会按照和输入完全相同的顺序返回这个由三个字符组成的数据流。
如果让键盘驱动程序打乱顺序来读字符串,或读取其他字符,都是没有意义的。
所以键盘就是一种典型的字符设备,它提供的就是用户从键盘输入的字符流。
对键盘进行读操作会得到一个字符流,首先是“f”,然后是“o”,最后是“x”,最终是文件的结束(eof)。当没人敲键盘时,字符流就是空的。
硬盘设备的情况就不大一样了。硬盘设备的 驱动可能要求读取磁盘上任意块的内容,然后又转去读取别的块的内容,
而被读取的块在磁盘上位置不一定要连续,所以说硬盘可以被随机访问,而不是以流的方式 被访问,显然它是一个块设备。
内核管理块设备要比管理字符设备细致得多,需要考虑的问题和完成的工作相比字符设备来说要复杂许多。
这是因为字符设备仅仅需要控制一个位置当前位置而块设备访问的位置必须能够在介质的不同区间前后移动。
所以事实上内核不必提供一个专门的子系统来管理字符设备,但是对块设备的管理却必须要有一个专门的提供服务的子系统。
不仅仅是因为块设备的复杂性远远高于字符设备,更重要的原因是块设备对执行性能的要求很高;
对硬盘每多一分利用都会对整个系统的性能带来提升,其效果要远远比键盘吞吐速度成倍的提高大得多。
另外,我们将会看到,块设备的复杂性会为这种优化留下很大的施展空间。
linux驱动程序中字符设备和块设备的三点区别
1.字符设备只能以字节为最小单位访问,而块设备以块为单位访问,例如512字节,1024字节等
2.块设备可以随机访问,但是字符设备不可以
3.字符和块没有访问量大小的限制,块也可以以字节为单位来访问
两种设备本身并没用严格的区分,主要是字符设备和块设备驱动程序提供的访问接口(file I/O API)是不一样的。本文主要就数据接口、访问接口和设备注册方法对两种设备进行比较。
1、数据结构
1.1字符设备数据结构
struct file;
struct inode;
file定义于 <linux/fs.h>, 是设备驱动中第二个最重要的数据结构. 文件结构代表一个打开的文件. 它由内核在 open 时创建, 并传递给在文件上操作的任何函数, 直到最后的关闭. 在文件的所有实例都关闭后, 内核释放这个数据结构。
inode 结构由内核在内部用来表示文件.inode 结构包含大量关于文件的信息其中dev_t i_rdev成员包含实际的设备编号.struct cdev *i_cdev中struct cdev 是内核的内部结构, 代表字符设备。
1.2块设备数据结构
struct gendisk (定义于 <linux/genhd.h>) 是单独一个磁盘驱动器的内核表示. 事实上, 内核还使用 gendisk 来表示分区。
2、设备访问接口
2.1字符设备访问接口
struct file_operations 其中file_operation 结构中的每个成员必须指向驱动中的函数, 这些函数实现一个特别的操作, 或者对于不支持的操作留置为 NULL. 当指定为 NULL 指针时内核的确切的行为是每个函数不同的,该结构中主要函数如下:
ssize_t read(struct file *filp, char __user *buff, size_t count, loff_t *offp);
ssize_t write(struct file *filp, const char __user *buff, size_t count, loff_t *offp);
filp 是文件指针, count 是请求的传输数据大小. buff 参数指向持有被写入数据的缓存, 或者放入新数据的空缓存. 最后, offp 是一个指针指向一个"long offset type"对象, 它指出用户正在存取的文件位置. 返回值是一个"signed size type"。
2.2块设备访问接口
字符设备通过 file_ 操作结构使它们的操作对系统可用. 一个类似的结构用在块设备上; 它是 struct block_device_operations, 定义在 <linux/fs.h>,其主要操作方法如下:
int (*open)(struct inode *inode, struct file *filp);
int (*release)(struct inode *inode, struct file *filp);
就像它们的字符驱动对等体一样工作的函数; 无论何时设备被打开和关闭都调用它们. 一个字符驱动可能通过启动设备或者锁住门(为可移出的介质)来响应一个 open 调用. 如果你将介质锁入设备, 你当然应当在 release 方法中解锁。
int (*ioctl)(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg);
实现 ioctl 系统调用的方法. 但是, 块层首先解释大量的标准请求; 因此大部分的块驱动 ioctl 方法相当短。
3、设备注册
3.1字符设备注册
int register_chrdev_region(dev_t first, unsigned int count, char *name)
int alloc_chrdev_region(dev_t *dev, unsigned int firstminor, unsigned int count, char *name)
void unregister_chrdev_region(dev_t first, unsigned int count);
允许驱动分配和释放设备编号的范围的函数. register_chrdev_region 应当用在事先知道需要的主编号时; 对于动态分配, 使用 alloc_chrdev_region 代替.
3.2块设备注册
int register_blkdev(unsigned int major, const char *name);
int unregister_blkdev(unsigned int major, const char *name);
register_blkdev 注册一个块驱动到内核, 并且, 可选地, 获得一个主编号. 一个驱动可被注销, 使用 unregister_blkdev