linux设备驱动开发学习之旅--异步通知
来源:互联网 发布:求婚大作战日剧知乎 编辑:程序博客网 时间:2024/05/20 17:40
- /**
- * Author:hasen
- * 参考 :《linux设备驱动开发详解》
- * 简介:android小菜鸟的linux
- * 设备驱动开发学习之旅
- * 主题:异步通知
- * Date:2014-11-05
- */
阻塞和非阻塞访问、poll()函数提供了较好地解决设备访问的机制,但是如果有了异步通知整套机制就更加完整了。
异步通知的意思是:一旦设备就绪,则主动通知应用程序,这样应用程序根本就不需要查询设备状态,这一点非常类似于硬件上“中断”的概念,比较准确的称谓是“信号驱动的异步I/O”。信号时在软件层次上对中断机制
的一种模拟,进程收到信号和处理器收到中断可以说是一样的。信号是异步的,进程不知道信号什么时候到达。
阻塞I/O意味着一直等待设备可访问后再访问,非阻塞I/O使用poll()意味着查询设备是否可访问,而异步通知则意味着设备通知自身可访问,实现了异步I/O。由此可见,这几种方式I/O可以互为补充。
二、Linux异步通知编程
1、Linux信号
使用信号进行进程间通信(IPC)是UNIX中一种传统机制,LInux也支持这种机制。在Linux中,异步通知
使用信号来实现,Linux中可用的信号及其定义如下表:
2、信号的接收
在用户程序中,为了捕获信号,可以使用signal()函数来设置对应信号的处理函数:
void (*signal (int signum,void(*handler))(int))(int) ;
该函数原型较难理解,它可以分解为:
typedef void (*sighandler_t) (int) ;
sighandler_t signal(int signum,sighandler_t sighandler) ;
第一个参数指定信号的值,第二个参数指定针对前面信号值的处理函数,若为SIG_IGN,表示忽略该信号;
若为SIG_DFL,表示采用系统默认方式处理信号;若为用户自定义函数,信号捕捉到后,将会执行该函数。
如果signal()调用成功,它返回最后一为信号signum绑定的处理函数handler的值,失败返回SIG_ERR。
在进程执行时,按下“Ctrl+c”将向其发出SIGINT信号,kill正在运行的进程将向其发送SIGTERM信号。
示例:进程捕捉SIGINT和SIGTERM信号并输出信号值
- void sigterm_handler(int signo)
- {
- printf("Have caught sig NO.%d\n",signo) ;
- exit(0) ;
- }
- int main(void)
- {
- signal(SIGINT,sigterm_handler) ;
- signal(SIGTERM,sigterm_handler) ;
- while(1) ;
- return 0 ;
- }
- int sigaction(int signum,const struct sigaction *act,struct sigaction oldact) ;
参数是指向结构体sigaction的一个实例指针,在结构体sigaction的实例中,指定了对特定信号的处理函数,若为
空,则进程会以缺省方式对信号进行处理。第三个参数指向的对象用来保存原来对相应信号的处理函数,可指定
oldact为NULL,当后面两个参数都为NULL时,那么该函数可用以检查信号的有效性。
下面是使用信号实现异步通知的实例,它通过signal(SIGIO,input_handler)对标准输入文件描述符
STDIN_FILENO启动信号机制。用户输入后,应用程序将接收到SIGNO信号,处理函数input_handler()将被调用。
示例:使用信号实现异步通知的应用程序
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <stdio.h>
- #include <fcntl.h>
- #include <signal.h>
- #include <unistd.h>
- #define MAX_LEN 100 ;
- void input_handler(int num)
- {
- char data[MAX_LEN] ;
- int len ;
- /*读取并输出STDIN_FILENO上的输入*/
- len = read(STDIN_FILENO,&data,MAX_LEN) ;
- data[len] = 0 ;
- printf("input available:%s\n,data") ;
- }
- main()
- {
- int oflags ;
- /*启动信号驱动机制*/
- signal(SIGIO,input_handler) ;//input_handler为信号处理函数
- fcntl(STDIN_FILENO,F_SETOWN,getpid()) ;//设备本进程为文件拥有者
- oflags = fcntl(STDIN_FILENO,F_GETFL) ;//得到文件标志
- fcntl(STDIN_FILENO,FSETFL,oflags|FASYNC) ;//为文件添加FASYNC标志(异步通知机制)
- /*最后进入一个死循环,仅为保持进程不终止,如果程序中没有这个死循环会立即执行完毕*/
- while(1) ;
- }
(1)通过F_SETOWN IO控制命令设置文件的拥有者为本进程,这样从设备驱动发出的信号才能被本进
程接收到。
(2)通过F_SETFL IO控制命令设置设备文件支持FASYNC,即异步通知模式。
(3)通过signal()函数连接信号和信号处理函数。
3、信号的释放
在设别驱动和应用程序的异步通知交互中,仅仅在应用程序端捕获信号是不够的,因为信号的源头
在设备驱动端,因此,在设备驱动中添加信号释放的代码。
为了使设别支持异步通知机制,驱动程序设计3项工作:
(1)支持F_SETOWN命令,能在这个控制命令处理中设置filp->f_owner为对应进程ID,不过,内核已
经处理了此项工作,设备驱动无需处理。
(2)支持F_SETFL命令的处理,每当FASYNC标志改变时,驱动程序中的fasync()函数将得以执行。因
此,驱动中应该实现fasync()函数。
(3)在设备资源可获得时,调用kill_fasync()函数激发相应的信号。
驱动中的工作与用户空间中的是一一对应的,下图是异步通信过程中用户空间和设备驱动的交互。
设备驱动中的异步通知编程,主要用到两个函数和一个结构体
- 结构体:fasync_struct
- 方法:
- (1)处理FASYNC标志变更的
- int fasync_helper(int fd,struct file *flip,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 ;/*cdev结构体*/
- ...
- struct fasync_struct *fasync_queue ;/*异步结构体指针*/
- }
作为第4个参数传入fasync_helper()函数即可。
示例:支持异步通知的设备驱动fasync()函数模板
- static int xxx_fasync()
- {
- struct xxx_dev *dev = filp->private_data;
- return fasync_helper(fd,filp,mode,&dev->async_queue) ;
- }
可写时第三个参数设置为POLL_OUT 。
示例:支持异步通知的设备驱动信号释放。
- 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) ;
- ...
- }
知的列表中删除。
示例:支持异步通知的设备驱动release()函数模板
- static ssize_t xxx_release(struct inode *inode,struct file filp)
- {
- struct xxx_dev *dev = filp->private_data ;
- ...
- /*产生异步读信号*/
- if(dev->async_queue)
- kill_fasync(&dev->async_queue,SIGIO,POLL_IN) ;
- ...
- }
在globalfifo驱动中增加异步通知
- int GLOBALFIFO_SIZE = 100 ;
- /*增加异步通知后的globalfifo设备结构体*/
- struct globalfifo_dev {
- struct cdev cdev ;/*cdev结构体*/
- unsigned int current_len ;/*fifo有效数据长度*/
- unsigned char mem[GLOBALFIFO_SIZE];/*全局内存*/
- struct semaphore sem ;/*并发控制用的信号量*/
- wait_queue_head_t r_wait ;/*阻塞读用的等待队列头*/
- wait_queue_head_t w_wait ;/*阻塞写用的等待队列头*/
- struct fasync_struct *async_queue ;/*异步结构体指针*/
- } ;
- /*支持异步通知的globalfifo设备驱动的fasync()函数*/
- static int globalfifo_fasync(int fd,struct file *flip,int mode)
- {
- struct globalfifo_dev *dev = flip->private_data ;
- return fasync_helper(fd,flip,mode,&dev->async_queue) ;
- }
- /*支持异步通知的globalfifo设备驱动写函数*/
- static ssize_t globalfifo_write(struct file *filp,const char __user *buf,
- size_t count,loff_t *ppos)
- {
- struct globalfifo_dev *dev = filp->private_data ;/*获得设备结构体指针*/
- int ret ;
- DECLARE_WAITQUEUE(wait,current) ;/*定义等待队列*/
- down(&dev->sem) ;/*获取信号量*/
- add_wait_queue(&dev->w_wait,&wait) ;/*进入写等待队列头*/
- /*等待fifo非满*/
- if(dev->current_len == GLOBALFIFO_SIZE) {
- if(filp->f_flag & O_NONBLOCK ){/*如果是非阻塞访问*/
- ret = -EAGAIN ;
- goto out ;
- }
- __set_current_state(TASK_INTERRPTIBLE) ;/*改变进程状态为睡眠*/
- up(&dev->sem) ;
- schedule() ;/*调度其他进程执行*/
- if(signal_pending(current)){ /*如果是因为信号唤醒*/
- ret = -ERESTARTSYS ;
- goto out2 ;
- }
- down(&dev->sem) ;/*获取信号量*/
- }
- /*从用户空间拷贝到内核空间*/
- if(count >GLOBALFIFO_SIZE - dev->current_len)
- count = GLOBALFIFO_SIZE - dev->current_len ;
- if(copy_from_user(dev->mem + dev->current_len , buf ,count)){
- ret = -EFAULT ;
- goto out ;
- }else{
- dev->current_len += count ;
- printk(KERN_INFO "writeen %d bytes(s),current_len:%d\n",count,dev->current_len) ;
- wake_up_interruptible(&dev->r_wait) ;/*唤醒读等待队列*/
- /*产生异步读信号*/
- if(dev->async_queue)
- /*释放信号,可写时为POLL_OUT,可读时为POLL_IN*/
- kill_fasync(&dev->async_queue,SIGIO,POLL_IN) ;
- ret = count ;
- }
- out: up(&dev->sem) ;/*释放信号量*/
- out2 :remove_wait_queue(&dev->w_wait,&wait) ;
- set_current_state(TASK_RUNNING) ;
- return ret ;
- }
- /*增加异步通知的globalfifo设备驱动release()函数*/
- int globalfifo_release(struct inode *inode ,struct file *filp )
- {
- /*将文件从异步通知列表中删除*/
- globalfifo_fasync(-1,filp,0) ;
- return 0 ;
- }
- #include <xxx.h>
- void input_handler(int signum)
- {
- printf("receive a signal from globalfifo,signum:%d\n",signum) ;
- }
- void main()
- {
- int fd , oflags ;
- fd = open("/dev/globalfifo",O_RDWR ,S_IRUSR|S_IWUSR) ;
- if(fd != -1){
- /*启动信号驱动机制*/
- signal(SIGIO,input_handler);/*让input_handler处理SIGIO信号*/
- fcntl(fd,F_SETOWN,getpid()) ;/*设置当前进程为文件所有者*/
- oflags = fcntl(fd,F_GETFL) ; /*得到文件的所有标志*/
- fcntl(fd.F_SETFL,oflags | FASYNC) ;/*给文件加上FASYNC标志,使支持异步通知模式*/
- while(1){
- sleep(100) ;
- }
- }else{
- printf("open driver failure\n") ;
- }
- }
0 0
- linux设备驱动开发学习之旅--异步通知
- Linux 设备驱动学习之 异步通知
- Hasen的linux设备驱动开发学习之旅--异步通知
- linux设备驱动开发-异步通知机制
- Linux设备驱动编程之异步通知
- Linux字符设备驱动之异步通知
- linux字符设备驱动之异步通知
- linux设备驱动之异步通知
- Linux设备驱动之异步通知
- Linux字符设备驱动之异步通知
- linux设备驱动开发学习之旅--异步I/O
- linux设备驱动之异步通知与异步I/O
- linux设备驱动--异步通知
- linux设备驱动异步通知
- linux驱动学习之异步通知
- linux驱动学习之异步通知
- linux驱动学习之异步通知 .
- linux设备驱动开发——异步通知
- 小总结
- 执行计划
- Linux Shell的锁:flock
- 重做日志相关查询
- sed使用命令详解--收藏
- linux设备驱动开发学习之旅--异步通知
- linux设备驱动开发学习之旅--中断
- map<string,vector<string>>两种字典排序 ——数据结构与算法分析上的
- 云监控服务比较
- 关于FlashBuilder+C#的项目的学习体验
- 执行计划sql
- 曾国藩名言警句
- 如何更改Mysql数据存储的位置
- WINDOS XP 服务优化批处理文件!