mmap將一個文件或者其它對象映射進內存。文件被映射到多個頁上,如果文件的大小不是所有頁的大小之和,最后一個頁不被使用的空間將會清零。munmap執行相反的操作,刪除特定地址區域的對象映射。 當使用mmap映射文件到進程后,就可以直接操作這段虛擬地址進行文件的讀寫等操作,不必再調用read,write等系統調用.但需注意,直接對該段內存寫時不會寫入超過當前文件大小的內容. 采用共享內存通信的一個顯而易見的好處是效率高,因為進程可以直接讀寫內存,而不需要任何數據的拷貝。對于像管道和消息隊列等通信方式,則需要在內核和用戶空間進行四次的數據拷貝,而共享內存則只拷貝兩次數據:一次從輸入文件到共享內存區,另一次從共享內存區到輸出文件。實際上,進程之間在共享內存時,并不總是讀寫少量數據后就解除映射,有新的通信時,再重新建立共享內存區域。而是保持共享區域,直到通信完畢為止,這樣,數據內容一直保存在共享內存中,并沒有寫回文件。共享內存中的內容往往是在解除映射時才寫回文件的。因此,采用共享內存的通信方式效率是非常高的。 基于文件的映射,在mmap和munmap執行過程的任何時刻,被映射文件的st_atime可能被更新。如果st_atime字段在前述的情況下沒有得到更新,首次對映射區的第一個頁索引時會更新該字段的值。用PROT_WRITE 和 MAP_SHARED標志建立起來的文件映射,其st_ctime 和 st_mtime在對映射區寫入之后,但在msync()通過MS_SYNC 和 MS_ASYNC兩個標志調用之前會被更新。 用法:
#include <sys/mman.h>void *mmap(void *start, size_t length, int prot, int flags,int fd, off_t offset);int munmap(void *start, size_t length);返回說明:
成功執行時,mmap()返回被映射區的指針,munmap()返回0。失敗時,mmap()返回MAP_FAILED[其值為(void *)-1],munmap返回-1。errno被設為以下的某個值 EACCES:訪問出錯 EAGAIN:文件已被鎖定,或者太多的內存已被鎖定 EBADF:fd不是有效的文件描述詞 EINVAL:一個或者多個參數無效 ENFILE:已達到系統對打開文件的限制 ENODEV:指定文件所在的文件系統不支持內存映射 ENOMEM:內存不足,或者進程已超出最大內存映射數量 EPERM:權能不足,操作不允許 ETXTBSY:已寫的方式打開文件,同時指定MAP_DENYWRITE標志 SIGSEGV:試著向只讀區寫入 SIGBUS:試著訪問不屬于進程的內存區
參數:
start:映射區的開始地址。 length:映射區的長度。 prot:期望的內存保護標志,不能與文件的打開模式沖突。是以下的某個值,可以通過or運算合理地組合在一起
PROT_EXEC //頁內容可以被執行 PROT_READ //頁內容可以被讀取 PROT_WRITE //頁可以被寫入 PROT_NONE //頁不可訪問flags:指定映射對象的類型,映射選項和映射頁是否可以共享。它的值可以是一個或者多個以下位的組合體
MAP_FIXED //使用指定的映射起始地址,如果由start和len參數指定的內存區重疊于現存的映射空間,重疊部分將會被丟棄。如果指定的起始地址不可用,操作將會失敗。并且起始地址必須落在頁的邊界上。 MAP_SHARED //與其它所有映射這個對象的進程共享映射空間。對共享區的寫入,相當于輸出到文件。直到msync()或者munmap()被調用,文件實際上不會被更新。 MAP_PRIVATE //建立一個寫入時拷貝的私有映射。內存區域的寫入不會影響到原文件。這個標志和以上標志是互斥的,只能使用其中一個。 MAP_DENYWRITE //這個標志被忽略。 MAP_EXECUTABLE //同上 MAP_NORESERVE //不要為這個映射保留交換空間。當交換空間被保留,對映射區修改的可能會得到保證。當交換空間不被保留,同時內存不足,對映射區的修改會引起段違例信號。 MAP_LOCKED //鎖定映射區的頁面,從而防止頁面被交換出內存。 MAP_GROWSDOWN //用于堆棧,告訴內核VM系統,映射區可以向下擴展。 MAP_ANONYMOUS //匿名映射,映射區不與任何文件關聯。 MAP_ANON //MAP_ANONYMOUS的別稱,不再被使用。 MAP_FILE //兼容標志,被忽略。 MAP_32BIT //將映射區放在進程地址空間的低2GB,MAP_FIXED指定時會被忽略。當前這個標志只在x86-64平臺上得到支持。 MAP_POPULATE //為文件映射通過預讀的方式準備好頁表。隨后對映射區的訪問不會被頁違例阻塞。 MAP_NONBLOCK //僅和MAP_POPULATE一起使用時才有意義。不執行預讀,只為已存在于內存中的頁面建立頁表入口。fd:有效的文件描述詞。如果MAP_ANONYMOUS被設定,為了兼容問題,其值應為-1。 offset:被映射對象內容的起點。
一般說來,進程在映射空間的對共享內容的改變并不直接寫回到磁盤文件中,往往在調用munmap()后才執行該操作??梢酝ㄟ^調用msync()實現磁盤上文件內容與共享內存區的內容一致。
1.使用普通文件提供的內存映射:適用于任何進程之間;此時,需要打開或創建一個文件,然后再調用mmap();典型調用代碼如下:
fd=open(name, flag, mode); if(fd<0) ... ptr=mmap(NULL, len , PROT_READ|PROT_WRITE, MAP_SHARED , fd , 0);2.使用特殊文件提供匿名內存映射:適用于具有親緣關系的進程之間;由于父子進程特殊的親緣關系,在父進程中先調用mmap(),然后調用fork()。那么在調用fork()之后,子進程繼承父進程匿名映射后的地址空間,同樣也繼承mmap()返回的地址,這樣,父子進程就可以通過映射區域進行通信了。注意,這里不是一般的繼承關系。一般來說,子進程單獨維護從父進程繼承下來的一些變量。而mmap()返回的地址,卻由父子進程共同維護。 對于具有親緣關系的進程實現共享內存最好的方式應該是采用匿名內存映射的方式。此時,不必指定具體的文件,只要設置相應的標志即可.
mmap系統調用的最終目的是將,設備或文件映射到用戶進程的虛擬地址空間,實現用戶進程對文件的直接讀寫,這個任務可以分為以下三步: 1.在用戶虛擬地址空間中尋找空閑的滿足要求的一段連續的虛擬地址空間,為映射做準備(由內核mmap系統調用完成) 每個進程擁有3G字節的用戶虛存空間。但是,這并不意味著用戶進程在這3G的范圍內可以任意使用,因為虛存空間最終得映射到某個物理存儲空間(內存或磁盤空間),才真正可以使用。 那么,內核怎樣管理每個進程3G的虛存空間呢?概括地說,用戶進程經過編譯、鏈接后形成的映象文件有一個代碼段和數據段(包括data段和bss段),其中代碼段在下,數據段在上。數據段中包括了所有靜態分配的數據空間,即全局變量和所有申明為static的局部變量,這些空間是進程所必需的基本要求,這些空間是在建立一個進程的運行映像時就分配好的。除此之外,堆棧使用的空間也屬于基本要求,所以也是在建立進程時就分配好的,如圖3.1所示:
圖3.1 進程虛擬空間的劃分
在內核中,這樣每個區域用一個結構struct vm_area_struct 來表示.它描述的是一段連續的、具有相同訪問屬性的虛存空間,該虛存空間的大小為物理內存頁面的整數倍。可以使用 cat /proc//maps來查看一個進程的內存使用情況,pid是進程號.其中顯示的每一行對應進程的一個vm_area_struct結構. 下面是struct vm_area_struct結構體的定義:
/* * This struct defines a memory VMM memory area. There is one of these * per VM-area/task. A VM area is any part of the process virtual memory * space that has a special rule for the page-fault handlers (ie a shared * library, the executable area etc). */struct vm_area_struct { /* The first cache line has the info for VMA tree walking. */ unsigned long vm_start; /* Our start address within vm_mm. */ unsigned long vm_end; /* The first byte after our end address within vm_mm. */ /* linked list of VM areas per task, sorted by address */ struct vm_area_struct *vm_next, *vm_prev; struct rb_node vm_rb; /* * Largest free memory gap in bytes to the left of this VMA. * Either between this VMA and vma->vm_prev, or between one of the * VMAs below us in the VMA rbtree and its ->vm_prev. This helps * get_unmapped_area find a free area of the right size. */ unsigned long rb_subtree_gap; /* Second cache line starts here. */ struct mm_struct *vm_mm; /* The address space we belong to. */ pgprot_t vm_page_prot; /* access permissions of this VMA. */ unsigned long vm_flags; /* Flags, see mm.h. */ /* * For areas with an address space and backing store, * linkage into the address_space->i_mmap interval tree, or * linkage of vma in the address_space->i_mmap_nonlinear list. * * For private anonymous mappings, a pointer to a null terminated string * in the user process containing the name given to the vma, or NULL * if unnamed. */ union { struct { struct rb_node rb; unsigned long rb_subtree_last; } linear; struct list_head nonlinear; const char __user *anon_name; } shared; /* * A file's MAP_PRIVATE vma can be in both i_mmap tree and anon_vma * list, after a COW of one of the file pages. A MAP_SHARED vma * can only be in the i_mmap tree. An anonymous MAP_PRIVATE, stack * or brk vma (with NULL file) can only be in an anon_vma list. */ struct list_head anon_vma_chain; /* Serialized by mmap_sem & * page_table_lock */ struct anon_vma *anon_vma; /* Serialized by page_table_lock */ /* Function pointers to deal with this struct. */ const struct vm_Operations_struct *vm_ops; /* Information about our backing store: */ unsigned long vm_pgoff; /* Offset (within vm_file) in PAGE_SIZE units, *not* PAGE_CACHE_SIZE */ struct file * vm_file; /* File we map to (can be NULL). */ void * vm_private_data; /* was vm_pte (shared mem) */#ifndef CONFIG_MMU struct vm_region *vm_region; /* NOMMU mapping region */#endif#ifdef CONFIG_NUMA struct mempolicy *vm_policy; /* NUMA policy for the VMA */#endif}通常,進程所使用到的虛存空間不連續,且各部分虛存空間的訪問屬性也可能不同。所以一個進程的虛存空間需要多個vm_area_struct結構來描述。在vm_area_struct結構的數目較少的時候,各個vm_area_struct按照升序排序,以單鏈表的形式組織數據(通過vm_next指針指向下一個vm_area_struct結構)。但是當vm_area_struct結構的數據較多的時候,仍然采用鏈表組織的化,勢必會影響到它的搜索速度。針對這個問題,vm_area_struct還添加了vm_avl_hight(樹高)、vm_avl_left(左子節點)、vm_avl_right(右子節點)三個成員來實現AVL樹,以提高vm_area_struct的搜索速度。 假如該vm_area_struct描述的是一個文件映射的虛存空間,成員vm_file便指向被映射的文件的file結構,vm_pgoff是該虛存空間起始地址在vm_file文件里面的文件偏移,單位為物理頁面。
圖3.2 進程虛擬地址示意圖
因此,mmap系統調用所完成的工作就是準備這樣一段虛存空間,并建立vm_area_struct結構體,將其傳給具體的設備驅動程序.
2.建立虛擬地址空間和文件或設備的物理地址之間的映射(設備驅動完成)
建立文件映射的第二步就是建立虛擬地址和具體的物理地址之間的映射,這是通過修改進程頁表來實現的.mmap方法是file_opeartions結構的成員:
int (*mmap)(struct file *,struct vm_area_struct *);linux有2個方法建立頁表: (1) 使用remap_pfn_range一次建立所有頁表. int remap_pfn_range(struct vm_area_struct *vma, unsigned long virt_addr, unsigned long pfn, unsigned long size, pgprot_t prot); 返回值: 成功返回 0, 失敗返回一個負的錯誤值 參數說明:
vma : 用戶進程創建一個vma區域virt_addr : 重新映射應當開始的用戶虛擬地址. 這個函數建立頁表為這個虛擬地址范圍從 virt_addr 到 virt_addr_size.pfn :頁幀號, 對應虛擬地址應當被映射的物理地址. 這個頁幀號簡單地是物理地址右移 PAGE_SHIFT 位. 對大部分使用, VMA 結構的 vm_paoff 成員正好包含你需要的值. 這個函數影響物理地址從 (pfn<<PAGE_SHIFT) 到 (pfn<<PAGE_SHIFT)+size.size : 正在被重新映射的區的大小, 以字節.prot : 給新 VMA 要求的"protection". 驅動可(并且應當)使用在vma->vm_page_prot 中找到的值.(2) 使用nopage VMA方法每次建立一個頁表項.
struct page *(*nopage)(struct vm_area_struct *vma, unsigned long address, int *type);返回值: 成功則返回一個有效映射頁,失敗返回NULL. 參數說明:
address :代表從用戶空間傳過來的用戶空間虛擬地址.返回一個有效映射頁.(3) 使用方面的限制: remap_pfn_range不能映射常規內存,只存取保留頁和在物理內存頂之上的物理地址。因為保留頁和在物理內存頂之上的物理地址內存管理系統的各個子模塊管理不到。640 KB 和 1MB 是保留頁可能映射,設備I/O內存也可以映射。如果想把kmalloc()申請的內存映射到用戶空間,則可以通過mem_map_reserve()把相應的內存設置為保留后就可以。
3.當實際訪問新映射的頁面時的操作(由缺頁中斷完成)
(1) page cache及swap cache中頁面的區分:一個被訪問文件的物理頁面都駐留在page cache或swap cache中,一個頁面的所有信息由struct page來描述。struct page中有一個域為指針mapping ,它指向一個struct address_space類型結構。page cache或swap cache中的所有頁面就是根據address_space結構以及一個偏移量來區分的。
(2) 文件與 address_space結構的對應:一個具體的文件在打開后,內核會在內存中為之建立一個struct inode結構,其中的i_mapping域指向一個address_space結構。這樣,一個文件就對應一個address_space結構,一個 address_space與一個偏移量能夠確定一個page cache 或swap cache中的一個頁面。因此,當要尋址某個數據時,很容易根據給定的文件及數據在文件內的偏移量而找到相應的頁面。 (3) 進程調用mmap()時,只是在進程空間內新增了一塊相應大小的緩沖區,并設置了相應的訪問標識,但并沒有建立進程空間到物理頁面的映射。因此,第一次訪問該空間時,會引發一個缺頁異常。
(4) 對于共享內存映射情況,缺頁異常處理程序首先在swap cache中尋找目標頁(符合address_space以及偏移量的物理頁),如果找到,則直接返回地址;如果沒有找到,則判斷該頁是否在交換區 (swap area),如果在,則執行一個換入操作;如果上述兩種情況都不滿足,處理程序將分配新的物理頁面,并把它插入到page cache中。進程最終將更新進程頁表。
注:對于映射普通文件情況(非共享映射),缺頁異常處理程序首先會在page cache中根據address_space以及數據偏移量尋找相應的頁面。如果沒有找到,則說明文件數據還沒有讀入內存,處理程序會從磁盤讀入相應的頁面,并返回相應地址,同時,進程頁表也會更新.(5) 所有進程在映射同一個共享內存區域時,情況都一樣,在建立線性地址與物理地址之間的映射之后,不論進程各自的返回地址如何,實際訪問的必然是同一個共享內存區域對應的物理頁面。
通過strace統計系統調用的時候,經??梢钥吹絤map()與mmap2()。系統調用mmap()可以將某文件映射至內存(進程空間),如此可以把對文件的操作轉為對內存的操作,以此避免更多的lseek()與read()、write()操作,這點對于大文件或者頻繁訪問的文件而言尤其受益。但有一點必須清楚:mmap的addr與offset必須對齊一個內存頁面大小的邊界,即內存映射往往是頁面大小的整數倍,否則maaped_file_size%page_size內存空間將被閑置浪費。
演示一下,將文件/tmp/file_mmap中的字符轉成大寫,分別使用mmap與read/write二種方法實現。
/** @file: t_mmap.c*/#include <stdio.h>#include <ctype.h>#include <sys/mman.h> /*mmap munmap*/#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <unistd.h>int main(int argc, char *argv[]){ int fd; char *buf; off_t len; struct stat sb; char *fname = "/tmp/file_mmap"; fd = open(fname, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR); if (fd == -1) { perror("open"); return 1; } if (fstat(fd, &sb) == -1) { perror("fstat"); return 1; } buf = mmap(0, sb.st_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); if (buf == MAP_FAILED) { perror("mmap"); return 1; } if (close(fd) == -1) { perror("close"); return 1; } for (len = 0; len < sb.st_size; ++len) { buf[len] = toupper(buf[len]); /*putchar(buf[len]);*/ } if (munmap(buf, sb.st_size) == -1) { perror("munmap"); return 1; } return 0;}#gcc –o t_mmap t_mmap.c#strace ./t_mmapopen("/tmp/file_mmap", O_RDWR|O_CREAT, 0600) = 3 //open,返回fd=3fstat64(3, {st_mode=S_IFREG|0644, st_size=18, ...}) = 0 //fstat, 即文件大小18mmap2(NULL, 18, PROT_READ|PROT_WRITE, MAP_SHARED, 3, 0) = 0xb7867000 //mmap文件fd=3close(3) = 0 //close文件fd=3munmap(0xb7867000, 18) = 0 //munmap,移除0xb7867000這里的內存映射雖然沒有看到read/write寫文件操作,但此時文件/tmp/file_mmap中的內容已由www.perfgeeks.com改變成了WWW.PERFGEEKS.COM .這里mmap的addr是0(NULL),offset是18,并不是一個內存頁的整數倍,即有4078bytes(4kb-18)內存空間被閑置浪費了。
#include <stdio.h>#include <string.h>#include <stdlib.h>#include <ctype.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <unistd.h>int main(int argc, char *argv[]){ int fd, len; char *buf; char *fname = "/tmp/file_mmap"; ssize_t ret; struct stat sb; fd = open(fname, O_CREAT|O_RDWR, S_IRUSR|S_IWUSR); if (fd == -1) { perror("open"); return 1; } if (fstat(fd, &sb) == -1) { perror("stat"); return 1; } buf = malloc(sb.st_size); if (buf == NULL) { perror("malloc"); return 1; } ret = read(fd, buf, sb.st_size); for (len = 0; len < sb.st_size; ++len) { buf[len] = toupper(buf[len]); /*putchar(buf[len]);*/ } lseek(fd, 0, SEEK_SET); ret = write(fd, buf, sb.st_size); if (ret == -1) { perror("error"); return 1; } if (close(fd) == -1) { perror("close"); return 1;}free(buf); return 0;}#gcc –o t_rw t_rw.copen("/tmp/file_mmap", O_RDWR|O_CREAT, 0600) = 3 //open, fd=3fstat64(3, {st_mode=S_IFREG|0644, st_size=18, ...}) = 0 //fstat, 其中文件大小18brk(0) = 0x9845000 //brk, 返回當前中斷點brk(0x9866000) = 0x9866000 //malloc分配內存,堆當前最后地址read(3, "www.perfgeeks.com/n", 18) = 18 //readlseek(3, 0, SEEK_SET) = 0 //lseekwrite(3, "WWW.PERFGEEKS.COM/n", 18) = 18 //writeclose(3) = 0 //close這里通過read()讀取文件內容,toupper()后,調用write()寫回文件。因為文件太小,體現不出read()/write()的缺點:頻繁訪問大文件,需要多個lseek()來確定位置。每次編輯read()/write(),在物理內存中的雙份數據。當然,不可以忽略創建與維護mmap()數據結構的成本。需要注意:并沒有具體測試mmap vs read/write,即不能一語斷言誰孰誰劣,具體應用場景具體評測分析。你只是要記?。簃map內存映射文件之后,操作內存即是操作文件,可以省去不少系統內核調用(lseek, read, write)。
使用strace調試的時候,通??梢钥吹酵ㄟ^mmap()創建匿名內存映射的身影。比如啟用dl(‘apc.so’)的時候,就可以看到如下語句。 mmap2(NULL, 31457280, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0) = 0xb5ce7000 //30M
通常使用mmap()進行匿名內存映射,以此來獲取內存,滿足一些特別需求。所謂匿名內存映射,是指mmap()的時候,設置了一個特殊的標志MAP_ANONYMOUS,且fd可以忽略(-1)。某些操作系統(像FreeBSD),不支持標志MAP_ANONYMOUS,可以映射至設備文件/dev/zero來實現匿名內存映射。使用mmap()分配內存的好處是頁面已經填滿了0,而malloc()分配內存后,并沒有初始化,需要通過memset()初始化這塊內存。另外,malloc()分配內存的時候,可能調用brk(),也可能調用mmap2()。即分配一塊小型內存(小于或等于128kb),malloc()會調用brk()調高斷點,分配的內存在堆區域,當分配一塊大型內存(大于128kb),malloc()會調用mmap2()分配一塊內存,與堆無關,在堆之外。同樣的,free()內存映射方式分配的內存之后,內存馬上會被系統收回,free()堆中的一塊內存,并不會馬上被系統回收,glibc會保留它以供下一次malloc()使用。
這里演示一下malloc()使用brk()和mmap2()。
/** file:t_malloc.c*/#include <stdio.h>#include <string.h>#include <stdlib.h>int main(int argc, char *argv){ char *brk_mm, *mmap_mm; printf("-----------------------/n"); brk_mm = (char *)malloc(100); memset(brk_mm, '/0', 100); mmap_mm = (char *)malloc(500 * 1024); memset(mmap_mm, '/0', 500*1024); free(brk_mm); free(mmap_mm); printf("-----------------------/n"); return 1;}#gcc –o t_malloc t_malloc.c#strace ./t_mallocwrite(1, "-----------------------/n", 24-----------------------) = 24brk(0) = 0x85ee000brk(0x860f000) = 0x860f000 //malloc(100)mmap2(NULL, 516096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7702000 //malloc(5kb)munmap(0xb7702000, 516096) = 0 //free(), 5kb write(1, "-----------------------/n", 24-----------------------) = 24通過malloc()分別分配100bytes和5kb的內存,可以看出其實分別調用了brk()和mmap2(),相應的free()也是不回收內存和通過munmap()系統回收內存。
內存映射mmap()的另一個外常見的用法是,進程通信。相較于管道、消息隊列方式而言,這種通過內存映射的方式效率明顯更高,它不需要任務數據拷貝。這里,我們通過一個例子來說明mmap()在進程通信方面的應用。我們編寫二個程序,分別是master和slave,slave根據master不同指令進行不同的操作。Master與slave就是通過映射同一個普通文件進行通信的。
/* *@file master.c */root@liaowq:/data/tmp# cat master.c #include <stdio.h>#include <time.h>#include <stdlib.h>#include <sys/mman.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <unistd.h>void listen();int main(int argc, char *argv[]){ listen(); return 0;}void listen(){ int fd; char *buf; char *fname = "/tmp/shm_command"; char command; time_t now; fd = open(fname, O_CREAT|O_RDWR, S_IRUSR|S_IWUSR); if (fd == -1) { perror("open"); exit(1); } buf = mmap(0, 4096, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); if (buf == MAP_FAILED) { perror("mmap"); exit(1); } if (close(fd) == -1) { perror("close"); exit(1); } *buf = '0'; sleep(2); for (;;) { if (*buf == '1' || *buf == '3' || *buf == '5' || *buf == '7') { if (*buf > '1') printf("%ld/tgood job [%c]/n", (long)time(&now), *buf); (*buf)++; } if (*buf == '9') { break; } sleep(1); } if (munmap(buf, 4096) == -1) { perror("munmap"); exit(1); }}/* *@file slave.c */#include <stdio.h>#include <time.h>#include <stdlib.h>#include <sys/mman.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <unistd.h>void ready(unsigned int t);void job_hello();void job_smile();void job_bye();char get_command(char *buf);void wait();int main(int argc, char *argv[]){ wait(); return 0;}void ready(unsigned int t){ sleep(t);}/* command 2 */void job_hello(){ time_t now; printf("%ld/thello world/n", (long)time(&now));}/* command 4 */void job_simle(){ time_t now; printf("%ld/t^_^/n", (long)time(&now));}/* command 6 */void job_bye(){ time_t now; printf("%ld/t|<--/n", (long)time(&now));}char get_command(char *buf){ char *p; if (buf != NULL) { p = buf; } else { return '0'; } return *p;}void wait(){ int fd; char *buf; char *fname = "/tmp/shm_command"; char command; time_t now; fd = open(fname, O_RDWR|O_CREAT, S_IRUSR|S_IWUSR); if (fd == -1) { perror("open"); exit(1); } buf = mmap(0, 4096, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); if (buf == MAP_FAILED) { perror("mmap"); exit(1); } if (close(fd) == -1) { perror("close"); exit(1); } for (;;) { command = get_command(buf); /*printf("%c/n", command);*/ switch(command) { case '0': printf("%ld/tslave is ready.../n", (long)time(&now)); ready(3); *buf = '1'; break; case '2': job_hello(); *buf = '3'; break; case '4': job_simle(); *buf = '5'; break; case '6': job_bye(); *buf = '7'; break; default: break; } if (*buf == '8') { *buf = '9'; if (munmap(buf, 4096) == -1) { perror("munmap"); exit(1); } return; } sleep(1); } if (munmap(buf, 4096) == -1) { perror("munmap"); exit(1); }}執行master與slave,輸出如下
root@liaowq:/data/tmp# echo “0″ > /tmp/shm_commandroot@liaowq:/data/tmp# ./master1320939445 good job [3]1320939446 good job [5]1320939447 good job [7]root@liaowq:/data/tmp# ./slave1320939440 slave is ready…1320939444 hello world1320939445 ^_^1320939446 |<--master向slave發出job指令2,4,6。slave收到指令后,執行相關邏輯操作,完成后告訴master,master知道slave完成工作后,打印good job并且發送一下job指令。master與slave通信,是通過mmap()共享內存實現的。
1、 Linux采用了投機取巧的分配策略,用到時,才分配物理內存。也就是說進程調用brk()或mmap()時,只是占用了虛擬地址空間,并沒有真正占用物理內存。這也正是free –m中used并不意味著消耗的全都是物理內存。 2、 mmap()通過指定標志(flag) MAP_ANONYMOUS來表明該映射是匿名內存映射,此時可以忽略fd,可將它設置為-1。如果不支持MAP_ANONYMOUS標志的類unix系統,可以映射至特殊設備文件/dev/zero實現匿名內存映射。 3、 調用mmap()時就決定了映射大小,不能再增加。換句話說,映射不能改變文件的大小。反過來,由文件被映射部分,而不是由文件大小來決定進程可訪問內存空間范圍(映射時,指定offset最好是內存頁面大小的整數倍)。 4、通常使用mmap()的三種情況.提高I/O效率、匿名內存映射、共享內存進程通信。
新聞熱點
疑難解答