1. 程式人生 > >46-記憶體對映的保護和同步

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:如果共享對映區和開啟的檔案中資料不一致,那麼共享對映區的內容就會失效,然後使用共享對映區的其他程序從開啟的檔案中複製對應的內容。