The write Method

文章讨论了Linux设备驱动中的write方法,指出它可能传输少于请求的数据,并详细解释了不同返回值的含义。同时提到了writev和readv系统调用,它们允许一次性处理多个数据缓冲区,提高效率。对于未实现向量操作的驱动,内核会使用多次读写调用来模拟。
摘要由CSDN通过智能技术生成

The write Method

write, like read, can transfer less data than was requested, according to the following rules for the return value: 根据以下返回值规则,写入与读取一样,可以传输比请求更少的数据:

If the value equals count, the requested number of bytes has been transferred. 如果该值等于 count,则请求的字节数已被传输。

If the value is positive, but smaller than count, only part of the data has been transferred. The program will most likely retry writing the rest of the data. 如果该值为正,但小于 count,则仅传输了部分数据。 该程序很可能会重试写入其余数据。

If the value is 0, nothing was written. This result is not an error, and there is no reason to return an error code. Once again, the standard library retries the call to write. We'll examine the exact meaning of this case in Chapter 6, where blocking write is introduced. 如果值为 0,则不写入任何内容。 这个结果不是错误,也没有理由返回错误代码。 再一次,标准库调用 write重试。 我们将在第 6 章研究这种情况的确切含义,其中介绍了阻塞写入。

A negative value means an error occurred; as for read, valid error values are those defined in <linux/errno.h>.负值表示发生错误; 至于read,有效的错误值是在 <linux/errno.h> 中定义的值。

Unfortunately, there may still be misbehaving programs that issue an error message and abort when a partial transfer is performed. This happens because some programmers are accustomed to seeing write calls that either fail or succeed completely, which is actually what happens most of the time and should be supported by devices as well. This limitation in the scull implementation could be fixed, but we didn't want to complicate the code more than necessary. 不幸的是,仍然可能有行为不端的程序发出错误消息并在执行部分传输时中止。 发生这种情况是因为一些程序员习惯于看到要么失败要么完全成功的写调用,这实际上是大多数时间发生的事情,也应该得到设备的支持。 scull 实现中的这个限制可以修复,但我们不想使代码过于复杂。

The scull code for write deals with a single quantum at a time, as the read method does: 用于 write 的 scull 代码一次处理一个量程,就像 read 方法一样:

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;

}

readv and writev

Unix systems have long supported two system calls named readv and writev. These "vector" versions of read and write take an array of structures, each of which contains a pointer to a buffer and a length value. A readv call would then be expected to read the indicated amount into each buffer in turn. writev, instead, would gather together the contents of each buffer and put them out as a single write operation. Unix 系统长期以来一直支持名为 readv 和 writev 的两个系统调用。 这些读写的“向量”版本采用结构数组,每个结构都包含一个指向缓冲区的指针和一个长度值。 然后,预计 readv 调用会依次将指示的数量读入每个缓冲区。 相反,writev 将收集每个缓冲区的内容并将它们作为单个写入操作输出。

If your driver does not supply methods to handle the vector operations, readv and writev are implemented with multiple calls to your read and write methods. In many situations, however, greater efficiency is acheived by implementing readv and writev directly. 如果您的驱动程序不提供处理向量操作的方法,则 readv 和 writev 通过多次调用您的 read 和 write 方法来实现。 然而,在许多情况下,通过直接实现 readv 和 writev 可以获得更高的效率。

The prototypes for the vector operations are: 向量操作的原型是:

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);

Here, the filp and ppos arguments are the same as for read and write. The iovec structure, defined in <linux/uio.h>, looks like: 此处,filp 和 ppos 参数与 read 和 write 的参数相同。 iovec 结构,在 <linux/uio.h> 中定义,如下所示:

struct iovec {

    void _  _user *iov_base;

    _ _kernel_size_t iov_len;

};

Each iovec describes one chunk of data to be transferred; it starts at iov_base (in user space) and is iov_len bytes long. The count parameter tells the method how many iovec structures there are. These structures are created by the application, but the kernel copies them into kernel space before calling the driver. 每个 iovec 描述了一个要传输的数据块; 它从 iov_base(在用户空间中)开始,长度为 iov_len 字节。 count 参数告诉方法有多少 iovec 结构。 这些结构由应用程序创建,但内核在调用驱动程序之前将它们复制到内核空间。

The simplest implementation of the vectored operations would be a straightforward loop that just passes the address and length out of each iovec to the driver's read or write function. Often, however, efficient and correct behavior requires that the driver do something smarter. For example, a writev on a tape drive should write the contents of all the iovec structures as a single record on the tape. 向量化操作的最简单实现是一个简单的循环,它只是将每个 iovec 的地址和长度传递给驱动程序的读取或写入函数。 然而,高效和正确的行为通常需要驱动程序做一些更聪明的事情。 例如,磁带驱动器上的 writev 应该将所有 iovec 结构的内容作为一条记录写入磁带。

Many drivers, however, gain no benefit from implementing these methods themselves. Therefore, scull omits them. The kernel emulates them with read and write, and the end result is the same. 然而,许多驱动程序并没有从自己实施这些方法中获益。 因此,scull 省略了它们。 内核通过读写来模拟它们,最终结果是一样的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

mounter625

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值