開発環境 C言語(その4)

【連載】

にわか管理者のためのLinux運用入門

【第159回】開発環境 C言語(その4)

[2019/01/02 08:00]後藤大地 ブックマーク ブックマーク

サーバ/ストレージ

前回、ファイルシステムイベントのモニタリングを実施するためのLinuxカーネルの機能としてinotify APIのマニュアルに掲載されているサンプルコードを挙げて紹介した。今回は、そのコードをシンプルにして動きを説明しよう。

inotify APIの使い方を知る

経験のないAPIの使い方を学ぶ方法として、サンプルコードをさらにシンプル化していくことで処理の本質を捉えるという方法がある。例えば、前回紹介したサンプルコードはエラー処理がちゃんと記載されていたし、複数の種類のイベントを追加することを想定したコーディングになっていた。便宜的に、こうした部分を削除して整理していけば、「inotify APIを試しに使ってみるだけ」のための非常にシンプルなソースコードにできる。

本連載はプログラミングについて解説するものではないので、そいでいく工程は省くが、興味がある方は自分でやってみていただきたい。

まず、コンパイルなどを行うためのMakefileを次に示す。前回は「try_inotify.c」というファイル名にしたので、今回は「try_inotify2.c」というファイル名にする。また、コンパイル後に生成するバイナリファイルの名前は「try_inotify2」にしてある。

SRCS=   try_inotify2.c
CMD=    try_inotify2

OBJS=   ${SRCS:.c=.o}

all: ${OBJS}
    cc -o ${CMD} ${OBJS}

.SUFFIXES: .c .c

.c.o:
    cc -c $<

clean:
    rm -f ${OBJS} ${CMD}

ここではエラー処理やより広く利用できるようにしている処理をそぎ落として、ファイルシステムのイベントだけをモニタリングするようにしたソースコードを掲載しておく。コメントも書いておいた。try_inotify2.cの中身は次の通りだ。

#include <errno.h>
#include <poll.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/inotify.h>
#include <unistd.h>

static void
handle_events(int fd, int *wd, int argc, char* argv[])
{
    char buf[4096], *ptr;
    const struct inotify_event *event;
    ssize_t len;

    /* 複数のイベントをバッファに読み込み */
    len = read(fd, buf, sizeof buf);

    ptr = buf;
    while (ptr < buf + len) {
        /* バッファにおけるイベントにアクセス */
        event = (const struct inotify_event *) ptr;

        /* イベント種類を確認 */
        if (event->mask & IN_MODIFY)
            printf("IN_MODIFY: ");

        /* 対象パスを出力
         * 引数にファイルパスが指定された場合にはここでファイルパスが出力される
         * 引数にディレクトリパスが指定された場合にはここでディレクトリパスが出力される */
        for (int i = 1; i < argc; ++i) {
            if (wd[i] == event->wd) {
                printf("%s", argv[i]);
                break;
            }
        }

        /* 対象ファイル名を出力
         * 引数にディレクトリパスが指定された場合にはここでファイル名が出力される */
        if (event->len)
            printf("name: %s", event->name);

        /* ファイルシステムオブジェクトの種類を表示 */
        if (event->mask & IN_ISDIR)
            printf(" [directory]\n");
        else
            printf(" [file]\n");

        /* バッファにおける次のイベントを指し示す  */
        ptr += sizeof(struct inotify_event) + event->len;
    }
}

int
main(int argc, char* argv[])
{
    int fd, poll_num;
    int wd[1024];
    nfds_t nfds;
    struct pollfd fds[1];

    /* inotify API にアクセスするためのファイルディスクリプタを生成 */
    fd = inotify_init1(IN_NONBLOCK);

    /* モニタリング対象を追加 */
    for (int i = 1; i < argc; i++)
        /* イベントの種類はIN_MODIFY */
        wd[i] = inotify_add_watch(fd, argv[i], IN_MODIFY);

    /* ポーリングを準備 */
    nfds = 1;
    fds[0].fd = fd;
    fds[0].events = POLLIN;

    /* ポーリング */
    while (1) {
        poll_num = poll(fds, nfds, -1);
        if (poll_num > 0) {
            if (fds[0].revents & POLLIN)
                /* イベントを処理する関数を呼び出し */
                handle_events(fd, wd, argc, argv);
        }
    }

    exit(EXIT_SUCCESS);
}

ある程度C言語のソースコードが読める方であれば、このくらいまでそいでおけば、何となくやっていることがわかってくるのではないかと思う。「C言語はよくわからない」という方向けに、try_inotify2.cの処理の流れを日本語でまとめたものを次に掲載しておく。

プログラム本体:
1. inotify APIにアクセスするためのものを取得
2. モニタリング対象を設定
3. モニタリングを準備
4. 永久ループ開始
  4.1 イベントが発生したら関数Aを実行
  4.に戻る

関数A:
1. 複数のイベントを取得
2. イベントが残っているなら次ループ内処理を行う
  2.1. 発生したイベントを取得
  2.2. イベントの種類を判定
  2.3. パスを出力
  2.4. ファイルシステムオブジェクトの種類を出力
  2.5. 次のイベントに進む
  2.6. 2.に戻る
3. 関数Aを終了

try_inotify2.cはもっと処理をそいで絞り込むことができるのだが、元のサンプルコードの構造をそれほど壊さない状態でシンプル化するとなると、このくらいが無難だろう。

ここで押さえておきたいのは、モニタリングを実施する部分と、発生したイベントを処理する部分が別の関数に実装されている点だ。また、イベントの発生を待つ方法として「poll(2)」が使われていることにも注目しておきたい。poll(2)はこのような仕組みでモニタリングを実施するシステムコールの1つで、AT&T System V UNIXというUNIX系OSの初期から存在している機能で、よくこういった実装を行う。これは考えても仕方ない部分なので、「こうやって使うものなんだなあ」と思っておいていただければよい。

サンプルコードを実行

では、try_inotify2.cをコンパイルして実行してみよう。

try_inotify2.cをコンパイルして実行

この状態で、別のターミナルから監視対象のファイル「log」にテキストデータを追記してみる。コマンドは以下の通りだ。

echo メッセージ >> log

すると、次のように、try_inotify2がイベントを検出して処理が行われることを確認できる。

ファイルの変更が検出されて処理が行われる

なお、try_inotify2は「Ctrl-C」で終了させることができる。

今度は次のように、モニタリング対象をファイルではなくディレクトリに変更してみる。

モニタリング対象をディレクトリにしてtry_inotify2を実行

この状態で、次のようにディレクトリ直下にあるlogファイルにテキストデータを追記してみる。

echo メッセージ >> log

try_inotify2がイベントを検出して処理が行われることを確認できるはずだ。また、パスを表示する部分の処理として、ファイルパスを指定したときには実行されなかった部分が実行されていることもわかる。inotifyでは対象がディレクトリだった場合、その直下のファイルやディレクトリに関してもイベントが処理されるため、このような動きをしている。

ファイルの変更が検出されて処理が行われる

指定したディレクトリ直下のほかの監視対象ファイル「log2」にメッセージを追記してみる。

echo メッセージ >> log2

この場合も、次のようにモニタリング対象として処理が行われていることがわかる。

モニタリング対象のファイルの変更が検出され、処理が行われている

このように、inotify APIを使うとファイルやディレクトリに対して発生した変更を検知して処理を行わせることができる。「ファイルへの書き込みをイベントの起点として何か処理を行わせる」というのは、管理業務でも結構便利に使える機能だ。

「今回のソースコードでも意味がよくわからなかった……」としても大丈夫。次回はさらにソースコードをそぎ落とし、もっとシンプル化した状態でinotify APIの使い方を説明しよう。

※ 本記事は掲載時点の情報であり、最新のものとは異なる場合がございます。予めご了承ください。

一覧はこちら

連載目次

もっと知りたい!こちらもオススメ

なぜ今、統合システムなのか? 押さえておくべき「3つのインパクト」

なぜ今、統合システムなのか? 押さえておくべき「3つのインパクト」

ガートナー ジャパンは10月31日~11月2日、都内で「Gartner Symposium/ITxpo 2017」を開催。11月1日には同社 主席アナリストの青山浩子氏が登壇し「CIOが理解すべき統合システムの3大インパクト」と題する講演を行った。本稿では、講演の内容をダイジェストでお届けする。

関連リンク

この記事に興味を持ったら"いいね!"を Click
Facebook で IT Search+ の人気記事をお届けします

会員登録(無料)

注目の特集/連載
知りたい! カナコさん 皆で話そうAIのコト
教えてカナコさん! これならわかるAI入門
対話システムをつくろう! Python超入門
Kubernetes入門
RPA入門
AWSで作るクラウドネイティブアプリケーションの基本
ソフトウェア開発自動化入門
PowerShell Core入門
Swagger 3.0 入門
徹底研究! ハイブリッドクラウド
マイナビニュース スペシャルセミナー 講演レポート/当日講演資料 まとめ
セキュリティアワード特設ページ

一覧はこちら

今注目のIT用語の意味を事典でチェック!

一覧はこちら

ページの先頭に戻る