2011-06-22 9 views
11

मैं एक फाइल में एक भौतिक स्मृति लिखना चाहता हूँ। स्मृति को फिर से स्पर्श नहीं किया जाएगा, इस प्रकार मैं सर्वोत्तम लेखन प्रदर्शन प्राप्त करने के लिए O_DIRECT का उपयोग करना चाहता हूं।O_DIRECT का उपयोग कर फ़ाइल में कर्नेल स्पेस मेमोरी (भौतिक पता) कैसे लिखें?

मेरा पहला विचार /dev/mem खोलना था और स्मृति को मिमी करना और फ़ाइल को सब कुछ लिखना था, जो O_DIRECT के साथ खोला गया है। लिखने की कॉल mmap द्वारा लौटाई गई स्मृति-पते पर (EFAULT) विफल हो जाती है। अगर मैं O_DIRECT का उपयोग नहीं करता, तो इसका परिणाम 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; 
} 

अगला चरण एक चार-डिवाइस या ब्लॉक-डिवाइस लिखना होगा, जो स्मृति-हस्तांतरण को संभालता है। लेकिन copy_to_user को कैसे बाईपास करें? लक्ष्य प्रणाली एक एम्बेडेड पावरपीसी आर्किटेक्चर है, जिसमें कमी है, जो हार्डड्राइव (डीएमए-कंट्रोलर का उपयोग करके) को उपयोगकर्ता-मेमोरी लिख रहा है, रैम से रैम तक memcpy से तेज है। इस प्रकार मुझे पेज-कैश को बाईपास करना होगा।

सादर

फ्रेडरिक

+0

harddrive करने के लिए लिख memcpy की तुलना में तेजी है? क्या आप गंभीर हैं? –

+1

हां, यह एक एम्बेडेड सैटा नियंत्रक और एक वास्तविक तेज़ एसएसडी के साथ एक एफपीजीए आधारित डिजाइन (एक तेज CPU नहीं) है। – Friedrich

+0

@ फ़्रेडरिक क्या आप O_DIRECT के साथ खोले गए फ़ाइल में एक और बफर (अच्छी तरह से गठबंधन) लिखने में सक्षम हैं? – ydroneaud

उत्तर

0

मैं आज रात परीक्षण किया है, यदि उपयोग के लिये O_DIRECT अपने लिखने सशर्त हो जाएगा।

1

आपकी समस्या थोड़ा अजीब लगती है। चूंकि आप हार्डवेयर के काफी करीब प्रोग्रामिंग कर रहे हैं, इसलिए आप प्रत्यक्ष मेमोरी एक्सेस (डीएमए) का उपयोग करने पर विचार कर सकते हैं। यह थोड़ा मुश्किल हो सकता है, क्योंकि आपको पेजिंग और आई/ओ तंत्र के कुछ हिस्सों को समझने की आवश्यकता है। आपको लगता है कि पढ़ने के लिए चाहते हो सकता है:

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

(यह विचार प्राप्त करने के सिर्फ मात्र परिचय है।)

संबंधित मुद्दे