Linux的异步通知字符设备驱动

原创 2017年01月10日 18:32:28

Fasync字符驱动:

1、在我们用户程序下所做的工作:

⑴ 注册信号处理函数。
通过signal 或sigaction()实现。
⑵ 使进程成为该文件的的属主进程。
 通过fcntl 的F_SETOWN命令来实现。如fcntl(fd, F_SETOWN, getpid());
⑶ 启用异步通知功能。
通过fcntl 的F_SETFL命令设置FASYNC标记。

2、驱动程序所做的工作:

在内核里异步通知是通过,异步通知队列struct fasync_struct来实现的。在这里,我们只需要用到内核提供的两个有关异步通知队列的函数就可以了。fasync_helper 和 kill_fasync。

fasync_helper 用于把文件指针加到(或移除,当参数on = 0)异步通知队列

kill_fasync用于发送信号给应用程序。

下面是异步通知函数在Linux内核实现的源代码:

fs/fcntl.c
int fasync_helper(int fd, struct file * filp, int on, struct fasync_struct **fapp)
{
	if (!on)
		return fasync_remove_entry(filp, fapp);
	return fasync_add_entry(fd, filp, fapp);
}

fs/fcntl.c
void kill_fasync(struct fasync_struct **fp, int sig, int band)
{
	/* First a quick test without locking: usually
 	* the list is empty.
 	*/
	if (*fp) {
		rcu_read_lock();
		kill_fasync_rcu(rcu_dereference(*fp), sig, band);
		rcu_read_unlock();
	}
}

接下来就是我们的驱动程序编写:

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <asm/signal.h>
#include <asm/siginfo.h>
#include <linux/device.h>


MODULE_LICENSE("GPL");

static struct cdev *pcdev;
static dev_t ndev;
static struct class *fa_cls;
static struct device *fadev;

unsigned long flag = 0;
struct fasync_struct *sigio_list;

/*===============================================================================================
	Description: 
			这是两个内核信号的读写处理函数
	@dev  :
			设备指针,及设备节点 
	@attr :	
			设备文件参数
	@count:	
			个数
	@buf  :
			读写缓存
================================================================================================*/

static ssize_t read_flag(struct device *dev,struct device_attribute *attr,char *buf)
{
	ssize_t count = 0;
	count += sprintf(&buf[count],"%lu\n",flag);

	return count;
}

static ssize_t write_flag(struct device *dev,struct device_attribute *attr,
	const char *buf,size_t count)
{
	flag = buf[0] - '0';

	//给所有的FASYNC标志的调用fcntl的应用发送信号
	kill_fasync(&sigio_list,SIGIO,POLL_IN);

	return count;
}



struct device_attribute flag_attr = __ATTR(flag,S_IRUGO|S_IWUSR,read_flag,write_flag);


/*===============================================================================================
	@Description: 
			下面两个函数是sruct file_operation的函数,即驱动最终调用的函数
	@inode:
			这个是设备结点,是在内核里面所创建的
	@filp:
			文件指针
	@fd:
			用户open函数调用会产生一个进程描述表其中就有fd
	@onflag:
			异步通知的打开或者关闭
================================================================================================*/

static int fa_open(struct inode *inode,struct file *filp)
{
	printk(KERN_INFO "OPEN is successful\n");
	
	return 0;
}


static int fa_fasync(int fd,struct file* filp,int onflag)
{
	printk(KERN_INFO "hello,welcome to the FASYNC_DEVICE\n");

	return fasync_helper(fd,filp,onflag, &sigio_list);	//这个onflag可以直接设置成0

}


struct file_operations ops=
{
	.owner = THIS_MODULE,
	.open = fa_open,
	.fasync = fa_fasync,
};


static int __init fa_init(void)
{
	int ret = 0;

	ret = alloc_chrdev_region(&ndev,0,1,"fa_dev");
	if(ret < 0)
		return ret;

	pcdev = cdev_alloc();
	cdev_init(pcdev,&ops);
	pcdev->owner = THIS_MODULE;
	cdev_add(pcdev,ndev,1);

	fa_cls = class_create(THIS_MODULE,"fa_dev");
	if(IS_ERR(fa_cls))
	{
		return PTR_ERR(fa_cls);
	}

	fadev = device_create(fa_cls,NULL, ndev,NULL,"fa_dev");
	if(IS_ERR(fadev))
	{
		return PTR_ERR(fadev);
	}

	//在sysfs文件的系统中生成一个名为 "flag"的文件
	ret = device_create_file(fadev,&flag_attr);
	
	return ret;
}


static void __exit fa_exit(void)
{
	device_remove_file(fadev,&flag_attr);
	device_destroy(fa_cls,ndev);
	class_destroy(fa_cls);
	cdev_del(pcdev);
	unregister_chrdev_region(ndev,1);
}



module_init(fa_init);
module_exit(fa_exit);


MODULE_AUTHOR("TangSirNan");
MODULE_DESCRIPTION("a simple fasync driver.");


接下来就是用户所做的程序:
/*=================================================================

	@Description:
		这是 关于一个异步通知的用户程序,里面用到了信号
		申请和处理函数。 利用信号signal和底层驱动的处理
		函数向关联起来组成一个异步信号通知。
		其中有些信号函数如下:
		sigemptyset(sigset_t *set)初始化由set指定的信号集,
		信号集里面的所有信号被清空;
		
		sigfillset(sigset_t *set)调用该函数后,set指向的
		信号集中将包含linux支持的64种信号;
		
		sigaddset(sigset_t *set, int signum)在set指向的
		信号集中加入signum信号;
		
		sigdelset(sigset_t *set, int signum)在set指向的
		信号集中删除signum信号;
		
		sigismember(const sigset_t *set, int signum)判定
		信号signum是否在set指向的信号集中。
		
		int sigaction( int sig, const struct sigaction *act,
			struct sigaction *oact )这是
		检查、修改和指定信号相关联的信号响应。

	@Author: TangSirNan

	@Time:   2017/1/10

	@E-MAIL: 1554055458@qq.com 
	
==================================================================*/

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


static unsigned long eflag = 1;			//这表示一个信号标志


/*=================================================================
	@Description:
		信号处理函数,用来处理信号的
	@sig: 
		信号集里面的某一个信号
	@return:
		没有返回值
==================================================================*/
static void sigio_handler(int sig)
{
	printf("make this user structure to change the world\n");

	eflag = 0;
}


/*=================================================================
	@Description:
		这是下面的所用到的信号处理结构体
		typedef struct {
			unsigned long sig[_NSIG_WORDS];
		} sigset_t
		设置成阻塞的信号处理。
		
	@parameter: 
		没有参数
	
	@return:
		返回一个整型数据ret
==================================================================*/
static int block_sigio(void)
{
	sigset_t set,old;
	int ret;

	sigemptyset(&set);
	sigaddset(&set,SIGIO);

	//调用函数sigprocmask可设定信号集内的信号阻塞或不阻塞
	/*
		SIG_BLOCK:增加一个信号集合到当前进程的阻塞集合之中
     	SIG_UNBLOCK:从当前的阻塞集合之中删除一个信号集合
     	SIG_SETMASK:将当前的信号集合设置为信号阻塞集合
	*/
	sigprocmask(SIG_BLOCK,&set,&old);	

	ret = sigismember(&old,SIGIO);

	return ret;
}


/*=================================================================
	@Description:
		这个函数是将信号设置不可阻塞的方式,既不需要等待

	@blocked: 
		表示一个标志位是否采取不可阻塞的方式 
		
	@return:
		没有返回值
		
==================================================================*/
static void unblock_sigio(int blocked)
{
	sigset_t set;
	
	if(!blocked)
	{
		sigemptyset(&set);
		sigaddset(&set,SIGIO);
		sigprocmask(SIG_UNBLOCK,&set,NULL);	//设置的关键
	}
	
}


int main(int argc,char const *argv[])
{
	int fd;
	struct sigaction sigact,oldact;
	int oflag;
	int blocked;

	blocked = block_sigio();

	sigemptyset(&sigact.sa_mask);
	sigaddset(&sigact.sa_mask,SIGIO);
	sigact.sa_flags = 0;
	sigact.sa_handler = sigio_handler;

	if(sigaction(SIGIO,&sigact,&oldact) < 0)
	{
		printf("sigaction is failed\n");
		unblock_sigio(blocked);
		return -1;
	}

	unblock_sigio(blocked);

	#ifndef DEVICE
	#define DEVICE 	"/dev/fa_dev"
	fd = open(DEVICE,O_RDWR);
	if(fd < 0)
	{
		perror("open failed");

		return fd;
	}
	#endif

	fcntl(fd,F_SETOWN,getpid());
	oflag = fcntl(fd,F_GETFL);
	fcntl(fd,F_SETFL,oflag|FASYNC);
	printf("Do everything you want until we get a signal...\n");
	while(eflag);

	close(fd);
		
	return 0;
}
以上就是异步通知的信号字符驱动。




版权声明:本文为博主原创文章,未经博主允许不得转载。

Linux字符设备驱动之异步通知

到这里我们觉得这下这个驱动应该就很完善了吧,但是不仅然,我们是不是想当有按键按下的时候,这个时候再去通知用户空间的read函数来读,这样是不是更方便的都,免得函数也老是在哪里休眠。在这里说下:我是不会...
  • qq_21792169
  • qq_21792169
  • 2015年09月13日 16:55
  • 2972

linux字符设备驱动-异步通知

异步通知关键步骤: 1,应用注册信号处理函数,使用signal函数; 2,谁来发:驱动发送通知信号; 3,发给谁:驱动发送通知给特定的应用程序,驱动需要知道应用程序的PID号; 4,怎么发:驱...
  • u010631857
  • u010631857
  • 2017年10月31日 17:15
  • 61

linux字符设备驱动-异步通知笔记

一、开发环境 1、内核:Linux 2.6.22.6; 2、JZ2440 3、ubuntu 9.10
  • cs953575
  • cs953575
  • 2017年03月07日 09:41
  • 145

字符设备驱动-异步通知

我们前面的三种按键操作中,都需要通过应用程序不断地主动通过read()来读驱动程序或者通过poll机制利用返回的信息做出决定。 我们想要当按下按键时利用驱动程序来通知应用程序则需要引入异步通知 ...
  • czg13548930186
  • czg13548930186
  • 2017年09月04日 15:25
  • 282

Linux实现字符设备驱动的基础步骤

Linux应用层想要操作kernel层的API,比如想操作相关GPIO或寄存器,可以通过写一个字符设备驱动来实现。 1、先在rootfs中的 /dev/ 下生成一个字符设备。注意主设备号 和...
  • liukang325
  • liukang325
  • 2014年07月16日 15:57
  • 2116

Linux的fasync驱动异步通知详解

首先还是先从init函数来总结:该驱动是一混杂设备驱动模型来写的,这个主要是借鉴网上的好多资料都是一这种模式来写的,Linux里面misc混杂设备驱动的主设备号是为10的驱动设备,init模块首先是用...
  • coding__madman
  • coding__madman
  • 2016年07月07日 17:43
  • 2441

linux下使用异步通知

阻塞式I/O是一直等待直到设备可以访问,非阻塞式I/O是定期轮询设备是否可以访问。 异步通知则是当设备可以访问时才主动通知应用程序,有点像设备的硬中断。   并不是所有的设备都支持异步通知,应用程序通...
  • zhangskd
  • zhangskd
  • 2015年05月23日 22:47
  • 4783

Linux字符设备驱动剖析

忠于源码,讲述linux字符设备驱动的那些事儿,重点讲述字符设备的创建和访问过程。...
  • yueqian_scut
  • yueqian_scut
  • 2015年05月23日 23:09
  • 3798

深入理解Linux字符设备驱动

文章从上层应用访问字符设备驱动开始,一步步地深入分析Linux字符设备的软件层次、组成框架和交互、如何编写驱动、设备文件的创建和mdev原理,对Linux字符设备驱动有全面的讲解。...
  • yueqian_scut
  • yueqian_scut
  • 2016年03月20日 11:09
  • 3423

linux设备驱动第三篇:写一个简单的字符设备驱动

在linux设备驱动第一篇:设备驱动程序简介中简单介绍了字符驱动,本篇简单介绍如何写一个简单的字符设备驱动。本篇借鉴LDD中的源码,实现一个与硬件设备无关的字符设备驱动,仅仅操作从内核中分配的一些内存...
  • HAOMCU
  • HAOMCU
  • 2015年03月28日 19:05
  • 24383
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Linux的异步通知字符设备驱动
举报原因:
原因补充:

(最多只允许输入30个字)