阻塞操作
描述:指在执行设备操作时,若不能获得资源则进程睡眠。当满足可操作的条件后,内核唤醒进程继续执行。
采用等待队列的方式实现,等待队列 以队列为基础数据结构,与进程调度机制紧密结合,能够用于实现内核中的异步事件通知机制,也可以用来同步对系统资源的访问(如信号量)
操作步骤:
定义“等待队列头”
wait_queue_head_t my_queue;
初始化“等待队列头”
init_waitqueue_head(&my_queue);
定义等待队列
DECLARE_WAITQUEUE(name, tsk)
添加/移除等待队列
void fastcall add_wait_queue(wait_queue_head_t *q,wait_queue_t *wait);
void fastcall remove_wait_queue(wait_queue_head_t *q,wait_queue_t *wait);
等待事件
wait_event(queue, condition)
wait_event_interruptible(queue, condition)
(推荐使用,安全性更高)
wait_event_timeout(queue,condition, timeout)
wait_event_interruptible_timeout(queue, condition,timeout)
唤醒队列
void wake_up(wait_queue_head_t *queue);
void wake_up_interruptible(wait_queue_head_t *queue);
(推荐使用,安全性更高)
在等待队列上睡眠
sleep_on(wait_queue_head_t *q );
interruptible_sleep_on(wait_queue_head_t *q );
(推荐使用,安全性更高)
例子:
//定义等待队列头
wait_queue_head_t my_queue;
//在init函数中初始化队列
init_waitqueue_head(&my_queue);
//等待事件(意为当flag大于或等于len时被才执行)
wait_event_interruptible(my_queue,flag>=len);
//唤醒事件
wake_up_interruptible(&my_queue);
非阻塞操作
描述:指进程在不能进行设备操作时并不睡眠而是立刻返回结果。
进程中调用poll和select操作来查询打开的I/O设备文件是否就绪(有资源)
设备驱动操作集合中的 unsigned int (*poll) (struct file *, poll_table *)被调用通过poll_wait可以向驱动向poll_table结构添加一个等待队列驱动的poll函数应该返回设备的当前状态
POLLIN,POLLOUT,POLLRDNORM,POLLERR
操作步骤:
用户空间使用的select函数:
int select(int numfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
其中readfds、writefds、exceptfds分别是被select()监视的读、写和异常处理的文件描述符集合,numfds的值是需要检查的号码最高的文件描述符加1。timeout参数是一个指向struct timeval类型的指针,它可以使select()在等待timeout时间后若没有文件描述符准备好则返回。
poll底层实现函数模板:
static unsigned int xxx_poll(struct file *filp, poll_table *wait)
{
unsigned int mask = 0;
struct xxx_dev *dev = filp->private_data; /*获得设备结构体指针*/
...
poll_wait(filp, &dev->r_wait, wait);//加读等待队列头
poll_wait(filp, &dev->w_wait, wait);//加写等待队列头
if (...)//可读
{
mask |= POLLIN | POLLRDNORM; /*标示数据可获得*/
}
if (...)//可写
{
mask |= POLLOUT | POLLWRNORM; /*标示数据可写入*/
}
...
return mask;
}
底层例子:
static struct file_operations myops={
.poll = myPoll
};
unsigned int myPoll(struct file *pf, struct poll_table_struct *wait)
{
unsigned int mask = 0;
poll_wait(pf,&my_queue,wait);
if(flag>0)
{
mask |= POLLIN | POLLRDNORM;
}
if(0 == flag)
{
mask |= POLLOUT | POLLWRNORM;
}
return mask;
}
应用层例子:
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include "mydev.h"
static char rbuf[32] = {0};
static char wbuf[32] = "1232145679";
static int fd = 0;
int main()
{
int ret = 0;
fd_set rfd;
fd_set wfd;
fd = open("/dev/mydev",O_RDWR);
if(fd<0)
{
printf("open failed.\n");
}
while(1)
{
FD_ZERO(&rfd);//将指定的文件描述符集清空,初始化操作
FD_ZERO(&wfd);
FD_SET(fd,&rfd);//用于在文件描述符集合中增加一个新的文件描述符。
FD_SET(fd,&wfd);
select(fd+1,&rfd,&wfd,NULL,0);
if(FD_ISSET(fd,&rfd))
{
read(fd,rbuf,32);
printf("read rbuf is %s\n",rbuf);
}
if(FD_ISSET(fd,&wfd))
{
write(fd,wbuf,32);
printf("write wbuf ok\n");
}
}
close(fd);
}
异步通知
描述:一旦设备就绪,则主动通知应用程序,这样应用程序根本就不需要查询设备状态,这一点非常类似于硬件上“中断”的概念,比较准确的称谓是“信号驱动的异步I/O”
操作步骤:
在用户程序中,为了捕获信号,可以使用signal()函数来设置对应信号的处理函数,如下所示:
void (*signal(int signum, void (*handler)(int)) (int) ;
捕获信号范例:
void sigterm_handler(int signo)
{
printf("Have caught sig N.O. %d\n", signo); exit(0);
}
int main(void)
{
signal(SIGINT, sigterm_handler);
signal(SIGTERM, sigterm_handler);
while(1);
return 0;
}
应用程序要指定进程为文件的属主
fcntl(STDIN_FILENO, F_SETOWN, getpid());
应用程序通过fcntl函数在设备中设置FASYNC标志
oflags = fcntl(fd, F_GETFL);
fcntl(fd, F_SETFL, oflags | FASYNC);
为了使设备支持异步通知机制,驱动程序中涉及以下3项工作。
支持F_SETOWN命令,能在这个控制命令处理中设置filp->f_owner为对应进程ID。不过此项工作已由内核完成,设备驱动无须处理。
支持F_SETFL命令的处理,每当FASYNC标志改变时,驱动程序中的fasync()函数将得以执行。因此,驱动中应该实现fasync()函数。
在设备资源可获得时,调用kill_fasync()函数激发相应的信号。
应用层:
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <signal.h>
static char rbuf[32] = {0};
static char wbuf[32] = "123456789";
static int fd = 0;
void handler(int s)
{
read(fd,rbuf,32);
printf("rbuf is %s\n",rbuf);
}
int main()
{
int oflags = 0;
int ret = 0;
fd = open("/dev/mydev",O_RDWR);
if(fd<0)
{
printf("open failed.\n");
}
signal(SIGIO, handler);
//绑定文件和进程号以便信号准确发送
fcntl(fd, F_SETOWN, getpid());
//在设备中设置FASYNC标志
oflags = fcntl(fd, F_GETFL);
fcntl(fd, F_SETFL, oflags | FASYNC);
write(fd,wbuf,32);
sleep(5);
close(fd);
}
驱动:
//定义信号队列
struct fasync_struct *async_queue;
//定义fasync函数
static struct file_operations myops={
.fasync = myfasync
};
//实现fasync函数,初始化信号队列
static int myfasync(int fd, struct file *filp, int mode)
{
return fasync_helper(fd, filp, mode, &async_queue);
}
//发送信号
kill_fasync(&async_queue, SIGIO, POLL_IN);