Linux设备驱动基础04之异步通知
Posted PanGC2014
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Linux设备驱动基础04之异步通知相关的知识,希望对你有一定的参考价值。
一、基础简介
使用阻塞/非阻塞方式读写设备驱动时,都需要应用程序主动发起,对于非阻塞方式来说还需通过poll函数不断轮询。这种情况下更好的解决方案是,设备驱动主动向应用程序发出通知,报告自己可以访问,然后应用程序再读写设备驱动。
Linux中这种机制叫做异步通知,异步通知的核心是信号,类似硬件中断,信号是软件层次上的软中断。设备驱动可以主动向应用程序发送信号,通知应用程序可以访问,应用程序中需要注册信号的处理函数,并将本进程号告诉内核。
在arch/xtensa/include/uapi/asm/signal.h文件中定义了Linux所支持的所有信号,如下所示:
#define SIGHUP 1 /* 终端挂起或控制进程终止 */
#define SIGINT 2 /* 终端中断(Ctrl+C 组合键) */
#define SIGQUIT 3 /* 终端退出(Ctrl+\\组合键) */
#define SIGILL 4 /* 非法指令 */
#define SIGTRAP 5 /* debug 使用,有断点指令产生 */
#define SIGABRT 6 /* 由 abort(3)发出的退出指令 */
#define SIGIOT 6 /* IOT 指令 */
#define SIGBUS 7 /* 总线错误 */
#define SIGFPE 8 /* 浮点运算错误 */
#define SIGKILL 9 /* 杀死、终止进程 */
#define SIGUSR1 10 /* 用户自定义信号 1 */
#define SIGSEGV 11 /* 段违例(无效的内存段) */
#define SIGUSR2 12 /* 用户自定义信号 2 */
#define SIGPIPE 13 /* 向非读管道写入数据 */
#define SIGALRM 14 /* 闹钟 */
#define SIGTERM 15 /* 软件终止 */
#define SIGSTKFLT 16 /* 栈异常 */
#define SIGCHLD 17 /* 子进程结束 */
#define SIGCONT 18 /* 进程继续 */
#define SIGSTOP 19 /* 停止进程的执行,只是暂停 */
#define SIGTSTP 20 /* 停止进程的运行(Ctrl+Z 组合键) */
#define SIGTTIN 21 /* 后台进程需要从终端读取数据 */
#define SIGTTOU 22 /* 后台进程需要向终端写数据 */
#define SIGURG 23 /* 有"紧急"数据 */
#define SIGXCPU 24 /* 超过 CPU 资源限制 */
#define SIGXFSZ 25 /* 文件大小超额 */
#define SIGVTALRM 26 /* 虚拟时钟信号 */
#define SIGPROF 27 /* 时钟信号描述 */
#define SIGWINCH 28 /* 窗口大小改变 */
#define SIGIO 29 /* 可以进行输入/输出操作 */
#define SIGPOLL SIGIO
#define SIGPWR 30 /* 断点重启 */
#define SIGSYS 31 /* 非法的系统调用 */
#define SIGUNUSED 31 /* 未使用信号 */
这些信号就相当于中断号,驱动程序可以通过向应用程序发送不同的信号来实现不同的功能,应用程序中必须设置信号所使用的信号处理函数。
二、设备驱动之异步通知
1、fasync_struct结构体
设备驱动中,异步通知机制是以struct fasync_struct结构体为中心,需要在驱动代码的设备结构体中定义一个fasync_struct结构体指针变量,该结构体定义如下:
struct fasync_struct
spinlock_t fa_lock;
int magic;
int fa_fd;
struct fasync_struct *fa_next;
struct file *fa_file;
struct rcu_head fa_rcu;
;
2、fasync函数
使用异步通知,需要在设备驱动程序中实现file_operations操作集中的fasync函数,函数格式如下:
int (*fasync) (int fd, struct file *filp, int on);
3、fasync_helper函数
在file_operations操作集中的fasync函数中,通过调用fasync_helper函数,来初始化自定义设备结构体中的fasync_struct结构体指针变量,该函数原型如下:
int fasync_helper(int fd, struct file * filp, int on, struct fasync_struct **fapp);
该函数前三个参数对应fasync中三个参数,第四个参数struct fasync_struct **fapp就是要初始化的fasync_struct结构体指针变量。当应用程序通过"fcntl(fd, F_SETFL, flags | FASYNC)"改变fasync标记时,驱动程序中file_operations操作集中的fasync函数就会执行。
4、release函数
int (*release)(struct inode *inode, struct file *filp);
5、kill_fasync函数
当设备可以访问时,驱动程序需要向应用程序发出信号,相当于产生"中断";驱动程序中使用kill_fasync发送指定的信号,函数原型如下:
void kill_fasync(struct fasync_struct **fp, int sig, int band);
函数参数含义:
fp:要操作的fasync_struct结构体指针。
sig:要发送的信号,比如。
band:可读时设置为POLL_IN,可写时设置为POLL_OUT。
6、使用示例
struct xxx_dev
......
struct fasync_struct *async_queue; /* 异步相关结构体 */
;
static int xxx_fasync(int fd, struct file *filp, int on)
/* 使能异步通知 */
struct xxx_dev *dev = (xxx_dev)filp->private_data;
if (fasync_helper(fd, filp, on, &dev->async_queue) < 0)
return -EIO;
return 0;
static int xxx_release(struct inode *inode, struct file *filp)
/* 删除异步通知 */
struct xxx_dev *dev = (xxx_dev)filp->private_data;
if (fasync_helper(-1, filp, 0, &dev->async_queue) < 0)
return -EIO;
return 0;
static struct file_operations xxx_ops =
......
.fasync = xxx_fasync,
.release = xxx_release,
......
;
三、应用程序之信号处理
1、基本处理流程
应用程序对异步通知的处理包括以下三步:
(1)、注册信号处理函数;使用signal函数来设置信号的处理函数。
(2)、将应用程序进程号告诉内核;使用fcntl(fd, F_SETOWN, gitpid())将本进程号告诉内核。
(3)、开启异步通知;开启的过程需要两步,如下所示:
flags = fcntl(fd, F_GETFL); /* 获取当前的进程状态 */
fcntl(fd, F_SETFL, flags | FASYNC); /* 通过设置进程状态为FASYNC,开启当前进程异步通知功能 */
2、信号处理函数原型
typedef void (*sighandler_t)(int);
3、设置信号处理函数
信号处理函数定义完之后,需要与对应的信号进行绑定,Linux应用程序中使用signal函数来设置指定信号的处理函数。signal函数原型如下:
sighandler_t signal(int signum, sighandler_t handler);
其中,signum是要设置处理函数的信号;handler是信号处理函数。返回值:成功则返回信号的前一个处理函数,失败则返回SIG_ERR。
4、使用示例
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
static void signal_handler(int num)
printf("\\r\\nSignal Handler Runing\\r\\n");
exit(0);
int main(int argc, char *argv[])
signal(SIGINT, signal_handler);
while(1);
return 0;
以上是关于Linux设备驱动基础04之异步通知的主要内容,如果未能解决你的问题,请参考以下文章