设备驱动中异步通知编程主要就是一些数据结构,和两个函数:
数据结构:fasync_struct结构体
函数:1.处理FASYNC标志变更的函数int fasync_helper(int fd, struct file * filp, int on, struct fasync_struct **fapp)
2.释放信号 void kill_fasync(struct fasync_struct **fa, int sig, int band);
此部分解释来自百度百科
其实在应用层启用异步通知只三个步骤:
其实在应用层启用异步通知只三个步骤:
1)signal(SIGIO, sig_handler);
调用signal函数,让指定的信号SIGIO与处理函数sig_handler对应。
2)fcntl(fd, F_SET_OWNER, getpid());
指定一个进程作为文件的“属主(filp->owner)”,这样内核才知道信号要发给哪个进程。
3)f_flags = fcntl(fd, F_GETFL);
fcntl(fd, F_SETFL, f_flags | FASYNC);
在设备文件中添加FASYNC标志,驱动中就会调用将要实现的test_fasync函数。
三个步骤执行后,一旦有信号产生,相应的进程就会收到。
驱动中需要实现的异步通知
3.1 异步通知内核实现
实现异步通知,内核需要知道几个东西:哪个文件(filp),什么信号(SIGIIO),发给哪个进程(pid),收到信号后做什么(sig_handler)。这些都由上述前两个步骤完成了,而这前两个步骤内核帮忙实现了,所以,我们只需要实现第三个步骤的一个简单的传参。
3.2 fasync_struct结构体
要实现传参,我们需要把一个结构体struct fasync_struct添加到内核的异步队列中,这个结构体用来存放对应设备文件的信息(如fd, filp)并交给内核来管理。一但收到信号,内核就会在这个所谓的异步队列头找到相应的文件(fd),并在filp->owner中找到对应的进程PID,并且调用对应的sig_handler了。
struct fasync_struct {
int magic;
int fa_fd;
struct fasync_struct *fa_next; /* singly linked list */
struct file *fa_file;
};
3.3 内核中我们的工作
上面说了前两个步骤会由内核完成,所以我们只要做两件事情:
1)定义结构体fasync_struct。
struct fasync_struct *async_queue;
2)实现test_fasync,把函数fasync_helper将fd,filp和定义的结构体传给内核。
int test_fasync (int fd, struct file *filp, int mode)
{
struct _test_t *dev = filp->private_data;
return fasync_helper(fd, filp, mode, &dev->async_queue);
}
函数fasync_helper的定义为:
int fasync_helper(int fd, struct file * filp, int on, struct fasync_struct **fapp)
前面的三个参数其实就是teat_fasync的三个参数,所以只要我们定义好的fasync_struct结构体也传进去就可以了。
3.4 其余的工作
另外还有两件事:
3)当设备可写时,调用函数kill_fasync发送信号SIGIO给内核。
if (dev->async_queue){
kill_fasync(&dev->async_queue, SIGIO, POLL_IN);
}
讲解一下这个函数:
void kill_fasync(struct fasync_struct **fp, int sig, int band)
sig就是我们要发送的信号。
band(带宽),一般都是使用POLL_IN,表示设备可读,如果设备可写,使用POLL_OUT
4)当设备关闭时,需要将fasync_struct从异步队列中删除:
test_fasync(-1, filp, 0);
删除也是调用test_fasync,不过改了一下参数而已。
基本的使用模板如下:
signal I/O:
18
19 struct fasync_struct *fapp;
20
21 // 当事件实际发生时 调用kill_faync发送信号
22 // 例如:mywrite 有数据到达 设备变为可读时
23 mywrite(filp, ...)
24 {
25 ...
26 kill_fasync(filp, SIGIO, 0);
27 }
28
29 // 初始化
30 myfasync(fd, filp, on)
31 {
32 return fasync_helper(fd, filp, on, &fapp);
33 }
34
35 struct file_operations fops = {
36 .fasync = myfasync,
37 };
下面是使用例子,驱动的实现
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#define SIZE 1024
#define NR 4
static int major = 100, minor = 0;
module_param(major, int, 0400);
module_param(minor, int, 0400);
// =================================================================
struct mypipe_t {
char buf[SIZE];
int in, out;
};
static void mypipe_init(struct mypipe_t *pipe)
{
pipe->in = pipe->out = 0;
}
static int mypipe_is_full(struct mypipe_t *pipe)
{
return (pipe->in + 1) % SIZE == pipe->out;
}
static int mypipe_is_empty(struct mypipe_t *pipe)
{
return pipe->in == pipe->out;
}
static int mypipe_read_one_char(struct mypipe_t *pipe, char *ch)
{
if (mypipe_is_empty(pipe))
{
return -1;
}
*ch = pipe->buf[pipe->out];
pipe->out = (pipe->out + 1) % SIZE;
return 0;
}
static int mypipe_write_one_char(struct mypipe_t *pipe, char ch)
{
if (mypipe_is_full(pipe))
{
return -1;
}
pipe->buf[pipe->in] = ch;
pipe->in = (pipe->in + 1) % SIZE;
return 0;
}
// =================================================================
struct mydev_t {
dev_t devno;
struct cdev cdev;
wait_queue_head_t rq, wq;
struct mypipe_t pipe;
};
struct mydev_t mydev[NR];
//==================================================================
struct fasync_struct *fapp;
static int myopen(struct inode *inode, struct file *filp)
{
#if 0
//MINOR(inode->i_rdev);
int index = iminor(inode);
filp->private_data = &mydev[index];
#else
filp->private_data = container_of(inode->i_cdev, struct mydev_t, cdev);
#endif
return 0;
}
static ssize_t myread(struct file *filp, char __user *buf,
size_t len, loff_t *off)
{
int i, ret;
char ch;
struct mydev_t *devp = filp->private_data;
ret = wait_event_interruptible(devp->rq, !mypipe_is_empty(&devp->pipe));
if (ret != 0)
{
return -EINTR;
}
for (i = 0; i < len; ++i)
{
ret = mypipe_read_one_char(&devp->pipe, &ch);
if (ret != 0) break;
//buf[i] = ch;
//copy_to_user(&buf[i], &ch, 1);
put_user(ch, &buf[i]);
}
wake_up_interruptible(&devp->wq);
return i;
}
static ssize_t mywrite(struct file *filp, const char __user *buf,
size_t len, loff_t *off)
{
int i, ret;
char ch;
struct mydev_t *devp = filp->private_data;
ret = wait_event_interruptible(devp->wq, !mypipe_is_full(&devp->pipe));
if (ret != 0)
{
return -EINTR;
}
for (i = 0; i < len; ++i)
{
get_user(ch, &buf[i]);
ret = mypipe_write_one_char(&devp->pipe, ch);
if (ret != 0) break;
}
wake_up_interruptible(&devp->rq);
printk(KERN_EMERG"kill fasync\n");
kill_fasync(&fapp, SIGIO, POLL_IN);
return i;
}
int myfasync(int fd, struct file *filp, int on)
{
printk(KERN_EMERG"myfasync %d\n",on);
return fasync_helper(fd, filp, on, &fapp);
}
static int myrelease(struct inode *inode, struct file *filp)
{
return 0;
}
int myfasync(int fd, struct file *filp, int on)
{
printk(KERN_EMERG"myfasync %d\n",on);
return fasync_helper(fd, filp, on, &fapp);
}
struct file_operations pipe_ops = {
.open = myopen,
.release = myrelease,
.read = myread,
.write = mywrite,
.fasync = myfasync,
};
//==================================================================
static int __init test_init(void)
{
int i;
dev_t devno;
struct mydev_t *devp;
devno = MKDEV(major, minor);
register_chrdev_region(devno, NR, "mypipe");
for (i = 0; i < NR; ++i)
{
devp = &mydev[i];
init_waitqueue_head(&devp->rq);
init_waitqueue_head(&devp->wq);
mypipe_init(&devp->pipe);
cdev_init(&devp->cdev, &pipe_ops);
devno = MKDEV(major, minor + i);
cdev_add(&devp->cdev, devno, 1);
}
return 0;
}
static void __exit test_exit(void)
{
int i;
dev_t devno;
struct mydev_t *devp;
for (i = 0; i < NR; ++i)
{
devp = &mydev[i];
cdev_del(&devp->cdev);
}
devno = MKDEV(major, minor);
unregister_chrdev_region(devno, NR);
}
module_init(test_init);
module_exit(test_exit);
MODULE_LICENSE("GPL");
#include <unistd.h>
#include <signal.h>
static int fd;
#define SIZE 1024
static void sig_handler(int signo)
{
char buffer[SIZE];
int ret;
ret = read(fd, buffer, SIZE);
buffer[ret] = 0;
printf("recv: %s", buffer);
}
int main(int argc, char *argv[])
{
struct sigaction sa;
int flags;
if (argc < 2) return -1;
sa.sa_handler = sig_handler;
sa.sa_flags = 0;
sigemptyset(&sa.sa_mask);
sigaction(SIGIO, &sa, NULL);
fd = open(argv[1], O_RDONLY | O_NONBLOCK);
fcntl(fd, F_SETOWN, getpid());
flags = fcntl(fd, F_GETFL);
flags |= O_ASYNC;
fcntl(fd, F_SETFL, flags);
while (1)
{
fprintf(stderr, "* ");
sleep(1);
}
close(fd);
return 0;
}