Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

How can I generate a signal interrupt on a file descriptor in Linux?

Motivation is to generate a interrupt in userland as we have in microcontrollers. I'll have file descriptor for I/O, and want to generate a interrupt whenever there will be a change in its status?

Can someone tell me? If possible please give me example.

share|improve this question
    
Are you asking how to generate a signal when a particular file descriptor is accessed? –  slm Aug 27 '13 at 14:07
    
This normally happens inside the kernel. Are you writing a driver for that device? –  Gilles Aug 27 '13 at 22:11

2 Answers 2

I don't think it's possible to receive a signal on a file change. However inotify, https://en.wikipedia.org/wiki/Inotify, should allow you to write a program that receives events as a file changes.

This site has a good write up about inotify: http://www.win.tue.nl/~aeb/linux/lk/lk-12.html. It also has a sample example that monitors events in the current directory.

share|improve this answer

Linux provides two mechanism for monitoring file system events; dnotify and inotify.

The older of the two, dnotify, was introduced in kernel version 2.4.0. It allows applications to register to receive notifications on changes in a directory via the fcntl() interface. The notifications themselves are delivered via signals. The dnotify mechanism is limited to monitoring changes in a directory, it does not allow monitoring of individual files. Furthermore, it requires maintaining an open file descriptor to the directory being monitored. The dnotify mechanism was deprecated in 2.6.13, when inotify was introduced.

New programs should use the inotify mechanism, which supports monitoring of both directories and individual files. It is, however, not based on signals. An inotify instance is associated with a file descriptor. Event notifications can be read from this file descriptor.

A limitation of both mechanisms is that there is no option to watch directories recursively. This means that monitoring must be established separately for each directory in the subtree which is to be watched.


Example (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);
    }
}

Explanation:

fcntl(fd, F_SETSIG, SIGRTMIN + 1)

Sets the signal sent when notification events occur. A value of zero indicates that SIGIO (the default) is sent. Any other value, including SIGIO, is interpreted as the signal to be sent instead. In the latter case, the signal handler receives a siginfo_t structure as its second argument, and the si_fd field of the structure will contain the file descriptor which generated the event.

If a realtime signal (>= SIGRTMIN) is used for notifications, multiple I/O events may be queued using the same signal number (depending on available memory). A realtime signal should be used especially when using DN_MULTISHOT.

fcntl(fd, F_NOTIFY, DN_MODIFY|DN_CREATE|DN_DELETE|DN_MULTISHOT)

Sets the events that will cause notifications when the directory referred to by fd, or any files it contains are changed. The available event types are:

  • DN_ACCESS A file is accessed.
  • DN_MODIFY A file is modified.
  • DN_CREATE A file is created.
  • DN_DELETE A file is unlinked.
  • DN_RENAME A file is renamed within the directory.
  • DN_ATTRIB The attributes of a file are changed.

Notifications are normally one-shot, i.e. the application must reregister to receive further notifications. If DN_MULTISHOT is specified, the notifications will remain in effect until explicitly removed.


Example (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");
}

Explanation:

fd = inotify_init()

Initializes a new inotify instance. The return value is a file descriptor associated with a newly created inotify event queue. By default, the file descriptor is blocking.

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

New items to the watch list, a.k.a. watches are added with inotify_add_watch(). The third argument is a bitmask used to indicate the inotify events to watch for. The available event types are:

  • IN_ACCESS File is accessed.
  • IN_ATTRIB The attributes of a file are changed.
  • IN_CLOSE_WRITE File opened for writing is closed.
  • IN_CLOSE_NOWRITE File opened read-only is closed.
  • IN_CREATE File or directory is created within watched directory.
  • IN_DELETE File or directory is deleted within watched directory.
  • IN_DELETE_SELF File or directory being watched is deleted.
  • IN_MODIFY File was modified.
  • IN_MOVE_SELF File or directory being watched is moved.
  • IN_MOVED_FROM File is moved out of watched directory.
  • IN_MOVED_TO File is moved into watched directory.
  • IN_OPEN File is opened.
  • IN_ALL_EVENT All of the above.
  • IN_MOVE Equivalent to IN_MOVED_TO|IN_MOVED_FROM
  • IN_CLOSE Equivalent to IN_CLOSE_WRITE|IN_CLOSE_NOWRITE

The following options may also be set in the mask argument of inotify_add_watch():

  • IN_DONT_FOLLOW Don't follow symlinks.
  • IN_EXCL_UNLINK Don't generate events for unlinked files that used to be in watched directory.
  • IN_MASK_ADD Add watched events cumulatively if watch already exists.
  • IN_ONESHOT Automatically remove watch after one event from it.
  • IN_ONLYDIR Only watch pathname if it is a directory.

The value returned by inotify_add_watch() is a watch descriptor associated with filesystem object being watched in the inotify instance indicated by fd. If the specified object is already being watched, the descriptor for the existing watch is returned.

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;
    }
}

Each successfull read() from the file descriptor associated with the inotify instance returns one or more inotify_event structs with the following fields.

  • int wd Watch descriptor of the triggered watch.
  • uint32_t mask Mask of events that triggered the watch.
  • uint32_t cookie Unique cookie associating related events.
  • uint32_t len Size of name field.
  • char name[] Optional null-terminated name of the file that triggered the event in watched directory.

In addition to the bits corresponding to the event types passed to inotify_add_watch(), the mask field may have the following status bits set:

  • IN_IGNORED Watch was removed (via inotify_rm_watch(), pathname unlinked etc.).
  • IN_ISDIR Event triggered by directory.
  • IN_Q_OVERFLOW Event queue overflowed. In addition wd is set to -1.
  • IN_UNMOUNT File system containing watched pathname was unmounted.

The length of each inotify_event struct is sizeof(inotify_event) + len due to the variable length name field.

Prior to kernel version 2.6.21, if the buffer passed to read() is too small to hold the next event, read() would return 0. Since 2.6.21, read() fails and errno is set to EINVAL.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.