1.Linux将设备分成哪几大类?
2.设备驱动分层结构
3.用户进程请求设备服务的流程
4.驱动的点点滴滴
什么是驱动?
(1)驱动程序是软件和硬件连接的桥梁。
(2)驱动程序是添加到操作系统中的特殊程序。
(3)驱动程序是一种可以使计算机和设备进行通信的特殊程序。
为什么需要驱动?
(1)软件系统无法直接识别要接入的设备是什么,有什么功能。
(2)硬件设备只能理解电子信号,无法直接理解软件系统下发的命令。
驱动是谁做的?
驱动程序是硬件厂商根据操作系统编写的配置文件。
驱动的作用
驱动是软件和硬件的桥梁
(1)将硬件本身的功能告诉操作系统,完成硬件设备电子信号与操作系统及软件的高级编程语言(java等)之间的相互翻译。
(2)将操作系统的标准指令传达给硬件设备
(3)当操作系统需要使用某个硬件时,比如让声卡播放音乐,它会先发送相应指令到声卡驱动程序,声卡驱动程序接收到后,马上将其翻译成声卡才能听懂的电子信号命令,从而让声卡播放音乐。
5.file_operations()这个结构体起什么作用?
file_operations结构体完成设备读取、写入、保存等等这些操作,都是由存储在file_operations结构体中的这些函数指针来处理的,这些函数指针所指向的函数都需要我们在驱动模块将其实现。
struct file_operations {
struct module *owner; //拥有该结构的模块的指针,一般为THIS_MODULES
......
int (*mmap) (struct file *, struct vm_area_struct *); //用于请求将设备内存映射到进程地址空间
......
int (*open) (struct inode *, struct file *); //打开
......
}
6.设备号在驱动程序中起什么作用?为什么要有主设备号和次设备号?
每一个文件是一个设备,设备号是由主设备号和次设备号组成
主设备号:
- 一般认为一个主设备号对应一个驱动程序
- 一个驱动程序可以管理多个设备,即一个主设备号对应多个设备
- 一个主设备号也可以对应多个驱动程序
次设备号:
- 一个次设备号对应一个设备
一个驱动程序可以管理多个此类型的设备,设备数可以有2的20次方个,原因是次设备号有20位,不过实际不可能有这么多设备
7.字符设备的驱动程序注册和注销函数是做什么?为什么要进注册和注销?
字符设备驱动程序的注册函数:
static inline int register_chrdev(unsigned int major, const char *name,
const struct file_operations *fops)
{
return __register_chrdev(major, 0, 256, name, fops);
}
/*__register_chrdev() - 创建并注册一个占有较小大小的字符设备
major:主设备号,当用户设置为0时,内核会动态分配一个设备号。
baseminor: 次设备号,要在一定范围内从0开始
count: 次设备号的范围
name: 设备名称
fops: 文件系统的接口指针
如果major == 0,此函数将动态分配一个major并返回它的编号。
如果major > 0,此函数将尝试使用给定的主设备号来保留设备,成功时将返回0。
失败时返回-ve errno。
*/
int __register_chrdev(unsigned int major, unsigned int baseminor,
unsigned int count, const char *name,
const struct file_operations *fops)
{
struct char_device_struct *cd;
struct cdev *cdev;
int err = -ENOMEM;
cd = __register_chrdev_region(major, baseminor, count, name); //注册一个主设备号和一个一定范围内具体的次设备号
if (IS_ERR(cd))
return PTR_ERR(cd);
cdev = cdev_alloc(); //分配空间
if (!cdev)
goto out2;
//对cdev进行赋值操作
cdev->owner = fops->owner;
cdev->ops = fops;
kobject_set_name(&cdev->kobj, "%s", name);
err = cdev_add(cdev, MKDEV(cd->major, baseminor), count); //把这个设备添加到系统中
if (err)
goto out;
cd->cdev = cdev; //完成cd与cdev的关联
return major ? 0 : cd->major;
out:
kobject_put(&cdev->kobj);
out2:
kfree(__unregister_chrdev_region(cd->major, baseminor, count));
return err;
}
字符设备驱动程序的注销函数:
static inline void unregister_chrdev(unsigned int major, const char *name)
{
__unregister_chrdev(major, 0, 256, name);
}
void __unregister_chrdev(unsigned int major, unsigned int baseminor,
unsigned int count, const char *name)
{
struct char_device_struct *cd;
cd = __unregister_chrdev_region(major, baseminor, count);
if (cd && cd->cdev)
cdev_del(cd->cdev);
kfree(cd);
}
注册是为了生成相应的设备驱动程序,以便使用
注销是为了释放内存,以便其他进程的运行
8.I/O端口和I/O内存
当位于I/O空间时,通常被称为I/O端口
当位于内存空间时,对应的内存空间被称为I/O内存
9.在用户程序中对字符设备中的全局内存进行读写操作
10.注册块设备驱动程序
11.块设备和字符设备的区别
(1)块设备上可以mount文件系统,而字符设备是不可以的。显然这是随机访问带来的优势,因为文件系统需要能按块存储数据,同时更需要能随机读写数据。
(2)数据经过块设备相比操作字符设备需要经历一个数据缓冲层,也就是说应用程序与块设备传递数据时不同于操作字符设备那样直接打交道,而必须经过一个中间缓冲层来存储数据,然后才可使用数据,这是为了提高系统整体性能(吞吐量)。
(3)字符驱动程序的接口相对清晰而且易于使用,但是块驱动程序的接口要稍微复杂一些。出现这种情况的原因有两个:一是因为其历史——块驱动程序接口从 Linux 第一个版本开始就一直存在于每个版本中,并且已经证明很难修改或改进;其二是因为性能,一个慢的字符设备驱动程序虽然不受欢迎,但仍可以接受,但一个慢的块驱动程序将影响整个系统的性能。因此,块驱动程序的接口设计经常受到速度要求的影响。