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