📄 ch03s07.html
字号:
up(&dev->sem); return retval;}</pre></div><div class="sect2" lang="zh-cn"><div class="titlepage"><div><div><h3 class="title"><a name="ThewriteMethod.sect"></a>3.7.2. write 方法</h3></div></div></div><p>write, 象 read, 可以传送少于要求的数据, 根据返回值的下列规则:</p><div class="itemizedlist"><ul type="disc"><li><p>如果值等于 count, 要求的字节数已被传送.</p></li><li><p>如果正值, 但是小于 count, 只有部分数据被传送. 程序最可能重试写入剩下的数据.</p></li><li><p>如果值为 0, 什么没有写. 这个结果不是一个错误, 没有理由返回一个错误码. 再一次, 标准库重试写调用. 我们将在第 6 章查看这种情况的确切含义, 那里介绍了阻塞.</p></li><li><p>一个负值表示发生一个错误; 如同对于读, 有效的错误值是定义于 <linux/errno.h>中.</p></li></ul></div><p>不幸的是, 仍然可能有发出错误消息的不当行为程序, 它在进行了部分传送时终止. 这是因为一些程序员习惯看写调用要么完全失败要么完全成功, 这实际上是大部分时间的情况, 应当也被设备支持. scull 实现的这个限制可以修改, 但是我们不想使代码不必要地复杂.</p><p>write 的 scull 代码一次处理单个量子, 如 read 方法做的:</p><pre class="programlisting">ssize_t scull_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos){ struct scull_dev *dev = filp->private_data; struct scull_qset *dptr; int quantum = dev->quantum, qset = dev->qset; int itemsize = quantum * qset; int item, s_pos, q_pos, rest; ssize_t retval = -ENOMEM; /* value used in "goto out" statements */ if (down_interruptible(&dev->sem)) return -ERESTARTSYS; /* find listitem, qset index and offset in the quantum */ item = (long)*f_pos / itemsize; rest = (long)*f_pos % itemsize; s_pos = rest / quantum; q_pos = rest % quantum; /* follow the list up to the right position */ dptr = scull_follow(dev, item); if (dptr == NULL) goto out; if (!dptr->data) { dptr->data = kmalloc(qset * sizeof(char *), GFP_KERNEL); if (!dptr->data) goto out; memset(dptr->data, 0, qset * sizeof(char *)); } if (!dptr->data[s_pos]) { dptr->data[s_pos] = kmalloc(quantum, GFP_KERNEL); if (!dptr->data[s_pos]) goto out; } /* write only up to the end of this quantum */ if (count > quantum - q_pos) count = quantum - q_pos; if (copy_from_user(dptr->data[s_pos]+q_pos, buf, count)) { retval = -EFAULT; goto out; } *f_pos += count; retval = count; /* update the size */ if (dev->size < *f_pos) dev->size = *f_pos;out: up(&dev->sem); return retval;}</pre></div><div class="sect2" lang="zh-cn"><div class="titlepage"><div><div><h3 class="title"><a name="readvandwritev.sect"></a>3.7.3. readv 和 writev</h3></div></div></div><p>Unix 系统已经长时间支持名为 readv 和 writev 的 2 个系统调用. 这些 read 和 write 的"矢量"版本使用一个结构数组, 每个包含一个缓存的指针和一个长度值. 一个 readv 调用被期望来轮流读取指示的数量到每个缓存. 相反, writev 要收集每个缓存的内容到一起并且作为单个写操作送出它们.</p><p>如果你的驱动不提供方法来处理矢量操作, readv 和 writev 由多次调用你的 read 和 write 方法来实现. 在许多情况, 但是, 直接实现 readv 和 writev 能获得更大的效率.</p><p>矢量操作的原型是:</p><pre class="programlisting">ssize_t (*readv) (struct file *filp, const struct iovec *iov, unsigned long count, loff_t *ppos);ssize_t (*writev) (struct file *filp, const struct iovec *iov, unsigned long count, loff_t *ppos);</pre><p>这里, filp 和 ppos 参数与 read 和 write 的相同. iovec 结构, 定义于 <linux/uio.h>, 如同:</p><pre class="programlisting">struct iovec{ void __user *iov_base; __kernel_size_t iov_len;};</pre><p>每个 iovec 描述了一块要传送的数据; 它开始于 iov_base (在用户空间)并且有 iov_len 字节长. count 参数告诉有多少 iovec 结构. 这些结构由应用程序创建, 但是内核在调用驱动之前拷贝它们到内核空间.</p><p>矢量操作的最简单实现是一个直接的循环, 只是传递出去每个 iovec 的地址和长度给驱动的 read 和 write 函数. 然而, 有效的和正确的行为常常需要驱动更聪明. 例如, 一个磁带驱动上的 writev 应当将全部 iovec 结构中的内容作为磁带上的单个记录.</p><p>很多驱动, 但是, 没有从自己实现这些方法中获益. 因此, scull 省略它们. 内核使用 read 和 write 来模拟它们, 最终结果是相同的.</p></div></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="ch03s06.html">上一页</a> </td><td width="20%" align="center"><a accesskey="u" href="ch03.html">上一级</a></td><td width="40%" align="right"> <a accesskey="n" href="ch03s08.html">下一页</a></td></tr><tr><td width="40%" align="left" valign="top">3.6. scull 的内存使用 </td><td width="20%" align="center"><a accesskey="h" href="index.html">起始页</a></td><td width="40%" align="right" valign="top"> 3.8. 使用新设备</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 + -