プライベートファイルマッピングは既に軽く紹介していますが、ここでは fork() を使うことで Copy-On-Write の仕組みをより良く理解したいと思います。
では実装例を見てみましょう。
main.c.
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 4 #include <sys/mman.h> 5 #include <sys/stat.h> 6 #include <sys/types.h> 7 #include <unistd.h> 8 #include <fcntl.h> 9 10 #define BUF_SIZE 8 11 12 int main() 13 { 14 int fd,i; 15 struct stat sb; 16 char buf[BUF_SIZE]; 17 char *mm; 18 19 memset(buf,0,BUF_SIZE); 20 21 fd = open("abc.txt",O_CREAT|O_RDWR|O_TRUNC,0644); 22 if(fd < 0) { 23 perror("ファイルを開けません"); 24 exit(1); 25 } 26 write(fd,buf,BUF_SIZE); 27 if(fstat(fd,&sb) == -1) { 28 perror("stat"); 29 exit(1); 30 } 31 32 mm = mmap(NULL,sb.st_size,PROT_WRITE|PROT_READ,MAP_PRIVATE,fd,0); 33 if(mm == MAP_FAILED){ 34 perror("mmap"); 35 exit(1); 36 } 37 38 pid_t id = fork(); 39 40 if(id == 0) { 41 for(i = 0; i < sb.st_size-2; i++) 42 mm[i] = 'a'+i; 43 mm[sb.st_size-2] = '\n'; 44 mm[sb.st_size-1] = '\0'; 45 46 } else { 47 for(i = 0; i < sb.st_size-2; i++) 48 mm[i] = 'a'; 49 mm[sb.st_size-2] = '\n'; 50 mm[sb.st_size-1] = '\0'; 51 } 52 53 for(i = 0; i < 10; i++) 54 printf("mm[%d] => %c\n",i,mm[i]); 55 56 if(munmap(mm,sb.st_size) == -1){ 57 perror("munmap"); 58 exit(1); 59 } 60 close(fd); 61 62 return 0; 63 }
ビルドと実行結果.
$ gcc main.c $ ./a.out mm[0] => a mm[1] => a mm[2] => a mm[3] => a mm[4] => a mm[5] => a mm[6] => mm[7] => mm[8] => mm[9] => mm[0] => a mm[1] => b mm[2] => c mm[3] => d mm[4] => e mm[5] => f mm[6] => mm[7] => mm[8] => mm[9] => $ cat abc.txt
このソースコードでは「 abc.txt 」ファイルを新規生成または開いて、それを mmap() でマッピングします。
21 fd = open("abc.txt",O_CREAT|O_RDWR|O_TRUNC,0644); 22 if(fd < 0) { 23 perror("ファイルを開けません"); 24 exit(1); 25 }
mmap() は以下のように MAP_PRIVATE を指定しています。
32 mm = mmap(NULL,sb.st_size,PROT_WRITE|PROT_READ,MAP_PRIVATE,fd,0); 33 if(mm == MAP_FAILED){ 34 perror("mmap"); 35 exit(1); 36 }
次に fork() で子プロセスを生成します。
38 pid_t id = fork(); 39 40 if(id == 0) { 41 for(i = 0; i < sb.st_size-2; i++) 42 mm[i] = 'a'+i; 43 mm[sb.st_size-2] = '\n'; 44 mm[sb.st_size-1] = '\0'; 45 46 } else { 47 for(i = 0; i < sb.st_size-2; i++) 48 mm[i] = 'a'; 49 mm[sb.st_size-2] = '\n'; 50 mm[sb.st_size-1] = '\0'; 51 }
子プロセス( id == 0 )は a,b,c,d,e,f という並びの文字配列に設定します。
親プロセス( id が 0 でない )の方は a,a,a,a,a,a という並びにします。
mm は MAP_PRIVATE でアクセスをするため、親プロセスの変更と子プロセスの変更は独立して行われてますね。
Copyright 2018-2019, by Masaki Komatsu