Understanding Linux Kernel Notifier Chains: Types, Structures, and Sample Implementation
This article explains the concept of Linux kernel notifier chains, describes the four chain types and their core data structures, details the registration and notification mechanisms, and provides a complete example with three kernel modules and a Makefile to demonstrate building and using a custom notifier chain.
Concept
Linux kernel subsystems are largely independent, but a subsystem may need to react to events generated by another. To satisfy this, the kernel provides notifier chains – function lists that are invoked when a specific event occurs. Notifier chains operate only within the kernel, not between kernel and user space.
Data Structures
There are four notifier‑chain types, each with its own head structure:
Atomic notifier chain – callbacks run in interrupt context and cannot block.
struct atomic_notifier_head {
spinlock_t lock;
struct notifier_block *head;
};Blocking notifier chain – callbacks run in process context and may block.
struct blocking_notifier_head {
rw_semaphore rwsem;
struct notifier_block *head;
};Raw notifier chain – no restrictions on callbacks; the caller handles locking.
struct raw_notifier_head {
struct notifier_block *head;
};SRCU notifier chain – a variant of the blocking chain.
struct srcu_notifier_head {
struct mutex mutex;
struct srcu_struct srcu;
struct notifier_block *head;
};The core element of every chain is struct notifier_block:
struct notifier_block {
int (*notifier_call)(struct notifier_block *, unsigned long, void *);
struct notifier_block *next;
int priority;
};Callbacks are linked via next and ordered by priority. Kernel code typically names a chain xxx_chain or xxx_notifier_chain.
Operation Mechanism
A notifier chain involves two roles:
Notifier (publisher) – defines the chain and triggers notifications.
Notified party (subscriber) – registers a callback function to the chain.
The registration process uses notifier_chain_register, which inserts a notifier_block into the chain according to its priority:
static int notifier_chain_register(struct notifier_block **nl,
struct notifier_block *n) {
while ((*nl) != NULL) {
if (n->priority > (*nl)->priority)
break;
nl = &((*nl)->next);
}
n->next = *nl;
rcu_assign_pointer(*nl, n);
return 0;
}Unregistration uses notifier_chain_unregister to remove a block, and notification is performed by notifier_call_chain, which walks the list and invokes each notifier_call:
static int __kprobes notifier_call_chain(struct notifier_block *nl,
unsigned long val, void *v,
int nr_to_call, int *nr_calls) {
int ret = NOTIFY_DONE;
struct notifier_block *nb, *next_nb;
nb = rcu_dereference(*nl);
while (nb && nr_to_call) {
next_nb = rcu_dereference(nb->next);
ret = nb->notifier_call(nb, val, v);
if (nr_calls)
(*nr_calls)++;
if (ret & NOTIFY_STOP_MASK)
break;
nb = next_nb;
nr_to_call--;
}
return ret;
}The val argument identifies the event type, while v points to event‑specific data (e.g., a net_device for network‑device events). Return codes such as NOTIFY_DONE, NOTIFY_OK, NOTIFY_BAD, NOTIFY_STOP control further processing.
Example Application
The following three kernel modules illustrate a complete custom notifier chain named test_chain:
buildchain.c – Define the chain and helper functions
#include <linux/module.h>
#include <linux/notifier.h>
MODULE_LICENSE("GPL");
/* Define a raw notifier chain */
RAW_NOTIFIER_HEAD(test_chain);
int register_test_notifier(struct notifier_block *nb) {
return raw_notifier_chain_register(&test_chain, nb);
}
EXPORT_SYMBOL(register_test_notifier);
int unregister_test_notifier(struct notifier_block *nb) {
return raw_notifier_chain_unregister(&test_chain, nb);
}
EXPORT_SYMBOL(unregister_test_notifier);
int test_notifier_call_chain(unsigned long val, void *v) {
return raw_notifier_call_chain(&test_chain, val, v);
}
EXPORT_SYMBOL(test_notifier_call_chain);
static int __init init_notifier(void) {
printk("init_notifier
");
return 0;
}
static void __exit exit_notifier(void) {
printk("exit_notifier
");
}
module_init(init_notifier);
module_exit(exit_notifier);regchain.c – Register three callbacks
#include <linux/module.h>
#include <linux/notifier.h>
MODULE_LICENSE("GPL");
extern int register_test_notifier(struct notifier_block *);
extern int unregister_test_notifier(struct notifier_block *);
static int test_event1(struct notifier_block *this, unsigned long event, void *ptr) {
printk("In Event 1: Event Number is %lu
", event);
return 0;
}
static int test_event2(struct notifier_block *this, unsigned long event, void *ptr) {
printk("In Event 2: Event Number is %lu
", event);
return 0;
}
static int test_event3(struct notifier_block *this, unsigned long event, void *ptr) {
printk("In Event 3: Event Number is %lu
", event);
return 0;
}
static struct notifier_block test_notifier1 = { .notifier_call = test_event1 };
static struct notifier_block test_notifier2 = { .notifier_call = test_event2 };
static struct notifier_block test_notifier3 = { .notifier_call = test_event3 };
static int __init reg_notifier(void) {
int err;
printk("Begin to register:
");
err = register_test_notifier(&test_notifier1);
if (err) return -1;
err = register_test_notifier(&test_notifier2);
if (err) return -1;
err = register_test_notifier(&test_notifier3);
if (err) return -1;
return 0;
}
static void __exit unreg_notifier(void) {
printk("Begin to unregister
");
unregister_test_notifier(&test_notifier1);
unregister_test_notifier(&test_notifier2);
unregister_test_notifier(&test_notifier3);
printk("Unregister finished
");
}
module_init(reg_notifier);
module_exit(unreg_notifier);notify.c – Trigger the chain
#include <linux/module.h>
#include <linux/notifier.h>
MODULE_LICENSE("GPL");
extern int test_notifier_call_chain(unsigned long, void *);
static int __init call_notifier(void) {
int err;
printk("Begin to notify:
");
err = test_notifier_call_chain(1, NULL);
if (err)
printk("notifier_call_chain error
");
return err;
}
static void __exit uncall_notifier(void) {
printk("End notify
");
}
module_init(call_notifier);
module_exit(uncall_notifier);Makefile
obj-m := buildchain.o regchain.o notify.o
CURRENT_PATH := $(shell pwd)
LINUX_KERNEL := $(shell uname -r)
KERNELDIR := /usr/src/linux-headers-$(LINUX_KERNEL)
all:
make -C $(KERNELDIR) M=$(CURRENT_PATH) modules
clean:
make -C $(KERNELDIR) M=$(CURRENT_PATH) cleanBuild and load the modules (as root):
make
insmod buildchain.ko
insmod regchain.ko
insmod notify.koKernel log output shows the registration of the three notifiers and their execution when test_notifier_call_chain is invoked:
init_notifier
Begin to register:
register test_notifier1 completed
register test_notifier2 completed
register test_notifier3 completed
Begin to notify:
==============================
In Event 1: Event Number is 1
In Event 2: Event Number is 1
In Event 3: Event Number is 1Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
ITPUB
Official ITPUB account sharing technical insights, community news, and exciting events.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
