TB 크기 mmap을 여는 방법

TB 크기 mmap을 여는 방법

거대한 메모리 맵을 열어야 해요. 파일은 1TB입니다.

그러나 나는 errno: 를 받았습니다 ENOMEM 12 Cannot allocate memory. 무엇이 나를 방해하는지 이해하지 못합니다. 요청된 값 RLIMIT_AS의 결과 : 18446744073709551615. 충분 해. 내 시스템도 64비트이므로 가상 메모리가 너무 작은 것은 아닙니다. ulimit -vulimited

np.lib.format.open_memmap물리적으로 가능하도록 데이터를 Python으로 만들었습니다 . C언어로 읽어보려고 합니다. 예 , Python을 읽는 데에는 문제가 없습니다 numpy.load('terabytearray.npy', mmap_mode='r').


이것은 최소한의 예입니다.

다음과 같이 numpy 배열을 만듭니다.

import numpy as np

shape = (75000, 5000000)
filename = 'datafile.obj'

if __name__ == '__main__':
  arr = np.lib.format.open_memmap(filename, mode='w+', dtype=np.float32, shape=shape)

다음과 같이 읽어보세요:

#include <stdbool.h>
#include <assert.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdint.h>
#include <unistd.h>

#include <sys/time.h>
#include <sys/resource.h>

#include <stdio.h>
#include <errno.h>

typedef enum {
  CNPY_LE, /* little endian (least significant byte to most significant byte) */
  CNPY_BE, /* big endian (most significant byte to least significant byte) */
  CNPY_NE, /* no / neutral endianness (each element is a single byte) */
  /* Host endianness is not supported because it is an incredibly bad idea to
     use it for storage. */
} cnpy_byte_order;

typedef enum {
  CNPY_B = 0, /* We want to use the values as index to the following arrays. */
  CNPY_I1,
  CNPY_I2,
  CNPY_I4,
  CNPY_I8,
  CNPY_U1,
  CNPY_U2,
  CNPY_U4,
  CNPY_U8,
  CNPY_F4,
  CNPY_F8,
  CNPY_C8,
  CNPY_C16,
} cnpy_dtype;

typedef enum {
  CNPY_C_ORDER,       /* C order (row major) */
  CNPY_FORTRAN_ORDER, /* Fortran order (column major) */
} cnpy_flat_order;

typedef enum {
  CNPY_SUCCESS,      /* success */
  CNPY_ERROR_FILE,   /* some error regarding handling of a file */
  CNPY_ERROR_MMAP,   /* some error regarding mmaping a file */
  CNPY_ERROR_FORMAT, /* file format error while reading some file */
} cnpy_status;

#define CNPY_MAX_DIM 4
typedef struct {
  cnpy_byte_order byte_order;
  cnpy_dtype dtype;
  cnpy_flat_order order;
  size_t n_dim;
  size_t dims[CNPY_MAX_DIM];
  char *raw_data;
  size_t data_begin;
  size_t raw_data_size;
} cnpy_array;

cnpy_status cnpy_open(const char * const fn, bool writable, cnpy_array *arr) {
  assert(arr != NULL);

  cnpy_array tmp_arr;

  /* open, mmap, and close the file */
  int fd = open(fn, writable? O_RDWR : O_RDONLY);
  if (fd == -1) {
    return CNPY_ERROR_FILE;
  }
  size_t raw_data_size = (size_t) lseek(fd, 0, SEEK_END);
  lseek(fd, 0, SEEK_SET);
  printf("%lu\n", raw_data_size);
  if (raw_data_size == 0) {
    close(fd); /* no point in checking for errors */
    return CNPY_ERROR_FORMAT;
  }
  if (raw_data_size == SIZE_MAX) {
    /* This is just because the author is too lazy to check for overflow on every pos+1 calculation. */
    close(fd);
    return CNPY_ERROR_FORMAT;
  }

  void *raw_data = mmap(
    NULL,
    raw_data_size,
    PROT_READ | PROT_WRITE,
    writable? MAP_SHARED : MAP_PRIVATE,
    fd,
    0 
  );

  if (raw_data == MAP_FAILED) {
    close(fd);
    return CNPY_ERROR_MMAP;
  }

  if (close(fd) != 0) {
    munmap(raw_data, raw_data_size);
    return CNPY_ERROR_FILE;
  }

  /* parse the file */
  // cnpy_status status = cnpy_parse(raw_data, raw_data_size, &tmp_arr); // library call ignore
  // if (status != CNPY_SUCCESS) {
  //   munmap(raw_data, raw_data_size);
  //   return status;
  // }
  // *arr = tmp_arr;

  return CNPY_SUCCESS;
}

int main(){

  cnpy_array arr = {};
  cnpy_status status = cnpy_open("datafile.obj", false, &arr);

  printf("status %i\n",(int) status);
  if(status != CNPY_SUCCESS){
    printf("failure\n");
    printf("errno %i\n", errno);
  }


    struct rlimit lim;
  printf("getrlimit RLIMIT_AS %s\n", (getrlimit(RLIMIT_AS, &lim) == 0 ? "success" : "failure") );
  printf("lim.rlim_cur %lu\n", lim.rlim_cur );
  printf("lim.rlim_max %lu\n", lim.rlim_max );
  printf("RLIM_INFINITY; %lu\n", RLIM_INFINITY );


  return 0;
}

컴파일용

gcc -std=c11 -o mmap_testing main.c

나는 그것을 사용하고 있다~quf/cnpy라이브러리에는 numpy 항목과 함께 작동하도록 관련 부분을 포함했습니다.

답변1

  void *raw_data = mmap(
    NULL,
    raw_data_size,
    PROT_READ | PROT_WRITE,
    writable? MAP_SHARED : MAP_PRIVATE,
    fd,
    0 
  );

따라서 가 및 에 대한 매핑을 writable == false요청합니다 . 이는 매핑된 메모리에 쓸 수 있지만 수정된 파일에는 쓸 수 없음을 의미합니다. 그럼 이렇게 쓰기를 하면 복사본이 만들어지는데(쓰기시 복사, 페이지별로), 어디에 저장되나요? 디스크에 파일을 다시 쓸 수 없으므로 실제 메모리나 스왑에만 상주할 수 있습니다. 따라서 이 호출은 실제로 1TB의 실제 메모리를 할당하거나 예약하도록 요청하고 있습니다. 아마도 당신은 그렇지 않을 것입니다.PROT_READ | PROT_WRITEMAP_PRIVATE

의견에서 논의한 바와 같이과다 사용이것이 작동하도록 허용합니다. 이 경우 시스템은 1TB를 예약하는 척하지만 실제로는 그렇게 하지 않습니다. 메모리를 너무 많이 쓰면 결국 프로세스가 종료되어 복구할 수 없게 됩니다(그리고 관련되지 않은 다른 프로세스도 종료될 수 있습니다).

하지만 이 경우에는 실제로 해당 메모리에 쓸 필요가 전혀 없는 것처럼 들리 writable == false므로 PROT_READ단독으로 사용하세요. 원하는 매개변수는 와 유사합니다 PROT_READ | (writable ? PROT_WRITE : 0).

관련 정보