2016-09-26 2 views
-3

나는 C에서 큰 .txt 파일을 읽으려고합니다. fgets()로 버전을 만들었지 만 성능은 I/O에 의해 제한됩니다. 그래서 다른 fgets()보다 성능이 더 좋은 것이 필요하다. 그리고 나는 mmap()이 I/O에 의해 제한되지 않는다는 것을 알았다. 그래서 내 질문은 mmap() 및 다중 스레드 (POSIX 스레드)를 사용하여 가능합니까?mmap 다중 스레딩 C 파일에서 파일 읽기

Different threads to read(mmap() or something else) different parts of the file simultaneously

내가 온라인 멀티 스레딩의 mmap()에 대한 모든 리소스를 찾을 수 없습니다, 누군가가 몇 가지 예제 코드를 도와하고 설명해주십시오 수 : 그리고 여기에 내가 필요로 무엇입니까? 메모리에 맵 파일 또는 장치

#include <sys/mman.h> 
void *mmap(void *addr, size_t len, int prot, int flags, int fildes, off_t off); 

mmap에 대한 설명은 말한다 -

의 mmap : 나는

+1

다른 스레드가 동시에 파일의 다른 부분을 읽길 원하십니까? – yano

+1

'\ n'으로 구분 된 레코드를 읽고 싶습니까? 선이 페이지 경계를 넘을 경우 어떻게해야합니까? – wildplasser

+0

@yano Yesss가 말했듯이, 그렇게 할 방법이 있습니까? – superrman777

답변

0

mmap 상태의 리눅스 매뉴얼 페이지가 당신의 도움을 주셔서 감사합니다 매우 감사하게 될 것입니다 :

mmap()은 가상 주소 spa에 새 매핑을 생성합니다. 호출 프로세스의 ce. 새 맵핑의 시작 주소는 addr에 지정됩니다. length 인수는 맵핑의 길이를 지정합니다.

다음은 man 페이지의 코드 예입니다.

#include <sys/mman.h> 
#include <sys/stat.h> 
#include <fcntl.h> 
#include <stdio.h> 
#include <stdlib.h> 
#include <unistd.h> 
#define handle_error(msg) do { perror(msg); exit(EXIT_FAILURE); } while (0) 
int main(int argc, char *argv[]) 
{ 
    char *addr; 
    int fd; 
    struct stat sb; 
    off_t offset, pa_offset; 
    size_t length; 
    ssize_t s; 
    if (argc < 3 || argc > 4) { 
     fprintf(stderr, "%s file offset [length]\n", argv[0]); 
     exit(EXIT_FAILURE); 
    } 
    fd = open(argv[1], O_RDONLY); 
    if (fd == -1) 
     handle_error("open"); 
    if (fstat(fd, &sb) == -1)   /* To obtain file size */ 
     handle_error("fstat"); 
    offset = atoi(argv[2]); 
    pa_offset = offset & ~(sysconf(_SC_PAGE_SIZE) - 1); 
     /* offset for mmap() must be page aligned */ 
    if (offset >= sb.st_size) { 
     fprintf(stderr, "offset is past end of file\n"); 
     exit(EXIT_FAILURE); 
    } 
    if (argc == 4) { 
     length = atoi(argv[3]); 
     if (offset + length > sb.st_size) 
      length = sb.st_size - offset; 
    } else { /* No length arg ==> display to end of file */ 
     length = sb.st_size - offset; 
    } 
    addr = mmap(NULL, length + offset - pa_offset, PROT_READ, 
       MAP_PRIVATE, fd, pa_offset); 
    if (addr == MAP_FAILED) 
     handle_error("mmap"); 
    s = write(STDOUT_FILENO, addr + offset - pa_offset, length); 
    if (s != length) { 
     if (s == -1) 
      handle_error("write"); 
     fprintf(stderr, "partial write"); 
     exit(EXIT_FAILURE); 
    } 
    exit(EXIT_SUCCESS); 
} 

이 모든 것은 내 작업이며, 모두 Linux 매뉴얼 페이지에 있습니다.

+0

답변 해 주셔서 감사합니다.하지만 필요한 것은 멀티 스레드와 함께 mmap입니다. – superrman777

1

아이디어 자체가 나쁘지 않습니다. 개행 문자로 구분 된 파일을 가정하면 (즉, 문제가없는 줄 사이를자를 수 있음) 블록의 내용을 다른 파일에서 뜯어 낼 수 있습니다 (다른 프로그램에서 찢어 졌으므로 먼저 확인하십시오).

// just in case 
#define _LARGEFILE_SOURCE 
#define _BSD_SOURCE 
#define _POSIX_C_SOURCE 200112L 

#include <stdio.h> 
#include <stdlib.h> 
#include <sys/types.h> 
#include <sys/stat.h> 
#include <unistd.h> 
#include <errno.h> 
#include <string.h> 

// TODO: should be calculated 
#define FILE_PARTS 100 
// TODO: should not be global 
off_t positions[FILE_PARTS + 1]; 

int slice_file(FILE * fp) 
{ 
    off_t curr_pos = 0; 
    off_t filesize = 0; 
    off_t chunk_size = 0; 
    int fd; 
    int i, res; 
    char c; 

    struct stat sb; 

    // get size of file 
    fd = fileno(fp); 
    if (fd == -1) { 
    fprintf(stderr, "EBADF in prepare_and_backup() for data-file pointer\n"); 
    return 0; 
    } 

    if (fstat(fd, &sb) == -1) { 
    fprintf(stderr, "fstat() failed\n"); 
    return 0; 
    } 
    // check if it is a regular file 
    if ((sb.st_mode & S_IFMT) != S_IFREG) { 
    fprintf(stderr, "Not a regular file\n"); 
    return 0; 
    } 
    // TODO: check if filesize and chunksize >> 1 
    filesize = sb.st_size; 
    chunk_size = filesize/((off_t) FILE_PARTS); 

    positions[0] = 0; 
    curr_pos = 0; 

    for (i = 1; i < FILE_PARTS; i++) { 
    res = fseeko(fp, curr_pos, SEEK_SET); 
    if (res == -1) { 
     fprintf(stderr, "Error in fseeko(): %s\n", 
       strerror(errno)); 
     return 0; 
    } 
    curr_pos += chunk_size; 
    // look for the end of the line to cut at useful places 
    while ((c = fgetc(fp)) != EOF) { 
     curr_pos++; 
     // TODO: add code to honor Apple's special needs 
     if (c == '\n') { 
     c = fgetc(fp); 
     if (c == EOF) { 
      break; 
     } 
     curr_pos++; 
     break; 
     } 
    } 
    positions[i] = curr_pos - 1; 
    } 
    // Position of the end of the file 
    positions[i] = filesize; 
    // Is that even needed? 
    rewind(fp); 
    return 1; 
} 

이제 스레드를 시작할 수 있습니다.이 스레드는 작업 할 블록의 시작과 끝을 지정하고 (위의 함수로 계산했을 수도 있고 계산하지 않았을 수도 있습니다) 걱정없이 개별 스레드 내에서 (m) 매핑을 수행 할 수 있습니다. 출력이 블록과 동일한 크기 인 경우 내부에서 작업 할 수도 있습니다. 당신은 당신이 NULL로 설정 특정 주소에 대한 관심하지 않는 경우

편집

mmap의 선언은

void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset); 

입니다.
length은 맵을 초기화 할 바이트 수입니다 (이 경우 파일 설명자 fd의 내용으로 채워짐).
해당 채우기 시작 부분은 offset으로 설정되며, 불편한주의 사항이 있습니다. 페이지 크기의 배수 여야합니다 (정확한 번호는 sysconf(_SC_PAGE_SIZE)으로 문의하십시오). 별로 문제는 아니며 시작하기 전에 페이지에 설정하고 실제 시작시 작업을 시작하면 필요한 모든 정보가 제공됩니다. 그 페이지의 나머지 부분은 무시할 수 있습니다.

또는 전체 파일을 가져 와서 드라이브에있는 파일을 사용할 때처럼 사용하십시오. 모든 스레드에 해당 맵 블록 (필요한 정보는 positions)을 입력하고 거기에서 작업하십시오.

첫 번째 이점 : 운영 체제가 여러 곳에서 쉽게 누를 수있는 여러 블록의 메모리가 있으며 여러 CPU에서 캐시 누락이 적거나 그렇지 않을 수도 있습니다. 모든 CPU/CPU 그룹에 고유 한 RAM 또는 적어도 매우 큰 캐시가있는 클러스터 또는 기타 아키텍처를 실행하는 경우에도 필수적입니다.

장점 : 구현하기는 더 간단하지만지도의 큰 덩어리가 하나 있습니다. 런타임에 영향을 미칠 수도 있고 그렇지 않을 수도 있습니다.

힌트 : 현대적이고 빠른 SSD에 대한 나의 경험 : 요즘 읽기 속도가 너무 높아서 매핑 대신 직접 파일 액세스로 쉽게 시작할 수 있습니다. 다소 느린 "일반"HDD를 사용하더라도 합리적인 속도를 얻을 수 있습니다. 내가 위의 발췌 문장을 찢어 버린 프로그램은 120GB 이상의 대용량 CSV 파일을 검색해야했는데, RAM을 충분히로드하지 않고 드라이브에 충분한 공간을 두어 DB에로드하지 못했습니다 (예, 여러 해 전에). 그것은 열쇠 -> "많은, 다른, 가치"파일이었고, 고맙게도 이미 정렬되었습니다. 그래서 위의 방법 (KEY-> position)으로 인덱스 파일을 만들었습니다 (예제에서 100보다 훨씬 더 많은 블록을 만들었습니다). 인덱스 파일의 키도 정렬되었으므로 검색하려는 키가 더 큰 경우 (데이터가 오름차순으로 정렬 된 경우) 인덱스 항목보다 키가 블록에 있음을 의미하는 올바른 블록을 찾았습니다 위치가있는 경우이를 나타냅니다. 블록은 RAM에서 일부를 캐시로 유지하기에 충분히 작았지만 많이 얻지는 않았지만 들어오는 요청은 상당히 균일했습니다.

그래서 가난한 사람의 DB는 사용자의 불만없이 일을 처리 할 수있을 정도로 빠릅니다.

재미있는 부가 메모 : 키는 영숫자이고 정렬 알고리즘은 "aAbBcC ..."로 정렬되었으므로 strcmp을 직접 사용할 수 없습니다. 잠시 내 머리를 긁적이긴하지만 솔루션은 다소 단순합니다. 대소 문자 무시 (예 : strcasecmp, 사용 가능한 경우)를 사용하고 이 아닌 경우 같음 그 결과를 반환하고 그렇지 않으면 정상적인 결과 인 strncmp을 반환합니다. 예 : 단지 return -strcmp(a,b);).

당신은 작업해야하는 데이터에 대해 매우 벙벙했기 때문에 위의 내용은 당신에게 조금이라도 관심이 없었을 것입니다.

+0

죄송합니다. mmap에 대한 다음 단계는 mmap입니다 (positions [i-> FILE_PARTS], chunk_size, PROT_READ, MAP_PRIVATE, fd, 0)? – superrman777