尝试使用mmap搜索大文件中的模式。文件很大(比物理内存多)。我担心的是,如果我使用文件大小作为mmap()的第二个参数,那么就不会有足够的物理内存来满足系统调用。所以我使用0x1000作为长度,希望操作系统在我的指针移动时自动映射文件的右侧部分。但是下面的代码片段给出了分段错误。
有什么想法吗?
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
long fileSize(char *fname) {
struct stat stat_buf;
int rc = stat(fname, &stat_buf);
return rc == 0 ? stat_buf.st_size : -1;
}
int main(int argc, char *argv[]) {
long size = fileSize(argv[1]);
printf("size=%ld\n", size);
int fd = open(argv[1], O_RDONLY);
printf("fd=%d\n", fd);
char *p = mmap(0, 0x1000, PROT_READ, MAP_SHARED, fd, 0);
if (p == MAP_FAILED) {
perror ("mmap");
return 1;
}
long i;
int pktLen;
int *pInt;
for (i=0; i < size; i+=4) {
pInt = (int*)(p+i);
if (pInt[i] == 0x12345678) {
printf("found it at %ld\n", i); break;
}
}
if (i == size) {
printf("didn't find it\n");
}
close(fd);
return 0;
}
更新 原来我有一个愚蠢的错误
这条线
if (pInt[i] == 0x12345678)
应该是if (pInt[0] == 0x12345678)
答案 0 :(得分:2)
使用
instance MonadTrans (StateT s) where
lift = ...
returnT = ...
bindT = ...
...
instance (MonadTrans t, Monad m) => Monad (t m) where
return = returnT
a >>= b = a `bindT` b
上面代码中的重点:
您需要使用例如
启动代码 struct stat info;
long page;
const char *map;
size_t size, mapping;
int fd, result;
page = sysconf(_SC_PAGESIZE);
if (page < 1L) {
fprintf(stderr, "Invalid page size.\n");
exit(EXIT_FAILURE);
}
fd = open(filename, O_RDONLY);
if (fd == -1) {
fprintf(stderr, "%s: Cannot open file: %s.\n", filename, strerror(errno));
exit(EXIT_FAILURE);
}
result = fstat(fd, &info);
if (result == -1) {
fprintf(stderr, "%s: Cannot get file information: %s.\n", filename, strerror(errno));
close(fd);
exit(EXIT_FAILURE);
}
if (info.st_size <= 0) {
fprintf(stderr, "%s: No data.\n", filename);
close(fd);
exit(EXIT_FAILURE);
}
size = info.st_size;
if ((off_t)size != info.st_size) {
fprintf(stderr, "%s: File is too large to map.\n", filename);
close(fd);
exit(EXIT_FAILURE);
}
/* mapping is size rounded up to a multiple of page. */
if (size % (size_t)page)
mapping = size + page - (size % (size_t)page);
else
mapping = size;
map = mmap(NULL, mapping, PROT_READ, MAP_SHARED | MAP_NORESERVE, fd, 0);
if (map == MAP_FAILED) {
fprintf(stderr, "%s: Cannot map file: %s.\n", filename, strerror(errno));
close(fd);
exit(EXIT_FAILURE);
}
if (close(fd)) {
fprintf(stderr, "%s: Unexpected error closing file descriptor.\n", filename);
exit(EXIT_FAILURE);
}
/*
* Use map[0] to map[size-1], but remember that it is not a string,
* and that there is no trailing '\0' at map[size].
*
* Accessing map[size] to map[mapping-1] is not allowed, and may
* generate a SIGBUS signal (and kill the process).
*/
/* The mapping is automatically torn down when the process exits,
* but you can also unmap it with */
munmap(map, mapping);
#define _POSIX_C_SOURCE 200809L
#define _BSD_SOURCE
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
需要_BSD_SOURCE
才能定义,即使它是GNU / Linux特有的功能。
MAP_NORESERVE
(man 2 mmap
中的mapping
)必须是页面大小的倍数(length
)。
sysconf(_SC_PAGESIZE)
标志告诉内核该映射仅由文件支持,因此允许大于可用RAM + SWAP。
您可以(但不需要)关闭引用映射文件的文件描述符而不会出现问题,因为映射本身包含内核中的引用。
多年前,在另一个论坛上,我使用这种方法显示a simple program to manipulate a terabyte of data(1 TiB = 1,099,511,627,776字节)(尽管它使用稀疏后备文件;即大多数是隐式零,实际小于250 MB写入备份文件的数据 - 主要是为了减少所需的磁盘空间量。当然,它需要运行Linux的64位机器,因为32位机器上的虚拟内存限制为2 32 = 4 GiB(Linux不支持分段内存模型)。
Linux内核在选择保留在RAM中的页面以及要逐出的页面方面效率惊人。当然,通过使用posix_madvise(address, length, advice)
MAP_NORESERVE
advice
告诉内核您不可能访问(因此可以驱逐)映射的哪些部分,您可以提高效率。 }或POSIX_MADV_DONTNEED
。这样做的好处是,与取消映射“dontneed”部分不同,如果需要,您可以重新访问映射的那一部分。 (如果页面已被逐出,则对映射的访问将直到页面被重新加载到内存中为止。换句话说,您可以使用POSIX_MADV_WILLNEED
来“优化”逐出逻辑,而不限制其中的哪一部分可以访问映射。)
在您的情况下,如果您使用数据对数据进行线性或半线性搜索。 memmem()
,您可以使用posix_madvise()
。
就个人而言,我首先运行搜索而不使用任何posix_madvise(map, mapping, POSIX_MADV_SEQUENTIAL)
调用,然后使用相同的数据集(当然还有几次运行)查看它是否产生了足够大的正面差异。 (您可以安全地 - 没有丢失任何数据的风险 - 使用posix_madvise()
清除测试运行之间的页面缓存,如果您希望在时间运行之间排除大型文件(大部分)已经缓存的影响。)
答案 1 :(得分:1)
SIGSEGV
是因为您在0x1000
字节之外(for
循环)访问了。您必须mmap()
size
的完整fd
个字节。
虚拟内存子系统中需求分页的概念有助于完全像您的情况一样 - 应用程序/应用程序数据大于物理内存大小。在mmap()
之后,当您访问(虚拟)地址时,如果没有映射到它的物理页面(页面错误),内核将找到可以是的物理页面使用(页面替换)。
fd = open(argv[1], O_RDONLY);
ptr = mmap(NULL, file_size, PROT_READ, MAP_PRIVATE, fd, 0);
/* Consume the entire file's data as needed */
munmap(ptr, file_size);
或者,您可以在mmap()/munmap()
周围放置一个循环,以PAGE_SIZE
或PAGE_SIZE
的倍数扫描文件。 mmap() - offset
的最后一个arg会派上用场。
来自man-page:
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
int munmap(void *addr, size_t length);
伪代码:
fd = open(argv[1], O_RDONLY);
last_block_size = file_size % PAGE_SIZE;
num_pages = file_size / PAGE_SIZE + (last_block_size ? 1 : 0)
for (int i = 0; i < num_pages; i++) {
block_size = last_block_size && (i == num_pages - 1) ? last_block_size : PAGE_SIZE;
ptr = mmap(NULL, block_size, PROT_READ, MAP_PRIVATE, fd, i * PAGE_SIZE);
/* Consume the file's data range (ptr, ptr+block_size-1) as needed */
munmap(ptr, block_size);
}
请使用 MAP_PRIVATE
,因为您的流程可能只需要映射。它只是避免内核为 MAP_SHARED
。
修改:应该MAP_PRIVATE
取代MAP_ANON
。改变。