モーダルを閉じる工作HardwareHub ロゴ画像

工作HardwareHubは、ロボット工作や電子工作に関する情報やモノが行き交うコミュニティサイトです。さらに詳しく

利用規約プライバシーポリシー に同意したうえでログインしてください。

ファイルディスクリプタ関連のシステムコールのサンプルコード (C 言語)

モーダルを閉じる

ステッカーを選択してください

お支払い手続きへ
モーダルを閉じる

お支払い内容をご確認ください

購入商品
」ステッカーの表示権
メッセージ
料金
(税込)
決済方法
GooglePayマーク
決済プラットフォーム
確認事項

利用規約をご確認のうえお支払いください

※カード情報はGoogleアカウント内に保存されます。本サイトやStripeには保存されません

※記事の執筆者は購入者のユーザー名を知ることができます

※購入後のキャンセルはできません

作成日作成日
2018/05/31
最終更新最終更新
2021/09/07
記事区分記事区分
一般公開

目次

    リアクティブプログラミングの魅力を伝えたい日々

    ファイル記述子 (File Descriptor) に関連するシステムコールを利用した C 言語のサンプルコードを記載します。

    ファイルの読み書き

    open/close

    main.c

    #include <unistd.h>
    #include <fcntl.h>
    #include <stdio.h>
    
    int main() {
        int fd_r, fd_w;
    
        // 読み込み専用
        if((fd_r = open("./main.c", O_RDONLY)) < 0) {
            perror("xxx");
            return 1;
        }
        // 存在しなければ新規作成して書き込み、存在していれば既存の内容を削除して書き込み
        // 8進数666 - umask でファイル作成 (umask 022 ならば 644 で作成)
        if((fd_w = open("/tmp/sample.txt", O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0) {
            // 追記する場合は O_WRONLY|O_APPEND
            perror("xxx");
            return 1;
        }
        if(close(fd_r) < 0) {
            perror("xxx");
            return 1;
        }
        if(close(fd_w) < 0) {
            perror("xxx");
            return 1;
        }
        return 0;
    }
    

    実行例 (関連: umask)

    $ gcc -Wall -O2 main.c && ./a.out 
    $ ls -l /tmp/sample.txt 
    -rw-r--r-- 1 vagrant vagrant 0 Jul 31 15:31 /tmp/sample.txt
    $ umask
    0022
    

    read/write

    #include <unistd.h>
    #include <stdio.h>
    
    int main() {
        char buf[1024];
        ssize_t n;
        if((n = read(0, buf, sizeof buf)) < 0) {
            perror("xxx");
            return 1;
        }
        write(1, buf, n);
        return 0;
    }
    

    実行例

    $ gcc -Wall -O2 main.c && echo 123 | ./a.out
    123
    

    lseek

    #include <unistd.h>
    #include <fcntl.h>
    #include <stdio.h>
    
    int main() {
        int fd;
        off_t offset;
        ssize_t n;
        char buf[1024];
    
        if((fd = open("./main.c", O_RDONLY)) < 0) {
            perror("open failed");
            return 1;
        }
    
        // 400 バイト進める
        if((offset = lseek(fd, 400, SEEK_SET)) < 0) {
            perror("lseek failed");
            return 1;
        }
    
        if((n = read(fd, buf, sizeof buf)) < 0) {
            perror("read failed");
            return 1;
        }
        write(1, buf, n);
        return 0;
    }
    

    実行例

    $ gcc -Wall -O2 main.c && ./a.out
    izeof buf)) < 0) {
            perror("read failed");
            return 1;
        }
        write(1, buf, n);
        return 0;
    }
    

    truncate

    #include <unistd.h>
    #include <stdio.h>
    
    int main() {
        if(truncate("sample.txt", 0) < 0) { // 0 バイトに切り詰める
            perror("truncate failed");
            return 1;
        }
        return 0;
    }
    

    ファイルの削除や権限変更

    mkdir/rmdir/unlink/rename

    #include <sys/stat.h>
    #include <unistd.h>
    #include <stdio.h>
    
    int main() {
    
        // mkdir/rmdir
        if(mkdir("mydir", 0777) < 0) { // 777 - umask
            perror("mkdir failed");
            return 1;
        }
        if(rmdir("mydir") < 0) {
            perror("rmdir failed");
            return 1;
        }
    
        // rename/unlink
        if(rename("sample.txt", "sample2.txt") < 0) {
            perror("rename failed");
            return 1;
        }
        if(unlink("sample2.txt") < 0) {
            perror("unlink failed");
            return 1;
        }
        return 0;
    }
    

    chmod/chown

    #include <sys/stat.h>
    #include <unistd.h>
    #include <stdio.h>
    
    int main() {
        if(chmod("sample.txt", 0777) < 0) { // umask は無関係
            perror("chmod failed");
            return 1;
        }
        if(chown("sample.txt", 123, 456) < 0) { // uid, gid
            perror("chown failed");
            return 1;
        }
        return 0;
    }
    
    #include <unistd.h>
    #include <stdio.h>
    
    int main() {
        ssize_t n;
        char buf[1024];
    
        if(symlink("./main.c", "mylink.c") < 0) { // mylink.c -> ./main.c
            perror("symlink failed");
            return 1;
        }
    
        if((n = readlink("mylink.c", buf, sizeof buf - 1)) < 0) {
            perror("readlink failed");
            return 1;
        }
    
        buf[n] = '\0';
        printf("%s\n", buf); // "./main.c"
    
        return 0;
    }
    

    ファイルの記述子の操作

    pipe

    シェルのパイプでも利用されているシステムコールです。例えば fork して作成した子プロセスからの書き込みを親プロセスで受け取ることができます。プロセス間通信 IPC (inter process communication) の実装で利用できます。

    #include <unistd.h>
    #include <stdio.h>
    
    int main() {
    
        // 親プロセスと子プロセスで利用する、
        // 接続されたファイル記述子を格納します。
        int pipe_fd[2];
    
        // 必須ではありませんがここでは子プロセスを生成します。
        pid_t child_pid;
    
        // 親プロセスでファイル記述子からデータを読み出すために利用します。
        ssize_t n;
        char buf[4096];
    
        // ファイル記述子を二つ作成して接続します。
        if(pipe(pipe_fd) < 0) {
            perror("pipe failed");
            return 1;
        }
        printf("%d => %d\n", pipe_fd[1], pipe_fd[0]);
    
        // 子プロセスを作成します。
        if((child_pid = fork()) < 0) {
            perror("fork failed");
            return 1;
        }
        else if(child_pid == 0) {
            // 子プロセスの場合の分岐
            close(pipe_fd[0]); // 使用しないため閉じます。
            write(pipe_fd[1], "IPC from child\n", 15); // IPC (inter process communication) プロセス間通信
            _exit(0);
        }
    
        // 親プロセスの場合の分岐
        close(pipe_fd[1]); // 使用しないため閉じます。
        if((n = read(pipe_fd[0], buf, sizeof buf)) < 0) {
            perror("read failed");
            return 1;
        }
        write(1, buf, n);
        return 0;
    }
    

    実行例

    $ gcc -Wall -O2 main.c && ./a.out
    4 => 3
    IPC from child
    

    poll

    #include <stdio.h>
    #include <poll.h>
    
    int main() {
        int n;
        // 簡単のため、ファイル記述子0 (標準入力) だけを監視してみます。
        struct pollfd fds[1];
        char buf[256];
    
        fds[0].fd = 0;
        fds[0].events = POLLIN;
    
        while(1) {
            n = poll(fds, 1, 5000);
            if(n < 0) {
                perror("poll");
                return 1;
            }
            else if(n == 0) {
                printf("no input\n");
            }
            else {
                if(fds[0].revents & (POLLERR | POLLHUP | POLLNVAL)) {
                    fprintf(stderr, "error\n");
                    return 1;
                }
                if(fds[0].revents & POLLIN) {
                    printf("input from fd0\n");
                    fgets(buf, 256, stdin); // 標準入力から一行読み込んで空にする。
                }
            }
        }
        return 0;
    }
    

    実行例

    $ gcc -Wall -O2 main.c && ./a.out
    a  ←エンター
    input from fd0
    aaa
    input from fd0
    no input  ←5秒経過
    

    select

    poll と異なり select では監視できるファイル記述子数に制限があります。

    #include <sys/select.h>
    #include <stdio.h>
    
    int main() {
    
        int n;
        fd_set readfds;
        struct timeval tv;
    
        // ファイル記述子 0 と 3 を監視
        FD_ZERO(&readfds);
        FD_SET(0, &readfds);
        FD_SET(3, &readfds);
    
        // 5 秒でタイムアウトするように設定
        tv.tv_sec = 5;
        tv.tv_usec = 0;
    
        // select では監視できるファイル記述子数に制限があります。
        printf("SETSIZE = %d\n", FD_SETSIZE);
    
        while(1) {
    
            // ファイル記述子 0 から 4-1 までを監視
            n = select(4, &readfds, NULL, NULL, &tv);
    
            if(n < 0) {
                perror("select failed");
                return 1;
            }
            else if(n == 0) {
                printf("no input\n");
            }
            else {
                if(FD_ISSET(0, &readfds)) {
                    printf("fd = 0\n");
                }
                if(FD_ISSET(3, &readfds)) {
                    printf("fd = 3\n");
                }
            }
        }
        return 0;
    }
    

    実行例

    gcc -Wall -O2 main.c && ./a.out 3<&0
    

    dup2/fcntl

    以下のサンプルではファイル記述子1 (標準出力) の複製であるファイル記述子3 を生成して利用しています。

    #include <unistd.h>
    #include <stdio.h>
    
    int main() {
    
        if(dup2(1, 3) < 0) {
            perror("dup2 failed");
            return 1;
        }
    
        write(3, "Hello\n", 6);
    
        return 0;
    }
    

    同様のことは汎用的なファイル記述子操作用のシステムコール fcntl に F_DUPFD を指定しても実現できます。

    #include <unistd.h>
    #include <fcntl.h>
    #include <stdio.h>
    
    int main() {
    
        if(fcntl(1, F_DUPFD, 3) < 0) {
            perror("fcntl failed");
            return 1;
        }
    
        write(3, "Hello\n", 6);
    
        return 0;
    }
    

    実行例

    $ gcc -Wall -O2 main.c && ./a.out
    Hello
    

    デバイスファイルにリクエストを発行

    デバイスファイルには、実際に接続されているハードウェアのデバイスドライバへのインタフェースとして機能するファイルや、擬似デバイスの /dev/null 等を含めて以下のようなものがあります。

    /dev/sda1 (ハードディスク)
    /dev/null
    /dev/zero
    /dev/stdin -> fd/0
    /dev/stdout -> fd/1
    /dev/stderr -> fd/2
    /dev/tty (接続している端末デバイス teletypewriter TTY)
    /dev/pts/{番号} (擬似端末)
    /dev/port (I/O ポートアクセス)
    

    TTY/PTS について

    疑似端末 (pseudo TTY) は SSH 等でリモートログインすると生成されます。以下では SSH クライアントが 4 つ接続している状態です。

    vagrant@stretch:~$ w
     14:15:03 up  2:36,  4 users,  load average: 0.00, 0.00, 0.00
    USER     TTY      FROM             LOGIN@   IDLE   JCPU   PCPU WHAT
    vagrant  pts/0    10.0.2.2         11:39   51:25   0.20s  0.20s -bash
    vagrant  pts/1    10.0.2.2         13:34   38:46   0.07s  0.02s pager
    vagrant  pts/2    10.0.2.2         13:37    2:04   0.12s  0.00s less
    vagrant  pts/3    10.0.2.2         14:08    0.00s  0.03s  0.00s w
    
    vagrant@stretch:~$ ls -l /dev/pts/
    total 0
    crw--w---- 1 vagrant tty  136, 0 Aug 12 13:23 0
    crw--w---- 1 vagrant tty  136, 1 Aug 12 13:36 1
    crw--w---- 1 vagrant tty  136, 2 Aug 12 14:13 2
    crw--w---- 1 vagrant tty  136, 3 Aug 12 14:15 3
    c--------- 1 root    root   5, 2 Aug 12 11:38 ptmx
    

    自分自身の番号は tty コマンドで確認できます。

    vagrant@stretch:~$ tty
    /dev/pts/3
    

    以下の三つは同じ挙動を示します。

    echo 123
    echo 123 > /dev/tty
    echo 123 > /dev/pts/3
    

    ps コマンドの TTY 列で各プロセスの制御端末の番号を確認できます。

    vagrant@stretch:~$ ps
      PID TTY          TIME CMD
    23929 pts/3    00:00:00 bash
    23971 pts/3    00:00:00 ps
    

    デバイスファイルの種類について

    デバイスファイルはデバイスドライバの API として機能しており open/close/read/write/ioctl を提供します。その際、一般にランダムアクセス等が可能なブロックデバイスと、そうではないキャラクタデバイスがあります。

    vagrant@stretch:~$ ls -l /dev/sda1
    brw-rw---- 1 root disk 8, 1 Aug 12 11:38 /dev/sda1  <-- 'b'lock
    
    vagrant@stretch:~$ ls -l /dev/zero
    crw-rw-rw- 1 root root 1, 5 Aug 12 11:38 /dev/zero  <-- 'c'haracter
    

    ioctl

    デバイスドライバによっては read/write に加えて利用できる特別なコマンドが用意されています。ioctl を利用してデバイスドライバに対してそれらコマンドを指定してリクエストを出すことができます。

    指定可能なリクエスト一覧

    man 2 ioctl_list
    

    特に制御端末に関するリクエストについて

    man 4 tty_ioctl
    

    制御端末のサイズを取得する例です。TIOCGWINSZ リクエストを発行しています。

    #include <fcntl.h>
    #include <sys/ioctl.h>
    #include <stdio.h>
    
    int main() {
        int fd;
        struct winsize win;
    
        if((fd = open("/dev/tty", O_RDONLY)) < 0) {
            perror("open failed");
            return 1;
        }
    
        if(ioctl(fd, TIOCGWINSZ, &win) < 0) {
            perror("ioctl failed");
            return 1;
        }
    
        printf("row: %d, col: %d\n", win.ws_row, win.ws_col);
        return 0;
    }
    

    実行例

    $ gcc -Wall -O2 main.c && ./a.out
    row: 52, col: 218
    

    その他

    getdtablesize

    システムリソースのうち open 可能なファイルディスクリプタ数の上限を調査できます。

    #include <unistd.h>
    #include <stdio.h>
    
    int main() {
        printf("%d\n", getdtablesize());
        return 0;
    }
    

    実行例

    $ gcc -Wall -O2 main.c && ./a.out
    256
    $ ulimit -S -n
    256
    $ ulimit -H -n
    unlimited
    

    sync

    #include <unistd.h>
    
    int main() {
        sync();
        return 0;
    }
    

    Python から利用する場合

    システムコールの多くは Python の os モジュールから利用できます。

    #!/usr/bin/python
    # -*- coding: utf-8 -*-
    
    from os import open as _open, read, O_RDONLY
    
    def Main():
        fd = _open("/dev/stdin", O_RDONLY)
        print read(fd, 5)
    
    if __name__ == '__main__':
        Main()
    

    実行例

    $ echo "Hello" | python sample.py
    Hello
    
    Likeボタン(off)0
    詳細設定を開く/閉じる
    アカウント プロフィール画像

    リアクティブプログラミングの魅力を伝えたい日々

    記事の執筆者にステッカーを贈る

    有益な情報に対するお礼として、またはコメント欄における質問への返答に対するお礼として、 記事の読者は、執筆者に有料のステッカーを贈ることができます。

    >>さらに詳しくステッカーを贈る
    ステッカーを贈る コンセプト画像

    Feedbacks

    Feedbacks コンセプト画像

      ログインするとコメントを投稿できます。

      ログインする

      関連記事

      • cmake で ccache を有効化するための設定
        YOCTO Linux で開発している場合など、ビルド速度が開発効率にそのまま影響する際は ccache (compiler cache) で C/C++ ビルドを高速化することを考えます。cmakeと併用する場合の設定およびコマンド例を記載します。 インストール sudo apt install ccache 以下のバイナリファイルに加えて $ w
        AWS NinjaAWS Ninja10/22/2022に更新
        いいねアイコン画像0
      • Python から C ライブラリを利用 (ctypes)
        FFI (Foreign Function Interface) の一つである ctypes を利用すると、C 言語のライブラリを Python から利用できます。サンプルコードを記載します。 適宜参照するための公式ドキュメント libm の sqrt を利用する例 main.py ``
        coderinacoderina8/2/2021に更新
        いいねアイコン画像0
      • 低レイヤーネットワークプログラミングに関する雑多な知識
        TCP/IP モデルのうちトランスポート層ではなく、インターネット層およびネットワークインターフェイス層のパケット (正確には PDU) を扱う低レイヤープログラミングの雑多なテクニックをまとめます。『ルーター自作でわかるパケットの流れ』などを参考にしています。バックアップ目的で書籍のサンプルコードをホスティングしました。 検証環境
      • C言語の資産を利用 (C++をもう一度)
        サンプルコード メルセンヌ・ツイスタなど、C言語で記述されたライブラリをC++から利用するためには extern "C" を利用します。その際、組み込みマクロ __cplusplus を利用するとC言語からもC++からも利用できるヘッダファイルを作成できます。 sub.h #ifndef SUB_H_ #define SUB_H_ #ifdef __cplu
      • C言語コード読解:size_t型とは
        サイズを表現するための符号なし整数型です。読み手にとっても、何らかのサイズを格納するための変数であることが分かりやすくなります。真偽値をbool型変数で扱う場合も同様です。 sample.cpp #include <iostream> using namespace std; int main() { size_t size = sizeof(int); cout &l...
        ほっこりさんほっこりさん4/13/2018に更新
        いいねアイコン画像0