【发布时间】:2025-12-25 14:20:11
【问题描述】:
我需要在嵌入式 Linux(2.6.37) 中尽可能快地将传入的 DMA 缓冲区写入 HD 分区作为原始设备 /dev/sda1。缓冲区按要求对齐,长度相等,为 512KB。该过程可能会持续很长时间并填充多达 256GB 的数据。 我需要使用内存映射文件技术(O_DIRECT 不适用),但无法理解如何执行此操作的确切方法。 所以,用伪代码“正常”写法:
fd=open(/dev/sda1",O_WRONLY);
while(1) {
p = GetVirtualPointerToNewBuffer();
if (InputStopped())
break;
write(fd, p, BLOCK512KB);
}
现在,我将非常感谢类似的伪/真实代码示例,说明如何在本文中使用内存映射技术。
更新2: 感谢 kestasx,最新的工作测试代码如下所示:
#define TSIZE (64*KB)
void* TBuf;
int main(int argc, char **argv) {
int fdi=open("input.dat", O_RDONLY);
//int fdo=open("/dev/sdb2", O_RDWR);
int fdo=open("output.dat", O_RDWR);
int i, offs=0;
void* addr;
i = posix_memalign(&TBuf, TSIZE, TSIZE);
if ((fdo < 1) || (fdi < 1)) {
printf("Error in files\n");
return -1; }
while(1) {
addr = mmap((void*)TBuf, TSIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fdo, offs);
if ((unsigned int)addr == 0xFFFFFFFFUL) {
printf("Error MMAP=%d, %s\n", errno, strerror(errno));
return -1; }
i = read(fdi, TBuf, TSIZE);
if (i != TSIZE) {
printf("End of data\n");
return 0; }
i = munmap(addr, TSIZE);
offs += TSIZE;
sleep(1);
};
}
更新3:
1. 为了精确模拟 DMA 工作,我需要将 read() 调用移到 mmp() 之前,因为当 DMA 完成时,它会为我提供它放置数据的地址。所以,在伪代码中:
while(1) {
read(fdi, TBuf, TSIZE);
addr = mmap((void*)TBuf, TSIZE, PROT_READ|PROT_WRITE, MAP_FIXED|MAP_SHARED, fdo, offs);
munmap(addr, TSIZE);
offs += TSIZE; }
此变体在(!)第一个循环后失败 - read() 在 TBuf 上显示 BAD ADDRESS。
在不完全理解我所做的事情的情况下,我将 munmap() 替换为 msync()。这非常有效。
那么,这里的问题——为什么取消映射地址会影响 TBuf?
2.通过前面的示例,我使用 DMA 进入了真实系统。与 read() 调用不同的是,相同的循环是等待 DMA 缓冲区准备好并提供其虚拟地址的调用。
没有错误,代码运行,但没有记录(!)。
我的想法是 Linux 没有看到该区域已更新,因此没有同步()一件事。
为了测试这一点,我在工作示例中消除了 read() 调用 - 是的,也没有记录任何内容。
所以,这里的问题 - 我如何告诉 Linux 映射区域包含新数据,请刷新它!
非常感谢!!!
【问题讨论】:
-
在更新的程序中我认为你不需要
posix_memalign(不需要分配内存)。调用mmap()后是否检查过addr 是否与TBuf 相同?我已将fdo更改为打开output.dat(预分配的64KB 零填充文件)并在mmap()之后分配TBuf=addr。有了这些修改程序对我来说工作正常。 -
@kestasx 是的,这个变量有效——这里的关键是 TBuf = addr;但这正是我做不到的,因为 TBuf 必须驻留在 DMA 传输的特殊内存中。在这个测试示例中它只被 read() 模仿...当我将 MAP_FIXED 添加到 mmap() 调用时,read(0 失败...
-
@kestasx 可能是我写的荒谬吗? TBuf 和 addr 都应该指向同一个物理内存,不是吗?
-
是的,在最终设置中,addr 和 TBuf 应该相同。至少这是一个想法。现在下一步是找出什么是障碍。一些技术细节可能会有所帮助(正在使用的硬件和操作系统)。
-
UPDATE3 有点不正确:如果您在 mmap 之前读取(进入相同的内存区域),您将失去您已阅读的内容。
msync()只是将 mmap() 内存页面推送到磁盘,但相同的文件区域保持映射(您需要在文件中前进)。你确定你没有 DMA 的例子真的可以正常工作(我怀疑它还不是:))?
标签: linux mmap memory-mapped-files dma