[英]How to splice /dev/mem?
我有一个外部FPGA设备,它通过PCIe将大量数据转储到保留的(使用引导程序参数)连续的内存区域。 该存储区域将始终从同一位置开始。
我现在想尽快通过UDP转储该数据。 我不在乎检查这些数据,因此无需将其带入用户空间。 因此,我的研究表明使用零复制是最快/最好的方法。
我试图int memFd = open("/dev/mem", O_RDONLY);
,然后在sendfile
和splice
函数调用中使用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);
我的系统信息:
CONFIG_STRICT_DEVMEM
编译 其他有趣的事实:
CONFIG_STRICT_DEVMEM
编译的,因此我将1MB的限制归因于此。 mmap
到内存区域,然后查看FPGA正在写入的数据。 我的问题是:
splice
是正确的方法吗? 有人认为有更好的方法吗? splice
正确,那么任何人都不会知道这里会发生什么? 会有一个内核编译器标志阻止它起作用吗? 我正在从splice.c
读取源代码,但是它不是3.1.10版本,所以也许发生了什么变化? 无论哪种方式,看到此功能在VM中都能正常运行而在嵌入式环境中却无法正常运行,真是令人遗憾。 编辑:我从kernal.org下载了3.1.10源码,不幸的是,与使用不同版本的free-electrons.com所见的内容没有太大不同。 在我看来,所有的拼接代码都在/fs/splice.c中。 do_splice(...)
必须是要执行的代码。 我对splice
第一个调用(使用memFd
和filedes[1]
)应该放到if (opipe) {
...在这里您可以看到,如果copy_from_user
或copy_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
内存区域,然后使用正则write
或vmsplice
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.