如何在mmap()返回的字符串中缺少NULL终止符?

时间:2014-11-24 01:58:48

标签: c linux unix posix mmap

当mmap()文本文件时,如此

int fd = open("file.txt", O_RDWR);
fstat(fd, &sb)
char *text = mmap(0, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);

文件内容直接映射到内存中,text它不包含NUL终结符,因此使用普通字符串函数对其进行操作将不安全。在Linux(至少)上,未使用页面的剩余字节是零填充的,因此在文件大小不是页面大小的所有情况下,有效地获得NUL终止符。

但依赖于那种感觉很脏并且其他mmap()实现(例如,在FreeBSD中,我认为)不会零填充部分页面。映射文件是页面大小的倍数也将缺少NUL终结符。

有合理的解决方法或添加NUL终止符吗?

我考虑过的事情

  1. 仅使用strn*()个函数并跟踪到缓冲区末尾的距离。
    • 优点:不需要NUL终结者
    • 缺点:解析文本时需要知道距离文件末尾的额外跟踪;某些str*()函数没有strn*()对应的内容,例如strstr
  2. 建议another answer,在映射文本文件后,在固定地址进行匿名映射。
    • 优点:可以使用常规的C str*()函数
    • 缺点:使用MAP_FIXED不是线程安全的;无论如何,似乎是一个可怕的黑客
  3. mmap()一个额外的字节,使地图可写,并写入NUL终结符。 OpenGroup的mmap man page表示您可以使映射大于对象的大小,但访问实际映射对象之外的数据将生成SIGBUS
    • 优点:可以使用常规的C str*()函数
    • 缺点:需要处理(忽略?)SIGBUS,这可能意味着发生了其他事情。我真的不确定写NUL终结器会起作用吗?
  4. 将尺寸为页面大小倍数的文件展开ftruncate()一个字节。
    • 优点:可以使用常规的C str*()函数; ftruncate()会将NUL字节写入新分配的区域
    • 缺点:意味着我们必须写入文件,这在所有情况下都是不可能或不可接受的;不解决不填充部分页面的mmap()实现的问题
  5. 只需read()将文件放入malloc()内存,忘记mmap()
    • 优点:避免所有这些解决方案; NUL
    • 易于malloc()和额外字节
    • 缺点:与mmap()
    • 不同的性能特征
  6. 解决方案#1似乎通常是最好的,只需要阅读文本功能的一些额外工作。

    是否有更好的替代方案,或者这些是最佳解决方案?我没有考虑过这些解决方案的某些方面是否会使它们或多或少具有吸引力?

1 个答案:

答案 0 :(得分:3)

我建议在这里进行范式转换。

您正在查看整个Universe,其中包含定义文本的'\ 0'分隔字符串。不要以这种方式看世界,为什么不尝试查看将文本定义为由开始和结束迭代器定义的序列的世界。

mmap您的文件,然后最初设置开始迭代器,将其称为beg_iter到mmap-ed段的开头,并将结束迭代器称为end_iter,以mmap-ed段中最后一个字节后面的第一个字节,或beg_iter+number_of_pages*pagesize,然后直到

A)end_iter等于beg_iter

B)beg_iter[-1]不是空字符,那么

C)递减end_iter,然后返回步骤A.

完成后,您将拥有一对迭代器,开始迭代器值以及定义文本字符串的结束迭代器值。

当然,在这种情况下,你的迭代器是普通的char *,但这真的不是很重要。重要的是,现在您可以使用C ++标准库中的一组丰富的算法和模板,这可以让您实现许多复杂的操作,包括可变的(如std::transform)和非可变的, (如std::find)。

Null终止的字符串实际上是普通C语言的延续。对于C ++,以null结尾的字符串有些过时,而且平凡。现代C ++代码应使用std::string个对象,以及由开始和结束迭代器定义的序列。

一个小脚注:你可能会发现更容易fstat()文件,并且得到文件的确切长度(以字节为单位),而不是弄清楚你在mmap-ing()中填充了多少NULL填充,在mmap之前。然后你现在确切地知道很多已经被mmaped,你不必通过查看填充来进行逆向工程。