异步信号与异步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有详细阐述,这里就不再赘言。 :em28: 这个东西感兴趣啊,先占楼,吃了晚饭来慢慢品。 很好阿,感谢哦阿阿阿
页:
[1]