Linux 程序通訊之:記憶體對映(Memory Map)
阿新 • • 發佈:2018-11-29
一、簡介
正如其名(Memory Map),mmap
可以將某個裝置或者檔案對映到應用程序的記憶體空間中。通過直接的記憶體操作即可完成對裝置或檔案的讀寫。.
通過對映同一塊實體記憶體,來實現共享記憶體,完成程序間的通訊。由於減少了資料複製的次數,一定程度上提高了程序間通訊的效率。
二、API 說明
1. 標頭檔案
#include <sys/mman.h>
2. 建立記憶體對映
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
addr
length
: 被對映到程序空間的記憶體塊大小prot
: 被對映記憶體的訪問許可權PROT_EXEC
: 記憶體頁可執行PROT_READ
: 記憶體頁可讀PROT_WRITE
: 記憶體頁可寫PROT_NONE
: 記憶體頁不可訪問
flags
: 指定程式對記憶體塊所做的改變將造成的影響,通常有:MAP_SHARED
: 共享的形式,對記憶體塊所做的修改將儲存到檔案中MAP_PRIVATE
MAP_FIXED
: 使用指定的對映起始地址MAP_ANONYMOUS
/MAP_ANON
: 匿名對映,即不和任何檔案關聯,同時將 fd 設定為 -1。通常需要程序間有一定關係才能使用這種對映方式
fd
: 檔案描述符,即open()
函式返回的值offset
: 指定從檔案的哪一部分開始對映,必須是記憶體頁的整數倍,通常為 0- 返回值 : 成功返回指向對映記憶體的指標,失敗返回 -1,並設定合適的 errno 值
3. 解除記憶體對映
int munmap(void *addr, size_t length);
addr
: 對映記憶體的起始指標,必須是 mmap 方法返回的那個值length
: 對映到程序空間的記憶體塊大小- 返回值 : 成功返回 0,失敗返回 -1,並設定合適的 errno 值
三、示例
1. 無血緣關係程序通訊
寫端:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
typedef struct _data {
int a;
char b[64];
} Data;
int main() {
Data *addr;
Data data = { 10, "Hello World\n" };
int fd;
fd = open("mmap_temp_file", O_RDWR|O_CREAT|O_TRUNC, 0644);
if (fd == -1) {
perror("open failed\n");
exit(EXIT_FAILURE);
}
ftruncate(fd, sizeof(data));
// 使用fd建立記憶體對映區
addr = (Data *)mmap(NULL, sizeof(data), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
if (addr == MAP_FAILED) {
perror("mmap failed!\n");
exit(EXIT_FAILURE);
}
close(fd); // 對映完後文件就可以關閉了
memcpy(addr, &data, sizeof(data)); // 往對映區寫資料
munmap(addr, sizeof(data)); // 釋放對映區
return 0;
}
讀端:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
typedef struct _data {
int a;
char b[64];
} Data;
int main() {
Data *addr;
int fd;
fd = open("mmap_temp_file", O_RDONLY);
if (fd == -1) {
perror("open failed\n");
exit(EXIT_FAILURE);
}
// 使用fd建立記憶體對映區
addr = (Data *)mmap(NULL, sizeof(Data), PROT_READ, MAP_SHARED, fd, 0);
if (addr == MAP_FAILED) {
perror("mmap failed!\n");
exit(EXIT_FAILURE);
}
close(fd); // 對映完後文件就可以關閉了
printf("read form mmap: a = %d, b = %s\n", addr->a, addr->b); // 往對映區寫資料
munmap(addr, sizeof(Data)); // 釋放對映區
return 0;
}
執行結果:
2. 血緣關係程序通訊
存在血緣關係的話,可以使用 匿名 的方式建立對映區,這樣就不需要那個臨時檔案了。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
int m_var = 100;
int main() {
int *addr;
pid_t child_pid;
// 以匿名的方式建立記憶體對映區,適用於存在血緣關係的程序間
addr = (int *)mmap(NULL, sizeof(int), PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANON, -1, 0);
if (addr == MAP_FAILED) {
perror("mmap failed!\n");
exit(EXIT_FAILURE);
}
child_pid = fork(); // 建立子程序
if (child_pid == 0) {
*addr = 666; // 往記憶體對映區寫資料
m_var = 200;
printf("child process: *addr = %d, m_var = %d\n", *addr, m_var);
} else {
sleep(1);
printf("parent process: *addr = %d, m_var = %d\n", *addr, m_var); // 讀記憶體對映區的資料
wait(NULL);
int ret = munmap(addr, sizeof(int)); // 釋放記憶體對映區
if (ret == -1) {
perror("munmap failed\n");
exit(EXIT_FAILURE);
}
}
return 0;
}
執行結果:
addr
的值,父子程序都成功改變了。全域性變數 m_var
的值,父子程序遵從 讀時共享,寫時複製 原則。