浅析 Linux fasync 异步通知_Linux学习之路的博客-程序员资料

技术标签: linux  异步通知  Linux Kernel  fasync  

    linux 系统中的 fasync 类似于 qt 的信号与槽机制,在应用程序向驱动程序请求数据时可以使用这种方法。驱动程序中有数据可读,发送信号给应用程序来读取。用法非常简单,大概的流程如下:


1、应用程序中注册信号处理函数 

    signal(SIGIO, my_signal_fun);
2、应用程序中设置设备支持异步通知
    fcntl(fd, F_SETOWN, getpid()); //设置发送给谁
    Oflags = fcntl(fd, F_GETFL); 
    fcntl(fd, F_SETFL, Oflags | FASYNC); //设置设备程序支持异步通知
3、驱动程序中分配一个 struct fasync_struct 指针  
4、应用程序改变 flag 导致驱动程序 xxx_fasync 被调用
    在 xxx_fasync 中 return fasync_helper (fd, filp, on, &button_async);
    在 fasync_helper 中,内核会帮助我们分配内存,记录信息,并使 button_async 指向它。
5、驱动程序发送信号给应用程序 
    kill_fasync (&button_async, SIGIO, POLL_IN);

6、应用程序收到信号,信号处理函数被调用


驱动程序:

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/interrupt.h>
#include <linux/sched.h> 
#include <linux/irq.h>
#include <asm/uaccess.h>
#include <mach/gpio-fns.h>  //s3c2410_gpio_getpin
#include <mach/gpio-nrs.h>	//S3C2410_GPG(x)
#include <linux/poll.h>

MODULE_LICENSE("GPL");

//中断触发方式的 一些宏定义
#define __IRQT_FALEDGE	IRQ_TYPE_EDGE_FALLING
#define __IRQT_RISEDGE	IRQ_TYPE_EDGE_RISING
#define __IRQT_LOWLVL	IRQ_TYPE_LEVEL_LOW
#define __IRQT_HIGHLVL	IRQ_TYPE_LEVEL_HIGH

#define IRQT_NOEDGE	(0)
#define IRQT_RISING	(__IRQT_RISEDGE)
#define IRQT_FALLING	(__IRQT_FALEDGE)
#define IRQT_BOTHEDGE	(__IRQT_RISEDGE|__IRQT_FALEDGE)
#define IRQT_LOW	(__IRQT_LOWLVL)
#define IRQT_HIGH	(__IRQT_HIGHLVL)
#define IRQT_PROBE	IRQ_TYPE_PROBE

static struct class *fasyncdrv_class;
static struct device *fasyncdrv_class_dev;
static unsigned char key_val = 0x22;	//保存键值
int major; //主设备号

static struct fasync_struct *button_async;	//存放信号接受者的进程ID等信息

/***************************************************************************************
*	结构名称:keys_desc
*	主要功能:描述 按键 寄存器地址、按下键值、弹起键值
*	备    注:
****************************************************************************************/
struct keys_desc{
	unsigned int key_addr;
	unsigned char key_value_down;
	unsigned char key_value_up;
};

/***************************************************************************************
*	数组名称:keys_desc
*	数组类型:struct keys_desc
*	备    注:mini2440开发板6个按键描述数组
****************************************************************************************/
struct keys_desc keys_desc[6]={
	{S3C2410_GPG(0)  ,0x01 ,0x81},
	{S3C2410_GPG(3) ,0x02 ,0x82},
	{S3C2410_GPG(5) ,0x03 ,0x83},
	{S3C2410_GPG(6) ,0x04 ,0x84},
	{S3C2410_GPG(7) ,0x05 ,0x85},
	{S3C2410_GPG(11) ,0x06 ,0x86},
};

/***************************************************************************************
*	函数名称:buttons_irq
*	主要功能:处理对应于各个中断号的中断
*	备    注:根据中断号irq以及dev_id区分不同中断
****************************************************************************************/
static irqreturn_t buttons_irq(int irq, void *dev_id)
{
	struct keys_desc * keys_desc = (struct keys_desc *)dev_id;
	unsigned int keyval;

	keyval = s3c2410_gpio_getpin(keys_desc->key_addr);

	if (keyval)
	{
		/* 松开 */
		key_val = keys_desc->key_value_up;
	}
	else
	{
		/* 按下 */
		key_val = keys_desc->key_value_down;
	}
	//发送信号给应用程序 数据可读
	kill_fasync (&button_async, SIGIO, POLL_IN);
	
	return IRQ_RETVAL(IRQ_HANDLED);
}

/***************************************************************************************
*	函数名称:fasync_drv_open
*	主要功能:向内核传递中断信息,中断号、中断处理函数、触发方式、名称、dev_id
*	备    注:驱动程序设备节点打开时自动运行
****************************************************************************************/
static int fasync_drv_open(struct inode *inode, struct file *file)
{
	//int request_irq(unsigned int irq, irq_handler_t handler, unsigned long irqflags, const char *devname, void *dev_id)

	request_irq(IRQ_EINT8,  buttons_irq, IRQT_BOTHEDGE, "S1", &keys_desc[0]);
	request_irq(IRQ_EINT11, buttons_irq, IRQT_BOTHEDGE, "S2", &keys_desc[1]);
	request_irq(IRQ_EINT13, buttons_irq, IRQT_BOTHEDGE, "S3", &keys_desc[2]);
	request_irq(IRQ_EINT14, buttons_irq, IRQT_BOTHEDGE, "S4", &keys_desc[3]);
	request_irq(IRQ_EINT15, buttons_irq, IRQT_BOTHEDGE, "S5", &keys_desc[4]);
	request_irq(IRQ_EINT19, buttons_irq, IRQT_BOTHEDGE, "S6", &keys_desc[5]);
	return 0;
}

/***************************************************************************************
*	函数名称:fasync_drv_close
*	主要功能:告诉内核 屏蔽这些中断,删除中断处理函数
*	备    注:关闭设备节点时自动运行
****************************************************************************************/
int fasync_drv_close(struct inode *inode, struct file *file){
	free_irq(IRQ_EINT8,  &keys_desc[0]);
	free_irq(IRQ_EINT11, &keys_desc[1]);
	free_irq(IRQ_EINT13, &keys_desc[2]);
	free_irq(IRQ_EINT14, &keys_desc[3]);
	free_irq(IRQ_EINT15, &keys_desc[4]);
	free_irq(IRQ_EINT19, &keys_desc[5]);
	return 0;
}

/***************************************************************************************
*	函数名称:fasync_drv_read
*	主要功能:应用层调用read函数获取数据
*	备    注:
****************************************************************************************/
static ssize_t fasync_drv_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
{

	/* 如果有按键动作, 返回键值 */
	copy_to_user(buf, &key_val, 1);


	return 0;
}

/***************************************************************************************
*	函数名称:fifth_drv_fasync
*	主要功能:调用fasync_helper初始化fasync_struct结构体,主要是应用程序的PID等信息
*	备    注:上层应用调用fcntl改变设备文件flag都会调用.fasync
****************************************************************************************/
static int fifth_drv_fasync (int fd, struct file *filp, int on)
{
	printk("driver: fifth_drv_fasync\n");
	return fasync_helper (fd, filp, on, &button_async);
}

/***************************************************************************************
*	结构名称:fasync_drv_fops
*	结构类型:file_operations
*	备    注:每一个设备号对应一个file_operations类型的结构,存放该驱动程序的各种操作函数
****************************************************************************************/
static struct file_operations fasync_drv_fops = {
	
	.owner  =   THIS_MODULE,   
    .open   =   fasync_drv_open,     
	.read	=	fasync_drv_read,
	.release =  fasync_drv_close,
	.fasync	 =  fifth_drv_fasync,
};

/***************************************************************************************
*	函数名称:fasync_drv_init	
*	主要功能:获取主设备号、注册设备类、创建设备节点
*	备    注:加载驱动程序时自动运行
****************************************************************************************/
static int __init fasync_drv_init(void){
	printk(KERN_ALERT "init OK\n");
	
	major = register_chrdev(0, "fasync_drv", &fasync_drv_fops);

	fasyncdrv_class = class_create(THIS_MODULE, "fasyncdrv");
	fasyncdrv_class_dev = device_create(fasyncdrv_class, NULL, MKDEV(major, 0), NULL, "button"); /* /dev/button */

	return 0;
}

/***************************************************************************************
*	函数名称:fasync_drv_exit	
*	主要功能:注销设备(释放设备号)、注销设备类、销毁设备节点
*	备    注:卸载驱动程序时自动运行
****************************************************************************************/
static void __exit fasync_drv_exit(void){
	printk(KERN_ALERT "exit OK\n");

	unregister_chrdev(major, "fasync_drv");	//取消注册

	device_unregister(fasyncdrv_class_dev);	//取消设备节点
	class_destroy(fasyncdrv_class);	//销毁类
}

module_init(fasync_drv_init);
module_exit(fasync_drv_exit);
应用程序:

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <poll.h>
#include <signal.h>

int fd;

void my_signal_fun(int signum)
{
	unsigned char key_val;
	read(fd, &key_val, 1);
	printf("key_val: 0x%x\n", key_val);
}

int main(int argc, char **argv)
{
	
	int ret;
	int Oflags;
	fd = open("/dev/button", O_RDWR);
	
	signal(SIGIO, my_signal_fun);
	
	if (fd < 0)
	{
		printf("can't open!\n");
	}
	
	fcntl(fd, F_SETOWN, getpid());	
	Oflags = fcntl(fd, F_GETFL); 
	fcntl(fd, F_SETFL, Oflags | FASYNC);//设置设备程序支持异步通知

	while (1)
	{	
		sleep(1000);
	}
	
	return 0;
}


版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/lizuobin2/article/details/52705254

智能推荐

mysql 多表查询练习题(二)答案_农夫三拳lhx的博客-程序员资料

这篇文章是sql语句的答案篇,数据库表信息请查看上一篇 “mysql 多表查询练习题(一)数据准备”1.查询平均成绩大于70分的同学的学号和平均成绩select sid,avg(score) avg_sc from sc group by sid having avg_sc&gt;702.查询所有同学的学号、姓名、选课数、总成绩select sc.sid,st.snam...

Fedora 19的一些配置_DevonLiu02的博客-程序员资料

ADSL拨号在F19中我没有找到拨号的图形操作,于是只好用命令来完全# 找到你的拨号网卡名ifconfig #这里的名字可能是eth0, em1, p2p1之类的# 开始设置拨号信息sudo pppoe-setup# Enter your login name (default root): 这里填ADSL帐号# (default eth0): 这里填从ifconfig得到的网卡名#

bzoj 4811 由乃的OJ_weixin_30346033的博客-程序员资料

bzoj 4811 由乃的OJ考虑树链剖分.树剖后用一颗线段树维护一段连续区间,类似于一个函数,各位上进入 \(0/1\) ,输出的数字分别是什么.注意到最多只有 \(64\) 位,可以用一个 \(unsigned\ long\ long\) 的大数状压表示,合并两段区间时推导一下可以做到 \(O(1)\) .注意 \(3 种\)位运算混在一起,满足交换律,却不满足结合律,所以从区间左/右...

VC++学习-控件篇(STATIC)_IMURKR的博客-程序员资料

CStatic m_loginstainfo;编程环境:VS2013,MFC如何实现静态文本框的刷新显示文字修改静态文本框的ID,这里改成了IDC_STAINFO方法一GetDlgItem(IDC_STAINFO)-&gt;ShowWindow(SW_HIDE);GetDlgItem(IDC_STAINFO)-&gt;SetWindowText(_T("登录成功!"));Ge...

C语言中的*和&符号_c语言*和&_XP_32986175的博客-程序员资料

之前对*和&符号一直理解的比较浅显。只知道: *p好像表示的是一个指针; &p表示的是一个地址。 然而这次当遇到了下面这个情况的时候: int a = 10; int *b = &a;printf(“%d\n”, a); printf(“%d\n”, &a); printf(“%d\n”, b); printf(“%d\n”, *b);结果: 10 6487620 648762

php 15约束,php composer 相关及版本约束等小技巧_weixin_39628271的博客-程序员资料

对于现代语言而言,包管理器基本上是标配。Java有Maven,Python有pip,Ruby有gem,Nodejs有npm。PHP的则是PEAR,不过PEAR坑不少:依赖处理容易出问题配置非常复杂难用的命令行接口好在我们有Composer,PHP依赖管理的利器。它是开源的,使用起来也很简单,提交自己的包也很容易。安装ComposerComposer需要PHP 5.3.2+才能运行。$ curl -...

随便推点

html5加css预加载进度条动画,超酷的CSS3 loading预加载动画特效_Kenjei的博客-程序员资料

HTML结构4种loading预加载动画的HTML结构分别如下:CSS样式然后分别为它们添加下面的CSS样式。/* KEYFRAMES */@keyframes spin {from {transform: rotate(0);}to{transform: rotate(359deg);}}@keyframes configure-clockwise {0% {transform: rotate(...

pycharm安装pytorch包后import torch 报错找不到指定程序,caffe2.dll_import torch 报错“找不到指定程序caffe2_nvrtc.dll”_cc77_2019的博客-程序员资料

大概原因就是,装的pytorch版本太老或者没装好,得利用官网的命令重新装一次进入https://pytorch.org/get-started/previous-versions/找到conda安装下的你的系统(windows)和CUDA版本对应的命令,复制下来。把后面的 -c pytorch 去掉不过,在运行这个命令前,你最好先给conda添加国内镜像源。conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anac

android shape自动旋转,android 通过自定义Drawble,Shape解决屏幕适配问题_weixin_39709674的博客-程序员资料

在android开发过程中,屏幕适配是一件非常重要的工作,主要原则有以下几点对于控件而言,尽量不要使用固定的宽度和高度,但推荐使用固定的外边距局和内边距。对于drawable-xxx而言,没必要每个dpi都放置图片,完全可以再drawable-hdpi中放入720px级别的图片就可以完成适配,此外,如果某些部位确实需要调整的话,可以结合values-xxxx。 对于平滑色彩,渐变色彩,点击按压效果...

nyoj 201 作业题_傻子和石像的博客-程序员资料

题目链接:http://acm.nyist.net/JudgeOnline/problem.php?pid=201////////////////////////////////////////////////////////此题的意思为找到严格递增或者严格递减的子序列的最长长度;由于x的值各不相同,所以可以按照x排序;然后对y求最长递增子序列和最长递减子序列,去两者较大的输出

靶机:narak_靶机网站_Go it alone'的博客-程序员资料

靶机网址:https://www.vulnhub.com/entry/ha-narak,569/目录一、信息搜集:二、漏洞利用与探测:三、提权一、信息搜集:获取IP:nmap -sP 192.168.8.0/24 端口扫描:nmap -sV -p- -sC 192.168.8.132 -n -vv22、80端口均开启访问192.168.8.132从页面搜索信息·翻译了一下网页内容信息提取关键字或许有用: yamdoot,...

python机器学习常用库及相关函数_机器学习求和函数_ThereIsNoBugAnymore的博客-程序员资料

求和相关使用求和函数sum([axis=0|1]),根据参数不同可实现不同求和方式。矩阵整体数据的求和运算使用无参求和函数,可实现对矩阵整体数据的求和运算。from numpy import matsample = mat([[1, 2], [3, 4]]sample.sum()执行结果:101010矩阵按列求和运算求和函数参数为0时,可实现矩阵按列求和运算。from num...

推荐文章

热门文章

相关标签