一、非阻塞IO
1、概念
在应用程序读取硬件数据时,不管硬件数据有没有准备好,应用程序中的read()函数都不会阻塞,程序继续往下执行
****************应用程序*****************
fd=open("/dev/mycdev",O_RDWR|O_NONBLOCK);
read(fd,buf,sizeof(buf));
**************驱动********************
mycdev_read(struct file*file,ubuf,size,lof)
{
if(file->f_flags&O_NONBLOCK)
{
//当前是非阻塞
//直接读取硬件寄存器的数据
// copy_to_user();
}
}
二、阻塞IO
1、概念
在应用程序读取硬件数据时,如果硬件数据有没有准备好,应用程序中的read()函数会阻塞,进程进入休眠态,阻塞等待硬件数据就绪
2、原理
应用程序中的read()函数会调用驱动中的mycdev_read()函数,存在一个标志变量,在驱动对应的函数中判断其真假。为真则表示硬件数据准备好,直接拷贝数据到用户空间;如果为假,则将进程的task_struct添加到等待队列,将进程切换为(可中断/不可中断)休眠状态。
1.定义等待队列头
wait_queue_head_t wq_head;
2.初始化等待队列头
init_waitqueue_head(&wq_head);
3.wait_event(wq_head, condition)
功能:检查condition的真假,如果为真,则函数执行结束,如果为假,将进程切换到不可中断休眠状态
参数:wq_head:等待队列头
condition:标志变量
4.wait_event_interruptible(wq_head, condition)
功能:检查condition的真假,如果为真,则函数执行结束,如果为假,将进程切换到可中断休眠状态
参数:wq_head:等待队列头
condition:标志变量
返回值:如果condition为真则返回0,如果被一个信号中断,则返回错误码-ERESTARTSYS
5.wake_up(&wq_head)
功能:将不可中断休眠态的进程唤醒,当执行这个函数时如果condition为假,则进程被唤醒后会再次休眠
参数:等待队列头地址
6.wake_up_interruptible(&wq_head)
功能:将 可中断休眠态的进程唤醒当执行这个函数时如果condition为假,则进程被唤醒后会再次休眠
参数:等待队列头地址
三、IO多路复用
1、概念
想要在一个进程下同时监听多个硬件数据,就需要使用IO多路复用(select、poll、epoll)。
主要是:在应用程序中将需要监听的事件的文件描述符放入事件集合中,调用函数判断集合中的文件描述符对应的硬件数据是否准备就绪,如果没有事件发生,将进程切换到休眠状态。当有一个或者多个硬件数据准备好了,将休眠的进程唤醒,对准备好的硬件数据进行读写。
2、select-API
************用户空间************
int fd1,fd2;
fd1=open("/dev/mycdev0",O_RDWR);
if(fd1<0)
{
printf("设备文件打开失败\n");
}
fd2=open("/dev/input/mouse0",O_RDWR);
if(fd2<0)
{
printf("设备文件打开失败\n");
}
//定义可读集合
fd_set readfds;
while(1)
{
//清空集合
FD_ZERO(&readfds);
//将监听的事件文件描述符添加到可读集合
FD_SET(fd1,&readfds);
FD_SET(fd2,&readfds);
select(fd2+1,&readfds)
//当select停止阻塞,判断发生的事件并且去读取数据
if(FD_ISSET(fd1,&readfds))
{
read(fd1,buf,sizeof(buf));
}
if(FD_ISSET(fd2,&readfds))
{
read(fd2,buf,sizeof(buf));
}
}
close(fd1);
close(fd2);
*************设备驱动层****************
//select/poll/epoll在设备驱动中的操作方法只有一个,就是下面的这个poll方法
__poll_t (*poll) (struct file *file, struct poll_table_struct *wait)
{
//将等待队列头向上提交
void poll_wait(struct file * filp, wait_queue_head_t * wait_address, poll_table *p)
{
功能:向上提交等待队列头
参数:
filp: 文件结构体指针
wait_address:要提交的等待队列头首地址
p: 将队列头向上提交的通道
}
if(condition)
return POLLIN;//POLLIN表示发生的事件为读事件POLLOUT表示写事件
else
return 0;
}
3、epoll-API
#include <sys/epoll.h>
1.int epoll_create(int size);
功能:创建一个新的epoll
参数:
size:大于0的整数
返回值:成功返回用于操作epoll的文件描述符,失败返回错误码
2.int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
功能:进行epoll管理
参数:
epfd:epoll_create生成的文件描述符
op:管理的类型选项
EPOLL_CTL_ADD:用于在epoll上添加文件描述符
EPOLL_CTL_MOD:用于修改文件描述符事件类型
EPOLL_CTL_DEL:从epoll上移除指定的文件描述符
fd:要操作的文件描述符
event:设置文件描述符属性的变量
struct epoll_event {
uint32_t events; /* Epoll events */
//EPOLLIN:读
//EPOLLOUT:写
epoll_data_t data; /* User data variable */
};
typedef union epoll_data {
void *ptr;
int fd;《=======使用这个
uint32_t u32;
uint64_t u64;
} epoll_data_t;
返回值: 成功返回0,失败返回错误码
3.int epoll_wait(int epfd, struct epoll_event *events,
int maxevents, int timeout);
功能:阻塞等待事件发生
参数:
epfd:epoll_create生成的文件描述符
events: 存放发生的事件的文件描述符数组的首地址
maxevents:监听的文件描述符最大个数
timeout:设置超时事件 毫秒级 -1表示不关注超时 >0超时事件
返回值:
>0:发生事件的文件描述符个数
==0:超时时间到达
<0:失败