46-記憶體對映的保護和同步
1. 保護許可權
mmap函式的prot引數可指定記憶體對映區的保護許可權,例如指定PROT_READ和PROT_EXEC,在呼叫open開啟檔案就應該指定O_RDONLY或者O_RDWR。如果指定了PROT_WRITE,開啟的檔案應該使用O_WRONLY或者O_RDWR。
由於一些硬體架構提供的記憶體保護粒度有所不同,情況也會變的複雜:
1. 一般開啟檔案指定O_RDWR標記就已經滿足基本的使用了。
2. 如果prot引數只指定了PROT_WRITE,且open以O_WRONLY標記開啟檔案,記憶體對映的保護許可權和開啟檔案的許可權理論上是相容的,但是呼叫mmap卻會失敗,設定errno為EACCES錯誤。因為一些硬體架構上PROT_WRITE隱含了PROT_READ,這意味著系統分頁可寫也可讀,但這與O_WRONLY標記實際上是不相容的
例如下面這一段程式碼:
//以O_WRONLY許可權開啟檔案 int fd = open("test.txt", O_WRONLY, 0644); if (fd < 0){ perror("open error"); } //拓展檔案大小4096 lseek(fd , 4096, SEEK_SET); write(fd , "0" , 1); //指向檔案開頭 lseek(fd , 0 , SEEK_SET); //建立共享對映區,指定PROT_WRITE許可權 addr =(char *)mmap(NULL , 4096 , PROT_WRITE , MAP_SHARED , fd , 0); if (addr == MAP_FAILED){ perror("mmap err: "); //進一步判斷是否為EACCES錯誤 if(errno == EACCES){ puts("EACCES error"); exit(-1); } }
程式執行結果:
3. 以O_RDONLY標記開啟一個檔案,mmap呼叫指定了MAP_PRIVATE建立私有記憶體對映,因為MAP_PRIVATE對映上的修改操作不會反映到開啟的檔案中,所以prot引數可以指定任意的組合許可權。
對於一個MAP_SHARED對映來說,在MAP_SHARED對映上的修改操作會反映到開啟的檔案中,唯一與O_RDONLY標記相容的記憶體保護許可權就是PROT_READ|PROT_EXEC。
2. 對映區域同步——msync
一般,為確保對映區的資料寫入物理磁碟上的檔案中,在呼叫munmap解除對映前需要呼叫msync函式。前面我們沒有呼叫msync函式依然能在檔案中看到寫入的資料,這是因為核心會自動將MAP_SHARED對映區的內容同步到開啟的檔案中,但核心並不保證何時同步資料。
通過呼叫msync函式可以讓應用程式顯式的控制檔案與記憶體對映中的資料同步。
#include <sys/mman.h>
int msync(void *addr, size_t length, int flags);
返回值:成功返回0,失敗返回-1並設定errno
addr:需要同步的記憶體對映區起始地址
length:需要同步的資料大小
flags:設定同步資料的方式(MS_SYNC,MS_ASYNC,MS_INVALIDATE)
MS_SYNC:以同步方式寫入資料,當資料更新完畢就會返回
MS_ASYNC:以非同步方式寫入資料,呼叫之後會立即返回,不會等到更新完畢
MS_INVALIDATE:如果共享對映區和開啟的檔案中資料不一致,那麼共享對映區的內容就會失效,然後使用共享對映區的其他程序從開啟的檔案中複製對應的內容。