异步通知:
使用异步通知机制可以提高查询设备的效率。通过使用异步通知,应用程序可以在数据可用时收到一个信号,而无需不停地轮询。
设置异步通知的步骤(针对应用层来说的):
1.首先制定一个进程作为文件的属主。通过使用fcntl系统调用执行F_SETOWN命令时,属主进程的ID号就会保存在filp->f_owner中,目的是为了让内核知道应该通知哪个进程。
2.在设备中设置FASYNC标志。通过fcntl调用的F_SETFL来完成。
设置晚以上两步后,输入文件就可以在新数据到达时请求发送一个SIGIO信号,该信号被发送到存放在filp->f_owner中的进程。
实例:启用stdin输入文件到当前进程的异步通知机制
fcntl(STDIN_FILENO,F_SETFL,oflags|FASYNC);//从新设置描述符
注意的问题:
1.应用程序通常只假设套接字和终端具备异步通知功能。
2.当进程受到SIGIO信号时,它并不知道哪个输入文件有了新的输入。如果有多于一个文件可以异步通知输入的进程,则应用程序必须借助于poll或select来确定输入的来源。
驱动程序的实现:
1、F_SETOWN被调用时对filp->f_owner赋值。
2.在执行F_SETFL启用FASYNC时,调用驱动程序的fasync方法。只要filp->f_owner中的FASYNC标志发生了变化,就会调用该方法,以便把这个变化通知驱动程序,使其正确响应。文件打开时,默认fasync标志是被清除的。
3.当数据到达时,所有注册为异步通知的进程都会收到一个SIGIO信号。
linux 这种调用方法基于一个数据结构和两个函数,对于驱动开发而言主要关注两个函数,内核会自己维护该数据结构,为驱动程序服务(书上对该数据结构也并未给出多少解释)。包含在头文件中。
struct
};
两个函数如下:
int fasnyc_helper(int fd,struct file *filp,int mode,struct fasync_struct **fa);
void kill_fasync(struct fasync_struct **fa,int sig,int band);
当一个打开的文件的FASYNC标志被修改时,调用fasync_helper以便从相关的进程列表中增加或删除文件。当数据到达时,可使用kill_fasync通知所有的相关进程。它的参数是要发送的信号(sig)和带宽(band)。
注意的地方:1.对于struct fasync_struct这个数据结构在编写驱动时并不需要特别关注,它会由内核来维护,驱动程序中调用它即可。如同poll的底层实现上poll_table这个内存页链表也是由内核来维护,驱动程序使用它即可。
2.对于用来通知可读的异步通知:band几乎总是为poll_in
对于用来通知可写的异步通知:band几乎总是为poll_out
3.wait_enevt_interruptible(dev->inq,(dev->rp != dev->wp));
wake_up_interruptible(&dev->inq);
在使进程休眠的时候使用的是值传递,但在唤醒进程的时候使用的指针传递。
实现:
static int scull_p_fasync(int fd,struct file *filp,int mode)
{
}
接着当数据到达时,必须执行下面的语句来通知异步读取进程。由于提供scullpipe的读取进程的新数据是在某个进程调用wirte产生的,所以这条语句在scullpipe的write方法中实现:
if(dev->async_queue)
最后注意在文件关闭之前,必须调用fasync方法,以便从活动的异步读取进程列表中删除文件。
scull_p_fasync(-1,filp,0);
下面是自己敲了一下scull_pipe的程序,顺便对前边学的复习一下:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
int scull_major = SCULL_MAJOR;
int scull_minor = 0;
int scull_p_nr_devs = SCULL_P_NR_DEVS;
int scull_p_buffer = SCULL_P_BUFFER;
module_param(scull_major,int,S_IRUGO);
module_param(scull_minor,int,S_IRUGO);
module_param(scull_p_nr_devs,int,0);
module_param(scull_p_buffer,int,0);
struct scull_pipe {
};
dev_t scull_devno;
static struct scull_pipe *scull_p_devices;
static int scull_p_fasync(int fd,struct file *filp,int mode);
static int spacefree(struct scull_pipe *dev);
static int scull_p_open(struct inode *inode,struct file *filp)
{
}
static int scull_p_release(struct inode *inode,struct file *filp)
{
}
static scull_p_read(struct file *filp,char __user *buf,size_t count,loof_t *f_pos)
{
}
static int scull_p_ioctl(struct inode *inode,struct file *filp,unsigned int cmd,unsigned long arg)
{
}
static int scull_getwritespace(struct scull_pipe *dev, struct file *filp)
{
}
这里结合scull代码分析下异步通知实现的过程:
异步机制在应用层设计FASYNC标志的置位,驱动层涉及到一个数据结构和两个函数的调用,首先来看驱动层,在scull的驱动代码中用scull_p_fasync函数实现了对fasync_helper的调用,如下:
static int scull_p_fasync(int fd, struct file *filp, int mode)
{
}
在scull_p_write函数中实现了对kill_fasync的调用,如下:
static ssize_t scull_p_write(struct file *filp, const char __user *buf, size_t count,loff_t *f_pos)
{
}
在程序的最后调用了kill_fasync,在调用kill_fasync后会向文件的属主进程发送SIGIO信号,从而在驱动层完成了异步机制的设置。下面来看应用程序:
#include
#include
#include
#include
#include
#include
int gotdata=0;
void sighandler(int signo)
{
}
char buffer[21];
int main(int argc, char **argv)
{
int pipetest0;
if ((pipetest0 = open("/dev/scullpipe0",O_RDONLY)) < 0)
printf("open scullpipe0 error! /n");
exit(1);
}
break;
close(pipetest0 );
printf("close pipetest0
printf("exit !/n");
}
完成了应用层异步通知的设置,然后只有在驱动层调用到了kill_fasync从而发出SIGIO信号后,激活sigaction的处理函数,设置了gotdata的值,并得到了POLL_IN状态表明可读了,这是调用read函数读出数据,并通过write将读出的数据发往到终端。以上就是异步交换的整个过程。