先简要说一下这两种机制:
中断、poll机制一般都是应用程序主动去查询有无中断发生,即在应用程序主函数中采用while(1)不断查询有无中断发生,这样要额外消耗一个线程去查询(CPU占用率极高),及时采用poll机制降低CPU占用率,但是还是必须消耗一个线程不断循环查询while(1);
而异步通知是当中断发生时,驱动程序发送一个信号去提醒应用程序去读并执行用户编写的信号处理函数(和中断服务函数类似,当有信号过来时自动执行此函数),它是通过信号signal实现的,不需要另外消耗一个线程去一直查询,当没有中断发生时是什么也不做;当有中断触发时,驱动程序告诉应用程序中断信号发生,调用相应信号处理函数,以此达到异步通知目的。
接下去的内容是在假设读者能理解并成功编写线程轮询的驱动函数及应用程序的情况下。
OK,进入正题
------------------------------------------------------
驱动程序方面
------------------------------------------------------
为了使设备支持异步通知机制,驱动程序涉及以下3项工作:
1.支持F_SETOWN命令,能在这个控制命令处理设置filp->f_owner为对应进程ID。此工作已由内核完成;
2.支持F_SETFL命令的处理,每当FASYNV标志改变时,驱动程序中的fasync()函数将得以执行,驱动中应该实现fasync()函数,通常通过函数结构体指定name_fasync函数来调用相应函数。
3.在设备资源可获得时,调用kill_fasync()函数激发相应的信号
1.驱动通过kill_fasync()发送信号给应用程序,触发应用程序调用信号处理函数(一般在中断触发函数里面唤醒进程之后):
void kill_fasync(struct fasync_struct **fp, int sig, int band)
2.它在fasync_helper()函数里完成*fp结构体的初始化:
int fasync_helper(int fd, struct file * filp, int on, struct fasync_struct **fapp)
3.并且要把它添加进函数结构体file operations里面,让应用程序能够调用:
.fasync = name_fasync,
fasync的函数原型:int (*fasync) (int, struct file *, int);
如:
static struct fasync_struct *fasync_queue;
...
kill_fasync(&fasync_queue, SIGIO, POLL_IN);
...
static int name_fasync(int fd, struct file *filp, int on)
{
return fasync_helper(fd, filp, on, &fasync_queue);
}
------------------------------------------------------
测试程序设计
------------------------------------------------------
1.编写信号处理函数signal_function();
2.在主函数里注册信号处理函数:
/*内核里面发出信号信号一般是发出SIGIO,表示IO口有数据可读或写*/
signal(SIGIO, signal_function);
3.应用程序调用fcntl()这一函数来告诉驱动程序它的进程pid号:
fcntl(fd, F_SETOWN, getpid());
4.应用程序读出flag,并在flag上修改,加上一个fasync:
flag = fcntl(fd, F_GETFL);
flag |= FASYNC;
5.应用程序调用fcntl()接口来调用驱动中的name_fasync函数(即调用fasync_helper函数):
fcntl(fd, F_SETFL, flag);
应用程序例子:
#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <string.h> #include <signal.h> int fd; void buttons_signal_function(int sig) { char buf[6]; read(fd, buf, 1); printf("key = %x\n", buf[0]); } int main(int argc, char **argv) { int flag; signal(SIGIO, buttons_signal_function); fd = open("/dev/button_led", O_RDWR); if (fd < 0) { printf("can't open /dev/buttons\n"); return -1; } fcntl(fd, F_SETOWN, getpid()); flag = fcntl(fd, F_GETFL); flag |= FASYNC; fcntl(fd, F_SETFL, flag); while (1) { sleep(2); } return 0; }