使用mmap读取文件

5
我正在尝试通过使用内存映射同一文件来允许两个不同的进程通信。但是,我在这方面遇到了一些问题。我有一种感觉,这与我使用open()调用并将我的文件描述符传递给mmap的方式有关。
以下是我的代码,请问您是否发现任何问题?
对象1的代码:
 16     FILE* temp = fopen(theSharedFileName, "w");
 17     fseek(temp, fileSize-1, SEEK_SET);
 18     fprintf(temp, "0"); // make the file a certain size
 19     fseek(temp, 0, SEEK_CUR);
 20 
 21     int sharedFileName = fileno(temp);
 ...
 31     sharedArea = (MyStruct*)mmap(0, fileSize,
 32         PROT_READ | PROT_WRITE | PROT_EXEC, MAP_SHARED, sharedFileName, 0);

我使用“w”文件模式,因为对象1只会被创建一次,并且我希望它重置任何先前存在的数据。

对象2的代码:

 130     FILE* tempFile = fopen(sharedFileName, "a");
 131     int theFile = fileno(tempFile);
 ...
 135     sharedArea = (MyStruct*)mmap(NULL, fileSize,
 136         PROT_READ | PROT_WRITE | PROT_EXEC, MAP_SHARED, theFile, 0);

你遇到了什么确切的问题?你能保证 obj2 在 obj1 之前永远不会访问该文件吗? - dirkgently
是的,obj1更像是一个服务器,而obj2则类似于稍后将要启动的客户端。 - samoz
3个回答

27

几个问题:

  1. 避免混合使用高级I/O函数(如fopen(),fseek())和一些低级操作(如mmap())。虽然可以使用fileno()获取低级文件描述符,但这就像是走远路去到达同一个地方。而且,仅使用mmap()会破坏BSD和POSIX以外的兼容性,因此使用标准C I/O函数得不到任何好处。直接使用open()和lseek()即可。
  2. 在同一文件上使用流格式化I/O函数(如fprintf())和内存映射是没有意义的。当你内存映射文件时,你隐含地告诉系统你将把它用作随机访问(直接索引)数据。fprintf()是用于流式输出的,通常用于顺序访问。实际上,尽管可能存在,但很少看到fprintf()和fseek()在同一描述符中(这甚至不具有可移植性,但由于前面的项目,我并不考虑可移植性)。
  3. 保护方式必须与打开文件的保护方式相匹配。由于你向fopen()传递了"w",而向mmap()传递了“PROT_READ | PROT_WRITE | PROT_EXEC”,这违反了这个限制。这也突出了为什么不应该混合使用高级I/O和内存映射:你如何保证将以正确的标志打开文件?这应该是C库的“实现细节”。如果想以读写权限内存映射一个文件,则应该使用底层的open(theSharedFileName, O_RDWR)来打开文件。
  4. 不要同时使用PROT_WRITEPROT_EXEC。这既不具有可移植性,又存在安全风险。请阅读关于W^X可执行空间保护的内容。

1
这个回答的一些内容似乎是误导性的。我不明白为什么你认为 fprintffseek 不能在可移植的环境下一起使用。至于第三点和文件模式,POSIX非常明确地指定了 fopen 模式字符串如何转换为文件描述符模式;这并非是实现特定的。 - R.. GitHub STOP HELPING ICE

2

正如其他人所说,不要使用fopen()和相关函数。

你遇到的问题的一部分原因可能是由于fprintf()可以有流缓冲区,所以它可能实际上并没有改变文件,因此在期望时对其他进程可见。你可以添加fflush(),但read()和write()不会进行任何应用程序级别的缓冲,这也是它们更适合的原因之一。


2
如果你能使用C++和像ACEBoost这样的库,它们可以为你屏蔽低级细节并提供更容易的IPC抽象。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接