前面已经说了阻塞与非阻塞的访问方式,这里我们就继续说下异步通知的机制。
什么是异步通知呢?异步通知的意思就是,一旦设备就绪,则主动通知应用程序,应用程序 根本就不需要查询设备状态,类似于中断的概念,一个进程收到一个信号与处理器收到一个中断请求可以说是一样的。信号是异步的,一个进程不必通过任何操作来 等待信号的到达。下面我们就看一下在linux中机制的实现方式。
在linux中,异步通知是使用信号来实现的,而在linux,大概有30种信号,比如大家熟悉的ctrl+c的SIGINT信号,进程能够忽略或者捕获除过SIGSTOP和SIGKILL的全部信号,当信号背捕获以后,有相应的函数来处理它。
在linux中,使用signal()函数来捕获信号,它的函数运行如下:
- #include
- typedef void (*sighandler_t)(int);
- sighandler_t signal(int signum, sighandler_t handler);
- signal(SIGINT,sigterm_handler);
下面我们来看一个异步通知的实例:
- #include
- #include
- #include
- #include
- #include
- #include
- #define MAX_LEN 100
- void input_handler(int num)
- {
- char data[MAX_LEN];
- int len;
- len=read(STDIN_FILENO,&data,MAX_LEN);
- data[len]=0;
- printf("input available:%s\n",data);
- }
- main()
- {
- int oflags;
- signal(SIGIO,input_handler);
- fcntl(STDIN_FILENO,F_SETOWN,getpid());
- oflags=fcntl(STDIN_FILENO,F_GETFL);
- fcntl(STDIN_FILENO,F_SETFL,oflags|FASYNC);
- while(1);
- }
- wanchres@wanchres-desktop:~/Linux/test/c code$ ./signal_test
- wanchres
- input available:wanchres
而在驱动程序和应用程序的异步通知的交互中,这种仅仅在应用程序端的,因为信号的源头在设备驱动端。因此,应该再合适的时候让设备驱动释放信号,其相关的步骤如下:
1、支持F_SETOWN命令,能在这个控制命令处理中设置filp->f_owner为对应进程ID,此工作已由内核完成。
2、支持F_SETFL命令的处理,每当FASYNC标志改变时,驱动中的fasync()函数将得以执行,所以驱动中应实现fasync()函数。
3、在设备资源可获得时,调用kill_fasync()函数激发相应的信号。
设备驱动中异步通知的编程实现中,主要用到一个结构体fasync_struct,其定义为:
- struct fasync_struct {
- int magic;
- int fa_fd;
- struct fasync_struct *fa_next; /* singly linked list */
- struct file *fa_file;
- };
处理FASYNC标志变更的函数:
- int fasync_helper(int fd, struct file * filp, int on, struct fasync_struct **fapp)
- void kill_fasync(struct fasync_struct **fp, int sig, int band)
设备结构体:
- struct xxx_dev{
- struct cdev cdev;
- ......
- struct fasync_struct *async_queue;
- };
- static int xxx_fasync(int fd,struct file *filp,int mode)
- {
- struct xxx_dev *dev = filp->private_data;
- return fasync_helper(fd,filp,mode,&dev->async_queue);
- }
- static ssize_t xxx_write(struct file *filp,const char __user *buf,size_t count,loff_t *ppos)
- {
- struct xxx_dev *dev = filp->private_data;
- ......
- if(dev->async_queue)
- kill_fasync(&dev->async_queue,SIGIO,POLL_IN);
- ......
- }
- int xxx_release(struct inode *inode,struct file *filp)
- {
- xxx_fasync(-1,filp,0);
- return 0;
- }
- struct globalmem_dev{
- struct cdev cdev;
- unsigned int current_len;
- unsigned char mem[GLOBALMEM_SIZE];
- struct semaphore sem;
- wait_queue_head_t r_wait;
- wait_queue_head_t w_wait;
- struct fasync_struct *async_queue;
- };
- static int globalmem_fasync(int fd,struct file *filp,int mode)
- {
- struct globalmem_dev *dev = filp->private_data;
- return fasync_helper(fd,filp,mode,&dev->async_queue);
- }
- static ssize_t globalmem_write(struct file *filp,const char __user *buf,size_t count,loff_t *ppos)
- {
- unsigned long p = *ppos;
- int ret = 0;
- struct globalmem_dev *dev = filp->private_data;
- DECLARE_WAITQUEUE(wait,current);
- down(&dev->sem);
- add_wait_queue(&dev->w_wait,&wait);
- while(dev->current_len==GLOBALMEM_SIZE){
- if(filp->f_flags & O_NONBLOCK){
- ret = -EAGAIN;
- goto out;
- }
- __set_current_state(TASK_INTERRUPTIBLE);
- up(&dev->sem);
- schedule();
- if(signal_pending(current)){
- ret = -ERESTARTSYS;
- goto out2;
- }
- down(&dev->sem);
- }
- if(count > GLOBALMEM_SIZE-dev->current_len)
- count = GLOBALMEM_SIZE-dev->current_len;
- if(copy_from_user(dev->mem+dev->current_len,buf,count)){
- ret = -EFAULT;
- goto out;
- }else{
- dev->current_len += count;
- printk(KERN_INFO "written %d bytes(s),current_len:%d\n",count,dev->current_len);
- wake_up_interruptible(&dev->r_wait);
- if(dev->async_queue)
- kill_fasync(&dev->async_queue,SIGIO,POLL_IN);
- ret = count;
- }
- out:up(&dev->sem);
- out2:remove_wait_queue(&dev->w_wait,&wait);
- set_current_state(TASK_RUNNING);
- return ret;
- }
- int globalmem_release(struct inode *inode,struct file *filp)
- {
- globalmem_fasync(-1,filp,0);
- return 0;
- }
代码写好以后,我们还需编写一个在用户空间验证globalmem异步通知的程序,程序的功能是接收到globalmem发出的信号后输出信号值,代码如下:
- #include
- #include
- #include
- #include
- #include
- #include
- void input_handler(int signum)
- {
- printf("receive a signal from globalmem,signalnum:%d\n",signum);
- }
- main()
- {
- int fd,oflags;
- fd = open("/dev/globalmem_sync",O_RDWR,S_IRUSR|S_IWUSR);
- if(fd != -1){
- signal(SIGIO,input_handler);
- fcntl(fd,F_SETOWN,getpid());
- oflags = fcntl(fd,F_GETFL);
- fcntl(fd,F_SETFL,oflags|FASYNC);
- while(1){
- sleep(100);
- }
- }else{
- printf("device open failure\n");
- }
- }