linux 异步通知机制,Linux内核开发之异步通知与异步I/O(一)

本文介绍了Linux设备驱动中的异步通知机制,它是如何在应用程序和设备间提供一种主动通知的方式,避免了阻塞和非阻塞访问的查询。通过与前文的阻塞与非阻塞I/O机制结合,提升设备访问效率。讨论了信号驱动的异步I/O概念,以及如何通过signal()函数设置信号处理函数、fasync()函数的实现和kill_fasync()函数释放信号。
摘要由CSDN通过智能技术生成

“小王,听说过锦上添花吧..”我拍拍下王的头说。本文引用地址:http://www.eepw.com.cn/article/201612/341125.htm

“还锦上添花你,为你上次提的几个东东,我是头上长包..”小王气愤地瞪着我。

“啊,为啥这样呢,本来还特意拒绝了MM的约会,抽出时间打算给你说点高级的东东,看来现在是不行了”我吃惊道,“这样吧,这次就给你讲些和前边有关的东西,也不失为锦上添花不是?”。

“好,我也是这么打算的,就是没好意思说,今天讲些啥呢?”小王暗淡的眼光总算闪了闪。(为啥这么难受呢,好像跟什么会嚎叫的特像,哈哈)

那就言归正传,今天我们讲---Linux设备驱动程序之异步通知与异步I/O.”

小王,前边不是讲了阻塞与非阻塞访问,poll()函数等提供的较好的解决设备访问的机制,那么通过这次有关异步通知整套机制的配合,就更相得益彰,锦上添花了。

啥叫异步通知:很简单,一旦设备准备好,就主动通知应用程序,这种情况下应用程序就不需要查询设备状态,这是不是特像硬件上常提的“中断的概念”。上边比较准确的说法其实应该叫做“信号驱动的异步I/O”,信号是在软件层次上对中断机制的一种模拟。

“小王,给你一个表现的机会,说说这个和前边的几点不同和差异。。”

“嗯,我的理解是这样的哈,阻塞I/O意味着一直等待设备可访问再访问,非阻塞I/O意味着使用poll()来查询是否可访问,而异步通知则意味着设备通知应用程序自身可访问。”看着小王聪明的眼睛和清晰的思路,我也忍不住给予一个鼓励的微笑啊。

说的好,我只是想强调一点:上面三种方式,其实本身是没有优劣的,应该根据不同的应用场景合理选择罢了。

说到信号,在应用程序中,为了捕获信号(还捕获呢, 不就是一个处理吗)可以使用signal()函数来设置对应的信号的处理函数。函数原型是

void (*signal(int signo,void (*func)(int))) (int) 这个看起来费劲吧,不光你,我看着也费劲,没关系,给你来个例子:

void sigterm_handler(int signo)

{

char data[MAX_LEN];

int len;

len=read(STDIN_FILENO, &data,MAX_LEN);

data[len]=0;

printf("Input available:%s\n",data);

exit(0);

}

int main(void)

{

int oflags;

//启动信号驱动机制

signal(SIGIO, sigterm_handler);

fcntl(STDIN_FILENO, F_SETOWN, getpid());

oflags = fcntl(STDIN_FILENO, F_GETFL);

fcntl(STDIN_FILENO, F_SETFL, oflags | FASYNC);

//建立一个死循环,防止程序结束

while(1);

return 0;

}

看了这段代码明白啥意思了吧,我也不多少了,咱们继续往下走..为了一个用户在用户空间中能处理一个设备释放的信号,它必须完成一下3份工作:

1)通过F_SETOWN控制指令设置设备文件的拥有者为本进程,这样从设备驱动中发出的信号才能被本进程收到。

2)通过F_SETFLIO控制命令设置设备文件支持FASYNC,即异步通知模式。

3)通过signal()链接信号和信号处理函数。

当然,如果你了解linux/Unix信号机制的话,你可能会问为啥没说sigaction函数,其实没关系,作用差不多,想知道的话,自己看书Apue的P261.

有了信号的发送,那么就一定得有信号的释放了:

在设备驱动和应用程序的异步通知交互中,仅仅在应用程序端捕获信号是不够的,因为信号没有的源头是在驱动端,因此要在适当的时机让设备驱动释放信号。

为了使设备支持异步通知机制,驱动程序中涉及三个操作:

1)支持F_SETOWN命令,能在这个控制命令处理中设置filp->f_owner为对应的进程ID。不过此项工作已由内核完成,设备驱动无须处理。

2)支持F_SETFL命令的处理,每当FASYNC标志改变时,驱动程序中fasync()函数将得以进行。因此,驱动程序必须实现fasync()函数。

3)在设备资源可获得时,调用kill_fasync()函数激发相应的信号。

驱动程序中上面的三步是和应用程序是一一对应的。如下图:

ff0ccc9ad9a63d948f27ff7d18d7642f.png

设备驱动中异步通知编程还是比较简单的,主要就是一些数据结构,和两个函数:

数据结构:fasync_struct结构体

函数:1)处理FASYNC标志变更的函数int fasync_helper(int fd, struct file *filp, int mode ,struct fasync_struct **fa);

2) 释放信号用的函数void kill_fasync(struct fasync_struct **fa, int sig, int band);

和其他设备驱动一样,一般将fasync_struct放到设备结构体中。下边是典型模版:

struct xxx_dev

{

struct cdev cdev;

...

struct fasync_struct *async_queue; //异步结构体

}

而在驱动的fasync()函数中,只需要简单的将该参数的3个参数以及fasync_struct结构体指针的指针作为第四个参数传给fasync_helper函数即可.下边是典型模版:

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);

}

一旦设备资源可以获得时,应该调用kill_fasync()释放SIGIO信号,可读时第三个参数设置为POLL_IN,可写时第三个参数设置为POLL_OUT,下边是释放信号的典型模版:

static ssize_t xxx_write(struct file *filp, const char __user *buf, size_t count,loff_t *f_ops)

{

struct xxx_dev *dev = filp->private_data;

....

//产生异步信号

if(dev->async_queue)

{

kill_fasync(&dev->async_queue, SIGIO, POLL_IN);

}

..

}

最后,在文件关闭时,即在设备驱动的release函数中,应调用设备驱动的fasync()函数将文件从异步通知的列表中删除,下边是设备驱动的释放函数的典型模版:

static int xxx_release(struct inode *inode, struct file *filp)

{

struct xxx_dev *dev = filp->private_data;

//将文件从异步通知列表中删除

xxx_fasync(-1,filp,0);

...

return 0;

}

“等等,我知道你明白,你是想向我个例子,我知道你啥意思,没关系,我下次补上 。”没等小王,我立刻给她堵上“你啊,心里几根小九九,我还不知道啊..”

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值