阻塞与非阻塞是 APP 询问 驱动设备。
异步通知是 驱动设备 主动通知 APP,可以使用信号来实现(类似于硬件上使用的“中断”,只不过信号是软件层次上的。算是在软件层次上对中断的一种模拟)。
1、流程图

2、分析
②:绑定信号与回调函数。使用sighandler_t signal(int signum, sighandler_t handler) 。
③:把 APP PID 告诉内核。同时,该 PID 会保存到该驱动的内核文件 file 结构体中。
④:读取该驱动程序文件的 Flag。
⑤:设置 Flag 里面的 FASYNC 位为 1。当 FASYNC 位发生变化时,该驱动会调用驱动操作 drv_fasync 函数。
⑥:驱动开发者实现的函数。主要是调用 fasync_helper 函数。
⑦:调用 fasync_helper() 函数,主要是把 驱动程序内核文件 file 结构体绑定到 button_async->fa_file 中。而 file 包含了 APP 的 PID。所以发送信号时,只需要使用 button_async 作为参数即可。
⑩:发送信号给对应的 APP。参数为 button_async。
注:button_async 结构体由驱动开发者创建,维护。
3、APP 信号编程步骤
①:编写信号处理函数。
static void sig_func(int sig)
{
int val;
read(fd, &val, 4);
printf("get button : 0x%x\n", val);
}
②:绑定信号与处理函数。
signal(SIGIO, sig_func);
③:打开驱动。
fd = open(argv[1], O_RDWR);
④:获取 PID ,告知内核。
fcntl(fd, F_SETOWN, getpid());
⑤:获取进程状态值。
flags = fcntl(fd, F_GETFL);
⑥:当前状态值添加异步功能,触发调用驱动异步处理函数。
fcntl(fd, F_SETFL, flags | FASYNC);
fcntl函数用法详解:
fcntl函数,也就是file control,提供了对文件描述符的各种操作。另一个常见的控制文件描述符的属性和行为的系统调用是ioctl,而且ioctl比fcntl能够执行更多的控制。但是,对于控制文件描述符常见的属性和行为,fcntl函数是由POSIX规范指定的首选方法
ioctl()是底层的系统调用(system call),所以跨平台特性不好。fcntl则是被封装的函数,各个OS都是支持的。

4、KERNEL 信号编程步骤
①:定义异步结构体。
②:实现异步操作函数,并把该函数填充到设备内核驱动操作结构体中。
该函数内容主要调用 fasync_helper() 函数,初始化异步结构体。(把 file ,内核PID,交给异步结构体)
③:发送信号:kill_fasync。
kill_fasync 函数:
函数原型:void kill_fasync(struct fasync_struct **fp, int sig, int band) :
- 功能:发送信号给
fp参数绑定的进程。(by PID) - 参考路径:
linux-5.12.8\fs\fcntl.c。 fp:需要操作的fasync_struct。sig:信号类型。band:可读时设置为POLL_IN;可写时设置为POLL_OUT。当然该参数还可以填POLL_MSG。以上三个值在应用层接收时,si_code分别为0x01、0x02、0x03。