1. 程式人生 > >Linux的記憶體I/O對映

Linux的記憶體I/O對映

記憶體的I/O對映是將檔案某區域的內容對映到程序的虛擬空間的技術

通過對檔案的記憶體IO對映,可使用使用者對檔案的操作轉換為對記憶體的操作,這樣不僅使用方便而且提高了儲存速度。

1.定義儲存空間

void *mmap(void *addr, size_t length, int prot, int flags,
                  int fd, off_t offset);

addr:引數用於指定對映儲存區的起始地址。通常將其設定為0,這表示由系統選擇該對映區的起始地址。此函式的返回值是該對映區的起始地址。

fd:引數是指定要被對映檔案的描述符。在檔案對映到地址空間之前,必須先開啟該檔案。

length:參,數是對映的位元組數,

offset:是要對映位元組在檔案中的起始偏移量(有關off值的一些限制將在後面說明),

prot:引數指定了對映儲存區的保護要求,如圖14-25所示。可設定為下面的任意組合

注意:prot的設定不能超過open呼叫的許可權,如open開啟為只讀,但在這用了可寫,這是不允許的

flags:會影響對映區域的各種特性

  • MAP_FIXED:如果引數 start 所指的地址無法成功建立對映時,則放棄對映,不對地址做修正。通常不鼓勵用此標誌。
  • MAP_SHARED:對對映區域的寫入資料會複製迴文件內,而且允許其他對映該檔案的程序共享。儲存操作立刻修改對映檔案內容
  • MAP_PRIVATE:對對映區域的寫入操作會產生一個對映檔案的複製,即私人的“寫入時複製”(copy on write)對此區域作的任何修改都不會寫回原來的檔案內容。儲存操作導致建立對映檔案的副本,並對副本讀寫
  • MAP_ANONYMOUS:建立匿名對映。此時會忽略引數fd,不涉及檔案,而且對映區域無法和其他程序共享。
  • MAP_DENYWRITE:只允許對對映區域的寫入操作,其他對檔案直接寫入的操作將會被拒絕。
  • MAP_LOCKED:將對映區域鎖定住,這表示該區域不會被置換(swap)。
  • 在呼叫mmap()時必須要指定MAP_SHARED 或MAP_PRIVATE。

off的值和addr的值(如果指定了MAP_FIXED)通常被要求是系統虛擬儲存頁長度的倍數。

虛擬儲存頁長可用帶引數_SC_PAGESIZE或_SC_PAGE_SIZE的sysconf函式(見2.5.4節)得到。因為off和addr常常指定為0,所以這種要求一般並不重要。

與對映區相關的訊號有SIGSEGV和SIGBUS

訊號SIGSEGV通常用於指示程序試圖訪問對它不可用的儲存區。如果對映儲存區被mmap指定成了只讀的,那麼程序試圖將資料存入這個對映儲存區的時候,也會產生此訊號。

如果對映區的某個部分在訪問時已不存在,則產生SIGBUS訊號。例如,假設用檔案長度映射了一個檔案,但在引用該對映區之前,另一個程序已將該檔案截斷。此時,如果程序試圖訪問對應於該檔案已截去部分的對映區,將會接收到SIGBUS訊號。

注意:

子程序能通過fork繼承儲存對映區(因為子程序複製父程序地址空間,而儲存對映區是該地址空間中的一部分),

但是由於同樣的原因,新程式則不能通過exec繼承儲存對映區。

2.更改現有對映的許可權

int mprotect(void *addr, size_t len, int prot);  

prot的合法值與mmap中prot引數的一樣(見圖14-25),請注意,地址引數addr的值必須是,系統頁長的整數倍。

如果修改的頁是通過MAP SHARED標誌對映到地址空間的,那麼修改並不會立即寫回到檔案中。相反,何時寫回髒頁由核心的守護程序決定,決定的依據是系統負載和用來限制在系統失敗事件中的資料損失的配置引數。因此,如果只修改了一頁中的一個位元組,當修改被寫回到檔案中,時,整個頁都會被寫回。

3.沖洗儲存對映區

如果共享對映中的頁已修改,那麼可以呼叫msync將該頁沖洗到被對映的檔案中. msync函式類似於fsync (見3.13節),但作用於儲存對映區。

int msync(void *addr, size_t length, int flags);

4.解除記憶體對映

int munmap(void *addr, size_t length);

例項程式碼:

功能:實現類似命令cp的功能

#include <stdio.h>
#include <pthread.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <syslog.h>
#include <string.h>
#include <sys/mman.h>


#define COPYINCR (1024*1024*1024) /* 1 GB */
int
main(int argc, char *argv[])
{
	int fdin, fdout;
	void *src, *dst;
	size_t copysz;
	struct stat sbuf;
	off_t fsz = 0;
	if (argc != 3)
		printf("usage: %s <fromfile> <tofile>", argv[0]);
	if ((fdin = open(argv[1], O_RDONLY)) < 0)
		printf("can’t open %s for reading", argv[1]);
	if ((fdout = open(argv[2], O_RDWR | O_CREAT | O_TRUNC, 0666)) < 0)
		printf("can’t creat %s for writing", argv[2]);
	if (fstat(fdin, &sbuf) < 0) /* need size of input file */
		printf("fstat error");
	if (ftruncate(fdout, sbuf.st_size) < 0) /* set output file size */
		printf("ftruncate error");
	while (fsz < sbuf.st_size) 
	{
		if ((sbuf.st_size - fsz) > COPYINCR)
			copysz = COPYINCR;
		else
			copysz = sbuf.st_size - fsz;
		if ((src = mmap(0, copysz, PROT_READ, MAP_SHARED, fdin, fsz)) == MAP_FAILED)
			printf("mmap error for input");
		if ((dst = mmap(0, copysz, PROT_READ | PROT_WRITE,MAP_SHARED, fdout, fsz)) == MAP_FAILED)
			printf("mmap error for output");
		memcpy(dst, src, copysz); /* does the file copy */
		munmap(src, copysz);
		munmap(dst, copysz);
		fsz += copysz;
	}
	exit(0);
}