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

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;
}
以上就是异步通知的信号字符驱动。




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值