Linux의 파일 설명자에서 신호 인터럽트를 생성하는 방법은 무엇입니까?
동기는 마이크로컨트롤러에서처럼 사용자 영역에서 인터럽트를 생성하는 것입니다. I/O 파일 디스크립터가 있고 상태가 변경될 때 인터럽트를 생성하고 싶습니까?
누군가 나에게 말해 줄 수 있습니까? 가능하다면 예를 들어주세요.
답변1
Linux는 파일 시스템 이벤트를 모니터링하기 위한 두 가지 메커니즘을 제공합니다.dnotify
그리고inotify
.
둘 중 오래된 것이 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)
알림 이벤트가 발생했을 때 전송될 시그널을 설정합니다. 0 값은 SIGIO
전송됨(기본값)을 의미합니다. 다른 값( 포함 SIGIO
)은 전송될 신호로 해석됩니다. 후자의 경우 신호 처리기는 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 <unistd.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()
. 세 번째 매개변수는 inotify
모니터링할 이벤트를 나타내는 비트마스크입니다 . 사용 가능한 이벤트 유형은 다음과 같습니다.
IN_ACCESS
파일에 액세스됩니다.IN_ATTRIB
파일의 속성이 변경되었습니다.IN_CLOSE_WRITE
쓰기 위해 열린 파일이 닫혔습니다.IN_CLOSE_NOWRITE
읽기 전용으로 열린 파일이 닫혔습니다.IN_CREATE
watch 디렉터리에 파일이나 디렉터리가 생성됩니다.IN_DELETE
감시 디렉터리에서 파일이나 디렉터리가 삭제되었습니다.IN_DELETE_SELF
모니터링 중인 파일 또는 디렉터리가 삭제되었습니다.IN_MODIFY
파일이 수정되었습니다.IN_MOVE_SELF
모니터링 중인 파일 또는 디렉터리가 이동되었습니다.IN_MOVED_FROM
파일이 감시 디렉토리 밖으로 이동되었습니다.IN_MOVED_TO
파일이 watch 디렉토리로 이동됩니다.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
한때 watch 디렉토리에 있었던 링크 해제된 파일에 대해서는 이벤트를 생성하지 마십시오.IN_MASK_ADD
모니터링이 이미 존재하는 경우 모니터링 이벤트가 누적되어 추가됩니다.IN_ONESHOT
이벤트가 발생한 후 자동으로 시계에서 시계를 제거합니다.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;
}
}
read()
인스턴스와 연결된 파일 설명자의 각 성공은 다음 필드가 포함된 inotify
하나 이상의 구조를 반환합니다 .inotify_event
int wd
트리거된 시계에 대한 시계 설명자입니다.uint32_t mask
감시를 트리거한 이벤트에 대한 마스크입니다.uint32_t cookie
관련 이벤트와 관련된 고유 쿠키입니다.uint32_t len
이름 필드의 크기입니다.char name[]
이벤트를 트리거한 감시 디렉터리에 있는 파일의 선택적 null 종료 이름입니다.
에 전달된 이벤트 유형에 해당하는 비트 외에도 inotify_add_watch()
마스크 필드는 다음 상태 비트를 설정할 수도 있습니다.
IN_IGNORED
감시가 제거되었습니다(inotify_rm_watch()
연결 해제 경로, 경로 이름 등).IN_ISDIR
디렉터리에 의해 트리거되는 이벤트입니다.IN_Q_OVERFLOW
이벤트 큐가 오버플로되었습니다. 또한 wd는 -1로 설정됩니다.IN_UNMOUNT
watch 경로 이름을 포함하는 파일 시스템이 마운트 해제됩니다.
inotify_event
각 구조의 길이는 sizeof(inotify_event) + len
가변 길이 name
필드에 따라 다릅니다.
커널 버전 2.6.21 이전에는 전달된 버퍼가 read()
너무 작아서 다음 이벤트를 저장할 수 없는 경우 read()가 0을 반환했습니다. 2.6.21부터 read()
실패하고 errno
로 설정됩니다 EINVAL
.