Linux 异步通知(信号)原理及架构

异步通知

异步通知的意思是:一旦设备就绪,则主动通知应用程序进行访问。这样,使用无阻塞IO的应用程序无需轮询的查询设备是否可访问,达到减少CPU消耗的目的。相似于硬件上的“中断”的概念,比较准确的称谓是“信号驱动的异步IO”。
信号:是在软件层次上对中断机制的模拟。
在原理上,进程接收到一个信号(软件层)<==>(硬件层)处理器接收到一个中断。
假设假设recvfrom函数是一个系统调用:信号驱动的异步IO
阻塞IO,结合poll()的非阻塞IO及异步通知的区别
在这里插入图片描述node

Linux异步通知编程

Linux应用层

Linux中可用的信号及含义
在这里插入图片描述
在这里插入图片描述
信号被捕获时,有相应的信号处理函数进行处理<==>当中断产生时,有相应的中断处理函数进行处理。web

信号的接收

在用户程序中,使用signal()函数来设置对应信号的处理函数
原型:编程

void (*signal(int signum, void (*handler))(int)))(int);

能够分解为app

typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler));

含义:
当接收到signum信号时,执行handler函数指针指向的信号处理函数
为了在用户空间中处理一个设备释放的信号,需完成一下几项工做:
(1)经过IO控制命令(F_SETOWN)设置设备文件的拥有者为本进程,这样从设备驱动发出的信号才能被本进程接收
(2)经过IO控制命令(F_SETFL)设置设备文件支持FASYNC(异步通知),即调用驱动层的xxx_fasync()
(3)经过signal()函数链接信号和信号处理函数异步

Linux驱动层

为了使设备支持异步通知,驱动程序须要作如下几项工做:
(1) 支持F_SETOWN命令,能在这个控制命令处理中设置filp->fowner为对应进程ID。
(2) 支持F_SETFL命令的处理,每当FASYNC标志改变时,驱动程序中的fasync()函数将获得执行。
(3) 在设备资源可得到时,调用kill_fasync()函数激发相应的信号
在这里插入图片描述
处理FASYNC标志变动的函数async

int fasync_helper(int fd, struct file *filp, int mode, struct fasync_struct **fapp)
{
	if(!on)
	{
		/* 将文件从异步通知中移除 */
		return fasync_remove_entry(filp, fapp); 
	}
	return fasync_add_entry(fd, filp, fapp);
	--> fasync_insert_entry(fd, filp, fapp, new);
	        /* fa_fd指向应层打开此驱动时获得的FD.这也就是后面发送信号时能找到程号的缘由。*/
	    --> new->fa_fd = fd;
	        rcu_assign_pointer(*fapp, new);
	        --> smp_store_release (&p, RCU_INITIALZER(V))
	            --> WRITE_OWCE(*p, v)
	                *p = v
}

通知应用程序资源可用的函数==>其实就是内核给应用程序发信号svg

void kill_fasync(struct fasync_struct **fp, int sig, int band)
{
	...
	kill_fasync_rcu(rcu_dereference(*fp), sig, band);
	--> fown = &fa->fa_file->f_owner;
	    /* 内核想应用程序发送信号,通知应用程序资源可用 */
	    send_sigio(fown, fa->fa_fd, band);
}

支持异步通知的设备驱动模板函数

struct xxx_dev {
	struct cdev cdev;
	...
	struct fasync_struct *async_queue;
}

static int xxx_fasync(int fd, struct file *filp, int mode)
{
	struct xxx_dev *dev = filp->private_data;
	return fasync_helper(fd, filp, mode, &dev->async_queue);
}

static ssize_t xxx_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos)
{
	struct xxx_dev *dev = filp->private_data;
	...
	if(dev->async_queue)
		kill_fasync(&dev->async_queue, SIGIO, POLL_IN);
}

static int xxx_release(struct inode *inode, struct file *filp)
{
	/* 将文件从异步通知列表中删除 */
	xxx_fasync(-1, filp, 0);
	...
}

static const struct file_operation xxx_fops = {
	...
	.fasync = xxx_fasync,
};

从内核态分析异步通知

应用程序直接调用fcntl3d

SYSCALL_DEFINE3(fcntl, unsigned int, fd, unsigned int, cmd, unsigned long, arg)
--> do_fcntl(fd, cmd, arg, f.file)
	--> switch(cmd)
		 --> case F_SETOWN:
		     --> f_setown(filp, arg, 1)
		         --> __f_setown(filp, pid, type, force)
		             --> f_modown(filp, pid, type, force)
		                  -->filp->f_owner.pid = get_pid(pid);//绑定进程号
    
         --> case F_SETFL:
             --> setl(fd, filp, arg)
             	 --> filp->f_op->fasync(fd, filp, (arg & FASYNC) != 0)