对于字符型驱动中经常会碰到应用程序和底层硬件数据交换的问题,常用的做法有:
1.阻塞:
通过读写函数中内嵌阻塞代码(信号量,等待队列)来实现不满足条件时的睡眠,等到满足条件了应用程序从睡眠中唤醒,继续下面的操作。
关于信号量和等待队列可以参考以前的ppt和之前的文章。
2.非阻塞:
非阻塞就是应用程序即使得不到硬件数据也不会睡眠,而是直接返回。当然真实的操作不会就这样返回推出了,而是先通过轮询的方式,也就是上一篇文章的select,(虽然这里说是非阻塞,但是select本身就有点阻塞的味道,如果加入的文件描述符都不满足,select会休眠,一旦下层驱动有变化则会通知select再次调用poll函数,这其实是一种改良的轮询,非常好的一种机制),通过select来实现轮询的方式,只要select能通过则表明可以进行读或者写了。
3.异步IO
一旦实现异步IO,底层的一旦有数据变化,就会像产生一个中断一样通知上层应用.异步通知的意思是:一旦设备就绪,则主动通知应用程序,这样应用程序就根本不需要查询设备状态(非常像中断吧!)
1.2之前都有提到怎么写,所以这里不多说了,谈一下怎么用异步io。
(1)首先在结构体中添加异步结构体指针:
struct fifodev
{
unsigned char buf[MAX_FIFO_BUF]; //按键缓冲区
unsigned int current_len;
wait_queue_head_t r_wait; //等待队列
wait_queue_head_t w_wait;
struct cdev cdev;
……
struct fasync_struct *async_queue;
} ;
(2)驱动设备的fasync()函数
static int sep4020_fifo_fasync(int fd, struct file *flip, int mode)
{
return fasync_helper(fd, flip, mode, &fifo_dev->async_queue);
}
(3)在相应的资源可以获得的地方添加释放sigio信号,比如在中断,读函数,写函数中,这里举例写函数中:
static ssize_t sep4020_fifo_write(struct file *filp, const char __user *buf, size_t size, loff_t *ppos)
{
……
wake_up_interruptible(&fifo_dev->r_wait);
if(fifo_dev->async_queue)
kill_fasync(&fifo_dev->async_queu,SIGIO, POLL_IN);//这里是写函数释放,当然是表示可以读了,同理在读中释放就要用poll_out了
out:
return ret;
}
(4)在文件关闭时,将文件从异步通知队列中删除。
static int sep4020_fifo_release(struct inode *inode, struct file *filp)
{
//调用之前写的fasync函数
sep4020_fifo_fasync(-1,flip, 0);
return 0;
}
/****************************************************************************/
下面是应用程序需要做的工作:
#include ……
void input_handler(int signum)
{
相应的处理,比如说程序中异步读通知的话,这里就可以实现读的操作
}
main()
{
int fd, oflags;
fd = open("dev/fifo, O_RDWR, S_IRUSR | S_IWUSR");
if(fd == -1)
{
printf("wrong\r\n");
exit(-1);
}
signal(SIGIO,input_handler);//让input_handler()处理SIGIO信号
fcntl(fd, F_SETOWN, getpid());//第二个参数的定义是设置异步io所有权,所以这句话的意思是设置本进程为fd文件的所有者
oflags = fcntl(fd, F_GETFL);//获得文件状态标志
fcntl(fd, F_SETFL, oflags | FASYNC);//用户程序必须对访问的设备文件设置FASYNC标志。F_SETFL命令表示设置文件状态标志位.
while(1)
{
sleep(100);
}
}
以下是网上摘录的:
驱动程序的实现需要:
当用户程序操作时,从内核驱动的角度来看:
(1) 当用户程序调用F_SETOWN命令时(通过fnctl系统调用),所设置的值保存在了驱动程序中的filp->f_owner结构体。
(2) 当用户程序调用F_SETFL命令设置FASYNC标志时,驱动中的fasync方法相应的被调用。fasync方法的实现样例如下:
static int scull_p_fasync(int fd, struct file *filp, int mode)
{
struct scull_pipe *dev = filp->private_data;
return fasync_helper(fd, filp, mode, &dev->async_queue);//
} |
(3) 当设备驱动准备好访问数据后,向所有注册异步通知的进程发送SIGIO信号。它的实现样例如下:
if (dev->async_queue)
kill_fasync(&dev->async_queue, SIGIO, POLL_IN);//POLL_IN指设备此时准备好供用户可读的数据;如果要对设备可写,这里应该用POLL_OUT |
(4) 当设备文件被关闭时,应当将设备文件从内核维护的活动异步读列表中删掉。它的实现样例如下:
/* remove this filp from the asynchronously notified filp's */
scull_p_fasync(-1, filp, 0); |
阅读(481) | 评论(0) | 转发(0) |