2013-04-05 15 views
6

Quando un evento è registrato con kqueue un ID relativo a tale evento tipo è fornito; per esempio un descrittore di file viene utilizzato per identificare un file di guardareUsa kqueue di rispondere a più di un tipo di evento

int kq; 
struct kevent ke; 

kq = kqueue(); 
fd = open(argv[1], O_RDONLY); 
EV_SET(&ke, fd, EVFILT_VNODE, EV_ADD, NOTE_DELETE | NOTE_RENAME, 0, NULL); 
kevent(kq, &ke, 1, NULL, 0, NULL); 

while (1) { 
    kevent(kq, NULL, 0, &ke, 1, NULL); 
    /* respond to file system event */ 
} 

Ora, se ho anche bisogno di rispondere ad altri tipi di eventi tali segnali abbiamo bisogno di una nuova istanza di kqueue in modo da evitare un conflitto con il ident argomento di kevent().

kq_sig = kqueue(); 
struct kevent ke_sig; 

/* set the handler and ignore SIGINT */ 
signal(SIGINT, SIG_IGN); 
EV_SET(&ke_sig, SIGINT, EVFILT_SIGNAL, EV_ADD, 0, 0, NULL); 
kevent(kq_sig, &ke_sig, 1, NULL, 0, NULL); 
while (1) { 
    kevent(kq_sig, NULL, 0, &ke_sig, 1, NULL); 
    /* respond signals */ 
} 

All'uscita più di un tipo di evento sembra richiedere più thread che agiscono sul stato condiviso (ricevente un segnale potrebbe chiudere un descrittore di file per esempio).

Esiste un meccanismo più generale per l'invio di un messaggio da un thread ad un altro utilizzando kqueue? In alcuni casi, posso immaginare di abilitare e disabilitare un filtro come mezzo di triggerare un altro kevent.

+0

Ma non è l'unico ID, in modo che si possono mescolare diversi tipi di eventi nello stesso evento q? O ho sbagliato la tua domanda? – dhein

+0

Esattamente a destra; l'ID deve essere univoco perché la struttura di kevent non memorizza il tipo di evento. Rispondere a un tipo di evento è molto semplice ciclo che file di KEVENT(), ma la gestione di più tipi di evento (vnode & SIGNAL nell'esempio) è considerevolmente più difficile perché un certo coordinamento tra le filettature (o processi) sembra essere richiesta. – eradman

+0

Hm, allora io sono fuori mi dispiace ... ma avrò bisogno di questo nel futur troppo .... ho solo pensato ci non sarà alcun troubble in più thread o processi, perché l'ID è unico nel sistema gamma. Ma seguirò questo. – dhein

risposta

7

La struct KEVENT fornisce in realtà informazioni sull'evento che si è verificato:

struct kevent { 
     uintptr_t  ident;   /* identifier for this event */ 
     int16_t   filter;   /* filter for event */ 
     uint16_t  flags;   /* general flags */ 
     uint32_t  fflags;   /* filter-specific flags */ 
     intptr_t  data;   /* filter-specific data */ 
     void   *udata;   /* opaque user data identifier */ 
}; 

Devi essere interessati a:

  • ident che nel tuo caso restituisce fd o SIGINT;
  • filter che (ancora nel tuo caso) restituisce EVFILT_VNODE o EVFILT_SIGNAL;
  • fflag che nel numero EVFILT_VNODE indicherà se l'evento del descrittore di file è stato NOTE_DELETE o NOTE_RENAME.

È possibile registrare due strutture KEVENT ad una singola coda e quindi utilizzare questi membri della struttura per determinare se l'evento era legato a un descrittore di file o un segnale.

Ecco un esempio completo che dimostra come eseguire questa operazione:

#include <stdlib.h> 
#include <stdio.h> 
#include <fcntl.h> 
#include <string.h> 
#include <sys/types.h> 
#include <sys/event.h> 
#include <sys/time.h> 

int 
main(int argc, char** argv) 
{ 
    /* A single kqueue */ 
    int kq = kqueue(); 
    /* Two kevent structs */ 
    struct kevent *ke = malloc(sizeof(struct kevent) * 2); 

    /* Initialise one struct for the file descriptor, and one for SIGINT */ 
    int fd = open(argv[1], O_RDONLY); 
    EV_SET(ke, fd, EVFILT_VNODE, EV_ADD | EV_CLEAR, NOTE_DELETE | NOTE_RENAME, 0, NULL); 
    signal(SIGINT, SIG_IGN); 
    EV_SET(ke + 1, SIGINT, EVFILT_SIGNAL, EV_ADD, 0, 0, NULL); 

    /* Register for the events */ 
    if(kevent(kq, ke, 2, NULL, 0, NULL) < 0) 
     perror("kevent"); 

    while(1) { 
     memset(ke, 0x00, sizeof(struct kevent)); 
     if(kevent(kq, NULL, 0, ke, 1, NULL) < 0) 
      perror("kevent"); 

     switch(ke->filter) 
     { 
      /* File descriptor event: let's examine what happened to the file */ 
      case EVFILT_VNODE: 
       printf("Events %d on file descriptor %d\n", ke->fflags, (int) ke->ident); 

       if(ke->fflags & NOTE_DELETE) 
        printf("The unlink() system call was called on the file referenced by the descriptor.\n"); 
       if(ke->fflags & NOTE_WRITE) 
        printf("A write occurred on the file referenced by the descriptor.\n"); 
       if(ke->fflags & NOTE_EXTEND) 
        printf("The file referenced by the descriptor was extended.\n"); 
       if(ke->fflags & NOTE_ATTRIB) 
        printf("The file referenced by the descriptor had its attributes changed.\n"); 
       if(ke->fflags & NOTE_LINK) 
        printf("The link count on the file changed.\n"); 
       if(ke->fflags & NOTE_RENAME) 
        printf("The file referenced by the descriptor was renamed.\n"); 
       if(ke->fflags & NOTE_REVOKE) 
        printf("Access to the file was revoked via revoke(2) or the underlying fileystem was unmounted."); 
       break; 

      /* Signal event */ 
      case EVFILT_SIGNAL: 
       printf("Received %s\n", strsignal(ke->ident)); 
       exit(42); 
       break; 

      /* This should never happen */ 
      default: 
       printf("Unknown filter\n"); 
     } 
    } 
} 

Si noti che qui usiamo un unico filo conduttore, che è il modo più efficiente e non richiede ulteriori sincronizzazione nello spazio utente.