web-dev-qa-db-ja.com

Linuxでファイル記述子に信号割り込みを生成する方法は?

Linuxでファイル記述子に信号割り込みを生成するにはどうすればよいですか?

動機は、マイクロコントローラーの場合と同様に、ユーザーランドで割り込みを生成することです。 I/Oのファイル記述子があり、ステータスが変化するたびに割り込みを生成しますか?

誰か教えてもらえますか?できれば例を挙げてください。

5

Linuxは、ファイルシステムイベントを監視するための2つのメカニズムを提供します。 dnotify および inotify

2つのうちの古い方であるdnotifyは、カーネルバージョン2.4.0で導入されました。これにより、アプリケーションは fcntl() インターフェイスを介してディレクトリの変更に関する通知を受信するように登録できます。通知自体はシグナルを介して配信されます。 dnotifyメカニズムは、ディレクトリ内の変更の監視に限定されており、個々のファイルの監視は許可されていません。さらに、監視対象のディレクトリへのオープンファイル記述子を維持する必要があります。 dnotifyメカニズムは、inotifyが導入された2.6.13で廃止されました。

新しいプログラムでは、ディレクトリと個々のファイルの両方の監視をサポートするinotifyメカニズムを使用する必要があります。ただし、信号に基づいたものではありません。 inotifyインスタンスはファイル記述子に関連付けられています。イベント通知は、このファイル記述子から読み取ることができます。

両方のメカニズムの制限は、ディレクトリを再帰的に監視するオプションがないことです。つまり、監視対象のサブツリー内のディレクトリごとに個別に監視を確立する必要があります。


例(dnotify):

_#define _GNU_SOURCE
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <unistd.h>

/* For error handling */
#include <stdlib.h>
#include <errno.h>
#include <error.h>

static volatile int event_fd;

static void handler(int sig, siginfo_t *si, void *data)
{
    event_fd = si->si_fd;
}

int main(int argc, char *argv[])
{
    struct sigaction sa;
    int fd;

if(argc < 2)
    error(EXIT_FAILURE, 0, "missing argument");

    sa.sa_sigaction = handler;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = SA_SIGINFO;
    sigaction(SIGRTMIN + 1, &sa, NULL);

    if((fd = open(argv[1], O_RDONLY)) < 0)
        error(EXIT_FAILURE, errno, "failed to open '%s'", argv[1]);

    if(fcntl(fd, F_SETSIG, SIGRTMIN + 1) < 0)
         error(EXIT_FAILURE, errno, "failed to set dnotify signal");

    if(fcntl(fd, F_NOTIFY, DN_MODIFY|DN_CREATE|DN_DELETE|DN_MULTISHOT))
    error(EXIT_FAILURE, errno, 
              "failed to register notification for '%s'", argv[1]);

    while (1) {
        pause();
        printf("event occured for fd=%d\n", event_fd);
    }
}
_

説明:

_fcntl(fd, F_SETSIG, SIGRTMIN + 1)
_

通知イベントが発生したときに送信されるシグナルを設定します。ゼロの値は、SIGIO(デフォルト)が送信されることを示します。 SIGIOを含むその他の値は、代わりに送信される信号として解釈されます。後者の場合、シグナルハンドラーは2番目の引数として_siginfo_t_構造体を受け取り、構造体の_si_fd_フィールドには、イベントを生成したファイル記述子が含まれます。

リアルタイム信号(_>= SIGRTMIN_)が通知に使用される場合、複数のI/Oイベントが同じ信号番号(使用可能なメモリに応じて)を使用してキューに入れられることがあります。特に_DN_MULTISHOT_を使用する場合は、リアルタイム信号を使用する必要があります。

_fcntl(fd, F_NOTIFY, DN_MODIFY|DN_CREATE|DN_DELETE|DN_MULTISHOT)
_

Fdによって参照されるディレクトリ、またはそれに含まれるファイルが変更されたときに通知を発生させるイベントを設定します。使用可能なイベントタイプは次のとおりです。

  • _DN_ACCESS_ファイルにアクセスします。
  • _DN_MODIFY_ファイルが変更されました。
  • _DN_CREATE_ファイルが作成されます。
  • _DN_DELETE_ファイルのリンクが解除されています。
  • _DN_RENAME_ファイルはディレクトリ内で名前が変更されます。
  • _DN_ATTRIB_ファイルの属性が変更されます。

通知は通常、ワンショットです。つまり、アプリケーションは、さらに通知を受け取るために再登録する必要があります。 _DN_MULTISHOT_が指定されている場合、通知は明示的に削除されるまで有効です。


例(inotify):

_#include <stdio.h>
#include <stdlib.h>
#include <sys/inotify.h>

/* For PATH_MAX */
#include <limits.h>

/* For error handling */
#include <errno.h>
#include <error.h>

int main(int argc, char *argv[]) {
    int fd, wd, len, i;
    char buf[sizeof(struct inotify_event) + PATH_MAX];

    if (argc < 2)
        error(EXIT_FAILURE, 0, "missing argument");

    if ((fd = inotify_init()) < 0)
        error(EXIT_FAILURE, errno, "failed to initialize inotify instance");

    for (i = 1; i < argc; i++) {
         if ((wd = inotify_add_watch (fd, argv[i], 
                                      IN_MODIFY | IN_CREATE | IN_DELETE)) < 0)
             error(EXIT_FAILURE, errno,
                   "failed to add inotify watch for '%s'", argv[i]);
    }

     while ((len = read(fd, buf, sizeof(buf))) > 0) {
         i = 0;
             while (i < len) {
                 struct inotify_event *ie = (struct inotify_event*) &buf[i];

                 printf("event occured for '%s': ", argv[ie->wd]);
                 if (ie->mask & IN_MODIFY)
                     printf("%s was modified\n", ie->len ? ie->name : "file");
                 else if (ie->mask & IN_CREATE)
                     printf("%s was created\n",  ie->name);
                 else if (ie->mask & IN_DELETE)
                     printf("%s was deleted\n",  ie->name);
                 else
                     printf("unexpected event\n");

                 i += sizeof(struct inotify_event) + ie->len;
             }
     }

    error(EXIT_FAILURE, len == 0 ? 0 : errno, "failed to read inotify event");
}
_

説明:

_fd = inotify_init()
_

新しいinotifyインスタンスを初期化します。戻り値は、新しく作成されたinotifyイベントキューに関連付けられたファイル記述子です。デフォルトでは、ファイル記述子はブロックされています。

_wd = inotify_add_watch (fd, argv[i], IN_MODIFY | IN_CREATE | IN_DELETE)
_

ウォッチリストの新しいアイテム、別名ウォッチはinotify_add_watch()で追加されます。 3番目の引数は、監視するinotifyイベントを示すために使用されるビットマスクです。使用可能なイベントタイプは次のとおりです。

  • _IN_ACCESS_ファイルにアクセスします。
  • _IN_ATTRIB_ファイルの属性が変更されます。
  • _IN_CLOSE_WRITE_書き込み用に開かれたファイルは閉じられています。
  • _IN_CLOSE_NOWRITE_読み取り専用で開かれたファイルは閉じられています。
  • _IN_CREATE_ファイルまたはディレクトリが監視ディレクトリ内に作成されます。
  • _IN_DELETE_監視対象ディレクトリ内のファイルまたはディレクトリが削除されました。
  • _IN_DELETE_SELF_監視されているファイルまたはディレクトリが削除されます。
  • _IN_MODIFY_ファイルが変更されました。
  • _IN_MOVE_SELF_監視されているファイルまたはディレクトリが移動されます。
  • _IN_MOVED_FROM_ファイルが監視ディレクトリから移動されました。
  • _IN_MOVED_TO_ファイルは監視ディレクトリに移動されます。
  • _IN_OPEN_ファイルが開かれています。
  • _IN_ALL_EVENT_上記すべて。
  • _IN_MOVE_ _IN_MOVED_TO|IN_MOVED_FROM_と同等
  • _IN_CLOSE_ _IN_CLOSE_WRITE|IN_CLOSE_NOWRITE_と同等

以下のオプションは、inotify_add_watch()のマスク引数にも設定できます。

  • _IN_DONT_FOLLOW_シンボリックリンクをたどらないでください。
  • _IN_EXCL_UNLINK_以前は監視ディレクトリにあったリ​​ンクされていないファイルのイベントを生成しません。
  • _IN_MASK_ADD_監視がすでに存在する場合、監視されるイベントを累積的に追加します。
  • _IN_ONESHOT_ 1つのイベントの後でウォッチを自動的に削除します。
  • _IN_ONLYDIR_ディレクトリの場合、パス名のみを監視します。

inotify_add_watch()によって返される値は、inotifyで示されるfdインスタンスで監視されているファイルシステムオブジェクトに関連付けられた監視記述子です。指定されたオブジェクトがすでに監視されている場合、既存の監視の記述子が返されます。

_while ((len = read(fd, buf, sizeof(buf))) > 0) {
    i = 0;
    while (i < len) {
        struct inotify_event *ie = (struct inotify_event*) &buf[i];
        /* ... */
        i += sizeof(struct inotify_event) + ie->len;
    }
}
_

inotifyインスタンスに関連付けられたファイル記述子からの各read()は、次のフィールドを持つ1つ以上の_inotify_event_構造体を返します。

  • _int wd_トリガーされたウォッチのウォッチ記述子。
  • _uint32_t mask_ウォッチをトリガーしたイベントのマスク。
  • _uint32_t cookie_関連イベントを関連付ける一意のCookie。
  • _uint32_t len_名前フィールドのサイズ。
  • _char name[]_監視対象ディレクトリでイベントをトリガーしたファイルのオプションのヌル終了名。

inotify_add_watch()に渡されるイベントタイプに対応するビットに加えて、マスクフィールドには次のステータスビットが設定される場合があります。

  • _IN_IGNORED_ウォッチが削除されました(inotify_rm_watch()、リンクされていないパス名など)。
  • _IN_ISDIR_ディレクトリによってトリガーされるイベント。
  • _IN_Q_OVERFLOW_イベントキューがオーバーフローしました。さらに、wdは-1に設定されます。
  • _IN_UNMOUNT_監視パス名を含むファイルシステムがマウント解除されました。

可変長のnameフィールドがあるため、各_inotify_event_構造体の長さはsizeof(inotify_event) + lenです。

カーネルバージョン2.6.21より前では、read()に渡されたバッファが小さすぎて次のイベントを保持できない場合、read()は0を返します。2.6.21以降、read()は失敗し、 errnoEINVALに設定されます。

11
Thomas Nyman

ファイルの変更時にシグナルを受信することはできないと思います。ただし、_ そのWikipediaページ に示されているinotifyを使用すると、ファイルの変更時にイベントを受け取るプログラムを作成できます。

このサイトinotifyについて良い記事を書いています。また、現在のディレクトリのイベントを監視するサンプルもあります。

3