【问题标题】:How to use regexec with memory mapped files?如何将 regexec 与内存映射文件一起使用?
【发布时间】:2012-06-14 16:35:45
【问题描述】:

我正在尝试在大型内存映射文件中查找正则表达式 通过使用 regexec() 函数。我发现程序在文件大小时崩溃 是页面大小的倍数。

有没有regexec()函数有字符串的长度 作为附加参数?

或者:

如何在内存映射文件中查找正则表达式?

这是总是崩溃的最小示例 (如果我运行少于 3 个线程程序不会崩溃):

ls -la ttt.txt 
-rwx------ 1 bob bob 409600 Jun 14 18:16 ttt.txt

gcc -Wall mal.c -o mal -lpthread -g && ./mal
[1]    11364 segmentation fault (core dumped)  ./mal

程序是:

#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>

#include <stdio.h>
#include <assert.h>
#include <pthread.h>
#include <regex.h>

void* f(void*arg) {
  int size = 409600;
  int fd = open("ttt.txt", O_RDONLY);
  char* text = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
  close(fd);

  fd = open("/dev/zero", O_RDONLY);
  char* end = mmap(text + size, 4096, PROT_READ, MAP_PRIVATE | MAP_FIXED, fd, 0);
  close(fd);

  assert(text+size == end);

  regex_t myre;
  regcomp(&myre, "XXXXX", REG_EXTENDED);
  regexec(&myre, text, 0, NULL, 0);
  regfree(&myre);
  return NULL;
}

int main(int argc, char* argv[]) {
  int n = 10;
  int i;
  pthread_t t[n];
  for (i = 0; i < n; ++i) {
    pthread_create(&t[n], NULL, f, NULL);
  }
  for (i = 0; i < n; ++i) {
    pthread_join(t[n], NULL);
  }
  return 0;
}

附: 这是 gdb 的输出:

gdb ./mal 
GNU gdb (Ubuntu/Linaro 7.4-2012.04-0ubuntu2) 7.4-2012.04
Copyright (C) 2012 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
For bug reporting instructions, please see:
<http://bugs.launchpad.net/gdb-linaro/>...
Reading symbols from /home/bob/prog/c/mal...done.
(gdb) r

Starting program: /home/srdjan/prog/c/mal 
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
[New Thread 0x7ffff77ff700 (LWP 11817)]
[New Thread 0x7ffff6ffe700 (LWP 11818)]
[New Thread 0x7ffff6799700 (LWP 11819)]
[New Thread 0x7fffeffff700 (LWP 11820)]

Program received signal SIGSEGV, Segmentation fault.
[Switching to Thread 0x7ffff6799700 (LWP 11819)]
__strlen_sse2 () at ../sysdeps/x86_64/multiarch/../strlen.S:72
72  ../sysdeps/x86_64/multiarch/../strlen.S: No such file or directory.
(gdb) bt
#0  __strlen_sse2 () at ../sysdeps/x86_64/multiarch/../strlen.S:72
#1  0x00007ffff78df254 in __regexec (preg=0x7ffff6798e80, string=0x7fffef79b000 'a' <repeats 200 times>..., nmatch=<optimized out>, 
pmatch=0x0, eflags=<optimized out>) at regexec.c:245
#2  0x00000000004008e6 in f (arg=0x0) at mal.c:24
#3  0x00007ffff7bc4e9a in start_thread (arg=0x7ffff6799700) at pthread_create.c:308
#4  0x00007ffff78f24bd in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:112
#5  0x0000000000000000 in ?? ()
(gdb) 

【问题讨论】:

  • open() 返回一个有效的 fd,mmap() 一个有效的指针,regcomp() 返回 0?

标签: c regex unix mmap


【解决方案1】:

Celada 正确识别问题 - 文件数据不一定包含空终止符。

您可以通过在文件后立即映射一页零来解决此问题:

int fd;
char *text;

fd = open("ttt.txt", O_RDONLY);
text = mmap(NULL, 409600, PROT_READ, MAP_PRIVATE, fd, 0);
close(fd);

fd = open("/dev/zero", O_RDONLY);
mmap(text + 409600, 4096, PROT_READ, MAP_PRIVATE | MAP_FIXED, fd, 0);
close(fd);

(请注意,您可以在mmap() 之后立即关闭fd,因为mmap() 添加了对打开文件描述的引用)。

您当然应该在上面添加错误检查。此外,许多 UNIX 系统支持 MAP_ANONYMOUS 标志,您可以使用它而不是打开 /dev/zero(但这不在 POSIX 中)。

【讨论】:

  • 我尝试了您的解决方案,它的工作线程少于 3 个。我将代码更改为使用 10 个线程,现在它几乎总是中断。我又添加了一个断言,以确保在 mmaped 文件之后分配空页面。
  • @user903597:大概有多个线程,它会破坏断言。您需要在两个mmap()s 周围锁定一个互斥锁,以确保来自两个线程的mmap()s 不会交错。
  • 如果紧跟在第一个mmap()'d 区域之后的页面恰好已经映射到某个东西,这种方法不会失败吗?
  • 正确的做法是预留足够的地址空间是第一个mmap调用,把使用MAP_FIXED映射的空白页放在里面。
【解决方案2】:

问题在于regexec() 用于将空终止字符串与预编译模式缓冲区进行匹配,但mmaped 文件不一定(实际上并非通常)空终止。因此,它会在文件末尾之外查找 NUL 字符(0 字节)。

您需要一个regexec() 版本,它接受一个缓冲区和一个大小参数,而不是一个以空值结尾的字符串,但似乎没有。

【讨论】:

  • 我知道空终止字符串的问题,因此我发布了这个问题。对我来说很奇怪,其他人没有在 C 中遇到这个问题。我试图找到解决方案但没有任何运气。
猜你喜欢
  • 1970-01-01
  • 2020-05-15
  • 1970-01-01
  • 2012-06-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-12-28
相关资源
最近更新 更多