繁体   English   中英

如何拼接/ dev / mem?

[英]How to splice /dev/mem?

我有一个外部FPGA设备,它通过PCIe将大量数据转储到保留的(使用引导程序参数)连续的内存区域。 该存储区域将始终从同一位置开始。

我现在想尽快通过UDP转储该数据。 我不在乎检查这些数据,因此无需将其带入用户空间。 因此,我的研究表明使用零复制是最快/最好的方法。

我试图int memFd = open("/dev/mem", O_RDONLY); ,然后在sendfilesplice函数调用中使用memFd ,但这些操作均失败。

花了几天的时间,但我终于在sendfile源中看到输入文件描述符必须是常规文件(据我所知,手册页中令人沮丧地遗漏了一个细节),并且/dev/mem不是常规文件。 无论如何,我环顾了四周,现在我有信心使用splice电话。

但是,这也以14-EFAULT的错误号失败,这意味着“错误地址”(同样令人沮丧的是,该错误代码未在splice手册页中提及)。 我查看了splice的源代码,可以看到几次返回EFAULT的地方,但是我只是看不到我传递的参数是如何引起问题的。

我的简化非错误检查代码如下;

 int filedes[2];
 int memFd = open("/dev/mem", O_RDONLY);
 int fileFd = open("myTestFile.txt", O_RDONLY);
 loff_t offset = START_OF_MEM_REGION;
 int sockFd = ConfigureMySocket();

 pipe(filedes);  // this returns 0, so the pipes are good

 int ret = splice(memFd, &offset, filedes[1], NULL, 128, SPLICE_F_MOVE); // this fails with EFAULT
 //int ret = splice(memFd, NULL, filedes[1], NULL, 128, 0); // this also fails with EFAULT
 //int ret = splice(fileFd, NULL, filedes[1], NULL, 128, 0); // this works just fine

 // this is never reached because the splice call above hangs. If I run the
 // fileFd splice call instead this works just fine
 ret = splice(filedes[0], NULL, sockFd, NULL, 128, 0);

我的系统信息:

  • 在ARM体系结构上运行linux 3.1.10的嵌入式设备
  • 以root用户身份运行
  • 内核未使用CONFIG_STRICT_DEVMEM编译

其他有趣的事实:

  • 我有一台2.6 linux CentOS虚拟机,此代码在〜1MB的偏移量下都能正常工作。 但是,此内核是使用CONFIG_STRICT_DEVMEM编译的,因此我将1MB的限制归因于此。
  • 我可以很好地mmap到内存区域,然后查看FPGA正在写入的数据。

我的问题是:

  1. 使用splice是正确的方法吗? 有人认为有更好的方法吗?
  2. 如果splice正确,那么任何人都不会知道这里会发生什么? 会有一个内核编译器标志阻止它起作用吗? 我正在从splice.c读取源代码,但是它不是3.1.10版本,所以也许发生了什么变化? 无论哪种方式,看到此功能在VM中都能正常运行而在嵌入式环境中却无法正常运行,真是令人遗憾。

编辑:我从kernal.org下载了3.1.10源码,不幸的是,与使用不同版本的free-electrons.com所见的内容没有太大不同。 在我看来,所有的拼接代码都在/fs/splice.c中。 do_splice(...)必须是要执行的代码。 我对splice第一个调用(使用memFdfiledes[1] )应该放到if (opipe) { ...在这里您可以看到,如果copy_from_usercopy_to_user失败..它们将返回EFAULT 不能有什么毛病我&offset变量,因为我得到了同样的错误,如果这是NULL ,如果我取代或无差错fileFd代替memFd 同样有趣的是,如果我将128替换为0(要写入的字节数),则不会有错误。 返回EFAULT的地方,我只是看不到文件描述符是如何加入该逻辑的,除非EFAULT被更深层的函数调用返回...

这些是来自splice.c的片段

SYSCALL_DEFINE6(splice, int, fd_in, loff_t __user *, off_in,
        int, fd_out, loff_t __user *, off_out,
        size_t, len, unsigned int, flags)
{
    long error;
    struct file *in, *out;
    int fput_in, fput_out;

    if (unlikely(!len))
        return 0;

    error = -EBADF;
    in = fget_light(fd_in, &fput_in);
    if (in) {
        if (in->f_mode & FMODE_READ) {
            out = fget_light(fd_out, &fput_out);
            if (out) {
                if (out->f_mode & FMODE_WRITE)
                    error = do_splice(in, off_in,
                              out, off_out,
                              len, flags);
                fput_light(out, fput_out);
            }
        }

        fput_light(in, fput_in);
    }

    return error;
}

static long do_splice(struct file *in, loff_t __user *off_in,
              struct file *out, loff_t __user *off_out,
              size_t len, unsigned int flags)
{
    struct pipe_inode_info *ipipe;
    struct pipe_inode_info *opipe;
    loff_t offset, *off;
    long ret;

    ipipe = get_pipe_info(in);
    opipe = get_pipe_info(out);

    if (ipipe && opipe) {
        if (off_in || off_out)
            return -ESPIPE;

        if (!(in->f_mode & FMODE_READ))
            return -EBADF;

        if (!(out->f_mode & FMODE_WRITE))
            return -EBADF;

        /* Splicing to self would be fun, but... */
        if (ipipe == opipe)
            return -EINVAL;

        return splice_pipe_to_pipe(ipipe, opipe, len, flags);
    }

    if (ipipe) {
        if (off_in)
            return -ESPIPE;
        if (off_out) {
            if (!(out->f_mode & FMODE_PWRITE))
                return -EINVAL;
            if (copy_from_user(&offset, off_out, sizeof(loff_t)))
                return -EFAULT;
            off = &offset;
        } else
            off = &out->f_pos;

        ret = do_splice_from(ipipe, out, off, len, flags);

        if (off_out && copy_to_user(off_out, off, sizeof(loff_t)))
            ret = -EFAULT;

        return ret;
    }

    if (opipe) {
        if (off_out)
            return -ESPIPE;
        if (off_in) {
            if (!(in->f_mode & FMODE_PREAD))
                return -EINVAL;
            if (copy_from_user(&offset, off_in, sizeof(loff_t)))
                return -EFAULT;
            off = &offset;
        } else
            off = &in->f_pos;

        ret = do_splice_to(in, off, opipe, len, flags);

        if (off_in && copy_to_user(off_in, off, sizeof(loff_t)))
            ret = -EFAULT;

        return ret;
    }

    return -EINVAL;
}

mmap内存区域,然后使用正则writevmsplice

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM