1. 程式人生 > >Docker容器使用/dev/mem與HOST端對映同一段實體記憶體

Docker容器使用/dev/mem與HOST端對映同一段實體記憶體

    我們知道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            /* 容器讀這段記憶體 */