日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

Linux系统编程---5(共享存储映射,存储映射I/O,mmap函数,父子进程间通信,匿名映射)

發布時間:2023/11/30 linux 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Linux系统编程---5(共享存储映射,存储映射I/O,mmap函数,父子进程间通信,匿名映射) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

共享存儲映射

文件進程間通信

使用文件也可以完成 IPC,理論依據是,fork 后,父子進程共享文件描述符。也就共享打開的文件。
編程:父子進程共享打開的文件。借助文件進行進程間通信。
測試代碼

/*** 父子進程共享打開的文件描述符----使用文件完成進程間通信**/#include<stdio.h> #include<unistd.h> #include<string.h> #include<stdlib.h> #include<fcntl.h> #include<sys/wait.h>int main(void) {int fd1,fd2;pid_t pid;char buf[1024]; char *str="----------test for shared fd in parent child process -----\n";pid=fork();if(pid<0){perror("fork error");exit(1);}else if(pid==0){fd1=open("test.txt",O_RDWR);if(fd1<0){perror("open error");exit(1);} write(fd1,str,strlen(str));printf("child wrote over...\n");}else{fd2=open("test.txt",O_RDWR);if(fd2<0){perror("open error");exit(1);}sleep(1); //保證子進程寫入數據int len =read(fd2,buf,sizeof(buf));write(STDOUT_FILENO,buf,len);wait(NULL);//回收防止僵尸進程}return 0; }

思考,無血緣關系的進程可以打開同一個文件進行通信嗎?為什么?
可以,,無血緣關系的進程也可以打開同一個文件進行通信,方法一樣,因為這些進程打開的是同一個進程。其實在打開文件時(調用open時),操作系統內核就調用了mmap。因為一個文件只有一個文件結構體(FILE),打開時位于內核,被打開這個文件的多個進程共享。

存儲映射 I/O

存儲映射 I/O(Memory-mappedI/O) 使一個磁盤文件與存儲空間中的一個緩沖區相映射。于是當從緩沖區中取 數據,就相當于讀文件中的相應字節。于此類似,將數據存入緩沖區,則相應的字節就自動寫入文件。這樣,就可 在不適用 read 和 write 函數的情況下,使用地址(指針)完成 I/O 操作。
使用這種方法,首先應通知內核,將一個指定文件映射到存儲區域中。這個映射工作可以通過 mmap 函數來實
現。

mmap 函數

void* mmap(void* adrr,size_t length,int prot,int flags,int fd,off_toffset); 返回:成功:返回創建的映射區首地址;失敗:MAP_FAILED 宏
參數:

  • addr: 建立映射區的首地址,由 Linux 內核指定。使用時,直接傳遞 NULL

  • length: 欲創建映射區的大小

  • prot: 映射區權限 PROT_READ、PROT_WRITE、PROT_READ|PROT_WRITE

  • flags: 標志位參數(常用于設定更新物理區域、設置共享、創建匿名映射區) MAP_SHARED: 會將映射區所做的操作反映到物理設備(磁盤)上。 MAP_PRIVATE: 映射區所做的修改不會反映到物理設備。

  • fd: 用來建立映射區的文件描述符

  • offset: 映射文件的偏移(4k 的整數倍)

    #include<stdio.h>#include<fcntl.h>#include<unistd.h>#include<string.h>#include<stdlib.h>#include<sys/mman.h>int main(void){int len,ret;char *p=NULL;int fd = open("mytest.txt",O_CREAT|O_RDWR,0644);if(fd<0){perror("open error");exit(1);} //確定文件大小len = ftruncate(fd,4);if(len == -1){perror("ftruncate error:");exit(1);} p = mmap(NULL, 4 , PROT_READ|PROT_WRITE , MAP_SHARED, fd, 0); if(p == MAP_FAILED){perror("mmap error:");exit(1);} strcpy(p,"abc");//寫數據ret = munmap(p,4);//釋放映射區 if(ret==-1){perror("munmap error:");exit(1);} close(fd);return 0;}
  • munmap 函數

    同 malloc 函數申請內存空間類似的,mmap 建立的映射區在使用結束后也應調用類似 free 的函數來釋放。 int munmap(void *addr,size_t length); 成功:0; 失敗:-1

    mmap 注意事項

    思考:

  • 可以 open 的時候 O_CREAT 一個新文件來創建映射區嗎?
    答:可以,但新CREATE出來的文件不行,必須要有實際的大小
  • 如果 open 時 O_RDONLY,mmap 時 PROT 參數指定 PROT_READ|PROT_WRITE 會怎樣?
    答:不行,創建映射區的權限小于等于打開文件的權限,映射區創建的過程中存在一次讀文件操作權限不足。
  • 文件描述符先關閉,對 mmap 映射有沒有影響?
    答: 沒有影響,文件描述符是操作文件的句柄,有映射區了,就是地址的方式操作,所以文件描述符就沒意義了。
  • 如果文件偏移量為 1000 會怎樣?
    答:不行,偏移量必須是一頁的大小(4k)
  • 對 mem 越界操作會怎樣?
    答:不行,釋放映射區可能會失敗,只有創建映射區的地址和釋放時的地址要是同一個地址
  • 如果 mem++,munmap 可否成功?
    答:不能,釋放映射區的時候要傳首地址和映射區大小給munmap,但++后首地址就不是創建時的首地址,只有創建映射區的地址和釋放時的地址要是同一個地址
  • mmap 什么情況下會調用失敗? 8. 如果不檢測 mmap 的返回值,會怎樣?
    答:返回值必須檢查,空間不能為0,文件大小和映射區要匹配等等情況下會失敗
  • 總結

  • 創建映射區的過程中,隱含著一次對映射文件的讀操作。
  • 當 MAP_SHARED 時,要求:映射區的權限應 <=文件打開的權限(出于對映射區的保護)。而 MAP_PRIVATE 則無所謂,因為 mmap 中的權限是對內存的限制。
  • 映射區的釋放與文件關閉無關。只要映射建立成功,文件可以立即關閉。
  • 特別注意,當映射文件大小為 0 時,不能創建映射區。所以:用于映射的文件必須要有實際大小!! mmap 使用時常常會出現總線錯誤,通常是由于共享文件存儲空間大小引起的。
  • munmap 傳入的地址一定是 mmap 的返回地址。堅決杜絕指針++操作。
  • 如果文件偏移量必須為 4K 的整數倍 7. mmap 創建映射區出錯概率非常高,一定要檢查返回值,確保映射區建立成功再進行后續操作。
  • mmap 父子進程通信

    父子等有血緣關系的進程之間也可以通過 mmap 建立的映射區來完成數據通信。但相應的要在創建映射區的時 候指定對應的標志位參數 flags:

  • MAP_PRIVATE: (私有映射) 父子進程各自獨占映射區;
  • MAP_SHARED: (共享映射) 父子進程共享映射區;
    編程:父進程創建映射區,然后 fork 子進程,子進程修改映射區內容,而后,父進程讀取映射區內容,查驗是 否共享
  • #include<stdio.h> #include<stdlib.h>#include<unistd.h>#include<fcntl.h>#include<sys/mman.h>#include<sys/wait.h>int var=100;int main(void ){int *p;pid_t pid;int fd;fd=open("temp",O_RDWR|O_CREAT|O_TRUNC,0644);if(fd<0){perror("open error:");exit(1);}unlink("temp"); //刪除臨時文件目錄項,使之具備被釋放條件,所有使用該文>件的進程結束后該文件才釋放ftruncate(fd,4); //創建文件大小p=(int *)mmap(NULL,4,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);//父子進程共享映射區// p=(int *)mmap(NULL,4,PROT_READ|PROT_WRITE,MAP_PRIVATE,fd,0);//對映射區各自獨>占 if(p==MAP_FAILED){ //不是p==NULL時出錯perror("mmap,error"); exit(1);}close(fd); //映射區建立完畢,即可關閉文件//完成數據傳遞pid=fork();if(pid==0){*p=2000;var=1000;printf("child,*p=%d,var = %d\n",*p,var);}else{sleep(1);printf("parent,*p = %d,var = =%d\n",*p,var);wait(NULL);int ret= munmap(p,4);if(ret==-1){perror("munmap error");exit(1);}}return 0;}

    結論:父子進程共享:

  • 打開的文件
  • mmap 建立的映射區(但必須要使用 MAP_SHARED)
  • 匿名映射

    通過使用我們發現,使用映射區來完成文件讀寫操作十分方便,父子進程間通信也較容易。但缺陷是,每次創 建映射區一定要依賴一個文件才能實現。通常為了建立映射區要 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);

    "4"隨意舉例,該位置表大小,可依實際需要填寫。

    需注意的是,MAP_ANONYMOUS 和 MAP_ANON 這兩個宏是 Linux 操作系統特有的宏。在類 Unix 系統中如無該 宏定義,可使用如下兩步來完成匿名映射區的建立。

  • fd=open("/dev/zero",O_RDWR);
  • p=mmap(NULL,size,PROT_READ|PROT_WRITE,MMAP_SHARED,fd,0);
  • 示例代碼:

    #include<stdio.h> #include<stdlib.h> #include<unistd.h> #include<fcntl.h> #include<sys/mman.h> #include<sys/wait.h>int var=100;int main(void ) {int *p; pid_t pid;//不使用文件參數傳-1 p=(int *)mmap(NULL,4,PROT_READ|PROT_WRITE,MAP_SHARED|MAP_ANON,-1,0);//對映射區>各自獨占if(p==MAP_FAILED){ //不是p==NULL時出錯perror("mmap,error"); exit(1);} //完成數據傳遞pid=fork();if(pid==0){*p=2000;var=1000;printf("child,*p=%d,var = %d\n",*p,var);}else{sleep(1);printf("parent,*p = %d,var = =%d\n",*p,var);wait(NULL);int ret= munmap(p,4); if(ret==-1){perror("munmap error");exit(1);}}return 0; }


    Linux下這兩個文件無大小

    第一個文件好比聚寶盆,可以隨意映射
    第二個文件,一般錯誤洗腦洗重定向到這個文件中

    mmap 無血緣關系進程間通信

    實質上 mmap 是內核借助文件幫我們創建了一個映射區,多個進程之間利用該映射區完成數據傳遞。由于內核 空間多進程共享,因此無血緣關系的進程間也可以使用 mmap 來完成通信。只要設置相應的標志位參數 flags 即可。 若想實現共享,當然應該使用 MAP_SHARED 了。

    讀數據

    /** 非血緣關系進程間通信*/ #include<stdio.h> #include<sys/stat.h> #include<fcntl.h> #include<unistd.h> #include<stdlib.h> #include<sys/mman.h> #include<string.h>struct STU{int id; char name[20];char sex; };//出錯處理函數 void sys_err(char *str) {perror(str);exit(-1); }int main(int argc,char *argv[]) {int fd; struct STU student;struct STU *mm;if(argc<2){printf("./a.out file_shared\n");exit(-1);} fd=open(argv[1],O_RDONLY);if(fd == -1) sys_err("open error");mm = mmap(NULL,sizeof(student),PROT_READ,MAP_SHARED,fd,0);if(mm == MAP_FAILED)sys_err("mmap error");close(fd);while(1){ //讀進程 printf("id=%d\tname=%s\t%c\n",mm->id,mm->name,mm->sex);sleep(2);}munmap(mm,sizeof(student));return 0; }

    寫數據

    /* * 非血緣關系之間的通信*/ #include<stdio.h> #include<sys/stat.h> #include<sys/types.h> #include<fcntl.h> #include<unistd.h> #include<stdlib.h> #include<sys/mman.h> #include<string.h>struct STU{int id;char name[20];char sex; };void sys_err(char *str) {perror(str);exit(1); }int main(int argc,char *argv[]) {int fd;struct STU student={10,"xiaoming",'m'};char *mm;if(argc<2){printf("./a.out file_shared\n");exit(-1);}fd = open(argv[1],O_RDWR | O_CREAT,0644);ftruncate(fd,sizeof(student));mm = mmap(NULL,sizeof(student),PROT_READ | PROT_WRITE,MAP_SHARED,fd,0);if(mm == MAP_FAILED)sys_err("mmap"); close(fd);while(1){memcpy(mm,&student,sizeof(student));student.id++; //循環向內存復制sleep(1);}munmap(mm,sizeof(student));return 0; }



    總結

    以上是生活随笔為你收集整理的Linux系统编程---5(共享存储映射,存储映射I/O,mmap函数,父子进程间通信,匿名映射)的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。