fasync

fasynckill_fasync是内核信号机制在驱动中的一个应用,低层原理是信号收发。让一个驱动一个进程绑定。由于驱动并不知道应用程序的ID,或者说应用程序的ID也并不是固定的。因此为了每次都正确的发送给一个固定应用及进程。则要对信号进行特殊应用。因为信号必须要知道进程ID才能正确的收发。LINUX系统对于每个应用进程号都是随机的每次开机或启动。这样就必须让一个特定驱动代码与特定进程通过每个变量来建立确定的关系。这就是fasync文件异步机制的真实背景。由于这个机制是对信号的应用。因此我们下面还是根据应用层到内核层的调节关系来分析问题。一个最基本的FASYNC框架应该如下:

A.注册fasync  

B.发送信号kill_fasync

A.注册fasync

它的作用就是让驱动与一个特定的进程绑定,这样KILL_FASYNC才能知道发给谁。

下面举一个从应用层到驱动的最简单例子:

  1.  sigaction(sigio.&sigaction_struct,null) //为当前进程注册一个接收sigio信号的支持回调函数sigaction_struct(实现参见信号框架梗概那节,把回调函数注册到一个当前进程所在的TODO链表中。)

  2. Fcntl(fd,F_SETOWN,getpid())// getpid()=current  fdCURRENT关联起来

    进入内核的系统调用do_fcntl------f_setown-----__f_setown----

    f_modown(filp, pid, type, force)-----------filp->f_owner.pid = get_pid(pid);//这一步是关键这儿的pid和上面注册回调函数得到的结构体是一样的

    static void f_modown(struct file *filp, struct pid *pid, enum pid_type type,

    filp->f_owner.pid = get_pid(pid);//为应用层利用FD来传给内核驱动时绑定进程号

    C.

    Flags = fcntl(fd,F_GETFL);

    fcntl(fd,F_SETFL,flags|FASYNC)进入内核setfl------

    filp->f_op->fasync(fd, filp, (arg & FASYNC)【这个函数是驱动指定的】--------

    .fasync = int (*fsync) (struct file *, int datasync);此函数中会固定调用

    int fasync_helper(int fd, struct file * filp, int on, struct fasync_struct **fapp)

    这个函数。其中fapp是在驱动中定义的全景变量。它的作用就是用来记录当前进程来操作这个驱动时FD里面指定的进程号current.当条件满足时来给应用层发送信号,调用回调函数。

    struct fasync_struct *fasync_insert_entry(int fd, struct file *filp, struct fasync_struct **fapp, struct fasync_struct *new)

    Fapp>fa_fd = fd;//这条说明我们在驱动中定义的全景变量fasync_struct **fapp中的fa_fd指向应层打开此驱动时得到的FD.这也就是后面发送信号时能找到程号的原因。

     

    经过上面三步完成了信号回调函数的注册和fasync_struct **fapp与进程的绑定。

    D:利用全局变量fapp中的进程号来发信号给应用层。

    void kill_fasync(struct fasync_struct **fp, int sig, int band)//这儿的FP就是我前面定义 的全局变量fapp。这个是驱动与特定驱动一一关联的纽带。

    kill_fasync_rcu(rcu_dereference(*fp), sig, band);

    static void kill_fasync_rcu(struct fasync_struct *fa, int sig, int band)

    send_sigio(fown, fa->fa_fd, band);//fa->fd发送信号。

    void send_sigio(struct fown_struct *fown, int fd, int band)

    {

    send_sigio_to_task(p, fown, fd, band, group);//唤醒fd中记录的进程。并且执行我们注册的回调函数。

    do_send_sig_info-send_signal-complete_signal-signal_wake_up(t, sig == SIGKILL);-kick_process(如果进程休眠唤醒它,会去执行回调函数)/set_tsk_thread_flag(如果进程没有休眠则去设置线程任务标记,可以认为像中断一样设置中断标记,告诉程序可以进入中断程序了。设置好这个标记,系统应该会去主动调用我们的回调函数根据进程current,一一对应的调用。)



异步通知fasync应用于系统调用signal和sigaction函数,简单的说,signal函数就是让一个信号与一个函数对应,每当接收到这个信号就会调用相应的函数。

函数概括

编辑
异步通知fasync应用于系统调用signal和sigaction函数,简单的说,signal函数就是让一个信号与一个函数对应,每当接收到这个信号就会调用相应的函数。[1] 
那么什么是异步通知?异步通知类似于中断的机制,当设备可写时,设备驱动函数发送一个信号给内核,告知内核有数据可读,在条件不满足之前,并不会造成阻塞。而不像之前学的阻塞型IO和poll,它们是调用函数进去检查,条件不满足时还会造成阻塞。

使用方式

编辑
其实在应用层启用异步通知只三个步骤:
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,不过改了一下参数而已。

阅读更多
换一批

fasync 入门问题,大家指教

12-19

rn#include rn#include rn#include rn#include rn#include rn#include rn#include rn#include rn#include rn#include rnrn#define DEVICE_NAME "chardev"rnrnstatic int chardev_open(struct inode *inodp, struct file *filp);rnstatic ssize_t chardev_read(struct file *filp, char __user *buff, size_t count, loff_t *f_pos);rnstatic ssize_t chardev_write(struct file *filp, const char __user *buff, size_t count, loff_t *f_pos);rnstatic int chardev_fasync(int fd, struct file *filp, int mode);rnstatic int chardev_release(struct inode *inodp, struct file *filp);rnrnstatic struct file_operations chardev_fops = rn .open = chardev_open,rn .read = chardev_read,rn .write = chardev_write,rn .fasync = chardev_fasync,rn .release = chardev_release,rn;rnrnstatic struct fasync_struct *async = NULL;rnstatic struct semaphore sem;rnstatic struct cdev *cdevp;rnstatic dev_t devno;rnstatic char buffer[8192];rnrnstatic int chardev_open(struct inode *inodp, struct file *filp)rnrn return 0;rnrnrnstatic ssize_t chardev_read(struct file *filp, char __user *buff, size_t count, loff_t *f_pos)rnrn if (down_interruptible(&sem))rn goto err;rnrn count = strlen(buffer);rn copy_to_user(buff, buffer, count);rn printk("<0>""read buff is %s\n",buffer);rnrn up(&sem);rn return count;rnerr:rn return -ERESTARTSYS;rnrnrnstatic ssize_t chardev_write(struct file *filp, const char __user *buff, size_t count, loff_t *f_pos)rnrn if (down_interruptible(&sem))rn goto err;rnrn memset(buffer, '\0', sizeof(buffer));rn copy_from_user(buffer, buff, count);rnrn printk("<0>""write buff is %s\n",buffer);rn up(&sem);rnrn if (async)rn kill_fasync(&async, SIGIO, POLL_IN);rnrn return count;rnerr:rn return -ERESTARTSYS;rnrnrnstatic int chardev_fasync(int fd, struct file *filp, int mode)rnrn //printk("<0>""\nin chardev_fasync\n");rn dump_stack();rn return fasync_helper(fd, filp, mode, &async);rnrnrnstatic int chardev_release(struct inode *inodp, struct file *filp)rnrn //printk("<0>""\nin chardev_release\n");rn //printk("<0>""\nin chardev_release twice\n");rn dump_stack();rn return chardev_fasync(-1, filp, 0);rnrnrnstatic int __init chardev_init(void)rnrn int ret;rnrn ret = alloc_chrdev_region(&devno, 0, 1, DEVICE_NAME);rn if (ret < 0)rn goto out;rnrn init_MUTEX(&sem);rn cdevp = cdev_alloc();rn if (!cdevp)rn goto alloc_err;rnrn cdev_init(cdevp, &chardev_fops);rn ret = cdev_add(cdevp, devno, 1);rn if (!ret)rn goto out;rnalloc_err:rn unregister_chrdev_region(devno, 1);rnout:rn return ret;rnrnrnstatic void __exit chardev_exit(void)rnrn cdev_del(cdevp);rnrn unregister_chrdev_region(devno, 1);rnrnrnMODULE_LICENSE("GPL");rnrnmodule_init(chardev_init);rnmodule_exit(chardev_exit);rnrnrn一个简单的例子,我从google copy来的,例子没问题,能跑。rnrn但是我不知道这东西就是fasync怎么用,那位兄弟能写个应用调我这个东西的例子,谢谢了。rnrn简单的能说明问题的就行rn

没有更多推荐了,返回首页