Linuxでファイル記述子に信号割り込みを生成するにはどうすればよいですか?
動機は、マイクロコントローラーの場合と同様に、ユーザーランドで割り込みを生成することです。 I/Oのファイル記述子があり、ステータスが変化するたびに割り込みを生成しますか?
誰か教えてもらえますか?できれば例を挙げてください。
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()
は失敗し、 errno
はEINVAL
に設定されます。
ファイルの変更時にシグナルを受信することはできないと思います。ただし、_ そのWikipediaページ に示されているinotify
を使用すると、ファイルの変更時にイベントを受け取るプログラムを作成できます。
このサイト はinotify
について良い記事を書いています。また、現在のディレクトリのイベントを監視するサンプルもあります。