alex__nuaa 发表于 2011-07-19 18:04

异步信号与异步I/O浅析

本帖最后由 alex__nuaa 于 2011-07-19 18:10 编辑

原文链接:http://wahaha02.info/archives/73.html

异步信号

使用信号可以实现设备驱动与用户程序之间的异步通知。为达到此目的:

   1. 用户空间需要设置设备文件的拥有者、FASYNC标志及捕获信号;
   2. 内核空间需响应对设备文件的拥有者、FASYNC标志的设置,并在资源可获得时释放信号。

1. 设置设备文件的拥有者

用户空间:fcntl(STDIN_FILE, F_SETOWN, getpid());设置设备文件的拥有者为本进程
内核空间:设置设备文件filp的f_owner的pid等相关信息。此部分由内核实现,设备驱动无须处理。
// in f_setown
filp->f_owner.pid = pid;
filp->f_owner.uid = uid;
filp->f_owner.euid = euid;2. 启用异步通知机制

用户空间:fcntl(STDIN_FILE, F_SETFL, old_flags | FASYNC);
内核空间:设置设备文件支持异步通知模式。
通过调用驱动file_operations函数fasync,设置异步模式。fasync内部会调用fasync_helper,来更新/添加异步通知链表rtc_async_queue。struct fasync_struct {
    int    magic;
    int    fa_fd;
    struct    fasync_struct    *fa_next; /* singly linked list */
    struct    file         *fa_file;
};
static struct fasync_struct *rtc_async_queue;

// setfl
if ((arg ^ filp->f_flags) & FASYNC) {
    if (filp->f_op && filp->f_op->fasync) {
      error = filp->f_op->fasync(fd, filp, (arg & FASYNC) != 0);
      if (error < 0)
            goto out;
    }
}

// rtc_fasync
static int rtc_fasync(int fd, struct file *file, int on)
{
    return fasync_helper(fd, file, on, &rtc_async_queue);
}

// fasync_helper
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;
    }
}

if (on) {
    new->magic = FASYNC_MAGIC;
    new->fa_file = filp;
    new->fa_fd = fd;
    new->fa_next = *fapp;
    *fapp = new;
    result = 1;
}
3. 释放/捕获异步信号

用户空间:signal(SIGIO, input_handler); 设置信号捕获处理函数。
内核空间:释放信号
首先 rtc_probe调用request_irq分配中断资源,设置中断处理函数elapsedtime_interrupt,中断函数内部再调用 kill_fasync,遍历异步通知链表rtc_async_queue,向匹配的f_owner进程发送异步信号(POLL_IN)。// rtc_probe
retval = request_irq(irq, elapsedtime_interrupt, SA_INTERRUPT,
                         "elapsed_time", NULL);
                        
// elapsedtime_interrupt                        
kill_fasync(&rtc_async_queue, SIGIO, POLL_IN);      

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;
    }
}异步I/O

AIO通过aiocb来标示。这个结构包含了有关传输的所有信息,包括为数据准备的用户缓冲区。当I/O 完成时,aiocb 结构就被用来惟一标识所完成的 I/O 操作。struct aiocb {

int aio_fildes;               // File Descriptor
int aio_lio_opcode;         // Valid only for lio_listio (r/w/nop)
volatile void *aio_buf;       // Data Buffer
size_t aio_nbytes;            // Number of Bytes in Data Buffer
struct sigevent aio_sigevent; // Notification Structure

/* Internal fields */
...

};异步读写通过aio_read/aio_write,这2个函数在请求进行排队之后会立即返回,所以需要aio_error/aio_return来检查/获取异步请求的状态。
如果不借助异步通知,我们就需要一直检查aio_error来判断AIO的状态,如ret = aio_read( &my_aiocb );
if (ret < 0) perror("aio_read");

while ( aio_error( &my_aiocb ) == EINPROGRESS ) ;

if ((ret = aio_return( &my_iocb )) > 0) {
    /* got ret bytes on the read */
} else {
    /* read failed, consult errno */
}异步通知有2中方法:信号或者回调函数。当AIO完成时,这种机制通过产生一个信号,或者调用用户空间的一个函数来实现异步通知功能。Boost application performance using asynchronous I/O有详细阐述,这里就不再赘言。

alex__nuaa 发表于 2011-07-20 21:34

:em28:

almeydifer 发表于 2011-07-21 16:16

这个东西感兴趣啊,先占楼,吃了晚饭来慢慢品。

jp1017 发表于 2015-01-12 15:34

很好阿,感谢哦阿阿阿
页: [1]
查看完整版本: 异步信号与异步I/O浅析