[C/C++] Virtual Memory - File Wrapper

Dieses Thema im Forum "Projekte / Codes" wurde erstellt von Rushh0ur, 21. Dezember 2010 .

  1. 21. Dezember 2010
    Virtual Memory - File Wrapper

    Nun ja, zur Vorgeschichte, mein Prof hat mich vor ein paar Tagen gefragt ob es möglich wäre den Arbeitsspeicher irgendwie auf die Festplatte auszulagern, er möchte gern 16GB reservieren und benutzen, er habe aber nur 8 drin .....
    Da ich darauf keine Antwort hatte hab ich mich ein wenig schlau gemacht und bin der MSDN und allgemein im Internet über ein paar interessante Funktionen gestolpert wodurch nun diese kleine Programm/Schnittstelle entstanden ist.

    Die meisten Programmierer kennen die Funktionen malloc, realloc und free um mit dem Arbeitsspeicher zu arbeiten (später wurde new und delete eingeführt). Nun wäre es doch praktisch wenn man eine Datei auf der Festplatte über eine virtuelle Addresse direkt beschrieben könnte ohne irgendwelche Read und Write Funktionen aufzurufen und genau dies ermöglicht der folgende Code.

    In der folgenden "Wrapper-Bibliothek" findet ihr die Funktionen f_malloc, f_realloc und f_free die eigentlich genau wie die alten Befehle arbeiten nur mit dem kleinen Unterschied das der zurück gegebene Zeiger und der damit verknüpfte Adressraum sich auf eine Datei beziehen.

    Kurz: Ihr schreibt oder lest Daten vom Speicher welche *direkt* (cached) in die Datei geschrieben/ausgelesen werden.

    fmalloc.h
    Spoiler
    Code:
    /****************************************************************************/
    /* filename: fmalloc.cpp */
    /* author: SubZeroX */
    /* date: 23.12.2010 13:15 */
    /* thx to: Gibbon for a few tips */
    /****************************************************************************/
    #ifndef _FMALLOC_H_INCLUDE_
    #define _FMALLOC_H_INCLUDE_
    
    #include <windows.h>
    #include <vector>
    #include <tchar.h>
    
    void *f_malloc (TCHAR *lpFileName, size_t size, DWORD dwCreationDisposition = CREATE_ALWAYS, DWORD flProtect = PAGE_READWRITE);
    void *f_realloc (void *memblock, size_t size);
    void f_free (void *memblock, bool cleanup = true);
    size_t f_getsize (void *memblock);
    
    #endif // _FMALLOC_H_INCLUDE_
    

    fmalloc.cpp
    Spoiler
    Code:
    /****************************************************************************/
    /* filename: fmalloc.cpp */
    /* author: SubZeroX */
    /* date: 23.12.2010 13:15 */
    /* thx to: Gibbon for a few tips */
    /****************************************************************************/
    #include "fmalloc.h"
    
    #ifndef _WIN64
     #define STRINGIFY(x) #x
     #define TOSTRING(x) STRINGIFY(x)
     #pragma message (__FILE__ "(" TOSTRING(__LINE__) ") : warning: It will be recommended to use this code only as Win64 build.")
    #endif
    
    typedef struct 
    {
     TCHAR *sFileName;
     VOID *ptr;
     DWORD flProtect;
     HANDLE hPageFile;
     HANDLE hPage;
    } FMemEntry;
    
    /// *************************************************************************************
    /// HeapList data manager
    /// *************************************************************************************
    static std::vector<FMemEntry> fHeapList;
    
    /// *************************************************************************************
    /// Replace all specific Charater in a String
    /// param str: character array
    /// param cold: the old character
    /// param cnew: the new character
    /// *************************************************************************************
    static void str_replace(TCHAR *str, TCHAR *cold, TCHAR *cnew)
    {
     while (*str != 0)
     {
     if (*str == *cold)
     *str = *cnew;
     str++;
     }
    }
    
    /// *************************************************************************************
    /// Find a heap entry with the specific entry
    /// param lpFileNamethe: filename of the heap entry
    /// return: iterator to the object
    /// *************************************************************************************
    static std::vector<FMemEntry>::const_iterator HeapFindFile(TCHAR *lpFileName)
    {
     for (std::vector<FMemEntry>::const_iterator it = fHeapList.begin(); it < fHeapList.end(); ++it)
     if (!_tcscmp(it->sFileName, lpFileName)) 
     return it;
    
     return fHeapList.end();
    }
    
    /// *************************************************************************************
    /// Find a heap entry with the specific memblock
    /// param memblock: memblock of the heap entry
    /// return: iterator to the object
    /// *************************************************************************************
    static std::vector<FMemEntry>::const_iterator HeapFindMemory(void *memblock)
    {
     for (std::vector<FMemEntry>::const_iterator it = fHeapList.begin(); it < fHeapList.end(); ++it)
     if (it->ptr == memblock) 
     return it;
    
     return fHeapList.end();
    }
    
    /// *************************************************************************************
    /// allocates virtuall memory with associated file
    /// param lpFileName: name of the associated file 
    /// param size: the amount of bytes to associate from begining of the file
    /// param dwCreationDisposition: CREATE_ALWAYS or OPEN_ALWAYS
    /// param flProtect: see param flProtect of CreateFileMapping function
    /// return: virtuall memory pointer
    /// *************************************************************************************
    void *f_malloc(TCHAR *lpFileName, size_t size, DWORD dwCreationDisposition, DWORD flProtect)
    {
     if (!lpFileName) return NULL;
    
     FMemEntry fHeapListEntry; 
     bool bError;
    
     // copy file name to HeapListEntry
     fHeapListEntry.sFileName = _tcsdup(lpFileName);
    
     { // check if file name already exist
     std::vector<FMemEntry>::const_iterator index = HeapFindFile(lpFileName);
     if (index < fHeapList.end())
     {
     free(fHeapListEntry.sFileName);
     return index->ptr;
     }
     }
    
     // create or open a file
     fHeapListEntry.hPageFile = CreateFile(lpFileName, GENERIC_READ | GENERIC_WRITE, 0, NULL, dwCreationDisposition, 
     FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED | FILE_FLAG_RANDOM_ACCESS, NULL);
     if (fHeapListEntry.hPageFile)
     {
     // create a map of the file
     TCHAR *lpName = _tcsdup(lpFileName);
     #ifdef UNICODE
     str_replace(lpName, L"\\", L"_");
     #else
     str_replace(lpName, "\\", "_"); 
     #endif
     #ifdef _WIN64
     fHeapListEntry.hPage = CreateFileMapping(fHeapListEntry.hPageFile, NULL, flProtect, 
     (size >> 32), size & 0xFFFFFFFF, lpName);
     #else
     fHeapListEntry.hPage = CreateFileMapping(fHeapListEntry.hPageFile, NULL, flProtect, 
     0, size & 0xFFFFFFFF, lpName);
     #endif
    
     if (fHeapListEntry.hPage)
     {
     // obtain a pointer of the mapped file
     fHeapListEntry.ptr = MapViewOfFile(fHeapListEntry.hPage, FILE_MAP_ALL_ACCESS, 0, 0, NULL);
    
     if (fHeapListEntry.ptr)
     {
     // add entry to HeapList
     fHeapListEntry.flProtect = flProtect;
     fHeapList.push_back(fHeapListEntry);
    
     bError = false;
     }
     else
     bError = true;
     }
     else
     bError = true;
    
     free(lpName);
     }
     else
     bError = true;
    
     // check error state
     if (bError)
     {
     CloseHandle(fHeapListEntry.hPage);
     CloseHandle(fHeapListEntry.hPageFile);
     free(fHeapListEntry.sFileName);
     return NULL;
     }
    
     // return the memory pointer to the file
     return fHeapListEntry.ptr;
    }
    
    /// *************************************************************************************
    /// free the virtual memory with associated file 
    /// param memblock: memblock of the associated file 
    /// param cleanup: if true the specific file will be delete
    /// *************************************************************************************
    void f_free(void *memblock, bool cleanup)
    {
     if (!memblock) return;
    
     std::vector<FMemEntry>::const_iterator index = HeapFindMemory(memblock);
     if (index < fHeapList.end())
     {
     TCHAR* lpFileName = index->sFileName;
     UnmapViewOfFile (index->ptr);
     CloseHandle (index->hPage);
     CloseHandle (index->hPageFile);
     fHeapList.erase (index);
     if (cleanup) DeleteFile(lpFileName);
     free(lpFileName);
     }
    }
    
    /// *************************************************************************************
    /// reallocate virtual memory with associated file and resize 
    /// param memblock: memblock of the associated file 
    /// param size: new size
    /// *************************************************************************************
    void *f_realloc(void *memblock, size_t size)
    {
     if (!memblock) return NULL;
    
     std::vector<FMemEntry>::const_iterator index = HeapFindMemory(memblock);
     if (index < fHeapList.end())
     {
     TCHAR *lpFileName = _tcsdup(index->sFileName);
     DWORD flProtect = index->flProtect;
     f_free(memblock, false);
    
     void *ptr = f_malloc(lpFileName, size, OPEN_ALWAYS, flProtect);
     free(lpFileName);
    
     return ptr;
     }
    
     return NULL;
    }
    
    /// *************************************************************************************
    /// get size of virtual memory region 
    /// param memblock: memblock of the associated file 
    /// return: size of block
    /// *************************************************************************************
    size_t f_getsize(void *memblock)
    {
     if (!memblock) return 0;
    
     std::vector<FMemEntry>::const_iterator index = HeapFindMemory(memblock);
     if (index < fHeapList.end())
     {
     #ifdef _WIN64
     DWORD dwHighSize;
     DWORD dwLowSize = GetFileSize(index->hPageFile, &dwHighSize); 
     return dwLowSize | ((size_t)dwHighSize << 32);
     #else
     return GetFileSize(index->hPageFile, NULL);
     #endif
     }
     else
     return 0;
    }
    

    Beispiel:
    Code:
    // Datei auf der Festplatte öffnen/erstellen
    char* str = (char*)f_malloc(L"D:\\test.txt", 256, OPEN_ALWAYS);
    
    printf("Alter Text:\n");
    printf("%s", str);
    printf("Geben Sie einen neuen Text ein:\n");
    scanf("%s", test);
    
    // Datei schliessen ohne zu löschen (false)
    f_free((void*)str, false);
    
    //Edit:
    Hab mal noch die Funktion f_getsize hinzugefügt, mit der man die Größe des Speicherblock auslesen kann.

    Die drei Funktionen sollten eigentlich selbsterklährend sein, wenn nicht im Sourcecode sind noch ein paar Kommentare, ansonste hier einfach nochmal nachfragen.
    Das man dadruch einfach mit Dateien arbeiten kann sollte auch klar sein oder auch damit benutzerdefniert den "Arbeitsspeicher" erweitern kann. (letzteres siehe Post 2 lieber VirtualAlloc benutzen)

    Das ganze sollte am besten in 64Bit Anwendungen benutz werden, da da der Virtuele Addresraum größer ist (für kleine Dateien auch unter 32Bit nutzbahr).

    Kritik und Verbesserungsvorschläge erwünscht.
    Solltet ihr den Code in eurem Projekt verwenden dann nennt doch Bitte die Autoren in den Credits.

    Mfg Rushh0ur
     
  2. 21. Dezember 2010
    AW: Virtual Memory - File Wrapper

    Hast du VirtualAlloc ausprobiert? Diese Funktion kann auf einem 64Bit System theoretisch 2^64 Byte reservieren. Du kannst auf einem 32-Bit System sowieso nicht mehr als 2^32 ansprechen...

    Paging/swapping ein Feature des Betriebssystems, also sollte man das Feature auch nutzen...
     
  3. 21. Dezember 2010
    AW: Virtual Memory - File Wrapper

    Jup kenne die Funktion. Das mit dem mehr "Arbeitsspeicher" wird etwas missverstanden. Da hast du natürlich recht, da sollte man VirtualAlloc benutzen.

    Mir geht es eher darum um schnelle bzw einfacher auf Dateien zuzugreifen.

    Mfg Rushh0ur
     
  4. 22. Dezember 2010
    AW: Virtual Memory - File Wrapper

    Hat es einen Grund, dass du über den std::vector per operator[] loopst, statt mit Iteratoren, die performanter sind? Würde dann beispielsweise so aussehen:

    Code:
    for (std::vector<FMemEntry>::const_iterator it = fHeapList.begin(); it != fHeapList.end(); ++it)
    // hier erfolgt dann der Zugriff z. B. auf sFilename per (*it).sFilename oder einfacher it->sFilename
    
    Alternativ kannst du natürlich noch statt std::vector<FMemEntry> ein typedef verwenden und dann darüber auf den const_iterator zugreifen:

    Code:
    typedef std::vector<FMemEntry> vec_t;
    
    for (vec_t::const_iterator .....)
     
  5. 23. Dezember 2010
    AW: Virtual Memory - File Wrapper

    Nein einen besonderen Grunds gibts eigentlich nicht, vermutlich weil ich mich mit der Syntax der Iteratoren nicht befreunden kann und die von den Standartsarrays bevorzuge.

    Aber es wird mal Zeit das neue auch zu akzeptieren und zu verwenden. Danke Dir, hab nun den Code dementsprechend geändert und nen kleinen "Bug" entfernt (== bei ner if-Abfrage).

    Mfg Rushh0ur
     
  6. 23. Dezember 2010
    AW: Virtual Memory - File Wrapper

    So richtig verstehe ich den Sinn auch nicht ganz. Das Betriebssystem hat ja, wie bereits ausgeführt, genau diese Funktionalität.

    Wenn Du komfortable mit Dateien wie mit Arbeitsspeicher arbeiten willst, stellen Betriebssystem dir soetwas wie mmap() zur Verfügung: Du bekommst einen Pointer im Speicher, der Speicherbereich wird synchron zu einer gegebenen Datei gehalten. Das ganze ist so ausgefuchst, dass sich mehrere Prozesse dadurch einen Speicherbereich teilen können, wenn sie die gleiche Datei angeben.

    Das ganze gibt es auch unter Windows, hatte ich auch einmal verwendet, aber wieder vergessen. Musst mal schauen, die Unix Funktion ist wiegesagt mmap()
     
  7. 23. Dezember 2010
    AW: Virtual Memory - File Wrapper

    Nun ja das Betriebsystem stellt diese Funktionalität zwar zur verfügung jedoch finde ich die Handhabung etwas komplitziert und der Code soll diese vereinfachen.

    Kann zu Unix-Programierung nichts sagen da ich damit nicht arbeite, aber es scheint, dass es da auch nicht grad "einfach" ist und man sich durch diversen Befehlen durchkämpfen muss.

    Mfg Rushh0ur
     
  8. 23. Dezember 2010
    AW: Virtual Memory - File Wrapper

    Wenn etwas vom Betriebssystem vorgegeben ist, sollte man sich schon mal "durchkämpfen", da die Performance oft gar nicht anders zu erreichen ist. Gut, ich hab unter Windows schon manch schlechte Erfahrung gemacht (strtod etwa, in manchen Fällen ist die BSD Version tausend mal schneller als die von Windows).

    So kompliziert ist es doch aber gar nicht, hier ist ein Beispiel aus dem MSDN:
    Creating Named Shared Memory (Windows)
    Statt INVALID_HANDLE_VALUE kannst Du auch ein File-Handle angeben, auf dem Du arbeiten willst. Wie Du am Ende siehst, bekommst Du ein Pointer pBuf, den Du wie einen normalen Pointer verwenden kannst. Dies Beispiel zielt eher auf IPC ab, wenn man ein bisschen schaut, finden sich sicherlich noch mehr Beispiele für eine andere Verwendung.
    Aber das ist doch im Prinzip genau das, was Du willst? Nur direkt vom Betriebssystem und tausendfach erprobt und bewährt
     
  9. 26. Dezember 2010
    AW: Virtual Memory - File Wrapper

    Em mein Code implementiert den Code den du verlinkt hast und kümmert sich für die Verwaltung....

    Du kannst das ganze mit ner String-Klasse vergleichen, die String Klasse kümmer sich um die verwaltung eines char/wchar_t-Arrays und mein Code kümmert sich um die Erstellung und die Friegabe der Handles die man dafür benötigt und bietet damit eine einfachere Handhabung. Aber wenn du das nicht brauchst dann kannst du es doch selbert so wie du es brauchst zussamenschrieben.
    Ich, finde es zumindest praktisch und eventuelle ein paar andere Leute auch.

    Ich hoffe der Zweck ist jetzt etwas näher gekommen.

    Mfg Rushh0ur
     
  10. 27. Dezember 2010
    AW: Virtual Memory - File Wrapper

    Haha, OK, hätte ich mal genauer nachschauen sollen. So ist mir jetzt wenigstens klar, was Du gemacht hast
     
  11. Video Script

    Videos zum Themenbereich

    * gefundene Videos auf YouTube, anhand der Überschrift.