2011-06-22 10 views
11

Chcę zapisać fizyczną pamięć do pliku. Sama pamięć nie zostanie ponownie dotknięta, dlatego chcę użyć O_DIRECT, aby uzyskać najlepszą wydajność zapisu.Jak napisać pamięć miejsca jądra (adres fizyczny) do pliku za pomocą O_DIRECT?

Mój pierwszy pomysł polegał na otwarciu /dev/mem i zapięciu pamięci i zapisaniu wszystkiego w pliku, który jest otwierany pod numerem O_DIRECT. Wywołanie zapisu nie powiedzie się (EFAULT) na adres pamięci zwróconej przez mmap. Jeśli nie użyję O_DIRECT, otrzymam numer 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; 
} 

Następnym krokiem byłoby napisanie urządzenia char lub urządzenie blokowe, które obsługuje przesyłanie pamięci. Ale jak ominąć copy_to_user? System docelowy jest wbudowaną architekturą PowerPC, z tą wadą, że zapis pamięci użytkownika na dysku twardym (przy użyciu kontrolera DMA) jest szybszy niż memcpy z pamięci RAM do pamięci RAM. Dlatego muszę ominąć pamięć podręczną strony.

poważaniem

Friedrich

+0

pisząc na twardy dysk jest szybszy niż memcpy? Mówisz poważnie? –

+1

Tak, jest to projekt oparty na FPGA (nie szybki procesor) z wbudowanym kontrolerem SATA i prawdziwym szybkim dyskiem SSD. – Friedrich

+0

@Friedrich jesteś w stanie napisać() inny bufor (dobrze wyrównany) do pliku otwartego przy pomocy O_DIRECT? – ydroneaud

Odpowiedz

0

Testowałem dzisiaj, jeśli użycie O_DIRECT Twój zapisu będzie warunkowa.

1

Twój problem wygląda trochę dziwnie. Ponieważ programujesz dość blisko sprzętu, możesz rozważyć użycie bezpośredniego dostępu do pamięci (DMA). Może to być nieco trudne, ponieważ musisz zrozumieć stronicowanie i części mechanizmu I/O. Może chcesz przeczytać, że:

http://www.linuxjournal.com/article/7104

(To tylko zwykłe wprowadzenie do pomysł.)

Powiązane problemy