韦东山uboot_内核_根文件系统学习笔记5.10-第005课_字符设备驱动_第010节_字符设备驱动程序之异步通知

应用程序:主动去读取硬件状态
①查询方式:非常消耗资源
②中断方式: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

  1. 目标:按下按键时,驱动程序通知应用程序
  2. 注册信号处理函数?应用程序
  3. 谁发信号?驱动程序发信号
  4. 发给谁?应用程序,应用程序需要告诉驱动它的PID号
  5. 怎么发?驱动程序调用kill_fasyn函数发送

下面看实现代码

为了使设备支持异步通知机制,驱动程序中涉及以下 3 项工作:

  1. 支持 F_SETOWN 命令,能在这个控制命令处理中设置 filp->f_owner 为对应进程 ID。不过此项工作已由内核完成,设备驱动无须处理。
  2. 支持 F_SETFL 命令的处理,每当 FASYNC 标志改变时,驱动程序中的 fasync()函数将得以
    执行。驱动中应该实现 fasync()函数。
  3. 在设备资源可获得时,调用 kill_fasync()函数激发相应的信号

一 驱动程序

  1. 定义结构体:static struct fasync_struct *button_async;
  2. 中断处理函数:
static irqreturn_t buttons_irq(int irq, void *dev_id)
{
    
    
	...
	kill_fasync (&button_async, SIGIO, POLL_IN);//发送的信号类型:SIGIO
	...
}

谁来初始化button_async结构体呢?

  1. 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结构体
}
  1. 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)

二 应用程序

  1. 声明信号响应函数和响应信号类型
//信号响应函数:my_signal_fun
//相应信号类型:SIGIO
signal(SIGIO, my_signal_fun);
  1. 信号响应函数定义
//从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;
}

猜你喜欢

转载自blog.csdn.net/xiaoaojianghu09/article/details/104334782