驱动篇:异步通知与异步 I/O(一)

驱动篇:异步通知与异步 I/O(一)

在设备驱动中使用异步通知可以使得对设备的访问可进行时,由驱动主动通知应用程序进行访问。这样,使用无阻塞I/O 的应用程序无须轮询设备是否可访问,而阻塞访问也可以被类似“中断”的异步通知所取代。
异步通知的意思是:一旦设备就绪,则主动通知应用程序,这样应用程序根本就不需要查询设备状态,这一点非常类似于硬件上“中断”的概念,比较准确的称谓是“信号驱动的异步I/O”。信号是在软件层次上对中断机制的一种模拟,在原理上,一个进程收到一个信号与处理器收到一个中断请求可以说是一样的。信号是异步的,一个进程不必通过任何操作来等待信号的到达,事实上,进程也不知道信号到底什么时候到达。
在这里插入图片描述阻塞、非阻塞I/O、异步通知本身没有优劣,应该根据不同的应用场景合理选择。Linux系统中,异步通知使用信号来实现。
除了SIGSTOP和SIGKILL两个信号外,进程能够忽略或捕获其他的全部信号。一个信号被捕获的意思是当一个信号到达时有相应的代码处理它。如果一个信号没有被这个进程所捕获,内核将采用默认行为处理。
信号的接收
使用signal()函数来设置对应信号的处理函数

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

该函数原型较难理解,它可以分解如下:
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);

第一个参数指定信号的值,第二个参数指定针对前面信号值的处理函数,若为SIG_IGN,表示忽略该信号;若为SIG_DFL,表示采用系统默认方式处理信号;若为用户自定义的函数,则信号被捕获到后,该函数将被执行。如果 signal()调用成功,它返回最后一次为信号signum 绑定的处理函数handler值,失败则返回SIG_ERR。

在进程执行时,按下[Ctrl+c]组合键将向其发出SIGINT信号,kill正在运行的
进程将向其发出SIGTERM信号,signal()捕获信号范例

void sigterm_handler(int signo)
 {
 printf("Have caught sig N.O. %d\n", signo);
 exit(0);
 }
 int main(void)
{
signal(SIGINT, sigterm_handler);
signal(SIGTERM, sigterm_handler);
while(1);
return 0;
}

除了 signal()函数外,sigaction()函数可用于改变进程接收到特定信号后的行为,它的原型如下:

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

该函数的第一个参数为信号的值,可以为除 SIGKILL 及 SIGSTOP 外的任何一个特定有效的信号。第二个参数是指向结构体 sigaction 的一个实例的指针,在结构体sigaction 的实例中,指定了对特定信号的处理函数,若为空,则进程会以默认方式对信号处理。第三个参数 oldact 指向的对象用来保存原来对相应信号的处理函数,可指定 oldact 为 NULL。如果把第二、第三个参数都设为 NULL,那么该函数可用于检查信号的有效性

使用信号实现异步通知的例子,它通过 signal(SIGIO, input_handler)对标准输入文件描述符 STDIN_FILENO 启动信号机制。用户输入后,应用程序将接收到 SIGIO 信号,其处理函数 input_handler()将被调用

使用信号实现异步通知的应用程序实例

#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <fcntl.h>
#include <signal.h>
#include <unistd.h>
#define MAX_LEN 100
void input_handler(int num)
{
char data[MAX_LEN];
int len;
//读取并输出 STDIN_FILENO 上的输入
len = read(STDIN_FILENO, &data, MAX_LEN);
data[len] = 0;
printf("input available:%s\n", data);
}
main()
{
int 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()作为处理函数,第 25 行设置本进程为 STDIN_FILENO 文件的拥有者(owner),没有这一步内核不会知道应该将信号发给哪个进程。而为了启用异步通知机制, 还需对设备设置 FASYNC 标志,第 26~27 行代码实现此目的。整个程序的执行效果如下:

[root@localhost driver_study]# ./signal_test
I am Chinese.
input available: I am Chinese.
I love Linux driver.
input available: I love Linux driver.

从中可以看出,当用户输入一串字符后,标准输入设备释放 SIGIO 信号,这个信号“中断”驱使对应的应用程序中的 input_handler()得以执行,将用户输入显示出来。由此可见,为了在用户空间中能处理一个设备释放的信号,它必须完成以下 3 项

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值