天天看點

Linux-記憶體共享映射 程序間通信-記憶體共享映射 一、簡述二、mmap 函數三、應用示例

程序間通信-記憶體共享映射

一、簡述

  • mmap()

    系統調用使得程序之間通過映射同一個普通檔案實作

    共享記憶體

    。普通檔案被映射到

    程序位址空間

    後,程序可以向通路普通記憶體一樣對檔案進行通路,不必再調用read(),write()等操作。
  • mmap()

    的作用是映射檔案描述符fd指定檔案的

    [off,off + len]

    區域至調用程序的

    [addr, addr + len]

    的記憶體區域, 如下圖所示:
Linux-記憶體共享映射 程式間通信-記憶體共享映射 一、簡述二、mmap 函數三、應用示例

二、mmap 函數

功能:

mmap() 可以把磁盤檔案的一部分直接映射到記憶體,這樣檔案中的位置直接就有對應的記憶體

位址,對檔案的讀寫可以直接用指針來做而不需要read/write函數。

頭檔案:

#include <sys/mman.h>
           

函數原型:

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

參數說明:

  • addr

    :指定檔案應被映射到程序空間的起始位址,一般被指定一個空指針,此時選擇起始位址的任務留給核心來完成。
  • len

    :是映射到調用程序位址空間的位元組數,它從被映射檔案開頭offset個位元組開始算起。
  • prot

    :指定空想記憶體的通路權限。可取如下幾個值的或:PROT_READ(可讀)、PROT_WRITE(可寫)、PROT_EXEC(可執行)、PROT_NONE(不可通路)。
  • flag

    :由以下幾個常值指定:MAP_SHARED、MAP_PRIVATE、MAP_FIXED,其中,MAP_SHARED,MAP_PRIVATE必選其一,而MAP_FIXED則不推薦使用。
    • MAP_SHARED:多個程序對同一個檔案的映射是共享的,一個程序對映射的記憶體做了修

      改,另一個程序也會看到這種變化。

    • MAP_PRIVATE:多個程序對同一個檔案的映射不是共享的,一個程序對映射的記憶體做了修

      改,另一個程序并不會看到這種變化,也不會真的寫到檔案中去。

  • fd

    :為即将映射到程序空間的檔案描述字,一般由open()傳回,同時,fd可以指定為-1,此時須指定flags參數中的MAP_ANON,表明進行的是匿名映射(不涉及具體的檔案名,避免了檔案的建立及打開,很顯然隻能用于具有親緣關系的程序間進行通信)。
  • offset

    :表示偏移量,一般設為0,表示從檔案頭開始映射。

傳回值:

函數的傳回值為最後檔案映射到程序空間的位址,程序可直接操作起始位址為該值的有效位址。

三、應用示例

mmap_w.c

/* process_mmap_w.c*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#define MAPLEN 0x1000 //十進制4096

//用于程序間通信,一般設定為結構體,用來傳輸資料 
struct STU {
	int id;
	char name[20];
	char sex;
};

//自定義的出錯的處理函數 
void sys_err(char *str, int exitno) {
	perror(str);
	exit(exitno);
}

//從終端傳入檔案名 
int main(int argc, char *argv[]) {
	struct STU *mm;
	int fd, i = 0;
	
	// 沒有從終端傳入檔案名産生錯誤 
	if (argc < 2) {
		printf("./a.out filename\n");
		exit(1);
	}
	
	//根據傳入的 argv[1]檔案名打開相應的臨時檔案 
	fd = open(argv[1], O_RDWR | O_CREAT, 0777);
	
	//磁盤檔案大小不為零 
	if (fd < 0)
		sys_err("open", 1);
	
	//在檔案MAPLEN位置寫入資料,進而擴從fd檔案大小為MAPLEN 
	if (lseek(fd, MAPLEN-1, SEEK_SET) < 0)
		sys_err("lseek", 3); 
	if (write(fd, "\0", 1) < 0)
		sys_err("write", 4);
		
		
	//共享記憶體映射 	
	//參數1 :null:由作業系統找記憶體的空位 
	//參數2:MAPLEN記憶體長度
	//參數3:port頁面屬性 讀寫
	//參數4: MAP_SHARED 記憶體更改 檔案也更改
	//參數5:fd 你要映射的磁盤檔案 
	//參數6:0 偏移量從 0 開始 
	mm = mmap(NULL, MAPLEN, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
	
	
	//映射發生錯誤 
	if (mm == MAP_FAILED)
		sys_err("mmap", 2);
	
	//關閉檔案描述符	
	close(fd);
	
	while (1) {
		
		//往記憶體中寫入資料 
		mm->id = i;
		sprintf(mm->name, "zhang-%d", i);
		if (i % 2 == 0)
			mm->sex = 'm';
		else
			mm->sex = 'w';
		i++;
		sleep(1);
	}
	
	//解除映射關系 
	munmap(mm, MAPLEN);
	
	return 0;
}
           

mmap_r.c

/* process_mmap_r.c*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#define MAPLEN 0x1000
struct STU {
	int id;
	char name[20];
	char sex;
};
void sys_err(char *str, int exitno) {
	perror(str);
	exit(exitno);
}
int main(int argc, char *argv[]) {
	struct STU *mm;
	int fd, i = 0;
	if (argc < 2) {
		printf("./a.out filename\n");
		exit(1);
	}
	fd = open(argv[1], O_RDWR);
	if (fd < 0)
		sys_err("open", 1);
	mm = mmap(NULL, MAPLEN, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
	if (mm == MAP_FAILED)
		sys_err("mmap", 2);
	close(fd);
	
	//釋放我們建立的本地檔案(映射的檔案設定為臨時檔案)
	unlink(argv[1]);
	
	while (1) {
		printf("%d\n", mm->id);
		printf("%s\n", mm->name);
		printf("%c\n", mm->sex);
		sleep(1);
	}
	munmap(mm, MAPLEN);
	return 0;
}
           

運作結果:

程序1:寫

Linux-記憶體共享映射 程式間通信-記憶體共享映射 一、簡述二、mmap 函數三、應用示例

程序2:讀

Linux-記憶體共享映射 程式間通信-記憶體共享映射 一、簡述二、mmap 函數三、應用示例

參考來源:

https://blog.csdn.net/windgs_yf/article/details/81146887

繼續閱讀