Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1808793
  • 博文数量: 272
  • 博客积分: 1272
  • 博客等级: 少尉
  • 技术积分: 1866
  • 用 户 组: 普通用户
  • 注册时间: 2011-03-09 15:51
文章分类

全部博文(272)

文章存档

2016年(16)

2015年(28)

2014年(97)

2013年(59)

2012年(25)

2011年(47)

分类: LINUX

2013-11-15 16:11:22

  套接字和终端通常都具有异步通知机制,即应用程序可以在数据可用的时候接收到一个信号SIGIO而不需要去轮询关注的数据。但是当对于多个数据源时,应用不能区分SIGIO的来源。为了实现异步通知机制,应用程序需要为数据源设置一个属主进程即用fcntl的F_SETOWN来设置属主进程,以及用fcntl的F_SETFL设置FASYNC标志来开启文件的异步通知机制。

     终端设备是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);


因为异步通知机制是分散的这节中就主要分析其实现机制,实现源码比较容易理解就没有列举出全部源码

阅读(806) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~