LINUXプログラミングインタフェース 4章 ファイルI/O : 統一されたインタフェース

この記事内では便宜上カーネルシステムコールを呼び出す関数のこともシステムコールと書いています

open()システムコール

ファイルのopenには open() システムコールを使います

以下のようなファイルを用意してみました。

#include <stdio.h>                                                                                                                                                                                                                                                             
#include <sys/stat.h>                                                                                                                                                                                                                                                          
#include <fcntl.h>                                                                                                                                                                                                                                                             
                                                                                                                                                                                                                                                                               
int main(){                                                                                                                                                                                                                                                                    
  int fd;                                                                                                                                                                                                                                                                      
  fd = open("./test.txt", O_RDWR);  // O_RDWR は読み取りと書き込みモード                                                                                                                                                                                                                                           
                                                                                                                                                                                                                                                                               
  if (fd == -1){                                                                                                                                                                                                                                                               
    printf("error!\n");                                                                                                                                                                                                                                                        
  }                                                                                                                                                                                                                                                                            
                                                                                                                                                                                                                                                                               
  printf("fd is %d\n", fd);                                                                                                                                                                                                                                                    
                                                                                                                                                                                                                                                                               
  return 0;                                                                                                                                                                                                                                                                    
} 

open() はファイルディスクリプタを返します。ファイルディスクリプタはあまり大きくない整数で、 -1 はエラーを表します。

まずはopenするファイルがない状態で実行します

$ cc opentest.c
$ ./a.out 
error!
fd is -1

想定通り、open()は-1を返していました。

次はopen()するファイルを用意して実行します

$ touch test.txt
$ ./a.out 
fd is 3

ファイルディスクリプタは 3 だったようです。

open() すると、ファイルディスクリプタは常に利用できる最小の値が割り当てられます。

何度実行しても 3 が返ります。

$ ./a.out 
fd is 3
$ ./a.out 
fd is 3
$ ./a.out 
fd is 3

read()システムコール

ssize_t read(int fd, void *buffer, size_t count)

read()は、引数にファイルディスクリプタ、読み取ったデータを格納するメモリバッファアドレス、読み取るバイト数 を指定します。 返り値は、成功時には読み取ったバイト数、エラー時には-1です。

※ちなみに、読み取るバイト数より実際のファイルのバイト数が小さい場合は、読み取れるだけ読み取ります。

open()で返ってきたfdをread()してみます

#include <stdio.h>                                                                                                                                                                                                                                                             
#include <sys/stat.h>                                                                                                                                                                                                                                                          
#include <fcntl.h>                                                                                                                                                                                                                                                             
#include <unistd.h>                                                                                                                                                                                                                                                            
                                                                                                                                                                                                                                                                               
int main(){                                                                                                                                                                                                                                                                    
  int fd;                                                                                                                                                                                                                                                                      
  fd = open("./test.txt", O_RDWR);                                                                                                                                                                                                                                             
                                                                                                                                                                                                                                                                               
  if (fd == -1){                                                                                                                                                                                                                                                               
    printf("error!\n");                                                                                                                                                                                                                                                        
  }                                                                                                                                                                                                                                                                            
                                                                                                                                                                                                                                                                               
  printf("fd is %d\n", fd);                                                                                                                                                                                                                                                    
  
  // ここまではopen()と同じ
                                                                                                                                                                                                                                                                             
  char buffer[100];                                                                                                                                                                                                                                                            
                                                                                                                                                                                                                                                                               
  ssize_t numRead = read(fd, buffer, 100);                                                                                                                                                                                                                                     
                                                                                                                                                                                                                                                                               
  printf("numRead is %d\n", numRead);
  printf(buffer);                                                                                                                                                                                                                                          
                                                                                                                                                                                                                                                                               
                                                                                                                                                                                                                                                                               
  return 0;                                                                                                                                                                                                                                                                    
} 

まずはファイルをtouchしたばかりの状態で試します

$ cc readtest.c
$ ./a.out 
fd is 3
numRead is 0

想定どおり、fd: 3のファイルから読み取ったバイト数は0ですね

次はなにか適当に文字列を入れた状態で試してみます

$ echo "is this a pen?" > test.txt 
$ ./a.out 
fd is 3
numRead is 15
is this a pen?

read()は読み取ったバイト数である、15を返してきました。bufferにもデータが格納されていることがわかります。

write()システムコール

ssize_t write(int fd, void *buffer, size_t count);

read()に似てますね

#include <stdio.h>                                                                                                                                                                                                                                                             
#include <sys/stat.h>                                                                                                                                                                                                                                                          
#include <fcntl.h>                                                                                                                                                                                                                                                             
#include <unistd.h>                                                                                                                                                                                                                                                            
                                                                                                                                                                                                                                                                               
int main(){                                                                                                                                                                                                                                                                    
  int fd;                                                                                                                                                                                                                                                                      
  fd = open("./test.txt", O_RDWR);                                                                                                                                                                                                                                             
                                                                                                                                                                                                                                                                               
  if (fd == -1){                                                                                                                                                                                                                                                               
    printf("error!\n");                                                                                                                                                                                                                                                        
  }                                                                                                                                                                                                                                                                            
                                                                                                                                                                                                                                                                               
  printf("fd is %d\n", fd);                                                                                                                                                                                                                                                    
                                                                                                                                                                                                                                                                               
  char buffer[7] = "abcdefg";                                                                                                                                                                                                                                                  
                                                                                                                                                                                                                                                                               
  ssize_t numWrite = write(fd, buffer, 3);                                                                                                                                                                                                                                     
                                                                                                                                                                                                                                                                               
  printf("numWrite is %d\n", numWrite);                                                                                                                                                                                                                                        
  printf(buffer);                                                                                                                                                                                                                                                              
                                                                                                                                                                                                                                                                               
  return 0;                                                                                                                                                                                                                                                                    
} 

bufferは"abcdefg"ですが、countが3バイトなので、abcだけがwriteされます。

$ cc writetest.c 
$ ./a.out 
fd is 3
numWrite is 3
abcdefg
$ cat test.txt 
abc

close()システムコール

int close(int fd)

返り値はcloseしたfdです

#include <stdio.h>                                                                                                                                                                                                                                                             
#include <sys/stat.h>                                                                                                                                                                                                                                                          
#include <fcntl.h>                                                                                                                                                                                                                                                             
#include <unistd.h>                                                                                                                                                                                                                                                            
                                                                                                                                                                                                                                                                               
int main(){                                                                                                                                                                                                                                                                    
  int fd;                                                                                                                                                                                                                                                                      
  fd = open("./test.txt", O_RDWR);                                                                                                                                                                                                                                             
                                                                                                                                                                                                                                                                               
  if (fd == -1){                                                                                                                                                                                                                                                               
    printf("error!\n");                                                                                                                                                                                                                                                        
  }                                                                                                                                                                                                                                                                            
                                                                                                                                                                                                                                                                               
  printf("fd is %d\n", fd);                                                                                                                                                                                                                                                    
                                                                                                                                                                                                                                                                               
  int closed = close(fd);                                                                                                                                                                                                                                                      
                                                                                                                                                                                                                                                                               
  printf("closed %d\n", fd);                                                                                                                                                                                                                                                   
                                                                                                                                                                                                                                                                               
  return 0;                                                                                                                                                                                                                                                                    
} 
$ cc closetest.c 
$ ./a.out 
fd is 3
closed 3

lseek()システムコール

off_t lseek(int fd, off_t offset, int whence)

offsetにはバイト数を指定、whenceにはSEEK_SET, SEEK_CUR, SEEK_ENDから指定します。

whenceがoffsetの原点になります。

まずはテスト用にファイルを準備します

$ echo "abcdefghij" > test.txt

以下は先頭をwhenceに指定して、offset 3でファイルを読み取る例です

#include <stdio.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

int main(){
  int fd;
  fd = open("./test.txt", O_RDWR);

  if (fd == -1){
    printf("error!\n");
  }

  printf("fd is %d\n", fd);

  lseek(fd, 3, SEEK_SET);

  char buffer[100];
  ssize_t numRead = read(fd, buffer, 100);

  printf("numRead is %d\n", numRead);
  printf(buffer);


  return 0;
}

d以降が読み取られていることがわかります

$ cc lseektest.c 
$ ./a.out 
fd is 3
numRead is 8
defghij

ファイルホール

ファイル末尾を超えた位置へシークしてread()すると、戻り値は0(end-of-file)になりますが、書き込みは可能です。
ファイル末尾からそれよりも後ろに新たに書き込まれた位置までをファイルホールといいます。

まずはテスト用にファイルを準備します

$ echo "abcdefghij" > test.txt

このファイルでは、whenceをSEEK_SET, offsetを11以上にするとファイル末尾を超えます。

試しにoffsetに20を設定してread()してみます

#include <stdio.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

int main(){
  int fd;
  fd = open("./test.txt", O_RDWR);

  if (fd == -1){
    printf("error!\n");
  }

  lseek(fd, 20, SEEK_SET);

  char buffer[100];
  ssize_t numRead = read(fd, buffer, 100);

  printf("numRead is %d\n", numRead);
  printf(buffer);


  return 0;
}

読み取ったバイト数は0になります

$ cc lseektest.c 
$ ./a.out 
numRead is 0

次は書き込みです

10バイトしか書き込まれていないファイルに対して、offset 20で"aaa"を書き込みます

#include <stdio.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

int main(){
  int fd;
  fd = open("./test.txt", O_RDWR);

  if (fd == -1){
    printf("error!\n");
  }

  lseek(fd, 20, SEEK_SET);

  char buffer[3] = "aaa";
  ssize_t numWritten = write(fd, buffer, 3);

  printf("numWritten is %d\n", numWritten);
  printf(buffer);


  return 0;
}

出力は通常の書き込みと同じです

$ cc lseektest.c 
$ ./a.out 
numWritten is 3
aaa

ファイルの中は以下のようになっています

$ cat test.txt 
abcdefghij
aaa

abcdefghij と aaaの間(ファイルホール)は改行コードがあるように見えますが実際にはNULLです。

ファイルシステムはファイルホールに対してはディスクブロックを割り当てず、あとでデータが書き込まれた時点で初めて割り当てます。少ないブロック数で済むのでファイルホールはディスク領域の節約にも繋がります。

コアダンプファイルはファイルホールを持つ代表例です。