Linux讀寫實體記憶體

超越夢想發表於2012-07-31

一、基礎知識

1.開啟裝置檔案:
    mem是一個字元裝置檔案,是計算機主存的一個映像。通常只有root使用者對其有讀寫許可權。因此只有root使用者能進行這些操作。
    如果要開啟裝置檔案/dev/mem,需要系統呼叫open()函式,作用是開啟一個檔案或裝置,其函式原型為:
    int open(const char *path, int flags);
    返回值:如果操作成功則返回一個檔案描述符,否則返回-1
    形  參:
            path      被開啟檔案的路徑即檔名描述。
            flags     檔案的訪問模式描述,可常用的選項見下:
                        O_RDONLY        只讀方式
                        O_WRONLY        只寫方式
                        O_RDWR          可讀寫方式
    說  明:此函式用於開啟檔案或者裝置
    標頭檔案:#include <fcntl.h> #include <stat.h>  定義在/usr/include/fcntl.h中
2.讀取記憶體映像:
    記憶體映像其實在記憶體中建立一個與外存中檔案完全相同的映像。使用者可以將整個檔案對映到記憶體中,也可以將檔案的一部分對映到記憶體中。
    使用操作記憶體的方法對檔案進行操作。系統會將記憶體映像檔案所做的改動反映到真實檔案中去。
    在記憶體映像I/O的實現過程中需要用到一些系統呼叫:
    首先是建立記憶體映像檔案的系統呼叫mmap()函式,其函式原型為:
    void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset);
    返回值:成功時,返回值為指向記憶體映像起始地址的指標,當呼叫失敗時,返回值為-1
    形  參:
            start      一個void指標,表示希望將檔案對映到此指標指向的位置,通常為NULL。
            length     定義記憶體映像檔案所佔用的記憶體空間大小,以位元組計。
            prot       記憶體映像檔案的安全屬性,注意和open函式中的flags屬性保持一致。它的可使用的選項如下:
                          Flags                          含義
                        PROT_EXEC       被映像記憶體可能含義機器碼,可被執行
                        PROT_NONE       映像記憶體不允許訪問
                        PROT_READ       映像記憶體可讀
                        PROT_WRITE      映像記憶體可寫
            flags      記憶體映像的標誌,選項如下:
                          Flags                                   含義
                        MAP_FIXED       指定對映起始地址,如果由start和len指定的記憶體區重疊於現存的對映空間,重疊部分將會被丟棄。
                                        如果指定的起始地址不可用,操作將會失敗。並且起始地址必須落在頁的邊界上。
                        MAP_SHARED      與其它所有對映這個物件的程式共享對映空間。對共享區的寫入,相當於輸出到檔案。
                                        直到msync()或者munmap()被呼叫,檔案實際上不會被更新。 
                        MAP_PRIVATE     建立一個寫入時拷貝的私有對映。記憶體區域的寫入不會影響到原檔案。
                                        這個標誌和以上標誌是互斥的,只能使用其中一個。 
                        MAP_NORESERVE   不要為這個對映保留交換空間。當交換空間被保留,對對映區修改的可能會得到保證。
                                        當交換空間不被保留,同時記憶體不足,對對映區的修改會引起段違例訊號。
                        MAP_LOCKED      鎖定對映區的頁面,從而防止頁面被交換出記憶體。 
                        MAP_GROWSDOWN   用於堆疊,告訴核心VM系統,對映區可以向下擴充套件。
                        MAP_ANONYMOUS   匿名對映,對映區不與任何檔案關聯。
                        MAP_ANON        MAP_ANONYMOUS的別稱,不再被使用。 
                        MAP_FILE        相容標誌,被忽略。 
                        MAP_32BIT       將對映區放在程式地址空間的低2GB,MAP_FIXED指定時會被忽略。
                                        當前這個標誌只在x86-64平臺上得到支援。
                        MAP_POPULATE    為檔案對映通過預讀的方式準備好頁表。隨後對對映區的訪問不會被頁違例阻塞。 
                        MAP_NONBLOCK    僅和MAP_POPULATE一起使用時才有意義。不執行預讀,只為已存在於記憶體中的頁面建立頁表入口。
            fd         要對映的檔案的描述符。
            offset     所對映的資料內容 距離檔案頭的偏移量。
    說  明:此函式用於將一個檔案或它的一部分對映到記憶體中。
    標頭檔案:#include <sys/mman.h> #include <sys/types.h>  函式定義在/usr/include/sys/mman.h
3.撤銷記憶體映像的修改
    另外我們使用完記憶體映像檔案後,要用系統呼叫函式munmap()函式來撤銷,其函式原型為:
    int munmap(void *start, size_t length);
    返回值:成功時,返回值為0;呼叫失敗時返回值為 -1,並將errno設定為相應值。
    形  參:
            start      要撤銷的記憶體映像檔案的起始地址。
            length     要撤銷的記憶體映像檔案的大小。
    說  明:當程式結束或利用exec相關函式來執行其他程式時,對映記憶體會自動解除,但關閉對應的檔案描述詞時不會解除對映。
    標頭檔案:#include <sys/mman.h>
4.將記憶體映像的改動儲存到外存中
    最後,如果我們要將記憶體映像的改動儲存到外存中,還需要系統呼叫msync()函式,其函式原型為:
    int msync(const void *start,size_t length,int flags);
    返回值:成功時,返回值為0;呼叫失敗時返回值為 -1,並將errno設定為相應值。
    形  參:
            start      要儲存到外存的那些原始檔的起始地址。
            length     表示記憶體映像檔案的大小。
            flags      設定了函式的相應操作,其具體選項如下:
                          Flags                      含義
                        MS_ASYNC           呼叫一個寫操作並返回
                        MS_INVALIDATE      映像到相同檔案的記憶體映像資料更新
                        MS_SYNC            完成寫操作後函式返回
    說  明:程式在對映空間的對共享內容的改變並不直接寫回到磁碟檔案中,往往在呼叫munmap()後才執行該操作。
            可以通過呼叫msync()函式來實現磁碟檔案內容與共享記憶體區中的內容一致,即同步操作。
    標頭檔案:#include <sys/mman.h>
5.通過/dev/mem裝置檔案和mmap系統呼叫,可以將線性地址描述的實體記憶體對映到程式的地址空間,然後就可以直接訪問這段記憶體了。

二、一個例子

#include <stdio.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main (int args, char* arg[])
{
    int i;
    int fd;
    char* mem;
    char *buff = "HELLO";
    //open /dev/mem with read and write mode
    if((fd = open ("/dev/mem", O_RDWR)) < 0)
    {
        perror ("open error");
        return -1;
    }

    //map physical memory 0-10 bytes
    mem = mmap (0, 10, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if (mem == MAP_FAILED)
    {
        perror ("mmap error:");
        return 1;
    }

    //Read old value
    for (i = 0; i < 5; i++)
    {
        printf("\nold mem[%d]:%d", i, mem[i]);
    }

    printf(The value is 0x%x\n, *((int *)mem));

    //write memory
    memcpy(mem, buff, 5);
    //Read new value
    for (i = 0; i<5 ; i++)
    {
        printf("\nnew mem[%d]:%c", i, mem[i]);
    }

    printf("\n");
    munmap (mem, 10); //destroy map memory
    close (fd);   //close file
    return 0;
}

相關文章