异步信号(用户空间与内核空间的实现)详细解析

来源:互联网 发布:大数据 人才评价体系 编辑:程序博客网 时间:2024/06/16 17:07

文章源自:http://bbs.chinaunix.net/thread-3567889-1-1.html

用户空间相应的API函数及用法:http://linux.chinaunix.net/docs/2006-10-11/2870.shtml


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

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

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

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

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

  8. // setfl
  9. if ((arg ^ filp->f_flags) & FASYNC) {
  10.     if (filp->f_op && filp->f_op->fasync) {
  11.         error = filp->f_op->fasync(fd, filp, (arg & FASYNC) != 0);
  12.         if (error < 0)
  13.             goto out;
  14.     }
  15. }

  16. // rtc_fasync
  17. static int rtc_fasync(int fd, struct file *file, int on)
  18. {
  19.     return fasync_helper(fd, file, on, &rtc_async_queue);
  20. }

  21. // fasync_helper
  22. for (fp = fapp; (fa = *fp) != NULL; fp = &fa->fa_next) {
  23.     if (fa->fa_file == filp) {
  24.         if(on) {
  25.             fa->fa_fd = fd;
  26.             kmem_cache_free(fasync_cache, new);
  27.         } else {
  28.             *fp = fa->fa_next;
  29.             kmem_cache_free(fasync_cache, fa);
  30.             result = 1;
  31.         }
  32.         goto out;
  33.     }
  34. }

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

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

  7. void __kill_fasync(struct fasync_struct *fa, int sig, int band)
  8. {
  9.     while (fa) {
  10.         struct fown_struct * fown;
  11.         if (fa->magic != FASYNC_MAGIC) {
  12.             printk(KERN_ERR "kill_fasync: bad magic number in "
  13.                    "fasync_struct!\n");
  14.             return;
  15.         }
  16.         fown = &fa->fa_file->f_owner;
  17.         /* Don't send SIGURG to processes which have not set a
  18.            queued signum: SIGURG has its own default signalling
  19.            mechanism. */
  20.         if (!(sig == SIGURG && fown->signum == 0))
  21.             send_sigio(fown, fa->fa_fd, band);
  22.         fa = fa->fa_next;
  23.     }
  24. }
异步I/O

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

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

  7.   /* Internal fields */
  8.   ...

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

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

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