在设备驱动中使用异步通知可以使得对设备的访问可进行时,由驱动主动通知应用程序进行访问。这样,使用无阻塞 I/O 的应用程序无需轮询设备是否可访问,而阻塞访问也可以被类似“中断”的异步通知所取代。
使用信号进行进程间通信(IPC)是 UNIX 中的一种传统机制,Linux 也支持这种机制。在Linux 中,异步通知使用信号来实现,Linux 中可用的信号如下:
信号 值 含义 SIGHUP 1 挂起 SIGINT(ctrl+c) 2 终端中断 SIGQUIT 3 终端退出 SIGILL 4 无效命令 SIGTRAP 5 跟踪陷阱 SIGIOT 6 IOT 陷阱 ... SIGKILL 9 强行终止(不能被捕获或忽略) SIGTERM 15 终止 SIGCHLD 17 子进程已经停止或退出 SIGSTOP 19 停止执行(不能被捕获或忽略) SIGIO 29 I/O
除了 SIGSTOP 和 SIGKILL 两个信号外,进程能够忽略或捕获其他的全部信号。一个信号被捕获的意思是当一个信号到达时有相应的代码处理它。如果一个信号没有被这个进程所捕获,内核将采用默认行为处理。
用户程序中,定义信号的处理函数用如下函数:
(*signal( signum, (*handler)()))();
第一个参数,指定信号的值,就是上边 SIGCHLD 之类的宏的值
第二个参数,指定针对前边信号值的处理函数
SIG_IGN,忽略信号
SIG_DFL,系统默认的方式处理
用户自定义函数,信号捕捉后函数被执行
返回值:
成功,函数的返回值
失败,SIG_ERR
一个用 ctrl+c 触发到自己的函数中的例子:
sigterm_handler( signo)
{
("Have caught sig N.O. %d\n", signo);
exit(0);
}
()
{
signal(SIGINT, sigterm_handler);
while(1);
return 0;
}
一个用实现输入字符,打印字符异步的例子:
#include
#include
#include
#include
#include
#include
#define MAX_LEN 100
input_handler( num)
{
char data[MAX_LEN];
len;
/* 读取并输出 STDIN_FILENO 上的输入 */
len = read(STDIN_FILENO, &data, MAX_LEN);
data[len] = 0;
("input available:%s\n", data);
}
()
{
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);
}
程序解释:
主函数中:
①为 SIGIO 信号安装 input_handler()作为处理函数
②fcntl(STDIN_FILENO, F_SETOWN, getpid()); 设置本进程为STDIN_FILENO 文件的拥有者(owner)
③对设备设置 FASYNC 标志
运行结果:
> ./signal_test
I am Chinese.
input available: I am Chinese.
I love Linux driver.
input available: I love Linux driver.
需要注意的是:当用户输入完字符串的时候,才会触发 SIGIO 的信号
用户空间实现异步通知的 3 项工作:
在设备驱动和应用程序的异步通知交互中,需要应用程序捕捉信号,驱动程序释放信号。
所涉及的 3 项工作:
可以看出,上述的3项工作和应用程序中的3项是对应的,他们之间的关系,如下图表现的很好:
在设备驱动中异步编程主要涉及一个数据结构、两个函数
一个数据结构:
fasync_struct 不需要知道具体的内容,只需要作为一个参数传递给 fasync_helper 就可以了
两个函数:
处理 FASYNC 标志的变更
fasync_helper( fd, struct file *filp, mode, struct fasync_struct **fa);
前三个参数,直接使用 static xxx_fasync( fd, struct file *filp, mode) 中的参数
fa,由用户定义的 fasync_struct 的机构体的指针
释放信号
kill_fasync(struct fasync_struct **fa, sig, band);
fa,由用户定义的 fasync_struct 的机构体的指针
sig,信号的号 / 表征信号的宏
band,POLL_IN,可读。POLL_OUT,可写
一般的格式和有关此部分的例子:
static xxx_fasync( fd, struct file *filp, 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 *f_pos)
{
struct xxx_dev *dev = filp->private_data;
...
/* 产生异步读信号 */
(dev->async_queue)
kill_fasync(&dev->async_queue, SIGIO, POLL_IN);
...
}
static xxx_release(struct inode *inode, struct file *filp)
{
/* 将文件从异步通知列表中删除 */
xxx_fasync(-1, filp, 0);
...
return 0;
}