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

【連載】

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

【第160回】開発環境 C言語(その5)

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

サーバ/ストレージ

前回は、ファイルシステムイベントのモニタリングを実施するためのLinuxカーネルの機能として「inotify API」を取り上げ、サンプルコードから便宜的にエラーハンドリングや汎用性を確保するための処理を抜いていって、APIそのものの使い方を調べていく方法を紹介した。

前回のサンプルコードで結構シンプルなところまでそぎ落としたが、前回のコードにはpoll(2)システムコールの機能が使われていた。inotify APIは、実際にはpoll(2)システムコールを使わなくても同じ処理を実施することができる。サンプルコードでpoll(2)システムコールが使われていたのは、inotify API以外のイベントも捕捉するためだった。今回は、poll(2)システムコールを削除すると共に、関数として分離していた部分をmain()内部に展開し、さらにシンプルなコードにして使い方を調べていく。

さらにシンプルにしてみよう

まず、今回のソースコード用のMakefileを用意する。ソースコードのファイル名は「try_inotify3.c」で、コンパイル後に生成されるバイナリファイル名は「try_inotify3」だ。

SRCS=   try_inotify3.c
CMD=    try_inotify3

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

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

.SUFFIXES: .c .c

.c.o:
    cc -c $<

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

Makefileは、次のように「make」でコンパイルを実施できる。

$ make

ソースコードも書き換えるなら、少し編集するごとにmakeコマンドでコンパイルし、確認しながら作業をしてみてほしい。

先に使い方も説明しておこう。コンパイルして生成されるバイナリファイル「try_inotify3」は次のようにパスを指定して実行する。パスはファイルパスでもディレクトリパスでもかまわない。ディレクトリを指定した場合、ディレクトリ直下のファイルやディレクトリが監視の対象となる。

$ ./try_inotify3 パス [パス パス ...]

try_inotify3を実行したら、別のターミナルからtry_inotify3に指定したファイルに対してテキストデータを追記するなどして、ファイルデータの更新を実施する。すると、try_inotify3側でイベントを検出して、ターミナルにメッセージが表示されるようになる。

$ echo message >> ファイルパス

続いて、poll(2)システムコールを使っていた部分を抜き、さらに別の関数に分離していた部分をmain()の中にマージしたのが次のコードだ。

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

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

    /* 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);

    /* モニタリングを実施 */
    for (;;) {
        /* 
         * 複数のイベントをバッファに読み込み
         * ここでイベントが発生するまで処理が待ち状態になる
         */
        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;
        }
    }

    exit(EXIT_SUCCESS);
}

特に、inotify APIを利用するための本質的な処理を抜き出すと次のようになる。

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

/* モニタリング対象を追加 */
wd[i] = inotify_add_watch(fd, argv[i], IN_MODIFY);

/* 
 * 複数のイベントをバッファに読み込み
 * ここでイベントが発生するまで処理が待ち状態になる
 */
len = read(fd, buf, sizeof buf);

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

「C言語はソースコードを見るだけでちょっと頭が痛い……」という方向けに、日本語的にまとめると、次のようになる。

  1. inotify_init1()でinotify APIを利用するための準備を実行
  2. inotify_add_watch()でモニタリング対象を追加
  3. read()で追加したモニタリング対象に何らかのイベントが発生するまで待機
  4. (const struct inotify_event *)でイベントデータにアクセス


これがinotify APIの最も基本的な使い方である。今回のソースコードで特に注目しておきたいのは「len = read(fd, buf, sizeof buf);」の記述だ。ここでバッファにイベントデータの読み込みを行っているのだが、イベントが発生するまで読み込みは行われない。つまり、監視対象のファイルやディレクトリに何らかの変更が発生するまでは、ここで処理が待ち状態に入り、ストップするのである。動きとしてはこの辺りを把握しておけば、今回掲載したサンプルコードの動作が見えてくるのではないだろうか。

かなりコードをシンプルにして説明してきたが、C言語のソースコードは最近主流の高級言語と比べると難しいところがあるので、いまいちピンと来ないとしても仕方ないのかもしれない。「こういうことができるらしい」ということだけでも、知っておいてもらえればと思う。

実用的なコマンドを作ってみよう

ここまでシンプル化したら、今度はこのサンプルコードを書き換えて実用的なコマンドを作ってみよう。前々回から今回に至るまでは”学習”に過ぎなかったが、実用的なコマンドを作成することで、やっと本連載の目的である「管理の手間を低減する」ことに結び付いてくる。

ファイルやディレクトリの変更をモニタリングして、変更が実施された場合には何らかのコマンドを実行したいというのは比較的ポピュラーな要望だ。簡単な処理だが、実際にこうした処理を行うコマンドがあるととても重宝する。次回は、このAPIを使った簡単なコマンドの開発例を紹介する。

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

一覧はこちら

連載目次

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

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

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

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

関連リンク

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

会員登録(無料)

注目の特集/連載
[解説動画] Googleアナリティクス分析&活用講座 - Webサイト改善の正しい考え方
[解説動画] 個人の業務効率化術 - 短時間集中はこうして作る
ミッションステートメント
教えてカナコさん! これならわかるAI入門
知りたい! カナコさん 皆で話そうAIのコト
対話システムをつくろう! Python超入門
Kubernetes入門
AWSで作るクラウドネイティブアプリケーションの基本
PowerShell Core入門
徹底研究! ハイブリッドクラウド
マイナビニュース スペシャルセミナー 講演レポート/当日講演資料 まとめ
セキュリティアワード特設ページ

一覧はこちら

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

一覧はこちら

ページの先頭に戻る