阻塞与非阻塞是 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
。