3. 共享存儲映射
(1)文件進程間通信
使用文件也可以完成IPC,理論依據是,fork后,父子進程共享文件描述符。也就共享打開的文件。
(2)存儲映射IO
存儲映射I/O (Memory-mapped I/O) 使一個磁盤文件與存儲空間中的一個緩沖區相映射。于是當從緩沖區中取數據,就相當于讀文件中的相應字節。于此類似,將數據存入緩沖區,則相應的字節就自動寫入文件。這樣,就可在不適用read和write函數的情況下,使用地址(指針)完成I/O操作。
使用這種方法,首先應通知內核,將一個指定文件映射到存儲區域中。這個映射工作可以通過mmap函數來實現。
1)mmap函數
void *mmap(void *adrr, size_t length, int prot, int flags, int fd, off_t offset);
返回:
成功:返回創建的映射區首地址;
失敗:MAP_FAILED宏
參數:
addr:建立映射區的首地址,由Linux內核指定。使用時,直接傳遞NULL
length:欲創建映射區的大小
prot: 映射區權限PROT_READ、PROT_WRITE、PROT_READ|PROT_WRITE
flags:標志位參數(常用于設定更新物理區域、設置共享、創建匿名映射區)
MAP_SHARED:? 會將映射區所做的操作反映到物理設備(磁盤)上。
MAP_PRIVATE: 映射區所做的修改不會反映到物理設備。
fd:用來建立映射區的文件描述符
offset:映射文件的偏移(4k的整數倍)
2)munmap函數
同malloc函數申請內存空間類似的,mmap建立的映射區在使用結束后也應調用類似free的函數來釋放。
int munmap(void *addr, size_t length); 成功:0; 失敗:-1
mmap.c
1 #include 2 #include 3 #include 4 #include 5 #include 6 #include 7 #include 8 9 int main() 10 { 11 int fd = open("mem.txt",O_RDWR);//創建并且截斷文件 12 //int fd = open("mem.txt",O_RDWR|O_CREAT|O_TRUNC,0664);//創建并且截斷文件 13 14 ftruncate(fd,8); 15 //創建映射區 16 char *mem = mmap(NULL,20,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0); 17 //char *mem = mmap(NULL,8,PROT_READ|PROT_WRITE,MAP_PRIVATE,fd,0); 18 19 if(mem == MAP_FAILED){ 20 perror("mmap err"); 21 return -1; 22 } 23 close(fd); 24 //拷貝數據 25 strcpy(mem,"helloworld"); 26 // mem++; 27 //釋放mmap 28 printf("mem = %s\n", mem); 29 30 if(munmap(mem,20) < 0){ 31 perror("munmap err"); 32 } 33 return 0; 34 }
執行結果:
執行mmap_size之前,mem.txt大小為30,代碼中?ftruncate(fd,8);?將文件截斷為8個字節大小,共享映射為20個字節,雖然文件大小(8) < 映射區大小(20),映射區可以存儲helloworld(10),同時修改文件mem.txt內容。
[root@centos 09-linux-day06]# cat mem.txt ***************************** [root@centos 09-linux-day06]# ./mmap_size mem = helloworld [root@centos 09-linux-day06]# cat mem.txt hellowor
借鑒malloc和free函數原型,嘗試裝自定義函數smalloc,sfree來完成映射區的建立和釋放。思考函數接口該如何設計?
mmap九問:
1. 如果更改mem變量的地址,釋放的時候munmap,傳入mem還能成功嗎? 不能!!
2. 如果對mem越界操作會怎么樣? 文件的大小對映射區操作有影響,盡量避免。
3. 如果文件偏移量隨便填個數會怎么樣? offset必須是 4k的整數倍
4 如果文件描述符先關閉,對mmap映射有沒有影響?沒有影響
5. open的時候,可以新創建一個文件來創建映射區嗎?不可以用大小為0的文件
6. open文件選擇O_WRONLY,可以嗎? 不可以: Permission denied
7. 當選擇MAP_SHARED的時候,open文件選擇O_RDONLY,prot可以選擇PROT_READ|PROT_WRITE嗎?Permission denied ,SHARED的時候,映射區的權限 <= open文件的權限
8. mmap什么情況下會報錯?很多情況
9. 如果不判斷返回值會怎么樣? 會死的很難堪!!

總結:使用mmap時務必注意以下事項:
1. 創建映射區的過程中,隱含著一次對映射文件的讀操作。
2. 當MAP_SHARED時,要求:映射區的權限應 <=文件打開的權限(出于對映射區的保護)。而MAP_PRIVATE則無所謂,因為mmap中的權限是對內存的限制。
3. 映射區的釋放與文件關閉無關。只要映射建立成功,文件可以立即關閉。
4. 特別注意,當映射文件大小為0時,不能創建映射區。所以:用于映射的文件必須要有實際大小!!??? mmap使用時常常會出現總線錯誤,通常是由于共享文件存儲空間大小引起的。
5. munmap傳入的地址一定是mmap的返回地址。堅決杜絕指針++操作。
6. 文件偏移量必須為4K的整數倍
7. mmap創建映射區出錯概率非常高,一定要檢查返回值,確保映射區建立成功再進行后續操作。
(3)mmap父子進程間通信
父子等有血緣關系的進程之間也可以通過mmap建立的映射區來完成數據通信。但相應的要在創建映射區的時候指定對應的標志位參數flags:
MAP_PRIVATE: ?(私有映射)? 父子進程各自獨占映射區;
MAP_SHARED:? (共享映射)? 父子進程共享映射區;
練習:父進程創建映射區,然后fork子進程,子進程修改映射區內容,而后,父進程讀取映射區內容,查驗是否共享。
父子進程共享映射區
1 #include 2 #include 3 #include 4 #include 5 #include 6 #include 7 #include 8 9 int main() 10 { 11 // 先創建映射區 12 int fd = open("mem.txt",O_RDWR); 13 int *mem = mmap(NULL, 4, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); 14 //int *mem = mmap(NULL,4, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0); 15 if(mem == MAP_FAILED){ 16 perror("mmap err"); 17 return -1; 18 } 19 // fork子進程 20 pid_t pid = fork(); 21 22 // 父進程和子進程交替修改數據 23 if(pid == 0 ){ 24 //son 25 *mem = 100; 26 printf("child,*mem = %d\n",*mem); 27 sleep(3); 28 printf("child,*mem = %d\n",*mem); 29 } 30 else if(pid > 0){ 31 //parent 32 sleep(1); 33 printf("parent,*mem=%d\n",*mem); 34 *mem = 1001; 35 printf("parent,*mem=%d\n",*mem); 36 wait(NULL); 37 } 38 39 munmap(mem,4); 40 close(fd); 41 42 return 0; 43 }
結論:父子進程共享:1. 打開的文件? 2. mmap建立的映射區(但必須要使用MAP_SHARED)
(4)匿名映射
通過使用我們發現,使用映射區來完成文件讀寫操作十分方便,父子進程間通信也較容易。但缺陷是,每次創建映射區一定要依賴一個文件才能實現。通常為了建立映射區要open一個temp文件,創建好了再unlink、close掉,比較麻煩。 可以直接使用匿名映射來代替。其實Linux系統給我們提供了創建匿名映射區的方法,無需依賴一個文件即可創建映射區。同樣需要借助標志位參數flags來指定。
使用MAP_ANONYMOUS (或MAP_ANON), 如:
int *p = mmap(NULL, 4, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0);
需注意的是,MAP_ANONYMOUS和MAP_ANON這兩個宏是Linux操作系統特有的宏。在類Unix系統中如無該宏定義,可使用如下兩步來完成匿名映射區的建立。
1)fd = open("/dev/zero", O_RDWR);
2)p = mmap(NULL, size, PROT_READ|PROT_WRITE, MMAP_SHARED, fd, 0);
mmap_anon.c
1 #include 2 #include 3 #include 4 #include 5 #include 6 #include 7 #include 8 9 int main() 10 { 11 int *mem = mmap(NULL,4,PROT_READ|PROT_WRITE,MAP_SHARED|MAP_ANON,-1,0); 12 13 if(mem == MAP_FAILED){ 14 perror("mmap err"); 15 return -1; 16 } 17 18 pid_t pid = fork(); 19 20 if(pid == 0 ){ 21 //son 22 *mem = 100; 23 printf("child,*mem=%d\n",*mem); 24 sleep(3); 25 printf("child,*mem=%d\n",*mem); 26 }else if(pid > 0){ 27 //parent 28 sleep(1); 29 printf("parent,*mem=%d\n",*mem); 30 *mem = 200; 31 printf("parent,*mem=%d\n",*mem); 32 wait(NULL); 33 } 34 35 munmap(mem,4); 36 37 return 0; 38 }
(5)mmap無血緣關系進程間通信
實質上mmap是內核借助文件幫我們創建了一個映射區,多個進程之間利用該映射區完成數據傳遞。由于內核空間多進程共享,因此無血緣關系的進程間也可以使用mmap來完成通信。只要設置相應的標志位參數flags即可。若想實現共享,當然應該使用MAP_SHARED了。
mmap_w.c
1 #include 2 #include 3 #include 4 #include 5 #include 6 #include 7 #include 8 9 typedef struct _Student{ 10 int sid; 11 char sname[20]; 12 }Student; 13 14 int main(int argc,char *argv[]) 15 { 16 if(argc != 2){ 17 printf("./a.out filename\n"); 18 return -1; 19 } 20 21 // 1. open file 22 int fd = open(argv[1],O_RDWR|O_CREAT|O_TRUNC,0666); 23 int length = sizeof(Student); 24 25 ftruncate(fd,length); 26 27 // 2. mmap 28 Student * stu = mmap(NULL,length,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0); 29 30 if(stu == MAP_FAILED){ 31 perror("mmap err"); 32 return -1; 33 } 34 int num = 1; 35 // 3. 修改內存數據 36 while(1){ 37 stu->sid = num; 38 sprintf(stu->sname,"from mmap write-%03d",num++); 39 sleep(1);//相當于沒隔1s修改一次映射區的內容 40 } 41 // 4. 釋放映射區和關閉文件描述符 42 munmap(stu,length); 43 close(fd); 44 45 return 0; 46 }
mmap_r.c
1 #include 2 #include 3 #include 4 #include 5 #include 6 #include 7 #include 8 9 typedef struct _Student{ 10 int sid; 11 char sname[20]; 12 }Student; 13 14 int main(int argc,char *argv[]) 15 { 16 //open file 17 int fd = open(argv[1],O_RDWR); 18 //mmap 19 int length = sizeof(Student); 20 Student *stu = mmap(NULL,length,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0); 21 if(stu == MAP_FAILED){ 22 perror("mmap err"); 23 return -1; 24 } 25 //read data 26 while(1){ 27 printf("sid=%d,sname=%s\n",stu->sid,stu->sname); 28 sleep(1); 29 } 30 //close and munmap 31 munmap(stu,length); 32 close(fd); 33 return 0; 34 }
練習:
1.通過命名管道傳輸數據,進程A和進程B,進程A將一個文件(MP3)發送給進程B?
2.實現多進程拷貝文件?
實現思路:
代碼實現:
多進程文件拷貝
1 #include 2 #include 3 #include 4 #include 5 #include 6 #include 7 #include 8 #include 9 10 int main(int argc, char *argv[]) 11 { 12 //拷貝文件起的進程數,通過參數輸入,默認是5個 13 int n = 5; 14 //輸入參數至少是3,第4個參數是進程個數 15 if (argc < 3) 16 { 17 printf("./a.out src dst [n]\n"); 18 return -1; 19 } 20 if (argc == 4) 21 { 22 n = atoi(argv[3]); 23 } 24 25 //打開源文件 26 int srcfd = open(argv[1], O_RDONLY); 27 if (srcfd < 0) 28 { 29 perror("open src err:"); 30 exit(1); 31 } 32 33 //打開目標文件 34 int dstfd = open(argv[2], O_RDWR|O_CREAT|O_TRUNC, 0644); 35 if (dstfd < 0) 36 { 37 perror("open dst err:"); 38 exit(1); 39 } 40 41 //目標拓展,從源文件獲得文件大小,stat 42 struct stat sb; 43 stat(argv[1], &sb); 44 //將目標文件設置為和源文件大小相同 45 int len = sb.st_size; 46 truncate(argv[2], len); 47 48 //將源文件映射到緩沖區 49 char *psrc = mmap(NULL, len, PROT_READ, MAP_SHARED, srcfd, 0); 50 if (psrc == MAP_FAILED) 51 { 52 perror("mmap src err:"); 53 exit(1); 54 } 55 56 //將目標文件映射 57 char *pdst = mmap(NULL, len, PROT_READ|PROT_WRITE, MAP_SHARED, dstfd, 0); 58 if (pdst == MAP_FAILED) 59 { 60 perror("mmap pdst err:"); 61 exit(1); 62 } 63 //創建多個子進程 64 int i = 0; 65 for (i = 0; i < n; i++) 66 { 67 if (fork() == 0) 68 { 69 break; 70 } 71 } 72 //計算子進程需要拷貝的起點大小 73 int cpsize = len/n; 74 int mod = len%n; 75 //數據拷貝,memcpy 76 if (i < n) 77 { 78 //最后一個子進程 79 if (i == n-1) 80 { 81 memcpy(pdst+i*cpsize, psrc+i*cpsize, cpsize+mod); 82 } 83 else 84 { 85 memcpy(pdst+i*cpsize, psrc+i*cpsize, cpsize); 86 } 87 } 88 else 89 { 90 for (i = 0; i < n; i++) 91 { 92 //回收子線程 93 wait(NULL); 94 } 95 } 96 //釋放映射區 97 if (munmap(psrc, len) < 0) 98 { 99 perror("munmap src err:"); 100 exit(1); 101 } 102 103 if (munmap(pdst, len) < 0) 104 { 105 perror("munmap dst err:"); 106 exit(1); 107 } 108 109 //關閉文件 110 close(srcfd); 111 close(dstfd); 112 113 return 0; 114 }
Linux 任務調度
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。