|
All'interno di un sistema operativo possono accadere diversi eventi, ed ancora di più ne accadono all'interno del kernel (quella parte di software, cioé, che è a diretto contatto con la CPU e tutte le periferiche).
Linux essendo il kernel dei sistemi GNU/Linux non è da meno ed offre una interfaccia abbastanza evoluta per la gestione della notifica di questi eventi verso chiunque ne faccia richiesta: questa interfaccia sono i kernel notifier.
Cosa sono
I kernel notifier non sono altro che delle code di funzioni (callback) da invocare ogni qual volta si verifica un evento notevole nel sistema. Chiunque voglia ricevere queste notifiche non fa altro che iscriversi ad una o più di queste code specificando anche la propria callback, e, quando l'evento si verifica, la callback viene automaticamente invocata.
Nati inizialmente per notificare gli eventi legati al sottosistema di networking, i kernel notifier si sono poi diffusi anche per altri compiti e vengono ora utilizzati per diverse applicazioni: eventi di power management, eventi di tastiera, errori notevoli, ecc..
L'interfaccia viene definita all'interno del file linux/kernel/notifier.c il quale esporta diversi simboli. I più importanti sono:
int atomic_notifier_chain_register(struct atomic_notifier_head *nh, struct notifier_block *n);
int atomic_notifier_chain_unregister(struct atomic_notifier_head *nh, struct notifier_block *n);
che registrano/deregistrano notiier proteggendoli con degli spinlock e quindi eseguibili anche in interrupt context, e:
int __kprobes atomic_notifier_call_chain(struct atomic_notifier_head *nh, unsigned long val, void *v);
che invoca i notifier registrati con le funzioni su riportate.
Nei casi, invece, in cui il codice che fa la richiesta di registrazione/deregistrazione esegua in process context si possono usare:
int blocking_notifier_chain_register(struct blocking_notifier_head *nh, struct notifier_block *n);
int blocking_notifier_chain_unregister(struct blocking_notifier_head *nh, struct notifier_block *n);
int blocking_notifier_call_chain(struct blocking_notifier_head *nh, unsigned long val, void *v);
Che usano come meccanismi di lock i dei semafori.
Come si usano
Utilizzare i kernel notifier è molto semplice, basta definire una coda su cui accodare le richieste di notifica e definire delle funzioni di registrazione/deregistrazione e di invocazione.
Per fare un esempio pratico si riporta il file linux/net/core/netevent.c che illustra benissimo questi concetti nella sua semplicità:
#include <linux/rtnetlink.h> #include <linux/notifier.h> #include <net/netevent.h>
static ATOMIC_NOTIFIER_HEAD(netevent_notif_chain);
/** * register_netevent_notifier - register a netevent notifier block * @nb: notifier * * Register a notifier to be called when a netevent occurs. * The notifier passed is linked into the kernel structures and must * not be reused until it has been unregistered. A negative errno code * is returned on a failure. */ int register_netevent_notifier(struct notifier_block *nb) { int err;
err = atomic_notifier_chain_register(&netevent_notif_chain, nb); return err; }
/** * netevent_unregister_notifier - unregister a netevent notifier block * @nb: notifier * * Unregister a notifier previously registered by * register_neigh_notifier(). The notifier is unlinked into the * kernel structures and may then be reused. A negative errno code * is returned on a failure. */
int unregister_netevent_notifier(struct notifier_block *nb) { return atomic_notifier_chain_unregister(&netevent_notif_chain, nb); }
/** * call_netevent_notifiers - call all netevent notifier blocks * @val: value passed unmodified to notifier function * @v: pointer passed unmodified to notifier function * * Call all neighbour notifier blocks. Parameters and return value * are as for notifier_call_chain(). */
int call_netevent_notifiers(unsigned long val, void *v) { return atomic_notifier_call_chain(&netevent_notif_chain, val, v); }
EXPORT_SYMBOL_GPL(register_netevent_notifier); EXPORT_SYMBOL_GPL(unregister_netevent_notifier); EXPORT_SYMBOL_GPL(call_netevent_notifiers);
Con la define ATOMIC_NOTIFIER_HEAD() si definisce allora la coda per gli eventi di tipo atomic e poi di esportano tre funzioni per la gestione degli eventi: una di registrazione (la register_netevent_notifier()), una di deregistrazuione (la unregister_netevent_notifier()) e una di inocazione (la unregister_netevent_notifier()).
Quando si vuol iscriversi a queste code basta allora definre la propria callback ed invocare la funzione di registrazione come segue:
static int netevent_callback(struct notifier_block *self, unsigned long event, void *ctx) { if (event == NETEVENT_NEIGH_UPDATE) { struct neighbour *neigh = ctx;
if (neigh->nud_state & NUD_VALID) { set_timeout(jiffies); } } return 0; }
static struct notifier_block nb = { .notifier_call = netevent_callback };
static int __init addr_init(void) { addr_wq = create_singlethread_workqueue("ib_addr"); if (!addr_wq) return -ENOMEM;
register_netevent_notifier(&nb); return 0; }
static void __exit addr_cleanup(void) { unregister_netevent_notifier(&nb); destroy_workqueue(addr_wq); }
Questa dispensa del corso Programmazione Linux è opera di Rodolfo Giometti (Copyright © 2010) ed è rilasciata dall'autore «as is» (così com'è) e distribuita sotto licenza Creative Commons Attribuzione – Condividi allo stesso modo 2.5 Italia.
|
La redazione con i controlli a campione si riserva di cancellare qualsiasi contenuto ingiurioso, volgare o illegale.
Nota bene: se non sei registrato il tuo commento verrà moderato e quindi non pubblicato immediatamente. Se, invece, sei registrato al portale e hai fatto login verrà visualizzato subito.