2016-02-26 1 views
1

Я работаю над проектом, который пытается найти конкретные байты (например, 0xAB) в файловой системе (например, ext2). Я смог найти то, что мне нужно, используя malloc(), realloc() и memchr(), но это казалось медленным, поэтому я искал использование mmap(). То, что я пытаюсь сделать, это найти конкретные байты, а затем скопировать их в структуру, поэтому у меня есть два вопроса: (1) использует mmap() наилучшую стратегию и (2) почему не работает следующий код (я получаю Ошибка EINVAL)?using mmap() для поиска по большому счёту (~ 1TB)

UPDATE: Следующая программа компилируется и работает, но я до сих пор есть несколько вопросов:
1) не будет отображаться правильный размер файла на больших файлов (отображается правильный размер для флэш-накопитель 1 ГБ, но не для 32 Гб) * ,
2) он неправильно искажает отображение **.

* THIS Возможное решение для получения правильного размера с помощью stat64()? Если да, то что-то я добавляю в свой Makefile? Я не работал с make-файлами, поэтому не знаю, как добавить что-то подобное.
** Это даже правильный способ поиска?

#define _LARGEFILE64_SOURCE 

#include <stdio.h> 
#include <fcntl.h> 
#include <stdlib.h> 
#include <string.h> 
#include <sys/stat.h> 
#include <sys/types.h> 
#include <unistd.h> 
#include <errno.h> 
#define handle_error(msg) \ 
do { perror(msg); exit(EXIT_FAILURE); } while (0) 

int main(int argc, char **argv) { 

    int fd = open("/dev/sdb1", O_RDONLY); 

    if(fd < 0) { 
     printf("Error %s\n", strerror(errno)); 
     return -1; 
    } 

    const char * map; 

    off64_t size; 
    size = lseek64(fd, 0, SEEK_END); 
    printf("file size: %llu\n", size); 
    lseek64(fd, 0, SEEK_SET);  

    map = mmap(0, size, PROT_READ, MAP_SHARED, fd, 0); 
    if (map == MAP_FAILED) { handle_error("mmap error"); } 

    printf("Searching for magic numbers...\n"); 
    for (i=0; i < size; i++) { 
    if(map[i] == 0X53 && map[i + 1] == 0XEF) { 
     if ((map[i-32] == 0X00 && map[i-31] == 0X00) ||    
      (map[i-32] == 0X01 && map[i-31] == 0X00) || 
      (map[i-32] == 0X02 && map[i-31] == 0X00)) { 
      if(j <= 5) { 
       printf("superblock %d found\n", j); 
       ++j; 
      } else break; 

    int q; 
    for(q=0; q<j; q++) { 
     printf("SUPERBLOCK[%d]: %d\n", q+1, sb_pos[q]); 
    } 

    fclose(fd); 
    munmap(map, size); 
    return 0; 
} 

Благодарим за помощь.

+0

вы должны проверить переменную errno, чтобы понять, почему mmap не удалось –

+0

Вы читали [ЭТО] (http://stackoverflow.com/questions/10088962/mmap-returns-einval) вопрос? – Shark

+0

Вероятно, он не работает, потому что он не может найти непрерывную полосу памяти с запрошенной длиной ('size'). – Shark

ответ

0

Я только заметил, что я использовал fopen(), вместо этого я должен использовать open()?

Да, вы должны использовать open() вместо fopen(). И вот почему вы получили ошибку EINVAL.

fopen ("/ dev/sdb1", O_RDONLY);

Этот код является полностью неправильным. O_RDONLY - это флаг, который должен использоваться с open() syscall, но не с fopen() libc functgion

Вы также должны отметить, что mmaping больших файлов доступен, только если вы работаете на платформе с большим виртуальным адресным пространством. Это очевидно: у вас должно быть достаточно виртуальной памяти для обращения к вашему файлу. Говоря о Intel, это должен быть только x86_64, а не x86_32.

Я не пробовал делать это с действительно большими файлами (> 4G). Может быть, некоторые дополнительные флаги должны быть переданы в open() syscall.

0

Я работаю над проектом, который пытается осуществить поиск конкретных байтов (например, 0xAB) в файловой системе (например, ext2)

Для MMAP() большой файл в память совершенно неправильно подход в вашем случае. Вам просто нужно обработать файл поэтапно с помощью кусков с фиксированным размером (что-то около 1 МБ). Вы можете использовать mmap() или просто читать() в свой внутренний буфер - это не имеет значения. Но вложение всего файла в память полностью переборщило, если вы просто хотите обработать его последовательно.

1

mmap - очень эффективный способ обработки большого файла, особенно в тех случаях, когда есть внутренняя структура, которую вы можете использовать (например, используя mmap в большом файле с фиксированными размерами, которые сортируются, позволит вам выполнить бинарный поиск, и будут затронуты только страницы, соответствующие чтению записей).

В вашем случае вам нужно скомпилировать 64 бит и включить поддержку большого файла (и использовать open(2)).

Если ваш /dev/sdb1 является устройством, а не файлом, я не думаю, что stat(2) покажет фактический размер. stat возвращает размер 0 для этих устройств на моих ящиках. Я думаю, вам нужно будет получить размер по-другому.

О адресном пространстве: x86-64 использует 2 × 48 байтов виртуального адресного пространства, которое составляет 256 TiB. Вы не можете использовать все это, но в большинстве процессов есть ~ 127 TiB смежного адресного пространства.

+0

да,/dev/sdb1 будет устройством, а программа работает на 64-битной машине Ubuntu. Если я использую следующее на USB-накопителе на 32 ГБ, я получаю 1493172224: 'int fd = open ("/dev/sdb1 ", O_RDONLY | O_LARGEFILE); ... off_t размер; size = lseek64 (fd, 0, SEEK_END); перемотка назад (fd); ' – user2341909