我需要尽可能快地将嵌入式Linux(2.6.37)写入到HD分区作为原始设备/ dev / sda1。缓冲区根据需要对齐,长度相等,为512KB。该过程可以持续很长时间并且填充例如256GB的数据。 我需要使用内存映射文件技术(O_DIRECT不适用),但不能理解如何执行此操作的确切方法。 所以,在伪代码"正常"写作:
fd=open(/dev/sda1",O_WRONLY);
while(1) {
p = GetVirtualPointerToNewBuffer();
if (InputStopped())
break;
write(fd, p, BLOCK512KB);
}
现在,我将非常感谢有关如何利用内存映射技术进行写作的类似伪/实际代码示例。
UPDATE2: 感谢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);
};
}
UPDATE3:
1.为了精确模仿DMA工作,我需要在mmp()之前调用read(),因为当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。
如果不完全理解我的做法,我用msync()替换了munmap()。这非常有效。
所以,这里的问题 - 为什么取消映射addr会影响TBuf?
2.前面的例子工作我用DMA去了真正的系统。相同的循环,而不是read()调用是等待DMA缓冲区准备好并提供其虚拟地址的调用。
没有错误,代码运行,但没有记录(!)。
我的想法是Linux没有看到该区域已更新,因此不会同步()一件事
为了测试这个,我在工作示例中删除了read()调用 - 是的,也没有记录任何内容。
所以,这里的问题 - 如何告诉Linux映射区域包含新数据,请冲洗它!
非常感谢!!!
答案 0 :(得分:0)
如果我正确理解,如果您mmap()
文件(不确定它是否可以mmap()
原始分区/块设备)并且通过DMA的数据直接写入此内存区域,则有意义。
为此工作您需要能够控制p
(放置新缓冲区的位置)或地址文件的地址。如果你没有 - 你必须复制内存内容(并且会失去mmap的一些好处)。
所以psudo代码是:
truncate("data.bin", 256GB);
fd = open( "data.bin", O_RDWR );
p = GetVirtualPointerToNewBuffer();
adr = mmap( p, 1GB, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset_in_file );
startDMA();
waitDMAfinish();
munmap( adr, 1GB );
这只是第一步,我不完全确定它是否适用于DMA(没有这样的经验)。
我认为它是32位系统,但即使这样,1GB映射文件的大小也可能太大(如果你的RAM较小,你将会交换)。
如果此设置可行,则下一步是循环以映射不同偏移量的文件区域,并取消已经填充的区域。
您很可能需要将addr
与4KB边界对齐。
当您取消映射区域时,它的数据将同步到磁盘。因此,您需要进行一些测试以选择适当的映射区域大小(而下一个区域由DMA填充,必须有足够的时间来取消映射/写入前一个区域。)
更新:
当您通过DMA填写mmap&#39; ed区域时到底发生了什么?我根本不知道(不确定如何检测到脏页:硬件做了什么,以及什么做了什么必须由软件完成。)
UPDATE2:据我所知:
DMA以下列方式工作:
这似乎很简单,而不涉及虚拟内存:DMA应独立于运行进程(CPU使用的实际VM表)工作。然而,由DMA物理RAM页面修改的CPU缓存无效应该是一些误解(不知道CPU是否需要做某事,或者它是由硬件自动完成的)。
mmap()
以下列方式分叉:
mmap()
后,磁盘上的文件附加处理内存范围(很可能在OS内核中填充了一些数据结构来保存此信息); msync()
或munmap()
理论上应该可以将DMA传输到mmaped范围,但是你需要找出,如何准确地将页面标记为 dirty (如果你需要做一些事情来通知内核哪些页面需要写入磁盘)。
UPDATE3:
即使被DMA修改了页面也没有标记为 dirty ,你应该能够通过重写(读取ant然后写入相同的)每个页面至少有一个值来标记标记(最有可能每个4KB)转移。只需确保编译器不会删除(优化)重写。
UPDATE4:
似乎文件已打开O_WRONLY无法进行mmap编辑(请参阅问题评论,我的实验也证实了这一点)。这是上述mmap()
工作的逻辑结论。确认相同here(参考POSIX标准要求,确保文件可读,无论maping保护标志如何)。
除非有某种方法,否则实际上意味着使用mmap()
您无法阅读结果文件(在您的情况下不必要的步骤)。
关于DMA传输到映射范围,我认为在DMA启动之前确保maped页面是preloalocated是必需的(所以真实内存分配给DMA和maped区域)。在Linux上有MAP_POPULATE mmap标志,但是从手动它接缝它只适用于MAP_PRIVATE映射(更改没有写入磁盘),因此很可能它是可用的。可能你必须通过访问每个maped页面来手动删除页面故障。这应该可以阅读结果文件。
如果你仍然希望一起使用mmap和DMA,但是避免读取结果文件,你必须修改内核内部以允许mmap使用O_WRONLY文件(例如通过零填充三角形页面,而不是从磁盘读取它们。