一、流程
1、概述:应用层启用signal功能->将PID传递给驱动层->获取FASYNC状态并使能它 驱动层文件操作结构体创建fasync函数并通过fasync_helper监测FASYNC状态变化绑定到PID上,中断来临后kill_fasync将signal发送到绑定的PID上。
2、 (1)打开设备文件
open
(2)app启用异步通知功能
signal(SIGIO, func);
(3)app告诉kernel他的PID(内核记录PID)
app:
fcntl(fd, F_SETOWN, getpid());
kernel:
记录PID
(4)获取文件标记位,设置flag(FASYNC),驱动调用 kill_fasync(PID, SIGIO)
app:
Oflags = fcntl(fd, F_GETFL);
fcntl(fd, F_SETFL, Oflags | FASYNC) FASYNC : 启动FASYNC功能(若要禁止FASYNC,只需要将FASYNC位清零);
kernel:
检测FASYNC变化
fasync_helper(fd, filp, on, &button_async);
on = FASYNC = 1 : button_async->fa_file = filp(含有PID);
0 : button_async->fa_file = NULL;
(5)app做其他任务
(6)按键按下–>调用了按键中断程序–>记录KEY值, 将信号发出
key_fasync(&button_async, SIGIO, POLL_IN);
判断:如果button_async->fa_file非空,取出里面的PID发送信号
(7)func被调用
(8)read
二、函数分析
1、signal用法:
kill -l 查询当前系统中已经定义的信号
信号 | 特点 | 默认对进程的处理方式 |
---|---|---|
SIGKILL | 当产生之后让一个进程结束,不可忽略,也不可捕捉,只能默认 | 结束进程 |
SIGSTOP | 当产生之后让一个运行的进程停止运行,不可忽略,也不可捕捉,只能默认 | 停止进程 |
SIGINT | 当使用快捷键ctrl c会产生这个信号 | 结束进程 |
SIGTSTP | 当使用快捷键ctrl z会产生这个信号 | 停止进程 |
SIGQUIT | 当使用快捷键ctrl \会产生这个信号 | 结束进程 |
SIGPIPE | 管道破裂无名管道只有写端没有读端时,执行写操作会产生这个信号有名管道一个进程只读,一个进程只写,当关闭读操作,写操作执行时会产生这个信号 | 结束进程 |
SIGCONT | 让停止的进程恢复执行 | 让停止的进程恢复执行 |
SIGALRM | 当调用alarm函数设定的时间到达时,会产生这个信号 | 结束进程 |
SIGCHLD | 当子进程结束时,父进程中会产生这个信号 | 忽略 |
SIGUSR1 | 用户自定义信号 | 忽略 |
SIGUSR2 | 用户自定义信号 | 忽略 |
kill -值 进程号 向一个进程发送一个信号
#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
功能:当信号产生后设置进程的处理方式
参数:
signum:信号
handler:信号的处理方式
SIG_DFL 以默认的方式处理
SIG_IGN 以忽略的方式处理
handler 以捕捉的方式,也就是通过信号处理函数执行
void handler(int sig)
{
}
返回值:
成功:当前信号原本默认的处理方式
2、fcntl用法
#include <fcntl.h>
#include <unistd.h>
int fcntl(int fd, int cmd, ... /* arg */ );
fd
:文件描述符。
cmd
:操作命令,用于指定 fcntl
的功能。
arg
:根据 cmd
的不同可能需要传入不同的参数,或可以忽略
fcntl(fd, F_SETWN, getpid());
Oflags = fcntl(fd, F_GETFL);
fcntl(fd, F_SETFL, Oflags | FASYNC)
3、fasync
static int drv_fasync (int fd, struct file *filp, int on);
4、fasync_helper
int fasync_helper(int fd, struct file *filp, int on, struct fasync_struct **fapp);
fd
- 文件描述符,由用户空间传递,用于区分不同的文件。
filp
- 指向文件结构体的指针(
struct file *
),表示对设备文件的操作。
on
-
一个布尔值,决定是启用还是禁用
FASYNC
功能:
- 非零值:启用
FASYNC
; - 零值:禁用
FASYNC
。
- 非零值:启用
fapp
-
指向
struct fasync_struct *
的指针,用于存储异步通知相关的信息。 -
成功时返回
0
。 -
失败时返回负的错误码,例如:
-ENOMEM
:内存分配失败。
5、kill_fasync
void kill_fasync(struct fasync_struct **fa, int sig, int band);
fa
- 指向
struct fasync_struct
的指针,表示异步通知队列。
sig
- 要发送的信号,通常是
SIGIO
(表示异步 I/O 可用)或SIGURG
(表示紧急数据可用)。
band
- 一个标志,用于指示事件的类型,例如:
POLL_IN
:有数据可读。POLL_OUT
:可以写数据。POLL_ERR
:发生错误。
三、修改应用层程序
#include <stdio.h>
#include <poll.h>
#include <fcntl.h> // open 函数及文件标志
#include <sys/stat.h> // 文件权限
#include <unistd.h> // 文件操作(如 close, read, write 等)
#include <signal.h>
static int fd;
void sighandle_func(int sig) {
int buf = 0;
int gpio = 0;
int level = 0;
int bank = 0;
char group = 0;
int number = 0;
printf("调用了信号函数 fd = %d\n", fd);
read(fd, &buf, 1);
printf("读出来数据了\n");
gpio = buf & 0x00ff;
bank = gpio / 32;
group = ((gpio % 32) / 8) + 'A';
number = gpio % 8;
buf = (buf >> 8) & 0xff;
printf("Received gpio%d_%c%d value:%d\n", bank, group, number, buf);
}
int main() {
int timeout = 5000;
int Oflags;
int buf;
fd = open("/dev/diy_key_double", O_RDWR);
if(fd == -1) {
perror("open");
return -1;
}
printf("按键设备文件为%d\n", fd);
/*传入PID*/
fcntl(fd, F_SETOWN, getpid());
/* 获取状态标志 */
Oflags = fcntl(fd, F_GETFL);
/* 修改并设置状态标志 */
fcntl(fd, F_SETFL, Oflags | FASYNC);
/* 设置信号处理函数 */
signal(SIGIO, sighandle_func);
while(1) {
read(fd, &buf, 1);
printf("wait for signal fd = %d\n", fd);
sleep(2);
}
return 0;
}
四、修改驱动层程序
static struct fasync_struct *key_async;
static int key_fasync(int fd, struct file *file, int on)
{
return fasync_helper(fd, file, on, &key_async;);
}
const struct file_operations key_fops = {
.owner = THIS_MODULE,
.open = key_open,
.release = key_release,
.read = key_read,
.write = key_write,
.poll = key_poll,
.fasync = key_fasync,
};
kill_fasync(&key_async, SIGIO, POLL_IN);
/* 第 1 个参数:button_async->fa_file 非空时,可以从中得到 PID,表示发给哪一个 APP;
第 2 个参数表示发什么信号:SIGIO;
第 3 个参数表示为什么发信号:POLL_IN,有数据可以读了。(APP 用不到这个参数)*/