Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1006658
  • 博文数量: 153
  • 博客积分: 4195
  • 博客等级: 上校
  • 技术积分: 2631
  • 用 户 组: 普通用户
  • 注册时间: 2009-06-22 11:32
文章存档

2012年(7)

2010年(35)

2009年(111)

分类:

2009-08-14 16:10:15

对于字符型驱动中经常会碰到应用程序和底层硬件数据交换的问题,常用的做法有:
 
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);

 

 

阅读(3341) | 评论(2) | 转发(4) |
给主人留下些什么吧!~~

myleeming2009-09-18 11:16:26

因此,在中断这种你无法控制的地方,用信号量来实现阻塞操作是很不合适的,还是选择等待队列,用在这种场合非常不错。(其实信号量在内核中也是依赖等待队列实现的)

myleeming2009-09-18 11:12:53

最近发现了我们sdk中运用信号量不恰当的地方,信号量的原理就是一个整数的增减,up=加1,down = 减1;当这个值>=1时它就是属于资源释放状态,此时使用down能获得,如果<=0则无法down获得。 这就会出现一个问题,其实信号量不是互斥的,linux内核说定义互斥信号量,只是说你把它初始化为1或者0,然后通过配对使用up down来保证。 而我们sdk中磁条卡驱动时这样写的,初始化一个读信号量,在中断中释放这个读信号量(因为中断意味着数据的获得),在写函数的时候获取这个读信号量。这样乍一看是没有问题的,但是如果我刷了两次卡,意味着我此时信号量的值是2,然后我就读了一次,读的时候把数据全部取走了,这时候我如果再读的话理论上应该进入休眠状态(没有数据),但是由于这种up和down的不对称导致此时会继续往下执行(因为此时是信号量值是1),所以很明显就产生问题了。