设备驱动的异步通知,在访问设备的时候,由驱动程序主动对应用程序访问,这样应用程序无需轮询,和中断类似:当条件满足(按键按下),驱动程序向应用程序发送一个通知,应用程序要暂停手边工作,去处理驱动的通知任务,执行完毕再返回原本的任务。
目录标题
一、Linux信号
1.信号
定义 | 符号 | 解释 |
---|---|---|
#define SIGHUP | 1 | /* Hangup (POSIX). 终端连接断开信号*/ |
#define SIGINT | 2 | /* Interrupt (ANSI). 中断信号,终端中输入ctrl+c,可中断前台进程*/ |
#define SIGQUIT | 3 | /* Quit (POSIX). 退出信号,终端中输入ctrl+*/ |
#define SIGILL | 4 | /* Illegal instruction (ANSI). 非法指令信号,4.3BSD的abort函数产生该信号 */ |
#define SIGTRAP | 5 | /* Trace trap (POSIX). 调试信号,当在程序中设置断点后,该信号使得调试程序获得控制权*/ |
#define SIGABRT | 6 | /* Abort (ANSI). 程序异常终止信号,abort函数产生该信号 */ |
#define SIGIOT | 6 | /* IOT trap (4.2 BSD). 功能同SIGABRT*/ |
#define SIGBUS | 7 | /* BUS error (4.2 BSD). 程序访问不存在的内存区域时,产生该信号*/ |
#define SIGFPE | 8 | /* Floating-point exception (ANSI). 算术异常,如除以0*/ |
#define SIGKILL | 9 | /* Kill, unblockable (POSIX). 不能被忽略,非阻塞,可杀死任意一个运行中的进程*/ |
#define SIGUSR1 | 10 | /* User-defined signal 1 (POSIX). 用户自定义1*/ |
#define SIGSEGV | 11 | /* Segmentation violation (ANSI). 当程序访问没有访问权限的内存区域,或者访问非可读的内存区域时,产生该信号,如数组越界 */ |
#define SIGUSR2 | 12 | /* User-defined signal 2 (POSIX). 用户自定义2 */ |
#define SIGPIPE | 13 | /* Broken pipe (POSIX). 当管道读端已关闭,继续往管道中写,产生该信号 */ |
#define SIGALRM | 14 | /* Alarm clock (POSIX). alarm函数超时时产生该信号,默认动作是程序终止 */ |
#define SIGTERM | 15 | /* Termination (ANSI). 终止程序信号,命令kill默认使用该参数*/ |
#define SIGSTKFLT | 16 | /* Stack fault. 栈异常 */ |
#define SIGCLD | SIGCHLD | /* Same as SIGCHLD (System V). */ |
#define SIGCHLD | 17 | /* Child status has changed (POSIX). 子进程终止或停止时,产生该信号,默认被忽略*/ |
#define SIGCONT | 18 | /* Continue (POSIX). 进程继续*/ |
#define SIGSTOP | 19 | /* Stop, unblockable (POSIX). 停止一个作业控制进程*/ |
#define SIGTSTP | 20 | /* Keyboard stop (POSIX). ctrl+z产生该信号,改信号使得前台进程挂起*/ |
#define SIGTTIN | 21 | /* Background read from tty (POSIX). 后台进程需要从终端读取数据*/ |
#define SIGTTOU | 22 | /* Background write to tty (POSIX).后台进程需要向终端写数据 */ |
#define SIGURG | 23 | /* Urgent condition on socket (4.2 BSD). 有"紧急"数据 */ |
#define SIGXCPU | 24 | /* CPU limit exceeded (4.2 BSD). 进程超过了CPU软限制产生该信号*/ |
#define SIGXFSZ | 25 | /* File size limit exceeded (4.2 BSD). 进程超过了文件大小软限制产生该信号*/ |
#define SIGVTALRM | 26 | /* Virtual alarm clock (4.2 BSD).虚拟时钟信号 */ |
#define SIGPROF | 27 | /* Profiling alarm clock (4.2 BSD). 时钟信号描述 */ |
#define SIGWINCH | 28 | /* Window size change (4.3 BSD, Sun). */ |
#define SIGPOLL | SIGIO | /* Pollable event occurred (System V). */ |
#define SIGIO | 29 | /* I/O now possible (4.2 BSD). */ |
#define SIGPWR | 30 | /* Power failure restart (System V). */ |
#define SIGSYS | 31 | /* Bad system call. */ |
#define SIGUNUSED | 31 |
除了SIGKILL(9)、SIGSTOP(19)不可忽略外,其他都可以忽略,这些信号相当于中断号,不同的中断号代表不同的中断,就要处理不同的任务。
2.处理函数
在应用程序使用信号处理函数,signal函数来指定处理函数,将信号和处理函数关联。
sighandler_t signal(int signum, //要处理的信号
sighandler_t handler) //信号处理函数
//成功返回前一个处理函数,失败返回SIG_ERR
信号处理函数原型如下:
typedef void (*sighandler_t)(int)
3.信号使用例子
当按下 CTRL+C ,向当前正在占用终端的应用程序发送SIGINT信号,默认动作是关闭当前应用程序。这里修改了默认处理函数,会先打印,再关闭程序。
#include "stdlib.h"
#include "stdio.h"
#include "signal.h"
void sigint_handler(int num)
{
printf("SIGINT signal");
return 0;
}
int main (void)
{
signal(SIGINT, sigint_handler);
while(1);
return 0;
}
二、驱动函数的信号处理
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;
}
流程如下:
1.设备结构体添加指针变量
struct imx6uirq_dev{
struct fasync_struct *async_queue;
};
2.编写fasync函数
函数原型:
int (*fasync) (int fd, struct file *filp, int on)
在fasync函数里要初始化定义的结构体指针,使用fasync_helper
int fasync_helper(int fd, struct file *filp, int on, struct fasync_struct **fapp)
fapp是上面定义结构体指针的地址。
3.释放异步通知
static int xxx_release(struct inode *inode, struct file *filp)
{
return xxx_fasync(-1, filp, 0); /* 删除异步通知 */
}
4.kill_fasync函数
当设备可以访问,驱动程序向应用程序发送信号,kill_fasync函数负责发送指定信号,原型如下:
void kill_fasync(struct fasync_struct **fp, //要操作的结构体指针
int sig, //信号类型
int band) //可读设置为POLL_IN,可写设置POLL_OUT
三、应用程序对异步通知的处理
1.注册信号处理函数
2.将进程号告诉内核
fcntl(fd, F_SETOWN, getpid())
3.开启异步通知
flags = fcntl(fd, F_GETFL); //获取当前进程状态
fcntl(fd, F_SETFL, flags|FASYNC); //开启当前进程异步通知功能
四、驱动程序
在按键按下后,释放异步通知信号,应用程序的signa函数就会执行。使用完毕后记得在release函数释放掉结构体
struct imx6uirq_dev{
wait_queue_head_t r_wait; /* 读等待队列头 */
struct fasync_struct *async_queue; /* 异步相关结构体 */
};
struct imx6uirq_dev imx6uirq; /* irq设备 */
void timer_function(unsigned long arg)
{
if(atomic_read(&dev->releasekey)) { /* 一次完整的按键过程 */
if(dev->async_queue)
kill_fasync(&dev->async_queue, SIGIO, POLL_IN); /* 释放SIGIO信号 */
}
}
/*
* @description : fasync函数,用于处理异步通知
* @param - fd : 文件描述符
* @param - filp : 要打开的设备文件(文件描述符)
* @param - on : 模式
* @return : 负数表示函数执行失败
*/
static int imx6uirq_fasync(int fd, struct file *filp, int on)
{
struct imx6uirq_dev *dev = (struct imx6uirq_dev *)filp->private_data;
return fasync_helper(fd, filp, on, &dev->async_queue);
}
/*
* @description : release函数,应用程序调用close关闭驱动文件的时候会执行
* @param - inode : inode节点
* @param - filp : 要打开的设备文件(文件描述符)
* @return : 负数表示函数执行失败
*/
static int imx6uirq_release(struct inode *inode, struct file *filp)
{
return imx6uirq_fasync(-1, filp, 0);
}
/* 设备操作函数 */
static struct file_operations imx6uirq_fops = {
.fasync = imx6uirq_fasync,
.release = imx6uirq_release,
};
五、编译测试
static void sigio_signal_func(int signum)
{
int err = 0;
unsigned int keyvalue = 0;
err = read(fd, &keyvalue, sizeof(keyvalue));
if(err < 0) {
/* 读取错误 */
} else {
printf("sigio signal! key value=%d\r\n", keyvalue);
}
}
int main(int argc, char *argv[])
{
int flags = 0;
char *filename;
if (argc != 2) {
printf("Error Usage!\r\n");
return -1;
}
filename = argv[1];
fd = open(filename, O_RDWR);
if (fd < 0) {
printf("Can't open file %s\r\n", filename);
return -1;
}
/* 设置信号SIGIO的处理函数 */
signal(SIGIO, sigio_signal_func);
fcntl(fd, F_SETOWN, getpid()); /* 设置当前进程接收SIGIO信号 */
flags = fcntl(fd, F_GETFL); /* 获取当前的进程状态 */
fcntl(fd, F_SETFL, flags | FASYNC); /* 设置进程启用异步通知功能 */
while(1) {
sleep(2);
}
close(fd);
return 0;
}