Docker容器使用/dev/mem與HOST端對映同一段實體記憶體
阿新 • • 發佈:2019-02-20
我們知道docker容器與HOST端有一定的隔離性,但同時也共享著一些資源,比如記憶體資源。今天我們就看一下Docker容器通過/dev/mem裝置節點與HOST共享一段實體記憶體。
要達到這個目的需進行如下操作:1) 劃分一段用於對映到/dev/mem裝置檔案的保留實體記憶體;2) 準備一段在容器和HOST端可執行的對映和讀寫實體記憶體的程式碼;3) 啟動一個帶有--privileged引數的容器; 4) 容器和HOST端分別通過程式對映和讀寫記憶體。
一 保留實體記憶體
可以參考inux通過核心啟動引數預留系統記憶體 這篇介紹在核心啟動時,通過啟動引數"memmap="來為系統保留一段記憶體。核心不會使用到這段保留出來的記憶體,這樣我們就可以放心使用這段記憶體而不用擔心衝突。
二 通過/dev/mem對映讀寫實體記憶體
有了保留記憶體,第二步就是寫一個小程式碼,通過將這段實體記憶體對映到/dev/mem以進行讀寫訪問。下面是程式碼dev_mem_rw.c:
#define _LARGEFILE64_SOURCE #include <unistd.h> #include <stdio.h> #include <sys/types.h> #include <sys/mman.h> #include <stdlib.h> #include <fcntl.h> #include <inttypes.h> #include <stdbool.h> #include <string.h> #if __LP64__ #define strtoptr strtoull #else #define strtoptr strtoul #endif static inline size_t minsize(size_t input1, size_t input2) { return input1>input2 ? input2:input1; } static int usage() { #ifdef READ fprintf(stderr,"dev_mem_read <address>\n"); #elif defined WRITE fprintf(stderr,"dev_mem_write <address> <value>\n"); #endif return -1; } int main(int argc, char *argv[]) { int fd = -1, default_map_size = 32; uintptr_t addr = 0, endaddr = 0; off64_t mmap_start = 0; size_t mmap_size = 0; char *buff = NULL, *end = NULL; void* pointer = NULL; size_t page_size = getpagesize(); #ifdef READ if(argc < 2) #elif defined WRITE if(argc < 3) #endif return usage(); addr = strtoptr(argv[1], 0, 16); endaddr = 0; end = strchr(argv[1], '-'); if (end) endaddr = strtoptr(end + 1, 0, 16); if (!endaddr) endaddr = addr + default_map_size - 1; if (endaddr <= addr) { fprintf(stderr, "end address <= start address\n"); return -1; } fd = open("/dev/mem", O_RDWR | O_SYNC); if(fd < 0) { fprintf(stderr,"cannot open /dev/mem\n"); return -1; } mmap_start = addr & ~(page_size - 1); mmap_size = endaddr - mmap_start + 1; mmap_size = (mmap_size + page_size - 1) & ~(page_size - 1); pointer = mmap64(0, mmap_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, mmap_start); if(pointer == MAP_FAILED) { fprintf(stderr,"cannot mmap region\n"); return -1; } #ifdef WRITE bzero(pointer, mmap_size); #endif buff = pointer; #ifdef READ printf("buff=%s\n", buff); #elif defined WRITE strncpy(buff, argv[2], minsize(mmap_size, strlen(argv[2]))); #endif return 0; }
接下來進行編譯:
1) 編譯生成記憶體讀程式dev_mm_r:
gcc dev_mm_rw.c -static -DREAD -o dev_mm_r
2) 編譯生成記憶體寫程式dev_mm_w: ;
gcc dev_mem_rw.c -static -DWRITE -o dev_mm_w
三 建立測試容器
建立並啟動一個容器tst_dev_mem:
docker create --name tst_dev_mem --privileged -it debian /bin/bash
docker start tst_dev_mem
四 進行測試驗證
1 host端寫Docker讀
假設在第一步中我們預留實體記憶體的起始地址為0x10DBFFFFF,則操作如下:
./dev_mem_w 0x10DBFFFFF hello_world /* 將hello_world寫入實體記憶體0x10DBFFFFF */
docker cp dev_mem_r tst_dev_mem:/ /* 將讀記憶體程式拷貝到容器 */
docker exec tst_dev_mem /dev_mem_r 0x10DBFFFFF /* 容器讀這段記憶體 */
2 host端讀Docker寫 docker cp dev_mem_w tst_dev_mem:/ /* 將寫記憶體程式拷貝到容器 */
docker exec tst_dev_mem /dev_mem_w 0x10DBFFFFF hello_world /* 將hello_world寫入實體記憶體0x10DBFFFFF */
./dev_mem_r 0x10DBFFFFF /* 容器讀這段記憶體 */