异步通知:意思就是,一旦设备就绪,则主动通知应用程序,这样应用程序根本不需要查询设备状态,非常类似于硬件上"中断的概念"
准确一点就叫:信号驱动(SIGIO)的异步I/O
实现异步通知,内核需要知道几个东西:哪个文件(filp),什么信号(SIGIIO),发给哪个进程(pid),收到信号后做什么(sig_handler)。这些都由上述前两个步骤完成了,而这前两个步骤内核帮忙实现了,所以,我们只需要实现第三个步骤的一个简单的传参。
设置异步通知的步骤(针对应用层来说的):
1.首先指定一个进程作为文件的属主。通过使用fcntl系统调用执行F_SETOWN命令时,属主进程的ID号就会保存在filp->f_owner中,目的是为了让内核知道应该通知哪个进程。
2.在设备中设置FASYNC标志。通过fcntl调用的F_SETFL来完成。
设置晚以上两步后,输入文件就可以在新数据到达时请求发送一个SIGIO信号,该信号被发送到存放在filp->f_owner中的进程。
通过signal(SIGIO,input_handler)对STDIN_FILENO启动信号机制,
/*
异步通知3个步骤:
1,signal(SIGIO, input_handler);
调用signal函数,让指定的信号SIGIO与处理函数input_handler对应;
2,fcntl(STDIN_FILENO, F_SETOWN, getpid());
指定一个进程作为文件的属主(filp->owner),这样内核才知道信号要发给哪个进程
3, oflags = fcntl(STDIN_FILENO, F_GETFL); //F_GETFL读取文件状态标识
fcntl(STDIN_FILENO, F_SETFL, oflags | FASYNC); //F_SETFL:设置文件状态标识
在设备文件中添加FASYNC标识,驱动中就会调用将要实现的input_handler函数
这3个步骤执行后,一旦有信号产生,相应的进程就会收到
*/
#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;
//函数原型:sig_t signal(int signum,sig_t handler);
//signum:指明要处理的信号类型
//handler:描述了与信号关联的动作,可以取以下3种值:
//1,一个返回值为正数的函数地址,此函数必须在signal被调用前申明,handler中为这个函数的名字
//当接收到一个类型为sig的信号时,就执行handler所指定的函数,这个函数的定义形式应该为:
//int func(int sig),sig是传递给它的唯一参数,执行了signal调用之后,进程只要收到类型为sig的信号
//不管其正在执行程序的哪一部分,就立即执行func()函数,当func()函数执行结束后,控制权返回进程被中断的那一点继续执行
//2,SIGIGN:表示忽略该信号,执行了相应的signal()调用后,进程忽略类型为sig的信号
//3,SIGDFL 这个符号表示恢复系统对信号的默认处理
//函数说明:signal()会依参数signum指定的信号编号来设置该信号的处理函数,当指定的信号到达时就会跳转到函数handler指定的函数执行
//当一个信号的信号处理函数执行时,如果进程又接收到了该信号,该信号会自动被储存而不会中断信号处理函数的执行
//直到信号处理函数执行完毕再重新调用相应的处理函数,但是如果在信号处理函数执行时进程收到了其它类型的信号,该函数的执行就会被中断
//返回值;返回先前的信号处理函数指针,如果有错误则返回SIG_ERR(-1).
//启动信号驱动机制
signal(SIGIO, input_handler);
//fcntl:根据文件描述词来操作文件的特性
//通过这个函数改变一个已打开的文件的属性,可以重新设置读,写,追加,非阻塞等标识
/*
用法:
int fcntl(int fd, int cmd);
int fcntl(int fd, int cmd, long arg);
int fcntl(int fd, int cmd, struct flock *lock);
参数:
fd:文件描述词。
cmd:操作命令。
arg:供命令使用的参数。
lock:同上。
*/
//getpid()用于取得目前进程的进程ID
//STDIN_FILENO:系统级API接口库
//F_SETOWN:设置将要在文件描述词fd上接收SIGIO或SIGURG事件信号的进程或进程组标识
fcntl(STDIN_FILENO, F_SETOWN, getpid());
oflags = fcntl(STDIN_FILENO, F_GETFL); //F_GETFL读取文件状态标识
fcntl(STDIN_FILENO, F_SETFL, oflags | FASYNC); //F_SETFL:设置文件状态标识
//其中O_RDONLY, O_WRONLY, O_RDWR, O_CREAT, O_EXCL, O_NOCTTY 和 O_TRUNC不受影响,可以更改的标志有 O_APPEND,O_ASYNC, O_DIRECT, O_NOATIME 和 O_NONBLOCK。
//最后进入一个死循环,程序什么都不干了,只有信号能激发 input_handler 的运行
//如果程序中没有这个死循环,会立即执行完毕
while (1);
}