第76章 fstat 関数を使ってファイルサイズを指定する場合

 fork() も軽く説明したことですし… mmap() と関係のあるもう一つの関数もついでに大バーゲンで説明しちゃいましょう!

 fstat() 関数については前にやりましたよね?

 stat 構造体とか… stat 構造体とか… stat 構造体とかです…

 覚えてますかね… (´・ω・`)

 struct stat 構造体にはファイルサイズのフィールド st_size があるので、オフセットとかなしでファイル全体を取り込むのであればこちらを mmap の length 引数に指定できます。

 では軽く実装してみましょう。

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_SHARED,fd,0);
 33   if(mm == MAP_FAILED){
 34     perror("mmap");
 35     exit(1);
 36   }
 37
 38   for(i = 0; i < sb.st_size-2; i++)
 39     mm[i] = 'a'+i;
 40   mm[sb.st_size-2] = '\n';
 41   mm[sb.st_size-1] = '\0';
 42
 43   msync(mm,sb.st_size,MS_SYNC);
 44   for(i = 0; i < 10; i++)
 45     printf("mm[%d] => %c\n",i,mm[i]);
 46
 47   munmap(mm,sb.st_size);
 48   close(fd);
 49
 50   return 0;
 51 }

ビルドと実行. 

$ gcc main.c
$ ./a.out
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
abcdef

 まず変数の宣言です。

 14   int fd,i;
 15   struct stat sb;
 16   char buf[BUF_SIZE];
 17   char *mm;

 fstat() 関数は fd と sb を引数とします。

 fd は open() 関数で開いたファイル記述子を代入します。

 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   }

 まあ「 abc.txt 」を開いて空の buf を書き込むことでファイル側のサイズが更新され、それを fstat() 関数で sb のアドレスに更新します。

 次は mmap() のコールです。

 32   mm = mmap(NULL,sb.st_size,PROT_WRITE|PROT_READ,MAP_SHARED,fd,0);
 33   if(mm == MAP_FAILED){
 34     perror("mmap");
 35     exit(1);
 36   }

 まずマップの種類は MAP_SHARED となっており、ファイルへの更新は他のプロセスから見れるようになってます。

 ただこのサンプルコードでは、fork() をコールしていないので MAP_PRIVATE と結果は変わらないです。

 まあ、このソースコードでは外から見える必要性はないですがね。

 43   msync(mm,sb.st_size,MS_SYNC);

 msync() はファイルに対してデータを反映させる関数です。

 一応 munmap() 関数でアンマップすると書き込みデータがマップしたファイルに確実に更新されるのですが、munmap() を呼ばないでファイル更新をするなら msync() を呼び出します。

 38   for(i = 0; i < sb.st_size-2; i++)
 39     mm[i] = 'a'+i;
 40   mm[sb.st_size-2] = '\n';
 41   mm[sb.st_size-1] = '\0';

 mm は mmap() でマッピングをしたバッファーなのでこれにデータを入れておいてますね。

 47   munmap(mm,sb.st_size);
 48   close(fd);

  munmap() は戻り値が -1 の場合にエラーとなりますんで、念の為にエラー処理を追加することができます。

 それとファイル記述子を close() で閉じてもマッピングは解除されないので munmap は必ず使うようにしましょう。

Copyright 2018-2019, by Masaki Komatsu