1. 程式人生 > >利用mmap /dev/mem 讀寫Linux記憶體

利用mmap /dev/mem 讀寫Linux記憶體

以下是我寫的一個sample

  1. #include<stdio.h>
  2. #include<unistd.h>
  3. #include<sys/mman.h>
  4. #include<sys/types.h>
  5. #include<sys/stat.h>
  6. #include<fcntl.h>
  7. int main()  
  8. {  
  9.     unsigned char * map_base;  
  10.     FILE *f;  
  11.     int n, fd;  
  12.     fd = open("/dev/mem", O_RDWR|O_SYNC);  
  13.     if
     (fd == -1)  
  14.     {  
  15.         return (-1);  
  16.     }  
  17.     map_base = mmap(NULL, 0xff, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0x20000);  
  18.     if (map_base == 0)  
  19.     {  
  20.         printf("NULL pointer!\n");  
  21.     }  
  22.     else
  23.     {  
  24.         printf("Successfull!\n");  
  25.     }  
  26.     unsigned long addr;  
  27.     unsigned char
     content;  
  28.     int i = 0;  
  29.     for (;i < 0xff; ++i)  
  30.     {  
  31.         addr = (unsigned long)(map_base + i);  
  32.         content = map_base[i];  
  33.         printf("address: 0x%lx   content 0x%x\t\t", addr, (unsigned int)content);  
  34.         map_base[i] = (unsigned char)i;  
  35.         content = map_base[i];  
  36.         printf("updated address: 0x%lx   content 0x%x\n", addr, (unsigned int)content);  
  37.     }  
  38.     close(fd);  
  39.     munmap(map_base, 0xff);  
  40.     return (1);  
  41. }  

上面的例子將起始地址0x20000(實體地址), 長度為0xff映射出來。 然後就可以像普通陣列一樣操作記憶體。

下面是輸出結果

address: 0x7f3f95391000   content 0x0           updated address: 0x7f3f95391000   content 0x0
address: 0x7f3f95391001   content 0x0           updated address: 0x7f3f95391001   content 0x1
address: 0x7f3f95391002   content 0x0           updated address: 0x7f3f95391002   content 0x2
address: 0x7f3f95391003   content 0x0           updated address: 0x7f3f95391003   content 0x3
address: 0x7f3f95391004   content 0x0           updated address: 0x7f3f95391004   content 0x4
。。。

我的測試機器是64位機。 該例子將實體地址0x20000對映到了虛擬地址0x7f3f95391000。

首先將當前地址下的內容輸出, 然後寫入新值。 

可以通過 hexedit /dev/mem 驗證新值已經寫入。

如果想在使用者態處理kernel分配的地址可以這麼做。 首先用virt_addr = get_free_pages(GFP_KERNEL, order)分配記憶體,通過phy_addr = __pa(virt_addr)得到實體地址,然後在使用者態將/dev/mem用mmap 映射出來, offset就是phy_addr, length設為 2^order。 此時就可以在使用者態讀寫核心分配的記憶體了。

注:該操作需要有root許可權。

轉載地址:http://blog.csdn.net/zhanglei4214/article/details/6653568

===============================================================================

/dev/mem是實體記憶體的全映像,可以用來訪問實體記憶體,一般用法是open("/dev/mem",O_RDWR|O_SYNC),然後mmap,接著就可以用mmap的地址來訪問實體記憶體,這實際上就是實現使用者空間驅動的一種方法。

有幾個論據傾向於使用者空間程式設計,有時編寫一個所謂的使用者空間裝置驅動對比鑽研核心是一個明智的選擇,使用者空間驅動的好處在於:

  1. 完整的C庫可以連結,驅動可以進行許多奇怪的任務,而不用依靠外面的程式(實現使用策略的工具程式,常常隨著驅動自身釋出)。  
  2. 程式設計師可以在驅動程式碼上執行常用的偵錯程式,而不必除錯一個執行中的核心的彎路。  
  3. 如果一個使用者空間驅動掛起了,你可以簡單地殺死它。使用者空間驅動出現問題不可能掛起整個系統,除非被控制的硬體真的瘋掉了。  
  4. 使用者記憶體是可交換的,不像核心記憶體。這樣一個不常使用卻有很大一個驅動的裝置不會佔據別的程式可以用到的RAM,除了在它實際在用時。  
  5. 一個精心設計的 驅動程式仍然可以如同核心空間驅動一樣允許對裝置的並行存取。  
  6. 如果你必須編寫一個封閉原始碼的驅動,使用者空間的選項使你容易辨明不明朗的許可的情況和改變的核心介面帶來的問題。  
  1. 完整的C庫可以連結,驅動可以進行許多奇怪的任務,而不用依靠外面的程式(實現使用策略的工具程式,常常隨著驅動自身釋出)。  
  2. 程式設計師可以在驅動程式碼上執行常用的偵錯程式,而不必除錯一個執行中的核心的彎路。  
  3. 如果一個使用者空間驅動掛起了,你可以簡單地殺死它。使用者空間驅動出現問題不可能掛起整個系統,除非被控制的硬體真的瘋掉了。  
  4. 使用者記憶體是可交換的,不像核心記憶體。這樣一個不常使用卻有很大一個驅動的裝置不會佔據別的程式可以用到的RAM,除了在它實際在用時。  
  5. 一個精心設計的 驅動程式仍然可以如同核心空間驅動一樣允許對裝置的並行存取。  
  6. 如果你必須編寫一個封閉原始碼的驅動,使用者空間的選項使你容易辨明不明朗的許可的情況和改變的核心介面帶來的問題。  

但是,使用者空間的裝置驅動有幾個缺點,最重要的是:

  1. 中斷在使用者空間無法使用,在某些平臺上有對這個限制的解決方法,例如在IA32體系結構上的vm86系統呼叫。  
  2. 只可能通過記憶體對映/dev/mem來使用DMA,而且只有特權使用者可以這樣做。  
  3. 存取I/O埠只能在呼叫ioperm或者iopl只有,此外,不是所有的平臺都支援這些系統呼叫,而存取/dev/port可能太慢而無效率,這些系統呼叫和裝置檔案都要求特權使用者。  
  4. 響應時間慢,因為需要上下文切換在使用者和硬體之間傳遞訊息和動作。  
  5. 更壞的是,如果驅動已經被交換到硬碟,響應時間會長到不可接受,使用mlock系統呼叫可能會有幫助,但是你需要經常鎖住許多記憶體頁,因為一個使用者空間程式依賴大量的庫程式碼,mlock也限制在授權使用者上。  
  6. 最重要的裝置不能在使用者空間處理,包括網路介面和塊裝置。  
  1. 中斷在使用者空間無法使用,在某些平臺上有對這個限制的解決方法,例如在IA32體系結構上的vm86系統呼叫。  
  2. 只可能通過記憶體對映/dev/mem來使用DMA,而且只有特權使用者可以這樣做。  
  3. 存取I/O埠只能在呼叫ioperm或者iopl只有,此外,不是所有的平臺都支援這些系統呼叫,而存取/dev/port可能太慢而無效率,這些系統呼叫和裝置檔案都要求特權使用者。  
  4. 響應時間慢,因為需要上下文切換在使用者和硬體之間傳遞訊息和動作。  
  5. 更壞的是,如果驅動已經被交換到硬碟,響應時間會長到不可接受,使用mlock系統呼叫可能會有幫助,但是你需要經常鎖住許多記憶體頁,因為一個使用者空間程式依賴大量的庫程式碼,mlock也限制在授權使用者上。  
  6. 最重要的裝置不能在使用者空間處理,包括網路介面和塊裝置。  

綜上,使用者空間驅動不能做的事情畢竟太多,感興趣的應用程式還是存在:對SCSI掃描器裝置的支援(由SANE包實現)和CD燒錄機(由cdrecord和別的工具實現)。在兩種情況下,使用者級別的裝置情況依賴"SCSI generic"核心驅動,它輸出了底層的SCSI功能給使用者程式,因此它們可以驅動它們自己的硬體。

當你開始處理新的沒有用過的硬體時,通過開發使用者空間驅動,你可以學習去管理你的硬體,不必擔心掛起整個系統,一旦你完成了,在一個核心模組中封裝軟體就會是一個簡單的操作了。

Notes: 新核心已經限制/dev/mem中1M以上的記憶體訪問,這是一個可配置的選項CONFIG_STRICT_DEVMEM,在我的機器上已經選擇n了,可是好像還是隻能對映1M一下實體記憶體。

在ULCC中通過mmap對映實體記憶體地址到虛擬地址,實現物理頁的noncacheable,這主要是通過對映/dev/mem修改PAT中的頁面屬性達到的。X86的頁面屬性表(PAT)能夠在頁面粒度上設定記憶體屬性,PAT是對MTRR的補充,通過MTRR可以為實體地址區域設定記憶體型別,但是PAT比MTRR更靈活,因為它可以在頁面級別設定屬性,而且硬體上也不限制屬性設定的數量。PAT相當靈活,即使多個虛擬地址對映到同一個實體記憶體地址,也不會引起記憶體型別的衝突,通過PAT能夠設定多種型別的記憶體屬性,其中最常用的有4種:Write-BackUcachedWrite-CombinedUncached-Minux

  1. fm = open("/dev/mem", O_RDWR|O_SYNC);  
  2. mmap((void *)addr, PAGE_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_FIXED, fm, pfn<<PAGE_SHIFT);  
  1. fm = open("/dev/mem", O_RDWR|O_SYNC);  
  2. mmap((void *)addr, PAGE_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_FIXED, fm, pfn<<PAGE_SHIFT);  

/dev/kmem:核心看到的虛擬記憶體的全映像,可以用來訪問kernel的內容。

What Is /proc/kcore?
None of the files in /proc are really there--they're all, "pretend," files made up by the kernel, to give you information about the system and don't take up any hard disk space.

/proc/kcore is like an "alias" for the memory in your computer. Its size is the same as the amount of RAM you have, and if you read it as a file, the kernel does memory reads.


轉載地址:http://blog.csdn.net/su_linux/article/details/8737690