应用程序:主动去读取硬件状态
①查询方式:非常消耗资源
②中断方式:read函数一直休眠,永远不返回
③poll:指定休眠时间
驱动程序触发应用程序,应用程序再去读取
④异步通知 signal 一旦设备就绪,则主动通知应用程序进行访问。这样,使用无阻塞IO的应用程序无需轮询的查询设备是否可访问,达到减小CPU消耗的目的。类似于硬件上的“中断”的概念,比较准确的称谓是“信号驱动的异步IO”。信号:是在软件层次上对中断机制的模拟。
在原理上,进程接收到一个信号(软件层)<==>(硬件层)处理器接收到一个中断。
kill命令:linux中的kill命令用来终止指定的进程(terminate a process)的运行,是Linux下进程管理的常用命令。通常,终止一个前台进程可以使用Ctrl+C键,但是,对于一个后台进程就须用kill命令来终止,我们就需要先使用ps/pidof/pstree/top等工具获取进程PID,然后使用kill命令来杀掉该进程。kill命令是通过向进程发送指定的信号来结束相应进程的。在默认情况下,采用编号为15的TERM信号。TERM信号将终止所有不能捕获该信号的进程。对于那些可以捕获该信号的进程就要用编号为9的kill信号,强行“杀掉”该进程。
kill -USER1(某个信号) PID(指定进程号)
eg:
1.查看进程号
2.kill -USER1 833
- 目标:按下按键时,驱动程序通知应用程序
- 注册信号处理函数?应用程序
- 谁发信号?驱动程序发信号
- 发给谁?应用程序,应用程序需要告诉驱动它的PID号
- 怎么发?驱动程序调用
kill_fasyn
函数发送
下面看实现代码
为了使设备支持异步通知机制,驱动程序中涉及以下 3 项工作:
- 支持 F_SETOWN 命令,能在这个控制命令处理中设置 filp->f_owner 为对应进程 ID。不过此项工作已由内核完成,设备驱动无须处理。
- 支持 F_SETFL 命令的处理,每当 FASYNC 标志改变时,驱动程序中的 fasync()函数将得以
执行。驱动中应该实现 fasync()函数。 - 在设备资源可获得时,调用 kill_fasync()函数激发相应的信号
一 驱动程序
- 定义结构体:
static struct fasync_struct *button_async;
- 中断处理函数:
static irqreturn_t buttons_irq(int irq, void *dev_id)
{
...
kill_fasync (&button_async, SIGIO, POLL_IN);//发送的信号类型:SIGIO
...
}
谁来初始化button_async
结构体呢?
fifth_drv_fasync
函数初始化button_async
结构体
static struct file_operations sencod_drv_fops = {
.owner = THIS_MODULE,
.open = fifth_drv_open,
.read = fifth_drv_read,
.release = fifth_drv_close,
.poll = fifth_drv_poll,
.fasync = fifth_drv_fasync, //
};
static int fifth_drv_fasync (int fd, struct file *filp, int on)
{
printk("driver: fifth_drv_fasync\n");
return fasync_helper (fd, filp, on, &button_async);//这里初始化button_async结构体
}
fifth_drv_fasync
函数什么时候调用?app程序调用驱动的.fasync
的时候就会调用。
//把自己的进程号pid告诉驱动程序;
//支持 F_SETOWN 命令,能在这个控制命令处理中设置 filp->f_owner 为对应进程 ID。不过此项工作已由内核完成,设备驱动无须处理。
app > fcntl(fd,F_SETOWN,pid)
//读出F_GETFL
//支持 F_SETFL 命令的处理,每当 FASYNC 标志改变时,驱动程序中的 fasync()函数将得以执行。驱动中应该实现 fasync()函数。
app > Oflags = fcntl(fd,F_GETFL)
app > fcnl(fd,F_SETFL,oflags | FASYNC)
二 应用程序
- 声明信号响应函数和响应信号类型
//信号响应函数:my_signal_fun
//相应信号类型:SIGIO
signal(SIGIO, my_signal_fun);
- 信号响应函数定义
//从fd中读取按键值到key_val
void my_signal_fun(int signum)
{
unsigned char key_val;
read(fd, &key_val, 1);
printf("key_val: 0x%x\n", key_val);
}
驱动程序的中断服务函数发现有信号则直接给app程序发送信号,app程序跳转到信号处理函数my_signal_fun
。
//发给谁?
//把自己的进程号pid告诉驱动程序;
//getpid()函数表示获取的进程号
fcntl(fd, F_SETOWN, getpid());
//改变FASYNC标记,最终会调用到驱动的fasync > fasync_helper:初始化/释放fasync_struct
//驱动程序中的 fasync()函数将得以执行
Oflags = fcntl(fd, F_GETFL);
fcntl(fd, F_SETFL, Oflags | FASYNC);//设置文件描述符标志
三 简单分析fasync_helper函数
调用:fasync_helper (fd, filp, on, &button_async)
作用:对结构体button_async
初始化
函数声明原型:
int fasync_helper(int fd, struct file * filp, int on, struct fasync_struct **fapp)
int fd= fd;
struct file * filp = filp;
int on = on;
struct fasync_struct **fapp = &button_async
函数定义:
int fasync_helper(int fd, struct file * filp, int on, struct fasync_struct **fapp)
{
...
//1. 参数on控制,如果为on分配一个结构体new
if (on) {
new = kmem_cache_alloc(fasync_cache, GFP_KERNEL);
if (!new)
return -ENOMEM;
}
write_lock_irq(&fasync_lock);
for (fp = fapp; (fa = *fp) != NULL; fp = &fa->fa_next) {
if (fa->fa_file == filp) {
if(on) {
fa->fa_fd = fd;
kmem_cache_free(fasync_cache, new);
} else {
*fp = fa->fa_next;
kmem_cache_free(fasync_cache, fa);
result = 1;
}
goto out;
}
}
//与1.对应,刚刚分配的结构体new进行初始化后,赋给fapp即&button_async
if (on) {
new->magic = FASYNC_MAGIC;
new->fa_file = filp;
new->fa_fd = fd;
new->fa_next = *fapp;
*fapp = new;
result = 1;
}
out:
write_unlock_irq(&fasync_lock);
return result;
}