• このエントリーをはてなブックマークに追加

スポンサードリンク

はじめに

CIFS プロトコルを勉強している.

CIFS プロトコルは, ファイル読み書きのシステムコールを そのままネットワーク上に流したような仕様になっている.

エクスプローラから見れば, ファイルシステムがリモートにあろうが ローカルにあろうが, 意識することなくファイルにアクセスできる. それは, ファイルアクセスをすると, Windows のカーネル・モードにあるリダイレクタが呼び出されて, 適切なデバイスに対してコマンドを発行してくれるから.

つまり, ファイルアクセスの仕組みを理解すれば, CIFS のプロトコルも理解が深まると言うことだ!

ということで, C 言語で システムコールを利用して ファイルアクセスをしてみた.

つごうにより, 環境は ArchLinux だけど…

ファイル作成

やること

/home/tsu-nera/tmp 配下に test というファイルを新規作成する.

touch で作成する

Unix のコマンド touch で作成する.

touch /home/tsu-nera/tmp/test

touch のソースを眺める

GNU coreutils のソースを眺める.

そして,fd_reopen という関数の中で open/close が利用されている. https://github.com/goj/coreutils/blob/master/lib/fd-reopen.c

strace でみてみる

プログラムが使用するシステムコールおよび受け取るシグナルを監視するツール.

strace touch /home/tsu-nera/tmp/test

こうなった.

execve ("/usr/bin/touch", ["touch", "/home/tsu-nera/tmp/test"], [/* 50 vars */]) = 0
brk (0)                                  = 0x6ce000
access ("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
open ("/usr/local/lib/tls/x86_64/libc.so.6", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat ("/usr/local/lib/tls/x86_64", 0x7ffffbbac6d0) = -1 ENOENT (No such file or directory)
open ("/usr/local/lib/tls/libc.so.6", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat ("/usr/local/lib/tls", 0x7ffffbbac6d0) = -1 ENOENT (No such file or directory)
open ("/usr/local/lib/x86_64/libc.so.6", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat ("/usr/local/lib/x86_64", 0x7ffffbbac6d0) = -1 ENOENT (No such file or directory)
open ("/usr/local/lib/libc.so.6", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat ("/usr/local/lib", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
open ("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 4
fstat (4, {st_mode=S_IFREG|0644, st_size=166490, ...}) = 0
mmap (NULL, 166490, PROT_READ, MAP_PRIVATE, 4, 0) = 0x7f042abdd000
close (4)                                = 0
open ("/usr/lib/libc.so.6", O_RDONLY|O_CLOEXEC) = 4
read (4, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0`\1\2\0\0\0\0\0"..., 832) = 832
fstat (4, {st_mode=S_IFREG|0755, st_size=1984416, ...}) = 0
mmap (NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f042abdc000
mmap (NULL, 3813200, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 4, 0) = 0x7f042a642000
mprotect (0x7f042a7db000, 2097152, PROT_NONE) = 0
mmap (0x7f042a9db000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 4, 0x199000) = 0x7f042a9db000
mmap (0x7f042a9e1000, 16208, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f042a9e1000
close (4)                                = 0
mmap (NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f042abdb000
mmap (NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f042abda000
arch_prctl (ARCH_SET_FS, 0x7f042abdb700) = 0
mprotect (0x7f042a9db000, 16384, PROT_READ) = 0
mprotect (0x60d000, 4096, PROT_READ)     = 0
mprotect (0x7f042ac06000, 4096, PROT_READ) = 0
munmap (0x7f042abdd000, 166490)          = 0
brk (0)                                  = 0x6ce000
brk (0x6ef000)                           = 0x6ef000
open ("/usr/lib/locale/locale-archive", O_RDONLY|O_CLOEXEC) = 4
fstat (4, {st_mode=S_IFREG|0644, st_size=2581856, ...}) = 0
mmap (NULL, 2581856, PROT_READ, MAP_PRIVATE, 4, 0) = 0x7f042a3cb000
close (4)                                = 0
open ("/home/tsu-nera/tmp/test", O_WRONLY|O_CREAT|O_NOCTTY|O_NONBLOCK, 0666) = 4
dup2 (4, 0)                              = 0
close (4)                                = 0
utimensat (0, NULL, NULL, 0)             = 0
close (0)                                = 0
close (1)                                = 0
close (2)                                = 0
exit_group (0)                           = ?
+++ exited with 0 +++

見にくいので, 統計情報を出力する.

あるディレクトリ配下にファイルを作成すだけでも, 結構な数のシステムコールが呼ばれていることが分かる.

[tsu-nera]% strace -c touch /home/tsu-nera/tmp/test
% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
  0.00    0.000000           0         1           read
  0.00    0.000000           0         8         4 open
  0.00    0.000000           0         7           close
  0.00    0.000000           0         4         3 stat
  0.00    0.000000           0         3           fstat
  0.00    0.000000           0         8           mmap
  0.00    0.000000           0         4           mprotect
  0.00    0.000000           0         1           munmap
  0.00    0.000000           0         3           brk
  0.00    0.000000           0         1         1 access
  0.00    0.000000           0         1           dup2
  0.00    0.000000           0         1           execve
  0.00    0.000000           0         1           arch_prctl
  0.00    0.000000           0         1           utimensat
------ ----------- ----------- --------- --------- ----------------
100.00    0.000000                    44         8 total

C 言語 ライブラリで実装してみる

C 言語で ファイル操作を行うために, fopen, fclose を利用する.

#include <stdio.h>
int main (void)
{
  FILE *fp;
  fp = fopen ("/home/tsu-nera/tmp/test", "w");
  fclose (fp);
  return 0;
}

C 言語 システムコールで実装してみる

では, 本題. opne/close を利用する.

fopen と open の違いはここがわかりやすかった.

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

int main (void)
{
  int fd;
  fd = open ("/home/tsu-nera/tmp/test", O_RDWR|O_CREAT, S_IREAD | S_IWRITE);
  close (fd);
  return 0;
}

ファイル書き込み

ファイル新規作成を応用して, ファイルにデータを書き込んでみる.

やること

/home/tsu-nera/tmp 配下に test という 1MB の ファイルを新規作成する.

dd でやってみる

Unix コマンドの dd を利用して, 1MB のファイルを作成する.

dd if=/dev/urandom of=/home/tsu-nera/tmp/test count=1024 bs=1024

dd コマンドのソースをながめる

GNU coreutils のソースを眺める.

たとえば以下のように write 関数が利用されている.

while (total_written < size) { ssize_t nwritten; process_signals (); nwritten = write (fd, buf + total_written, size - total_written); if (nwritten < 0) { if (errno != EINTR) break; } else if (nwritten == 0) { /* Some buggy drivers return 0 when one tries to write beyond a device's end. (Example: Linux kernel 1.2.13 on /dev/fd0.) Set errno to ENOSPC so they get a sensible diagnostic. */ errno = ENOSPC; break; } else total_written += nwritten; } [/sourcecode]

C 言語 ライブラリで実装してみる

fwrite 関数で書き込む.

#include
#include

#define WRITE_SIZE 1024
#define WRITE_COUNT 1024

int main (void)
{
int i;
char r[WRITE_SIZE];
FILE *fp;

// generate random value
for (i = 0; i < WRITE_SIZE; i++) { r[i] = rand (); } // open fp = fopen ("/home/tsu-nera/tmp/test", "w"); // write for (i = 0; i < WRITE_COUNT; i++) { fwrite (r, sizeof (char), WRITE_SIZE, fp); } // close fclose (fp); return 0; } [/sourcecode]

C 言語 システムコールで実装してみる

write 関数で書き込む.

#include
#include
#include
#include
#include

#define WRITE_SIZE 1024
#define WRITE_COUNT 1024

int main (void)
{
int i;
int fd;
char r[WRITE_SIZE];

// generate random value
for (i = 0; i < WRITE_SIZE; i++) { r[i] = rand (); } // open fd = open ("/home/tsu-nera/tmp/test", O_RDWR|O_CREAT, S_IREAD|S_IWRITE); // write for (i = 0; i < WRITE_COUNT; i++) { write (fd, r, WRITE_SIZE); } // close close (fd); return 0; } [/sourcecode]