前面文章介紹了進程間常用的通信方式: 無名管道和命名管道,這篇文章介紹內存映射,內存映射在多進程訪問文件讀寫的時候非常方便。
1. 內存映射mmap函數介紹
mmap函數可以將磁盤上的文件映射到內存空間中,返回映射的首地址。
相關函數: mmap munmap msync

函數原型與參數介紹:
#include #include int msync(const void *start, size_t length, int flags); 函數功能: 把對內存區域所做的更改同步到文件 void *mmap(void *addr, size_t length, int prot, int flags,int fd, off_t offset); 函數功能: 用來將某個文件內容映射到內存中,對該內存區域的存取即是直接對該文件內容的讀寫。通過這樣可以加快文件訪問速度。 返回值:成功返回映射的內存的起始地址。 (1) 第一個參數start指向欲對應的內存起始地址,通常設為NULL,代表讓系統自動選定地址,對應成功后該地址會返回。 (2) 第二個參數length代表將文件中多大的部分對應到內存。 (3) 第三個參數prot代表映射區域的保護方式有下列組合: PROT_EXEC 映射區域可被執行 PROT_READ 映射區域可被讀取 PROT_WRITE 映射區域可被寫入 PROT_NONE 映射區域不能存取 (4) 第四個參數 flags會影響映射區域的各種特性: MAP_FIXED 如果參數start所指的地址無法成功建立映射時,則放棄映射,不對地址做修正。通常不鼓勵用此旗標。 MAP_SHARED 對映射區域的寫入數據會復制回文件內,而且允許其他映射該文件的進程共享。 MAP_PRIVATE 對映射區域的寫入操作會產生一個映射文件的復制,即私人的“寫入時復制”, 對此區域作的任何修改都不會寫回原來的文件內容。 MAP_ANONYMOUS 建立匿名映射。此時會忽略參數fd,不涉及文件,而且映射區域無法和其他進程共享。 MAP_DENYWRITE 只允許對映射區域的寫入操作,而不能對fd指向的文件進行讀寫,對該文件直接寫入的操作將會被拒絕。 MAP_LOCKED 將映射區域鎖定住,這表示該區域不會被置換(swap)。 在調用mmap()時必須要指定MAP_SHARED 或MAP_PRIVATE。 (5) 第五個參數fd為open()返回的文件描述詞,代表欲映射到內存的文件。 (6) 第六個參數offset為文件映射的偏移量,通常設置為0,代表從文件最前方開始對應,offset必須是分頁大小的整數倍。 用法示例: fb_mem=mmap(NULL,smem_len,PROT_READ|PROT_WRITE,MAP_SHARED,fb,0); int munmap(void *addr, size_t length); 函數功能: 取消映射,用來取消參數start所指的映射內存起始地址,參數length則是欲取消的內存大小。當進程結束,映射內存會自動解除,但關閉對應的文件描述詞時不會解除映射。返回值:如果解除映射成功則返回0,否則返回-1。 通過內存映射進行進程通信,多個進程可以同時映射同一個文件到內存空間,只要一個進程對文件進行了修改,其他進程都可以得到修改的數據。 注意: 打開文件的權限需要與映射的權限一致!
2. 案例代碼: mmap用法示例(1)
下面代碼的功能: 創建一個新文件,設置文件大小,使用mmap函數映射文件地址出來,對地址直接拷貝數據進入,再取消映射。 這時再打開文件,數據已經存放到到文件中了。演示通過mmap映射文件地址方式讀寫文件。
#include #include #include #include #include #include #include #include #include int main(int argc,char **argv) { if(argc!=2) { printf("./a.out <文件>\n"); return 0; } /*1. 創建一個文件*/ int fd; fd=open(argv[1],O_RDWR|O_CREAT,S_IRWXU); if(fd<0) { printf("%s 文件打開失敗.\n",argv[1]); return 0; } /*2. 設置文件的大小*/ ftruncate(fd, 1024); /*3. 獲取文件大小*/ struct stat s_buf; fstat(fd,&s_buf); printf("文件的大小:%d Byte\n",s_buf.st_size); /*4. 映射文件到內存空間*/ unsigned char *mem_p=NULL; mem_p=mmap(NULL,s_buf.st_size,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0); if(mem_p==NULL) { printf("文件映射失敗.\n"); close(fd); return 0; } /*5. 關閉文件*/ close(fd); /*6. 實現文件讀寫*/ strcpy(mem_p,"mmap函數測試.實現文件讀寫."); /*7. 打印文件里的內容*/ printf("mem_p=%s\n",mem_p); /*8. 取消映射*/ munmap(mem_p,s_buf.st_size); return 0; }
3. 案例代碼: mmap用法示例(2)
下面代碼的功能: 程序運行時,從命令行傳入一個存在的文件路徑進去,再調用open打開文件,再通過mmap映射文件地址,得到地址之后向文件拷貝一串字符串數據進去。
注意: 通過mmap映射的地址寫數據一定要保證范圍不能超過文件的本身大小范圍。超過就段錯誤了。
#include #include #include #include #include #include #include #include #include #include char buff[]="內存映射測試"; int main(int argc,char **argv) { if(argc!=2) { printf("./app <文件>\n"); return 0; } char *m_p; struct stat stat_buf; stat(argv[1],&stat_buf); int fd=open(argv[1],O_RDWR); m_p=mmap(0,stat_buf.st_size,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0); if(m_p!=NULL)printf("映射成功!\n"); printf("m_p=%s\n",m_p); //memset(m_p,0,stat_buf.st_size); memcpy(m_p,buff,strlen(buff)); munmap(m_p,stat_buf.st_size); close(fd); return 0; }
4. 案例代碼: 多進程并發拷貝一個大文件
代碼要求: 使用mmap函數映射文件到內存。 memcpy()
使用多進程并發拷貝一個大文件,鞏固mmap的用法
詳細要求: 創建5個子進程同時拷貝一個文件,每個進程拷貝文件的一部分。
int truncate(const char *path, off_t length)設置指定文件的大小。
拷貝結構圖如下:
示例代碼:
#include #include #include #include #include #include #include #include #include #include //定義子進程的數量 #define FORK_NUMBER 4 /* 實現多進程并發拷貝大文件---mmap */ int main(int argc,char **argv) { if(argc!=3) { printf("./a.out <拷貝的源文件> <新文件>\n"); return 0; } /*1. 打開源文件*/ int src_fd=open(argv[1],O_RDWR); if(src_fd<0) { printf("%s 源文件打開失敗.\n",argv[1]); return -1; } /*2. 創建新文件*/ int new_fd=open(argv[2],O_RDWR|O_CREAT,S_IRUSR|S_IWUSR); if(new_fd<0) { printf("%s 新文件創建失敗.\n",argv[2]); return -2; } /*3. 獲取源文件大小設置新文件的大小*/ struct stat s_buff; fstat(src_fd,&s_buff); printf("源文件的字節大小:%d\n",s_buff.st_size); ftruncate(new_fd,s_buff.st_size); /*4. 映射源文件到內存空間*/ unsigned char *src_p; src_p=mmap(NULL,s_buff.st_size,PROT_READ|PROT_WRITE,MAP_SHARED,src_fd,0); if(src_p==NULL) { close(src_fd); printf("源文件映射失敗.\n"); return -3; } /*5. 映射新文件到內存空間*/ unsigned char *new_p; new_p=mmap(NULL,s_buff.st_size,PROT_READ|PROT_WRITE,MAP_SHARED,new_fd,0); if(new_p==NULL) { close(new_fd); printf("新文件映射失敗.\n"); return -4; } /*6. 關閉文件*/ close(new_fd); close(src_fd); /*7. 計算子進程和父進程拷貝的文件字節大小*/ int cp_size; int main_size; cp_size=s_buff.st_size/FORK_NUMBER; //每個子進程拷貝的大小 main_size=s_buff.st_size%FORK_NUMBER; //父進程拷貝的大小 /*8. 創建子進程*/ int i; for(i=0;ilinux 任務調度
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。