第9章 异步通知和异步I/O

1.概念与作用

异步通知:一单设备就绪,则主动通知应用程序,此时应用程序不需要查询设备状态。类似于“中断”概念,一个进程收到一个信号与处理器收到一个中断请求可以说是一样的。

阻塞、非阻塞、异步通知 这几种本身没有优劣 应根据不同的应用场景合理选择、

2.Linux异步通知编程

  2.1Linux信号

       参考: https://www.linuxidc.com/Linux/2016-08/134303.htm

信号种类:

对信号的三种处理方式

  1. 忽略此信号:大多数信号都可使用这种方式进行处理,但有两种信号却决不能被忽略。它们是:SIGKILL和SIGSTOP。这两种信号不能被忽略的,原因是:它们向超级用户提供一种使进程终止或停止的可靠方法。另外,如果忽略某些由硬件异常产生的信号(例如非法存储访问或除以0),则进程的行为是示定义的。
  2. 直接执行进程对于该信号的默认动作 :对大多数信号的系统默认动作是终止该进程。
  3. 捕捉信号:执行自定义动作(使用signal函数),为了做到这一点要通知内核在某种信号发生时,调用一个用户函数handler。在用户函数中,可执行用户希望对这种事件进行的处理。注意,不能捕捉SIGKILL和SIGSTOP信号。

 2.2 信号接收

在用户程序中,为了捕获信号 可以使用signal()函数来设置对应信号的处理函数

void (*signal(int signum,void (*handler)) (int))) (int);

signal函数的作用:给某一个进程的某一个特定信号(标号为signum)注册一个相应的处理函数,即对该信号的默认处理动作进行修改,修改为handler函数所指向的方式。

  • 第一个参数是信号的标号
  • 第二个参数,sighandler_t是一个typedef来的,原型是void (*)(int)函数指针,int的参数会被设置成signum
1
2
3
#include <signal.h>
typedef void ( *sighandler_t)( int );
sighandler_t signal ( int signum, sighandler_t handler);

除了signal()函数外还有sigaction()改变进程接收到特定信号的行为

int sigaction(int signum,const struct sigaction *act,struct sigaction *oldact);

2.3 信号释放

信号源头在设备驱动端,应该在合适的时机让设备驱动释放信号。应在设备驱动程序中增加信号释放的相关代码。

1)支持F_SETOWN命令,能在这个控制命令处理中设置filp->f_owner为对应进程ID。不过此项工作
已由内核完成,设备驱动无须处理。
2)支持F_SETFL命令的处理,每当FASYNC标志改变时,驱动程序中的fasync()函数将得以执行。 因此,驱动中应该实现fasync()函数。
3)在设备资源可获得时,调用kill_fasync()函数激发相应的信号。
设备驱动中异步通知编程比较简单,主要用到一项数据结构和两个函数。数据结构是fasync_struct结
构体,两个函数分别是:
1)处理FASYNC标志变更的函数。
int fasync_helper(int fd, struct file *filp, int mode, struct fasync_struct **fa);
2)释放信号用的函数。
void kill_fasync(struct fasync_struct **fa, int sig, int band);


支持异步通知的设备结构体模板

 struct xxx_dev {

struct cdev cdev;                    /* cdev结构体*/

...  

struct fasync_struct *async_queue;   /* 异步结构体指针 */

};

在设备驱动的fasync()函数中,只需要简单地将该函数的3个参数以及fasync_struct结构体指针的指 针作为第4个参数传入fasync_helper()函数即可。模板:

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);
}

在设备资源可以获得时,应该调用kill_fasync()释放SIGIO信号。在可读时,第3个参数设置为

POLL_IN,在可写时,第3个参数设置为POLL_OUT。模板:

static ssize_t xxx_write(struct file *filp, const char __user *buf, size_t count,loff_t *f_pos)
{
struct xxx_dev *dev = filp->private_data;
...
/* 产生异步读信号*/

 if (dev->async_queue)
kill_fasync(&dev->async_queue, SIGIO, POLL_IN);

}

fasync是为了使驱动的读写和应用程序的读写分开,使得应用程序可以在驱动读写的时候去做别的事。

应用程序通过fcntl给自己的SIGIO信号安装自己的响应函数,

驱动通过kill_fasync(&async, SIGIO, POLL_IN); 发SIGIO信号给应用程序,应用程序就调用自己安装的响应函数去处理。

fasync_helper作用就是初始化fasync这个东西,包括分配内存和设置属性,最后在驱动的release里把fasync_helper初始化的东西free掉。


猜你喜欢

转载自blog.csdn.net/qq_28449863/article/details/80507382