分类: LINUX
2013-11-15 16:11:22
终端设备是tty设备的一种,其异步通知机制的实现在驱动中是分布的:
1)首先在F_SETOWN被调用时对filp->owner赋值。
2)文件打开时默认FASYNC标志是清除的,当设置这个标志式调用fasync方法。fasync方法依赖于struct fasync_struct 结构和fasync_helper函数。下面具体分析该函数:
static DEFINE_RWLOCK(fasync_lock);
static struct kmem_cache *fasync_cache __read_mostly;
/*
* fasync_helper() is used by some character device drivers (mainly mice)
* to set up the fasync queue. It returns negative on error, 0 if it did
* no changes and positive if it added/deleted the entry.
*/
//fasync_helper从相关的进程列表中增加或删除文件,on为0表示删除,非0表示增加
int fasync_helper(int fd, struct file * filp, int on, struct fasync_struct **fapp)
{
struct fasync_struct *fa, **fp;
struct fasync_struct *new = NULL;
int result = 0;
//若在进程列表中增加文件则从后备高速缓存中分配一个struct fasync_struct 结构
if (on) {
new = kmem_cache_alloc(fasync_cache, GFP_KERNEL);
if (!new)
return -ENOMEM;
}
write_lock_irq(&fasync_lock);
//遍历进程的异步通知文件列表,若存在相关文件且为增加则删除前面分配的fasync_struct 对象,若为删除则删除文件对应
//的fasync_struct对象
for (fp = fapp; (fa = *fp) != NULL; fp = &fa->fa_next) {
if (fa->fa_file == filp) {
if(on) {
fa->fa_fd = fd;
kmem_cache_free(fasync_cache, new);
} else {
*fp = fa->fa_next;
kmem_cache_free(fasync_cache, fa);
result = 1;
}
goto out;
}
}
//列表中不存在相关文件则初始化faync_struct 对象并加入到列表头
if (on) {
new->magic = FASYNC_MAGIC;
new->fa_file = filp;
new->fa_fd = fd;
new->fa_next = *fapp;
*fapp = new;
result = 1;
}
out:
write_unlock_irq(&fasync_lock);
return result;
}
//下面再来看看tty核心中fasync方法tty_fasync函数
static int tty_fasync(int fd, struct file *filp, int on)
{
struct tty_struct *tty;
unsigned long flags;
int retval = 0;
lock_kernel();
tty = (struct tty_struct *)filp->private_data;
if (tty_paranoia_check(tty, filp->f_path.dentry->d_inode, "tty_fasync"))
goto out;
retval = fasync_helper(fd, filp, on, &tty->fasync); //增加或删除文件到进程列表
if (retval <= 0)
goto out;
if (on) {
enum pid_type type;
struct pid *pid;
if (!waitqueue_active(&tty->read_wait))
tty->minimum_to_wake = 1;
spin_lock_irqsave(&tty->ctrl_lock, flags);
if (tty->pgrp) {
pid = tty->pgrp;
type = PIDTYPE_PGID;
} else {
pid = task_pid(current);
type = PIDTYPE_PID;
}
spin_unlock_irqrestore(&tty->ctrl_lock, flags);
retval = __f_setown(filp, pid, type, 0); //设置文件的属主
if (retval)
goto out;
} else {
if (!tty->fasync && !waitqueue_active(&tty->read_wait))
tty->minimum_to_wake = N_TTY_BUF_SIZE;
}
retval = 0;
out:
unlock_kernel();
return retval;
}
3)上面的步骤完成后就是当数据到达时给应用程序发送SIGIO信号,这一步是一般分布在数据的读写操作中,我们具体分析内核中的辅助函数kill_fasync其作用是当数据到达时通知所有相关进程
//sig表示要发送的信号,band表示模式读为POLL_IN 写为POLL_OUT
void kill_fasync(struct fasync_struct **fp, int sig, int band)
{
/* First a quick test without locking: usually
* the list is empty.
*/
if (*fp) {
read_lock(&fasync_lock);
/* reread *fp after obtaining the lock */
__kill_fasync(*fp, sig, band);
read_unlock(&fasync_lock);
}
}
//遍历struct fasync_struct 链表并对相关进程发送信号
void __kill_fasync(struct fasync_struct *fa, int sig, int band)
{
while (fa) {
struct fown_struct * fown;
if (fa->magic != FASYNC_MAGIC) {
printk(KERN_ERR "kill_fasync: bad magic number in "
"fasync_struct!/n");
return;
}
fown = &fa->fa_file->f_owner;
/* Don't send SIGURG to processes which have not set a
queued signum: SIGURG has its own default signalling
mechanism. */
if (!(sig == SIGURG && fown->signum == 0))
send_sigio(fown, fa->fa_fd, band);
fa = fa->fa_next;
}
}
4)最好在关闭进程时再把文件从进程列表中清除即tty_fysnc(-1,flip,0);
因为异步通知机制是分散的这节中就主要分析其实现机制,实现源码比较容易理解就没有列举出全部源码