天天看点

9---异步通知与异步IO

 异步通知的概念

一旦设备就绪,则主动通知应用程序,这样应用程序根不需要查询设备状态。在原理上,一个进程收到一个信号与处理器收到一个中断请求可以说是一样的

阻塞、非阻塞、异步通知几种方式I/O互补 ,根据场合合理选择

  • 阻塞 I/O :一直等待设备可访问后再访问,
  • 非阻塞I/O:使用poll()查询设备是否可访问,
  • 异步通知:设备通知自身可访问,实现了异步I/O

 异步通知编程

信号的接收

1 void sigterm_handler(int signo) {
3 printf("Have caught sig N.O. %d\n", signo);//进程捕获信号并输出信号值
4 exit(0);
5 }
6
7 int main(void){
9 signal(SIGINT, sigterm_handler);//按下[Ctrl+c]组合键将向其发出SIGINT信号
10 signal(SIGTERM, sigterm_handler);//kill正在运行的进程将向其发出SIGTERM信号
11 while(1);
13 return 0;
14 }

//函数signal
sighandler_t signal(int signum,//信号的值
                   sighandler_t handler);//针对前面信号值的处理函数
           

在用户空间接受一个设备释放的信号,需完成以下三步

  • 通过F_SETOWN IO 控制命令设置设备文件的拥有者为本进程,这样从设备驱动发出的信号才能被本进程接收到。
  • 通过F_SETFL IO 控制命令设置设备文件支持FASYNC,即异步通知模式。
  • 通过signal()函数连接信号和信号处理函数。

应用实例

1 #include <sys/types.h>
2 #include <sys/stat.h>
3 #include <stdio.h>
4 #include <fcntl.h>
5 #include <signal.h>
6 #include <unistd.h>
7 #define MAX_LEN 100
8 void input_handler(int num) {
10 char data[MAX_LEN];
11 int len;
13 //读取并输出STDIN_FILENO上的输入
14 len = read(STDIN_FILENO, &data, MAX_LEN);
15 data[len] = 0;
16 printf("input available:%s\n", data);
17 }
18
19 main(){
21 int oflags;
23 //启动信号驱动机制
24 signal(SIGIO, input_handler);
25 fcntl(STDIN_FILENO, F_SETOWN, getpid());//设置本进程为STDIN_FILENO 文件的拥有者(owner),让内核知道应将信号发给哪个进程
26 oflags = fcntl(STDIN_FILENO, F_GETFL);
27 fcntl(STDIN_FILENO, F_SETFL, oflags | FASYNC);//为启用异步通知,对设备设置FASYNC标志
29 //进入一个死循环来保持进程不终止,如果程序中没有这个死循环,会立即执行完毕
31 while (1);
32 }

整个程序的执行效果如下:
[[email protected] driver_study]# ./signal_test
I am Chinese.
input available: I am Chinese.
I love Linux driver.
input available: I love Linux driver
           

信号的释放

为了使设备支持异步通知机制,驱动程序中涉及以下3 项工作,和应用程序中的3 项工作是一一对应。

  • 支持 F_SETOWN 命令,在这个控制命令处理中内核完成设置filp->f_owner 为对应进程ID。
  • 支持F_SETFL命令的处理,每当FASYNC标志改变时,驱动程序中定义并执行fasync()函数。
  •  在设备资源可获得时,调用kill_fasync()函数激发相应的信号。
//将fasync_struct 结构体指针放在设备结构体中
1 struct xxx_dev {
3 struct cdev cdev; /*cdev结构体*/
4 ...
5 struct fasync_struct *async_queue; /* 异步结构体指针*/
6 };

//支持异步通知的设备驱动fasync()函数的模板
1 static int xxx_fasync(int fd, struct file *filp, int mode){
3 struct xxx_dev *dev = filp->private_data;
//处理 FASYNC标志变更的函数
4 return fasync_helper(fd, filp, mode, &dev->async_queue);
5 }

//支持异步通知的设备驱动信号释放的模板
1 static ssize_t xxx_write(struct file *filp, const char _ _user *buf, size_t count, loff_t *f_pos){
4 struct xxx_dev *dev = filp->private_data;
5 ...
6 /* 产生异步读信号*/
7 if (dev->async_queue)
//释放信号用的函数
8 kill_fasync(&dev->async_queue, SIGIO, POLL_IN);
9 ...
10 }

//支持异步通知的设备驱动release()函数的模板
1 static int xxx_release(struct inode *inode, struct file *filp) {
3 struct xxx_dev *dev = filp->private_data;
4 /* 将文件从异步通知列表中删除*/
5 xxx_fasync(-1, filp, 0);
6 ...
7 return 0;
8 }
           

继续阅读