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字符设备驱动-异步通知

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

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

在linux中,异步通知是使用信号来实现的,而在linux,大概有30种信号,比如大家熟悉的ctrl+c的SIGINT信号,进程能够忽略或者捕获除过SIGSTOP和SIGKILL的全部信号,当信号背捕...
  • Lyanzh
  • Lyanzh
  • 2017年08月10日 10:09
  • 181

07-S3C2440驱动学习(一)嵌入式linux字符设备驱动-按键驱动程序之异步通知机制+原子操作+互斥信号量+阻塞与非阻塞+定时器去抖

一、异步通知机制 从按键的实现方式来说,可以分为以下几种方式 查询方式,极度耗费CPU资源中断方式,平时休眠,按键按下,唤醒休眠poll机制,不需要一直read,根据poll返回值来决定是否rea...

字符设备驱动之Buttons-异步通知(fasync)

buttons.c #include #include #include #include #include #include #include #i...

字符设备驱动第八课----异步通知(信号驱动IO)

概述类比运用程序中的kill-----signal,在运用程序中常常一个进程用kill(pid,sig)向另一 进程发信号,另一个进程用signal(sig,handler)绑定相应的处理函数,实现了...

linux 设备驱动异步通知总结

  • 2016年02月18日 15:18
  • 16KB
  • 下载

linux设备驱动开发学习之旅--异步通知

[cpp] view plaincopy /**   * Author:hasen   * 参考 :《linux设备驱动开发详解》   * 简介:android小菜...

深入浅出 Linux设备驱动异步通知介绍

结合阻塞与非阻塞访问、poll函数可以较好地解决设备的读写,但是如果有了异步通知就更方便了。异步通知的意思是:一旦设备就绪,则主动通知应用程序,这样应用程序根本就不需要查询设备状态,这一点非常类似于硬...
  • ailinty
  • ailinty
  • 2011年09月01日 14:45
  • 320

Linux设备驱动之异步通知

Linux设备驱动之异步通知的原理及简单代码实现
  • ljmiaw
  • ljmiaw
  • 2017年07月16日 21:02
  • 69

linux设备驱动归纳总结(三):7.异步通知fasync

linux设备驱动归纳总结(三):7.异步通知fasync xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Linux的异步通知字符设备驱动
举报原因:
原因补充:

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