【问题标题】:Python writing into a mapped file - strange behaviourPython写入映射文件 - 奇怪的行为
【发布时间】:2020-03-26 07:38:39
【问题描述】:

我有一个可以成功将二进制数据写入文件的 Python 脚本:

iterable_array = [i + 32 for i in range(50)]
file_handle = open("a.bin", "wb")
bytes_to_write = bytearray(iterable_array)
file_handle.write(bytes_to_write)
file_handle.close()

但是,我收到以下错误:

Traceback (most recent call last):
File "python_100_chars.py", line 20, in <module>
file_handle = open("a.bin", "wb")
OSError: [Errno 22] Invalid argument: 'a.bin'

当我在执行以下程序(最初来自 Microsoft 文档)时尝试编写,该程序创建文件映射并在按键后读取数据:

HANDLE hFile = CreateFileA( "a.bin",
                            GENERIC_READ | GENERIC_WRITE,
                            FILE_SHARE_WRITE | FILE_SHARE_READ,
                            NULL,
                            CREATE_ALWAYS,
                            FILE_ATTRIBUTE_NORMAL,
                            NULL);
HANDLE hMapFile;
LPCTSTR pBuf;
hMapFile = CreateFileMapping(
             hFile,    // use paging file
             NULL,                    // default security
             PAGE_EXECUTE_READWRITE,          // read/write access
             0,                       // maximum object size (high-order DWORD)
             BUF_SIZE,                // maximum object size (low-order DWORD)
             szName);                 // name of mapping object

if (hMapFile == NULL)
{
   _tprintf(TEXT("Could not create file mapping object (%d).\n"),
          GetLastError());
   return 1;
}
pBuf = (LPTSTR) MapViewOfFile(hMapFile,   // handle to map object
                     FILE_MAP_ALL_ACCESS, // read/write permission
                     0,
                     0,
                     BUF_SIZE);
if (pBuf == NULL)
{
   _tprintf(TEXT("Could not map view of file (%d).\n"),
          GetLastError());

    CloseHandle(hMapFile);

   return 1;
}


 _getch(); 

 printf("string inside file:%s",(char *)((void *)pBuf));
UnmapViewOfFile(pBuf);

CloseHandle(hMapFile);

我已经测试过,我可以通过以下方式使用基本 I/O 写入内存映射文件(并查看结果):

HANDLE hFile =  CreateFileA(         "a.bin",
                               GENERIC_WRITE,
          FILE_SHARE_WRITE | FILE_SHARE_READ,
                                        NULL,
                               OPEN_EXISTING,
                      FILE_ATTRIBUTE_NORMAL ,
                                        NULL);
char *p = "bonjour";
DWORD bw;
WriteFile(   hFile,
                 p,
                 8,
               &bw,
               NULL);
  • python 脚本在做什么阻止它写入?

感谢您的任何反馈!

【问题讨论】:

  • 请注意,wb截断文件。也许你想做r+b
  • 我不确定 Python 在做什么,但我相信 w 可以转换为 CREATE_ALWAYS
  • 更改为r+b 已修复,谢谢!是的,它可能正在设置 CREATE_ALWAYS。我会尝试在编写的C程序中设置它,看看它是否会抛出任何错误。
  • 好的,正如人们所期望的那样:ERROR_USER_MAPPED_FILE (1224) 请求的操作无法在打开了用户映射部分的文件上执行。如果你把它变成一个答案,我会接受它顺便说一句:)
  • 已答复。我还添加了一个 POSIX 示例和解释,说明 python 程序可以在 Linux、OS X 等中正确运行。

标签: python c windows io shared-memory


【解决方案1】:

我没有 Windows,所以我无法完全测试行为,但我相信这是因为 Windows 不完全遵循 POSIX 语义,open(name, 'wb') 将代替截断现有文件,使用CREATE_ALWAYS 打开它,这将与在另一个进程中映射的文件冲突。 ab 模式也可以工作,但是......作为Python documentation says

'a' 用于追加(在某些 Unix 系统上,这意味着所有写入都追加到文件末尾,而不管当前查找位置如何)。

不幸的是,open 函数对标志使用 C 语义,因此无法将所需模式指定为“仅在不截断文件的情况下打开文件以进行随机访问写入”,因此您能做的最好的事情是“在不截断文件的情况下打开它进行读写”,即r+b


正如用户 Eryk Sun 指出的那样,如果存在任何内存映射,问题是 Windows 根本不支持 file truncation

如果 调用CreateFileMapping为hFile创建文件映射对象,必须先调用UnmapViewOfFile取消所有视图的映射并调用CloseHandle关闭文件映射对象才能调用SetEndOfFile

同样,没有比现有文件更大的空间映射 - 如果映射比文件大小长,则文件被扩展...


在 POSIXly 正确的平台上,类似的程序

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <time.h>
#include <unistd.h>


int main(int argc, char const *argv[])
{
    struct stat s;
    int fd = open("a.bin", O_RDWR);
    fstat(fd, &s);
    unsigned char *d = mmap(0, s.st_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
    getchar();

    for (int i = 0; i < s.st_size; i++) {
        putchar(d[i]);
    }

    putchar('\n');
    fflush(stdout);
    getchar();
}

不会干扰运行上述 Python 程序。但是,如果这个 C 程序在窗口内访问文件而该文件已被截断为 0,则会在 C 进程中引发 SIGBUS 信号。

【讨论】:

  • WINAPI CREATE_ALWAYS 对应于 NT FILE_OVERWRITE_IF,它要么截断现有文件,要么创建一个新文件——相比之下,WINAPI CREATE_NEW 或 NT FILE_CREATE 总是创建一个新文件文件,如果文件已经存在则失败。问题是 Windows(尤其是 WINAPI)不允许更改映射文件的大小,因此不允许截断文件(例如,请参阅SetEndOfFile)。
  • @ErykSun 没有在 all 处更改文件?
  • @ErykSun 哎呀,关键词 size 丢失了......即文件甚至无法扩展?
  • 如果调用 CreateFileMapping 为 hFile 创建文件映射对象,则必须先调用 UnmapViewOfFile 取消映射所有视图,并调用 CloseHandle 关闭文件映射对象,然后才能调用 SetEndOfFile。我>
  • 我记得不太正确。文件大小可以扩展,但不能缩小到小于映射大小。因此不允许截断,但允许扩展。我测试并确认映射文件可以通过 Python 文件方法 writetruncate(更大的大小)增长到超出映射大小,但尝试通过 truncate 减小大小失败并出现权限错误。
猜你喜欢
  • 2014-04-19
  • 2013-11-27
  • 1970-01-01
  • 2016-12-15
  • 1970-01-01
  • 2010-11-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多