26 Sep 2014, 12:43

C 言語でシステムコールを利用してファイル操作をしてみる.

はじめに

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

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

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

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

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

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

ファイル作成

やること

<div class="outline-text-3" id="text-2-1">
  <p>
    /home/tsu-nera/tmp 配下に test というファイルを新規作成する.
  </p>

  <ul class="org-ul">
    <li>
      <a href="https://gist.github.com/tsu-nera/f0be86e0a704471372b3">https://gist.github.com/tsu-nera/f0be86e0a704471372b3</a>
    </li>
  </ul>
</div>

touch で作成する

<div class="outline-text-3" id="text-2-2">
  <p>
    Unix のコマンド touch で作成する.
  </p>

  <ul class="org-ul">
    <li>
      <a href="http://ja.wikipedia.org/wiki/Touch_(UNIX)">touch (UNIX) &#8211; Wikipedia</a>
    </li>
  </ul>

  <p>
    [sourcecode language=&#8221;bash&#8221; title=&#8221;&#8221; ]<br /> touch /home/tsu-nera/tmp/test<br /> [/sourcecode]
  </p>
</div>

<div id="outline-container-sec-2-2-1" class="outline-4">
  <h4 id="sec-2-2-1">
    touch のソースを眺める
  </h4>

  <div class="outline-text-4" id="text-2-2-1">
    <p>
      GNU coreutils のソースを眺める.
    </p>

    <ul class="org-ul">
      <li>
        <a href="https://github.com/goj/coreutils/blob/master/src/touch.c">https://github.com/goj/coreutils/blob/master/src/touch.c</a>
      </li>
    </ul>

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

<div id="outline-container-sec-2-2-2" class="outline-4">
  <h4 id="sec-2-2-2">
    strace でみてみる
  </h4>

  <div class="outline-text-4" id="text-2-2-2">
    <p>
      プログラムが使用するシステムコールおよび受け取るシグナルを監視するツール.
    </p>

    <ul class="org-ul">
      <li>
        <a href="http://ja.wikipedia.org/wiki/Strace">strace &#8211; Wikipedia</a>
      </li>
    </ul>

    <p>
      [sourcecode language=&#8221;text&#8221; title=&#8221;&#8221; ]<br /> strace touch /home/tsu-nera/tmp/test<br /> [/sourcecode]
    </p>

    <p>
      こうなった.
    </p>

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

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

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

    <p>
      [sourcecode language=&#8221;text&#8221; title=&#8221;&#8221; ]<br /> [tsu-nera]% strace -c touch /home/tsu-nera/tmp/test<br /> % time seconds usecs/call calls errors syscall<br /> &#8212;&#8212; &#8212;&#8212;&#8212;&#8211; &#8212;&#8212;&#8212;&#8211; &#8212;&#8212;&#8212; &#8212;&#8212;&#8212; &#8212;&#8212;&#8212;&#8212;&#8212;-<br /> 0.00 0.000000 0 1 read<br /> 0.00 0.000000 0 8 4 open<br /> 0.00 0.000000 0 7 close<br /> 0.00 0.000000 0 4 3 stat<br /> 0.00 0.000000 0 3 fstat<br /> 0.00 0.000000 0 8 mmap<br /> 0.00 0.000000 0 4 mprotect<br /> 0.00 0.000000 0 1 munmap<br /> 0.00 0.000000 0 3 brk<br /> 0.00 0.000000 0 1 1 access<br /> 0.00 0.000000 0 1 dup2<br /> 0.00 0.000000 0 1 execve<br /> 0.00 0.000000 0 1 arch_prctl<br /> 0.00 0.000000 0 1 utimensat<br /> &#8212;&#8212; &#8212;&#8212;&#8212;&#8211; &#8212;&#8212;&#8212;&#8211; &#8212;&#8212;&#8212; &#8212;&#8212;&#8212; &#8212;&#8212;&#8212;&#8212;&#8212;-<br /> 100.00 0.000000 44 8 total<br /> [/sourcecode]
    </p>
  </div>
</div>

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

<div class="outline-text-3" id="text-2-3">
  <p>
    C 言語で ファイル操作を行うために, fopen, fclose を利用する.
  </p>

  <ul class="org-ul">
    <li>
      <a href="http://linuxjm.sourceforge.jp/html/LDP_man-pages/man3/fopen.3.html">Man page of FOPEN</a>
    </li>
    <li>
      <a href="http://linuxjm.sourceforge.jp/html/LDP_man-pages/man3/fclose.3.html">Man page of FCLOSE</a>
    </li>
  </ul>

  <p>
    [sourcecode language=&#8221;c&#8221; title=&#8221;&#8221; ]<br /> #include <stdio.h><br /> int main (void)<br /> {<br /> FILE *fp;<br /> fp = fopen (&#8220;/home/tsu-nera/tmp/test&#8221;, &#8220;w&#8221;);<br /> fclose (fp);<br /> return 0;<br /> }<br /> [/sourcecode] </div> </div> 

    <div id="outline-container-sec-2-4" class="outline-3">
      <h3 id="sec-2-4">
        C 言語 システムコールで実装してみる
      </h3>

      <div class="outline-text-3" id="text-2-4">
        <p>
          では, 本題. opne/close を利用する.
        </p>

        <ul class="org-ul">
          <li>
            <a href="http://linuxjm.sourceforge.jp/html/LDP_man-pages/man2/open.2.html">Man page of OPEN</a>
          </li>
          <li>
            <a href="http://linuxjm.sourceforge.jp/html/LDP_man-pages/man2/close.2.html">Man page of CLOSE</a>
          </li>
        </ul>

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

        <ul class="org-ul">
          <li>
            <a href="http://d.hatena.ne.jp/skyjoker/20130102/1357093289">fopen (高水準入出力) と open (低水準入出力) の違い &#8211; skyjoker (飛びます)</a>
          </li>
        </ul>

        <p>
          [sourcecode language=&#8221;c&#8221; title=&#8221;&#8221; ]<br /> #include <sys/types.h><br /> #include <sys/stat.h><br /> #include <fcntl.h><br /> #include <unistd.h>
        </p>

        <p>
          int main (void)<br /> {<br /> int fd;<br /> fd = open (&#8220;/home/tsu-nera/tmp/test&#8221;, O_RDWR|O_CREAT, S_IREAD | S_IWRITE);<br /> close (fd);<br /> return 0;<br /> }<br /> [/sourcecode]
        </p>
      </div>
    </div></div> 

    <div id="outline-container-sec-3" class="outline-2">
      <h2 id="sec-3">
        ファイル書き込み
      </h2>

      <div class="outline-text-2" id="text-3">
        <p>
          ファイル新規作成を応用して, ファイルにデータを書き込んでみる.
        </p>
      </div>

      <div id="outline-container-sec-3-1" class="outline-3">
        <h3 id="sec-3-1">
          やること
        </h3>

        <div class="outline-text-3" id="text-3-1">
          <p>
            /home/tsu-nera/tmp 配下に test という 1MB の ファイルを新規作成する.
          </p>
        </div>
      </div>

      <div id="outline-container-sec-3-2" class="outline-3">
        <h3 id="sec-3-2">
          dd でやってみる
        </h3>

        <div class="outline-text-3" id="text-3-2">
          <p>
            Unix コマンドの dd を利用して, 1MB のファイルを作成する.
          </p>

          <p>
            [sourcecode language=&#8221;bash&#8221; title=&#8221;&#8221; ]<br /> dd if=/dev/urandom of=/home/tsu-nera/tmp/test count=1024 bs=1024<br /> [/sourcecode]
          </p>
        </div>

        <div id="outline-container-sec-3-2-1" class="outline-4">
          <h4 id="sec-3-2-1">
            dd コマンドのソースをながめる
          </h4>

          <div class="outline-text-4" id="text-3-2-1">
            <p>
              GNU coreutils のソースを眺める.
            </p>

            <ul class="org-ul">
              <li>
                <a href="https://github.com/goj/coreutils/blob/master/src/dd.c">https://github.com/goj/coreutils/blob/master/src/dd.c</a>
              </li>
            </ul>

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

            <p>
              [sourcecode language=&#8221;c&#8221; title=&#8221;&#8221; ]<br /> 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] </div> </div> </div> 

              <div id="outline-container-sec-3-3" class="outline-3">
                <h3 id="sec-3-3">
                  C 言語 ライブラリで実装してみる
                </h3>

                <div class="outline-text-3" id="text-3-3">
                  <p>
                    fwrite 関数で書き込む.
                  </p>

                  <ul class="org-ul">
                    <li>
                      <a href="http://linuxjm.sourceforge.jp/html/LDP_man-pages/man3/fwrite.3.html">Man page of FREAD</a>
                    </li>
                  </ul>

                  <p>
                    [sourcecode language=&#8221;c&#8221; title=&#8221;&#8221; ]<br /> #include <stdio.h><br /> #include <stdlib.h>
                  </p>

                  <p>
                    #define WRITE_SIZE 1024<br /> #define WRITE_COUNT 1024
                  </p>

                  <p>
                    int main (void)<br /> {<br /> int i;<br /> char r[WRITE_SIZE];<br /> FILE *fp;
                  </p>

                  <p>
                    // generate random value<br /> 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] </div> </div> 

                    <div id="outline-container-sec-3-4" class="outline-3">
                      <h3 id="sec-3-4">
                        C 言語 システムコールで実装してみる
                      </h3>

                      <div class="outline-text-3" id="text-3-4">
                        <p>
                          write 関数で書き込む.
                        </p>

                        <ul class="org-ul">
                          <li>
                            <a href="http://linuxjm.sourceforge.jp/html/LDP_man-pages/man2/write.2.html">Man page of WRITE</a>
                          </li>
                        </ul>

                        <p>
                          [sourcecode language=&#8221;c&#8221; title=&#8221;&#8221; ]<br /> #include <sys/types.h><br /> #include <sys/stat.h><br /> #include <fcntl.h><br /> #include <unistd.h><br /> #include <stdlib.h>
                        </p>

                        <p>
                          #define WRITE_SIZE 1024<br /> #define WRITE_COUNT 1024
                        </p>

                        <p>
                          int main (void)<br /> {<br /> int i;<br /> int fd;<br /> char r[WRITE_SIZE];
                        </p>

                        <p>
                          // generate random value<br /> 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] </div> </div> </div> 

                          <div id="outline-container-sec-4" class="outline-2">
                            <h2 id="sec-4">
                              Special Thanks
                            </h2>

                            <div class="outline-text-2" id="text-4">
                              <ul class="org-ul">
                                <li>
                                  <a href="http://www.atmarkit.co.jp/ait/articles/1111/16/news161.html">知ってトクするシステムコール (1):システムコールについてどれくらいご存じですか? (1/2) &#8211; @ IT</a>
                                </li>
                                <li>
                                  <a href="http://linuxc.info/file/file1/">LinuxC | ファイルのオープン, クローズ</a>
                                </li>
                                <li>
                                  <a href="http://curtaincall.weblike.jp/portfolio-unix/api.html">システムコールを理解する | UNIX world</a>
                                </li>
                              </ul>
                            </div>
                          </div>