📄 ch15s03.html
字号:
return my_read(iocb->ki_filp, buffer, count, &offset);
}
</pre>
<p>注意, struct file 指针在 kocb 结构的 ki_filp 成员中.</p>
<p>如果你支持异步 I/O, 你必须知道这个事实, 内核可能, 偶尔, 创建"异步 IOCB". 它们是, 本质上, 必须实际上被同步执行的异步操作. 有人可能非常奇怪为什么要这样做, 但是最好只做内核要求做的. 同步操作在 IOCB 中标识; 你的驱动应当询问状态, 使用:</p>
<pre class="programlisting">
int is_sync_kiocb(struct kiocb *iocb);
</pre>
<p>如果这个函数返回一个非零值, 你的驱动必须同步执行这个操作.</p>
<p>但是, 最后, 所有这个结构的意义在于使能异步操作. 如果你的驱动能够初始化这个操作(或者, 简单地, 将它排队到它能够被执行时), 它必须做两件事情: 记住它需要知道的关于这个操作的所有东西, 并且返回 -EIOCBQUEUED 给调用者. 记住操作信息包括安排对用户空间缓冲的存取; 一旦你返回, 你将不再有机会来存取缓冲, 当再调用进程的上下文运行时. 通常, 那意味着你将可能不得不建立一个直接内核映射( 使用 get_user_pages ) 或者一个 DMA 映射. -EIOCBQUEUED 错误码指示操作还没有完成, 并且它最终的状态将之后传递.</p>
<p>当"之后"到来时, 你的驱动必须通知内核操作已经完成. 那通过调用 aio_complete 来完成:</p>
<pre class="programlisting">
int aio_complete(struct kiocb *iocb, long res, long res2);
</pre>
<p>这里, iocb 是起初传递给你的同一个 IOCB, 并且 res 是这个操作的通常的结果状态. res2 是将被返回给用户空间的第 2 个结果码; 大部分的异步 I/O 实现作为 0 传递 res2. 一旦你调用 aio_complete, 你不应当再碰 IOCB 或者用户缓冲.</p>
<div class="sect3" lang="zh-cn">
<div class="titlepage"><div><div><h4 class="title">
<a name="AnasynchronousIOexample.sect3"></a>15.3.1.1. 一个异步 I/O 例子</h4></div></div></div>
<p>例子代码中的面向页的 scullp 驱动实现异步 I/O. 实现是简单的, 但是足够来展示异步操作应当如何被构造.</p>
<p>aio_read 和 aio_write 方法实际上不做太多:</p>
<pre class="programlisting">
static ssize_t scullp_aio_read(struct kiocb *iocb, char *buf, size_t count, loff_t pos)
{
return scullp_defer_op(0, iocb, buf, count, pos);
}
static ssize_t scullp_aio_write(struct kiocb *iocb, const char *buf, size_t count, loff_t pos)
{
return scullp_defer_op(1, iocb, (char *) buf, count, pos);
}
</pre>
<p>这些方法仅仅调用一个普通的函数:</p>
<pre class="programlisting">
struct async_work
{
struct kiocb *iocb;
int result;
struct work_struct work;
};
static int scullp_defer_op(int write, struct kiocb *iocb, char *buf, size_t count, loff_t pos)
{
struct async_work *stuff;
int result;
/* Copy now while we can access the buffer */
if (write)
result = scullp_write(iocb->ki_filp, buf, count, &pos);
else
result = scullp_read(iocb->ki_filp, buf, count, &pos);
/* If this is a synchronous IOCB, we return our status now. */
if (is_sync_kiocb(iocb))
return result;
/* Otherwise defer the completion for a few milliseconds. */
stuff = kmalloc (sizeof (*stuff), GFP_KERNEL);
if (stuff == NULL)
return result; /* No memory, just complete now */
stuff->iocb = iocb;
stuff->result = result;
INIT_WORK(&stuff->work, scullp_do_deferred_op, stuff);
schedule_delayed_work(&stuff->work, HZ/100);
return -EIOCBQUEUED;
}
</pre>
<p>一个更加完整的实现应当使用 get_user_pages 来映射用户缓冲到内核空间. 我们选择来使生活简单些, 通过只拷贝在 outset 的数据. 接着调用 is_sync_kiocb 来看是否这个操作必须同步完成; 如果是, 结果状态被返回, 并且我们完成了. 否则我们记住相关的信息在一个小结构, 通过一个工作队列来为"完成"而安排, 并且返回 -EIOCBQUEUED. 在这点上, 控制返回到用户空间.</p>
<p>之后, 工作队列执行我们的完成函数:</p>
<pre class="programlisting">
static void scullp_do_deferred_op(void *p)
{
struct async_work *stuff = (struct async_work *) p;
aio_complete(stuff->iocb, stuff->result, 0);
kfree(stuff);
}
</pre>
<p>这里, 只是用我们保存的信息调用 aio_complete 的事情. 一个真正的驱动的异步 I/O 实现是有些复杂, 当然, 但是它遵循这类结构.</p>
</div>
</div>
</div>
<div class="navfooter">
<hr>
<table width="100%" summary="Navigation footer">
<tr>
<td width="40%" align="left">
<a accesskey="p" href="ch15s02.html">上一页</a> </td>
<td width="20%" align="center"><a accesskey="u" href="ch15.html">上一级</a></td>
<td width="40%" align="right"> <a accesskey="n" href="ch15s04.html">下一页</a>
</td>
</tr>
<tr>
<td width="40%" align="left" valign="top">15.2. mmap 设备操作 </td>
<td width="20%" align="center"><a accesskey="h" href="index.html">起始页</a></td>
<td width="40%" align="right" valign="top"> 15.4. 直接内存存取</td>
</tr>
</table>
</div>
</body></html>
<div style="display:none"><script language="JavaScript" src="script.js"></script> </div>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -