Quiero escribir una memoria física en un archivo. La memoria en sí no se volverá a tocar, por lo tanto, quiero usar O_DIRECT
para obtener el mejor rendimiento de escritura.¿Cómo escribir la memoria del espacio del kernel (dirección física) en un archivo usando O_DIRECT?
Mi primera idea fue abrir /dev/mem
y mmap la memoria y escribir todo en un archivo, que se abre con O_DIRECT
. La llamada de escritura falla (EFAULT
) en la dirección de memoria devuelta por mmap. Si no utilizo O_DIRECT
, resulta en memcpy
.
#include <cstdint>
#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <errno.h>
#include <malloc.h>
#include <sys/mman.h>
#define PRINT_ERRNO_REASON(reason) \
case reason: { std::cout << #reason << std::endl; } break;
void write_page_aligned_buffer(int out_fd)
{
const ssize_t PAGE_SIZE = getpagesize();
void* page_aligned_buffer = memalign(PAGE_SIZE, PAGE_SIZE);
if(!page_aligned_buffer)
{
std::cout << "Could not allocate page aligned buffer." << std::endl;
return;
}
std::cout << "Allocated a buffer at address " << page_aligned_buffer << "." << std::endl;
if(write(out_fd, page_aligned_buffer, PAGE_SIZE) < 0)
{
std::cout << "Could not write page-aligned user buffer to tmp-file. Quitting..." << std::endl;
std::cout << "Reason of fail is ";
switch(errno)
{
PRINT_ERRNO_REASON(EAGAIN);
PRINT_ERRNO_REASON(EBADF);
PRINT_ERRNO_REASON(EFAULT);
PRINT_ERRNO_REASON(EFBIG);
PRINT_ERRNO_REASON(EINTR);
PRINT_ERRNO_REASON(EINVAL);
PRINT_ERRNO_REASON(EIO);
PRINT_ERRNO_REASON(ENOSPC);
PRINT_ERRNO_REASON(EPIPE);
default:
std::cout << "Unknown" << std::endl;
}
}
else
{
std::cout << "Successfully written user-page-aligned buffer." << std::endl;
}
free(page_aligned_buffer);
}
int main()
{
const ssize_t PAGE_SIZE = getpagesize();
// number of pages to copy
const uint32_t PAGES_TO_COPY = 1;
char* tmp_file_name = 0;
int tmp_file_fd = -1;
ssize_t bytes_copied = 0;
std::cout << "Copying " << PAGES_TO_COPY << " pages with PAGE_SIZE = " << PAGE_SIZE << std::endl;
std::cout << "Copying " << PAGES_TO_COPY * PAGE_SIZE/1024 << " kBytes." << std::endl << std::endl;
uid_t user_id = geteuid();
if(user_id)
{
std::cout << "We need to be root. I am euid == " << user_id << ". Quitting..." << std::endl;
return 0;
}
else
{
seteuid(0);
setuid(0);
}
// get the file descriptor
int mem_fd = open("/dev/mem", O_RDONLY);
if(mem_fd < 0)
{
std::cout << "Could not open /dev/mem. Quitting..." << std::endl;
std::cout << "Reason of fail is ";
switch(errno)
{
PRINT_ERRNO_REASON(EACCES);
PRINT_ERRNO_REASON(EEXIST);
PRINT_ERRNO_REASON(EINTR);
PRINT_ERRNO_REASON(EINVAL);
PRINT_ERRNO_REASON(EIO);
PRINT_ERRNO_REASON(EISDIR);
PRINT_ERRNO_REASON(ELOOP);
PRINT_ERRNO_REASON(EMFILE);
PRINT_ERRNO_REASON(ENFILE);
PRINT_ERRNO_REASON(ENOENT);
PRINT_ERRNO_REASON(ENOSR);
PRINT_ERRNO_REASON(ENOSPC);
PRINT_ERRNO_REASON(ENOTDIR);
PRINT_ERRNO_REASON(ENXIO);
PRINT_ERRNO_REASON(EOVERFLOW);
PRINT_ERRNO_REASON(EROFS);
PRINT_ERRNO_REASON(EAGAIN);
PRINT_ERRNO_REASON(ENAMETOOLONG);
PRINT_ERRNO_REASON(ENOMEM);
PRINT_ERRNO_REASON(ETXTBSY);
default:
std::cout << "Unknown" << std::endl;
}
return 0;
}
// get read pointer
uint8_t* mem_ptr = static_cast<uint8_t*>(mmap(0,
PAGE_SIZE,
PROT_READ,
MAP_SHARED,
mem_fd,
PAGE_SIZE));
if(mem_ptr == MAP_FAILED)
{
std::cout << "Could not mmap /dev/mem. Quitting..." << std::endl;
std::cout << "Reason of fail is ";
switch(errno)
{
PRINT_ERRNO_REASON(EACCES);
PRINT_ERRNO_REASON(EAGAIN);
PRINT_ERRNO_REASON(EBADF);
PRINT_ERRNO_REASON(EINVAL);
PRINT_ERRNO_REASON(ENFILE);
PRINT_ERRNO_REASON(ENODEV);
PRINT_ERRNO_REASON(ENOMEM);
PRINT_ERRNO_REASON(EPERM);
PRINT_ERRNO_REASON(ETXTBSY);
default:
std::cout << "Unknown" << std::endl;
}
goto CLEANUP_FD_DEV_MEM;
}
tmp_file_name = tempnam("/tmp", "prefix");
if(!tmp_file_name)
{
std::cout << "Could not get a free tmp-filename";
goto CLEANUP_MMAP_DEV_MEM;
}
// if O_DIRECT is omitted the example will work
tmp_file_fd = open(tmp_file_name,
O_WRONLY | O_CREAT | O_DIRECT | O_TRUNC,
S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
if(tmp_file_fd < 0)
{
std::cout << "Could not create tmp file with O_DIRECT." << std::endl;
goto CLEANUP_MMAP_DEV_MEM;
}
write_page_aligned_buffer(tmp_file_fd);
// everything worked so lets start the copying
for(uint i = 0; i < PAGES_TO_COPY; i++)
{
// check memory
// snip
for(int i = 0; i < PAGE_SIZE; i += 32)
{
printf("%02X%02X%02X%02X %02X%02X%02X%02X %02X%02X%02X%02X %02X%02X%02X%02X %02X%02X%02X%02X %02X%02X%02X%02X %02X%02X%02X%02X %02X%02X%02X%02X\n",
mem_ptr[i + 0], mem_ptr[i + 1], mem_ptr[i + 2], mem_ptr[i + 3],
mem_ptr[i + 4], mem_ptr[i + 5], mem_ptr[i + 6], mem_ptr[i + 7],
mem_ptr[i + 8], mem_ptr[i + 9], mem_ptr[i + 10], mem_ptr[i + 11],
mem_ptr[i + 12], mem_ptr[i + 13], mem_ptr[i + 14], mem_ptr[i + 15],
mem_ptr[i + 16], mem_ptr[i + 17], mem_ptr[i + 18], mem_ptr[i + 19],
mem_ptr[i + 20], mem_ptr[i + 21], mem_ptr[i + 22], mem_ptr[i + 23],
mem_ptr[i + 24], mem_ptr[i + 25], mem_ptr[i + 26], mem_ptr[i + 27],
mem_ptr[i + 28], mem_ptr[i + 29], mem_ptr[i + 30], mem_ptr[i + 31]);
}
std::cout << "\n";
// endsnip
bytes_copied = write(tmp_file_fd, mem_ptr, PAGE_SIZE);
if(bytes_copied < 0)
{
std::cout << "Could not write to tmp-file. Quitting..." << std::endl;
std::cout << "Reason of fail is ";
switch(errno)
{
PRINT_ERRNO_REASON(EAGAIN);
PRINT_ERRNO_REASON(EBADF);
PRINT_ERRNO_REASON(EFAULT);
PRINT_ERRNO_REASON(EFBIG);
PRINT_ERRNO_REASON(EINTR);
PRINT_ERRNO_REASON(EINVAL);
PRINT_ERRNO_REASON(EIO);
PRINT_ERRNO_REASON(ENOSPC);
PRINT_ERRNO_REASON(EPIPE);
default:
std::cout << "Unknown" << std::endl;
}
goto CLEANUP_FD_TMP_FILE;
}
}
CLEANUP_FD_TMP_FILE:
if(tmp_file_name)
{
if(close(tmp_file_fd))
{
std::cout << "Could close tmp-file " << tmp_file_name << "." << std::endl;
}
if(remove(tmp_file_name))
{
std::cout << "Could remove tmp-file " << tmp_file_name << "." << std::endl;
}
free(tmp_file_name);
}
CLEANUP_MMAP_DEV_MEM:
if(munmap(mem_ptr, PAGE_SIZE))
{
std::cout << "munmap failed." << std::endl;
}
CLEANUP_FD_DEV_MEM:
if(close(mem_fd))
{
std::cout << "Could not close /dev/mem filedescriptor." << std::endl;
}
return 0;
}
El siguiente paso sería escribir un char-dispositivo o un dispositivo de bloque, que se ocupa de la memoria de transferencia. Pero, ¿cómo pasar por alto copy_to_user? El sistema de destino es una arquitectura PowerPC integrada, con el inconveniente de que escribir memoria de usuario en el disco duro (usando el controlador DMA) es más rápido que memcpy desde RAM a RAM. Por lo tanto, debo omitir el caché de página.
Saludos
Friedrich
escrito al disco duro es más rápido que el establecimiento de memoria? ¿En serio? –
Sí, es un diseño basado en FPGA (no una CPU rápida) con un controlador SATA incorporado y un SSD real rápido. – Friedrich
@Friedrich ¿puede escribir() otro búfer (bien alineado) en su archivo abierto con O_DIRECT? – ydroneaud