Linux驱动学习记录-16.异步通知及其实验

设备驱动的异步通知,在访问设备的时候,由驱动程序主动对应用程序访问,这样应用程序无需轮询,和中断类似:当条件满足(按键按下),驱动程序向应用程序发送一个通知,应用程序要暂停手边工作,去处理驱动的通知任务,执行完毕再返回原本的任务。

一、Linux信号

1.信号

定义符号解释
#define SIGHUP1/* Hangup (POSIX). 终端连接断开信号*/
#define SIGINT2/* Interrupt (ANSI). 中断信号,终端中输入ctrl+c,可中断前台进程*/
#define SIGQUIT3/* Quit (POSIX). 退出信号,终端中输入ctrl+*/
#define SIGILL4/* Illegal instruction (ANSI). 非法指令信号,4.3BSD的abort函数产生该信号 */
#define SIGTRAP5/* Trace trap (POSIX). 调试信号,当在程序中设置断点后,该信号使得调试程序获得控制权*/
#define SIGABRT6/* Abort (ANSI). 程序异常终止信号,abort函数产生该信号 */
#define SIGIOT6/* IOT trap (4.2 BSD). 功能同SIGABRT*/
#define SIGBUS7/* BUS error (4.2 BSD). 程序访问不存在的内存区域时,产生该信号*/
#define SIGFPE8/* Floating-point exception (ANSI). 算术异常,如除以0*/
#define SIGKILL9/* Kill, unblockable (POSIX). 不能被忽略,非阻塞,可杀死任意一个运行中的进程*/
#define SIGUSR110/* User-defined signal 1 (POSIX). 用户自定义1*/
#define SIGSEGV11/* Segmentation violation (ANSI). 当程序访问没有访问权限的内存区域,或者访问非可读的内存区域时,产生该信号,如数组越界 */
#define SIGUSR212/* User-defined signal 2 (POSIX). 用户自定义2 */
#define SIGPIPE13/* Broken pipe (POSIX). 当管道读端已关闭,继续往管道中写,产生该信号 */
#define SIGALRM14/* Alarm clock (POSIX). alarm函数超时时产生该信号,默认动作是程序终止 */
#define SIGTERM15/* Termination (ANSI). 终止程序信号,命令kill默认使用该参数*/
#define SIGSTKFLT16/* Stack fault. 栈异常 */
#define SIGCLDSIGCHLD/* Same as SIGCHLD (System V). */
#define SIGCHLD17/* Child status has changed (POSIX). 子进程终止或停止时,产生该信号,默认被忽略*/
#define SIGCONT18/* Continue (POSIX). 进程继续*/
#define SIGSTOP19/* Stop, unblockable (POSIX). 停止一个作业控制进程*/
#define SIGTSTP20/* Keyboard stop (POSIX). ctrl+z产生该信号,改信号使得前台进程挂起*/
#define SIGTTIN21/* Background read from tty (POSIX). 后台进程需要从终端读取数据*/
#define SIGTTOU22/* Background write to tty (POSIX).后台进程需要向终端写数据 */
#define SIGURG23/* Urgent condition on socket (4.2 BSD). 有"紧急"数据 */
#define SIGXCPU24/* CPU limit exceeded (4.2 BSD). 进程超过了CPU软限制产生该信号*/
#define SIGXFSZ25/* File size limit exceeded (4.2 BSD). 进程超过了文件大小软限制产生该信号*/
#define SIGVTALRM26/* Virtual alarm clock (4.2 BSD).虚拟时钟信号 */
#define SIGPROF27/* Profiling alarm clock (4.2 BSD). 时钟信号描述 */
#define SIGWINCH28/* Window size change (4.3 BSD, Sun). */
#define SIGPOLLSIGIO/* Pollable event occurred (System V). */
#define SIGIO29/* I/O now possible (4.2 BSD). */
#define SIGPWR30/* Power failure restart (System V). */
#define SIGSYS31/* Bad system call. */
#define SIGUNUSED31

除了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;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值