【问题标题】:need help understanding the read part of read_write.c需要帮助理解 read_write.c 的读取部分
【发布时间】:2016-12-11 07:53:25
【问题描述】:

您好,我希望有人可以帮助我理解 read_write.c 内核文件的读取部分,当我看到它时,我什么都不懂。

考虑到有几个调用读取函数的实例,我无法真正分辨出哪个部分实际上正在读取文件。我问是因为我必须知道在哪里修改它以及如何进行分配,我必须在不实际修改文件的情况下修改读取的输出。

顺便说一句,我使用的是 kernel.org 版本 4.9 的最新版本的 Linux 内核,感谢您的任何帮助。以下是我认为正在阅读的地方。

typedef ssize_t (*iter_fn_t)(struct kiocb *, struct iov_iter *);

const struct file_operations generic_ro_fops = {
.llseek     = generic_file_llseek,
.read_iter  = generic_file_read_iter,
.mmap       = generic_file_readonly_mmap,
.splice_read    = generic_file_splice_read,
};

static ssize_t do_iter_readv_writev(struct file *filp, struct iov_iter   *iter,
    loff_t *ppos, iter_fn_t fn, int flags)
{
    struct kiocb kiocb;
    ssize_t ret;

    if (flags & ~(RWF_HIPRI | RWF_DSYNC | RWF_SYNC))
        return -EOPNOTSUPP;

        init_sync_kiocb(&kiocb, filp);
    if (flags & RWF_HIPRI)
        kiocb.ki_flags |= IOCB_HIPRI;
    if (flags & RWF_DSYNC)
        kiocb.ki_flags |= IOCB_DSYNC;
    if (flags & RWF_SYNC)
        kiocb.ki_flags |= (IOCB_DSYNC | IOCB_SYNC);
        kiocb.ki_pos = *ppos;

    ret = fn(&kiocb, iter);
    BUG_ON(ret == -EIOCBQUEUED);
    *ppos = kiocb.ki_pos;
    return ret;
}

ssize_t rw_copy_check_uvector(int type, const struct iovec __user * uvector,
              unsigned long nr_segs, unsigned long fast_segs,
              struct iovec *fast_pointer,
              struct iovec **ret_pointer)
{
    unsigned long seg;
    ssize_t ret;
    struct iovec *iov = fast_pointer;

    /*
     * SuS says "The readv() function *may* fail if the iovcnt argument
     * was less than or equal to 0, or greater than {IOV_MAX}.  Linux has
     * traditionally returned zero for zero segments, so...
     */
    if (nr_segs == 0) {
        ret = 0;
        goto out;
    }

    /*
     * First get the "struct iovec" from user memory and
     * verify all the pointers
     */
    if (nr_segs > UIO_MAXIOV) {
        ret = -EINVAL;
        goto out;
    }
    if (nr_segs > fast_segs) {
        iov = kmalloc(nr_segs*sizeof(struct iovec), GFP_KERNEL);
        printk(KERN_DEBUG "Hello from read_write.c\n");
        printk(KERN_DEBUG "Inside the copy check uvector method\n");
        if (iov == NULL) {
            ret = -ENOMEM;
            goto out;
    }
    }
    if (copy_from_user(iov, uvector, nr_segs*sizeof(*uvector))) {
        ret = -EFAULT;
        goto out;
    }

    /*
     * According to the Single Unix Specification we should return EINVAL
     * if an element length is < 0 when cast to ssize_t or if the
     * total length would overflow the ssize_t return value of the
     * system call.
     *
     * Linux caps all read/write calls to MAX_RW_COUNT, and avoids the
     * overflow case.
     */
     ret = 0;
     for (seg = 0; seg < nr_segs; seg++) {
        void __user *buf = iov[seg].iov_base;
        ssize_t len = (ssize_t)iov[seg].iov_len;

        /* see if we we're about to use an invalid len or if
         * it's about to overflow ssize_t */
        if (len < 0) {
            ret = -EINVAL;
            goto out;
        }
        if (type >= 0
            && unlikely(!access_ok(vrfy_dir(type), buf, len))) {
            ret = -EFAULT;
            goto out;
        }
        if (len > MAX_RW_COUNT - ret) {
            len = MAX_RW_COUNT - ret;
            iov[seg].iov_len = len;
        }
        ret += len;
    }
out:
    *ret_pointer = iov;
    return ret;
}


/* Do it by hand, with file-ops */
static ssize_t do_loop_readv_writev(struct file *filp, struct iov_iter *iter,
    loff_t *ppos, io_fn_t fn, int flags)
{
    ssize_t ret = 0;

    if (flags & ~RWF_HIPRI)
        return -EOPNOTSUPP;

    while (iov_iter_count(iter)) {
            struct iovec iovec = iov_iter_iovec(iter);
            ssize_t nr;

            nr = fn(filp, iovec.iov_base, iovec.iov_len, ppos);

            if (nr < 0) {
                if (!ret)
                    ret = nr;
                break;
            }
            ret += nr;
            if (nr != iovec.iov_len)
                break;
            iov_iter_advance(iter, nr);
        }

        return ret;
}

static ssize_t do_readv_writev(int type, struct file *file,
                               const struct iovec __user * uvector,
                               unsigned long nr_segs, loff_t *pos,
                               int flags)
{
    size_t tot_len;
    struct iovec iovstack[UIO_FASTIOV];
    struct iovec *iov = iovstack;
    struct iov_iter iter;
    ssize_t ret;
    io_fn_t fn;
    iter_fn_t iter_fn;

    ret = import_iovec(type, uvector, nr_segs,
                       ARRAY_SIZE(iovstack), &iov, &iter);
    if (ret < 0)
        return ret;

    tot_len = iov_iter_count(&iter);
    if (!tot_len)
        goto out;
    ret = rw_verify_area(type, file, pos, tot_len);
    if (ret < 0)
        goto out;

    if (type == READ) {
        fn = file->f_op->read;
        iter_fn = file->f_op->read_iter;
    } else {
        fn = (io_fn_t)file->f_op->write;
        iter_fn = file->f_op->write_iter;
        file_start_write(file);
    }

    if (iter_fn)
        ret = do_iter_readv_writev(file, &iter, pos, iter_fn, flags);
    else
        ret = do_loop_readv_writev(file, &iter, pos, fn, flags);

    if (type != READ)
        file_end_write(file);

    out:
        kfree(iov);
        if ((ret + (type == READ)) > 0) {
            if (type == READ)
                fsnotify_access(file);
            else
                fsnotify_modify(file);
        }
        return ret;
}

ssize_t vfs_readv(struct file *file, const struct iovec __user *vec,
                  unsigned long vlen, loff_t *pos, int flags)
{
    if (!(file->f_mode & FMODE_READ))
        return -EBADF;
    if (!(file->f_mode & FMODE_CAN_READ))
        return -EINVAL;

    return do_readv_writev(READ, file, vec, vlen, pos, flags);
}

EXPORT_SYMBOL(vfs_readv);
static ssize_t do_readv(unsigned long fd, const struct iovec __user *vec,
                        unsigned long vlen, int flags)
{
    struct fd f = fdget_pos(fd);
    ssize_t ret = -EBADF;

    if (f.file) {
        loff_t pos = file_pos_read(f.file);
        ret = vfs_readv(f.file, vec, vlen, &pos, flags);
        if (ret >= 0)
            file_pos_write(f.file, pos);
        fdput_pos(f);
    }

    if (ret > 0)
        add_rchar(current, ret);
    inc_syscr(current);
    return ret;
}

static ssize_t do_preadv(unsigned long fd, const struct iovec __user *vec,
                         unsigned long vlen, loff_t pos, int flags)
{
    struct fd f;
    ssize_t ret = -EBADF;

    if (pos < 0)
        return -EINVAL;

    f = fdget(fd);
    if (f.file) {
        ret = -ESPIPE;
        if (f.file->f_mode & FMODE_PREAD)
            ret = vfs_readv(f.file, vec, vlen, &pos, flags);
        fdput(f);
    }

    if (ret > 0)
        add_rchar(current, ret);
    inc_syscr(current);
    return ret;
}


static ssize_t compat_do_readv_writev(int type, struct file *file,
               const struct compat_iovec __user *uvector,
               unsigned long nr_segs, loff_t *pos,
               int flags)
{
    compat_ssize_t tot_len;
    struct iovec iovstack[UIO_FASTIOV];
    struct iovec *iov = iovstack;
    struct iov_iter iter;
    ssize_t ret;
    io_fn_t fn;
    iter_fn_t iter_fn;

    ret = compat_import_iovec(type, uvector, nr_segs,
              UIO_FASTIOV, &iov, &iter);
    if (ret < 0)
        return ret;

    tot_len = iov_iter_count(&iter);
    if (!tot_len)
        goto out;
        ret = rw_verify_area(type, file, pos, tot_len);
    if (ret < 0)
        goto out;

    if (type == READ) {
        fn = file->f_op->read;
        iter_fn = file->f_op->read_iter;
    } else {
            fn = (io_fn_t)file->f_op->write;
            iter_fn = file->f_op->write_iter;
            file_start_write(file);
    }

    if (iter_fn)
        ret = do_iter_readv_writev(file, &iter, pos, iter_fn, flags);
    else
        ret = do_loop_readv_writev(file, &iter, pos, fn, flags);

    if (type != READ)
        file_end_write(file);

    out:
        kfree(iov);
        if ((ret + (type == READ)) > 0) {
            if (type == READ)
                fsnotify_access(file);
            else
                fsnotify_modify(file);
        }
        return ret;
}

static size_t compat_readv(struct file *file,
           const struct compat_iovec __user *vec,
           unsigned long vlen, loff_t *pos, int flags)
{
    ssize_t ret = -EBADF;

    if (!(file->f_mode & FMODE_READ))
       goto out;

    ret = -EINVAL;
    if (!(file->f_mode & FMODE_CAN_READ))
        goto out;

    ret = compat_do_readv_writev(READ, file, vec, vlen, pos, flags);

     out:
        if (ret > 0)
            add_rchar(current, ret);
        inc_syscr(current);
        return ret;
}

static size_t do_compat_readv(compat_ulong_t fd,
             const struct compat_iovec __user *vec,
             compat_ulong_t vlen, int flags)
{
    struct fd f = fdget_pos(fd);
    ssize_t ret;
    loff_t pos;

    if (!f.file)
        return -EBADF;
    pos = f.file->f_pos;
    ret = compat_readv(f.file, vec, vlen, &pos, flags);
    if (ret >= 0)
        f.file->f_pos = pos;
    fdput_pos(f);
    return ret;

}

COMPAT_SYSCALL_DEFINE3(readv, compat_ulong_t, fd,
        const struct compat_iovec __user *,vec,
        compat_ulong_t, vlen)
{
    return do_compat_readv(fd, vec, vlen, 0);
}

static long do_compat_preadv64(unsigned long fd,
              const struct compat_iovec __user *vec,
              unsigned long vlen, loff_t pos, int flags)
{
    struct fd f;
    ssize_t ret;

    if (pos < 0)
        return -EINVAL;
    f = fdget(fd);
    if (!f.file)
        return -EBADF;
    ret = -ESPIPE;
        if (f.file->f_mode & FMODE_PREAD)
            ret = compat_readv(f.file, vec, vlen, &pos, flags);
        fdput(f);
        return ret;
}

【问题讨论】:

  • 如果你想吸引更多的读者,你应该对你的代码应用一些合适的格式。缺乏适当的缩进使得它真的很难阅读。
  • @Gerhardh:我完成了这项工作。 Linus 坚持使用原来的 8 个字符宽度的 TAB。因此,由于大多数当前系统将 TAB 扩展到 4 个空格,并且发布的代码更加混乱。
  • 我编辑并添加了我认为相关的部分,希望这能让大家更清楚,谢谢。

标签: c linux io linux-kernel ubuntu-16.04


【解决方案1】:

除了格式错误之外,您还可以轻松地看到do_readvdo_preadv 都调用vfs_readv。在这些函数中,没有看到它们自己进行一些阅读的提示。

您还可以看到vfs_readv 不做任何读取,而只调用do_readv_writev

实际阅读在这里完成:

if (type == READ) {
  fn = file->f_op->read;
  iter_fn = file->f_op->read_iter;
} 
...

if (iter_fn)
  ret = do_iter_readv_writev(file, &iter, pos, iter_fn, flags);
else
  ret = do_loop_readv_writev(file, &iter, pos, fn, flags);

嗯,这不是实际读数,但它是您可以从代码 sn-p 中获得的最接近读数。 这些函数中发生了什么,更重要的是,存储在 iter_fnfn 中的内容在您的代码中是不可见的。

我不是 Linux 专家,无法告诉你更多细节。

【讨论】:

  • 我添加了更多代码,我认为在您告诉我的内容之后,如果您愿意,可以再看一遍。我非常感谢您对此事有任何进一步的见解。
  • 对不起,这对我来说太深入了 Linux 内核。我只是在研究 C 相关部分的问题,但您的问题不是语言问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-04-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-06-15
相关资源
最近更新 更多