设备访问有阻塞与非阻塞两种方式。
阻塞操作是指在执行设备操作时若不能获得资源则挂起进程,直到满足操作条件后再进行操作。被挂起的进程进入休眠状态,从运行队列移走,直到等待的条件满足。
非阻塞操作在不能进行设备操作时并不挂起,它或者放弃,或者不停的查询,直到可以进行操作为止。
阻塞的例子:
res = read(fd, &buf, 1);
if (res==1) //当有输入时才返回
printf("ok");
非阻塞的例子:
while(read(fd, &buf, 1) != 1); 无输入时也返回,循环尝试读取
printf("ok");
可以使用等待队列实现阻塞进程的唤醒。
定义等待队列头:wait_queue_head_t my_queue;
初始化等待队列头:init_waitqueue_head(&my_queue):
定义并初始化:DECLARE_WAIT_QUEUE_HEAD(name);
定义等待队列:DECLARE_WAITQUEUE(name,tsk);
添加或移除等待队列:void fastcall add_wait_queue(wait_queue_head_t *q, wait_queue_t *wait); ...remove_...
等待事件:wait_event(queue,condition); ...interruptible(), ...timeout(), ...interruptible_timeout();当condition不满足时阻塞。与唤醒队列对应
唤醒队列: wake_up(), wake_up_interruptible()
在队列上睡眠:sleep_on(wait_queue_head_t *q);将目前进程状态设置成TASK_UNINTERRUPTIBLE,并且定义一个等待队列,将它附属到等待队列头q,直到资源可获得,队列被唤醒。
interruptible_sleep_on(),设置成TASK_INTERRUPTIBLE. 与唤醒队列对应。
在驱动程序中改变进程状态并且调用schedule的模板:
1,定义等待队列
2,添加等待队列
3,如果设备不可写,改变状态进程(__set_current_state())
4,并且如果为非阻塞操作(if (file->f_flags & O_NONBLOCK)则立即退出
5,否则用schedule()放弃cpu,调度其他进程执行
6,如果因为信号而被唤醒(signal_pending)则退出
7,当设备可写开始写设备缓冲区,
8,写完后将等待队列移出等待队列头,将进程状态设置为TASK_RUNNING
轮询。使用非阻塞I/O的应用程序通常会使用select()和poll()系统调用查询是否可对设备进行无阻塞的房屋内。他们两个本质上是一样的。select()和poll()系统调用最终会引发设备驱动中的poll()函数被执行。
unsigned int (*poll)(struct file *filp, struct poll_table* wait);
第二个参数为轮询表指针,poll函数进行以下两项工作:
1,对可能引起设备文件状态变化的等待队列调用poll_wait()函数,将对应的等待队列头添加到poll_table;
2,返回表示能否对设备进行无阻塞读、写访问的掩码。
poll_wait()把当前进程添加到wait参数指定的等待列表(poll_table)中。
因此poll()函数的典型模板如下
static unsigned int xxx_poll(struct file *filp, poll_table *wait)
{
...
poll_wait(filp, &dev->r_wait, wait); //加读等待队列头
poll_wait(filp, &dev->w_wait, wait); //加写等待队列头
...
if(...) //可读
mask |= POLLIN | POLLRDNORM;
if(...) //可写
mask |= POLLOUT | POLLRDNORM;
return mask;
}
以下是对设备进行poll操作的应用程序。
------------------------------------------------------------------------------
#include
#include
#include
#include
#include
#include
#define FIFO_CLEAR 0x1
#define BUFFER_LEN 20
main()
{
int fd, num;
char rd_ch[BUFFER_LEN];
fd_set rfds,wfds;
/*以非阻塞方式打开/dev/globalmem设备文件*/
fd = open("/dev/globalfifo", O_RDONLY | O_NONBLOCK);
if (fd != - 1)
{
/*FIFO清0*/
if (ioctl(fd, FIFO_CLEAR, 0) < 0)
{
printf("ioctl command failed\n");
}
while (1)
{
FD_ZERO(&rfds);
FD_ZERO(&wfds);
FD_SET(fd, &rfds);
FD_SET(fd, &wfds);
select(fd + 1, &rfds, &wfds, NULL, NULL); //select中第一个参数是需要检查号码最高的文件描述符加1.
/*数据可获得*/
if (FD_ISSET(fd, &rfds))
{
printf("Poll monitor:can be read\n");
}
/*数据可写入*/
if (FD_ISSET(fd, &wfds))
{
printf("Poll monitor:can be written\n");
}
}
}
----------------------------------
没写入前结果一直循环显示可写,写入后显示可写可读,若写满则会显示可读。
阅读(1613) | 评论(0) | 转发(2) |