IO模型
【1】IO模型
1)阻塞IO
2)非阻塞IO
3)IO多路复用
4)异步IO
【2】IO多路复用
多路复用在阻塞IO模型上封装的
应用程序:
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
功能:实现对文件描述符的监听(如果某一个文件描述符准备就绪,select立刻返回)
函数本身阻塞函数,实现对文件描述符轮询检测
void FD_CLR(int fd, fd_set *set); 从文件描述符集合中将描述符移除
int FD_ISSET(int fd, fd_set *set); 查看当期的文件描述符是否在文件描述符集合中
void FD_SET(int fd, fd_set *set); 向文件描述符集合中将描述符添加
void FD_ZERO(fd_set *set); 清空文件描述符集合
驱动:
unsigned int (*poll) (struct file *, struct poll_table_struct *);
a)对可能引起设备文件状态变化的等待队列调用poll_wait函数,将对应的等待队列头添加到poll_table
由内核检测poll_table
static inline void poll_wait(struct file * filp, wait_queue_head_t * wait_address, poll_table *p)
功能:将等待队列加入到poll_table表
参数:filep
wait_address 等待队列头
p poll_table表
b)返回表示是否能对设备进行无阻塞读,写访问的掩码
POLLIN,POLLOUT,POLLRDNORM,POLLERR
可读 可写 正常 错误
return POLLIN|POLLRDNORM
【3】异步IO
异步通知的意思是:一旦设备就绪,则主动通知应用程序,这样应用程序根本就不需要查询设备状态
应用程序:
应用程序要捕捉SIGIO信号,注册信号
signal(SIGIO, handler);
应用程序要指定进程为文件的属主
当前文件描述符属于该进程,进程需要对该文件描述符状态发生改变之后,处理相应的任务
fcntl(fd, F_SETOWN, getpid());
int fcntl(int fd, int cmd, ... /* arg */ );
参数:fd 文件描述符
cmd 定义对文件描述符的操作
arg 附件参数
应用程序通过fcntl函数在设备中设置FASYNC标志
oflags = fcntl(fd, F_GETFL); 获取当前文件描述符的标志位
fcntl(fd, F_SETFL, oflags | FASYNC); 重新设置标志位
驱动:
1)支持F_SETOWN命令,能在这个控制命令处理中设置filp->f_owner为对应进程ID。不过此项工作已由内核完成,设备驱动无须处理。
2)支持F_SETFL命令的处理,每当FASYNC标志改变时,驱动程序中的fasync()函数将得以执行。因此,驱动中应该实现fasync()函数
fasync_helper(int fd, struct file * filp, int on, struct fasync_struct **fapp)
功能:将文件描述符添加到异步通知队列中
参数:fd 文件描述符
filep
on 打开设备的标志位
fapp 异步通知队列
3)在设备资源可获得时,调用kill_fasync()函数激发相应的信号
void kill_fasync(struct fasync_struct **fp, int sig, int band)
功能:向应用程序发送信号
参数:fp 异步通知队列
sig 发送的信号类型
band 掩码 POLLIN可读
POLLOUT可写
【4】Linux驱动中设备模型
在Linux内核中,将对设备的操作代码抽象成一个对象(驱动)
将对设备的硬件描述抽象成一个对象(设备)
将该设备使用的总线抽象成一个对象(总线)
在系统每注册一个设备的时候,会寻找与之匹配的驱动;相反的,在系统每注册一个驱动的时候,会寻找与之匹配的设备,而匹配由总线完成
总线可以是物理存在的,也可以是虚拟的。内核中对应的结构为struct bus_type(描述一个总线对象)
struct bus_type {
const char *name; 总线的名称
int (*match)(struct device *dev, struct device_driver *drv); 实现设备与驱动的匹配
}
设备是连接到某条物理或者虚拟总线上的对象。可能是真正物理对象,也可能是虚拟对象。内核中对应的结构为struct device
struct device{
struct kobject kobj; 所有的设备对象的父亲
const char *init_name; 设备的名称
struct bus_type *bus; 总线的名称
void *platform_data; 私有数据,存放的是硬件的信息
void (*release)(struct device *dev); 卸载设备模块时,自动执行此函数
}
驱动是用来和设备通信的软件程序。驱动可以从设备中获取数据,也可以把相应数据发给设备进行处理。内核中对应的结构为struct devicc_driver
struct device_driver{
const char *name; 名称
struct bus_type *bus; 总线的名称
struct module *owner; THIS_MODULE
const struct of_device_id *of_match_table; 设备树匹配
int (*probe) (struct device *dev); 回调函数(一但设备与驱动匹配成功,自动执行此函数,存放对设备的操作的代码)
int (*remove) (struct device *dev); 卸载函数(存放的与probe执行相反的代码)
}
如何实现Linux设备模型:
1)构建总线对象,并且向内核完成注册
注册:int bus_register(struct bus_type *bus)
注销:void bus_unregister(struct bus_type *bus)
2)构建设备对象,并且向内核完成注册
注册:int device_register(struct device *dev)
注销:void device_unregister(struct device *dev)
3)构建驱动对象,并且向内核完成注册
注册:int driver_register(struct device_driver *drv)
注销:void driver_unregister(struct device_driver *drv)
4)实现设备与驱动的匹配
实现匹配操作
5)实现驱动对设备的操作
【5】platform设备模型
将设备,驱动,总线抽象成一个对象
设备对象:描述硬件信息
struct platform_device {
const char *name; 设备的名称
struct device dev; 描述设备对象的结构体
void (*release)(struct device *dev);
u32 num_resources; 描述硬件资源的数量
struct resource *resource; 描述硬件资源的信息
};
struct resource {
resource_size_t start; 起始地址
resource_size_t end; 结束地址
unsigned long flags 标志-描述地址属性
};
#define IORESOURCE_MEM 0x00000200 内存地址
#define IORESOURCE_REG 0x00000300 寄存器的偏移量
#define IORESOURCE_IRQ 0x00000400 中断
驱动对象:描述驱动代码
struct platform_driver {
int (*probe)(struct platform_device *); 回调函数(一但设备与驱动匹配成功,自动执行此函数,存放对设备的操作的代码)
int (*remove)(struct platform_device *); 卸载函数(存放的与probe执行相反的代码)
struct device_driver driver; 描述所有驱动对象的结构体
const char *name;
struct module *owner;
const struct of_device_id *of_match_table; 实现设备树的匹配
const struct platform_device_id *id_table; 匹配表
};
总线对象:描述总线信息
struct bus_type platform_bus_type = {
.name = "platform", 总线的名称
.match = platform_match, 匹配方法
};
platform平台匹配方法
static int platform_match(struct device *dev, struct device_driver *drv)
{
struct platform_device *pdev = to_platform_device(dev);
struct platform_driver *pdrv = to_platform_driver(drv);
/* Attempt an OF style match first */ 设备树匹配
if (of_driver_match_device(dev, drv))
return 1;
/* Then try to match against the id table */ 匹配表匹配
if (pdrv->id_table)
return platform_match_id(pdrv->id_table, pdev) != NULL;
/* fall-back to driver name match */
return (strcmp(pdev->name, drv->name) == 0); 名字匹配
}
如何实现platform平台模型:
1)构建设备对象,并且向内核完成注册
注册:int platform_device_register(struct platform_device *pdev)
注销:void platform_device_unregister(struct platform_device *pdev)
2)构建驱动对象,并且向内核完成注册
注册:#define platform_driver_register(drv) __platform_driver_register(drv, THIS_MODULE)
注销:platform_driver_unregister(struct platform_driver *);
3)实现设备与驱动的匹配
匹配方法已实现
4)实现驱动对设备的操作
获取硬件资源,实现对设备操作