Timeline
Timeline
2026-05-24
init
This article introduces the core data structures with high practical usage frequency in the Linux 6.x kernel, detailing the definitions, core APIs, and typical use cases of structures such as doubly linked circular lists, hash linked lists, red-black trees, radix trees, extensible arrays, and priority linked lists.
Overview
A large number of general-purpose data structures are defined in the Linux kernel, which run through various subsystems such as process scheduling, memory management, file systems, and device drivers. This article organizes these data structures bypractical usage frequencyfrom high to low, covering data structure definitions, core APIs, and typical use cases.
This article is based on the Linux 6.x kernel, and some APIs may vary slightly between different versions.
Basic Containers
list_head — Doubly Linked Circular List
Usage frequency: Highest. This is the most widely used data structure in the Linux kernel, bar none. Almost every subsystem uses it.
The Linux linked list implementation separatesdata from list nodes, with list nodes embedded into the structure rather than the structure containing list pointers. The essence of this design is that a single set of list operations applies to all data types.
Header file:<linux/list.h>
Data structure:
1 | struct list_head { |
Core APIs:
| API | Description |
|---|---|
LIST_HEAD(name) | Statically define and initialize a list head |
INIT_LIST_HEAD(ptr) | Dynamically initialize a list head |
list_add(new, head) | Insert after head |
list_add_tail(new, head) | Insert before head (tail) |
list_del(entry) | Delete node |
list_del_init(entry) | Delete and reinitialize node |
list_empty(head) | Determine if the list is empty |
list_entry(ptr, type, member) | Get the containing structure from a list_head pointer |
list_for_each(pos, head) | Traverse the linked list |
list_for_each_entry(pos, head, member) | Traverse the linked list and get the containing structure |
list_for_each_entry_safe(pos, n, head, member) | Safe traversal (can delete during traversal) |
list_move(list, head) | Move node to a new linked list |
list_splice(list, head) | Merge two linked lists |
Usage example:
1 |
|
Run
1 | ~ # insmod list_test.ko |
list_addandlist_add_tail:
1 | /** |
list_add_tailis inserted intohead->prevandheadbetween. But because this is a ring, “in front of head” logically equals “the end of the linked list”.
list_delandlist_del_initfunction
1 | /** |
list_replacecan replace a linked list node
1 | /** |
hlist — Hash linked list
hlistis specifically designed forHash Tabledesigned as a variant of a doubly linked list. That is, when hashing data to be stored, if a collision occurs, usea linked listchain the conflicting data together for storage. Typically, the order of element usage in a hash table is: data storage —> data retrieval —> data deletion. Compared withlist_headthe difference is: the head node only uses onestruct hlist_head(single pointer), saving memory in the hash table array.
Its core design motivation is to solve the problem of standard doubly circular linked lists
list_headexisting when used as a hash bucketmemory wasteandSemantic mismatchProblem.
| Features | list_head(Standard linked list) | hlist_head+hlist_node(Hash linked list) |
|---|---|---|
| Head node structure | Complete bidirectional pointer (next,prev) | Only one unidirectional pointer (first) |
| Data node structure | Bidirectional pointer (next,prev) | Bidirectional pointer (next,pprev) |
| Is circular | Yes (head and tail connected) | No (ends with NULL) |
| Empty list check | head->next == head | head->first == NULL |
| Memory overhead (head node) | 2 pointers (16 bytes/64-bit) | 1 pointer (8 bytes/64-bit) |
Header file:<linux/list.h>
Data structure:
1 | struct hlist_head { |
pprev's type isstruct hlist_node **(pointer to pointer).
It does not store “the address of the previous node”, but “the memory address of the next field in the previous node”。For hlist chain head -> A -> B
- For node B in the middle of the linked list:
B->pprev == &(A->next)- For node A of the linked list:
A->pprev == &(head->first)
This design facilitates deletion
1 | static inline void __hlist_del(struct hlist_node *n) |
add related
1 | /** |
rbtree — Red-Black Tree
The most used in the kernelSelf-balancing binary search tree, providing O(log n) search, insertion, and deletion. Each node in a Red-Black Tree has a storage bit indicating the node’s color, which can be Red or Black. Properties of Red-Black Trees:
- Every node is either red or black.
- The root is black.
- Every leaf (NIL) is black. [Note: Here, leaf nodes refer to empty (NIL or NULL) leaf nodes!]
- If a node is red, then its children must be black.
- All paths from a node to its descendant leaves contain the same number of black nodes. This property ensures that no path is more than twice as long as any other, making the Red-Black Tree relatively balanced.
All operations on a Red-Black Tree must maintain its properties. Red-Black Trees are widely used, mainly for storing ordered data, with a time complexity of O(log n) and very high efficiency. cfs_rq uses a Red-Black Tree to store tasks.
Header file:<linux/rbtree.h>/<linux/rbtree_augmented.h>
Data structure:
1 | struct rb_node { |
At a glance, it seems there is no field defined for color here, but this is the clever part of this Red-Black Tree implementation.
__rb_parent_colorThis field actually contains both color information and the parent node’s pointer. Because this field is of type long and requires alignment of size sizeof(long), on a typical 32-bit machine, the last two bits are always 0, so one of these bits can be used to represent the color.The key is Memory Alignment:
struct rb_nodeis forced to align bysizeof(long)alignment.- On a 32-bit system,
sizeof(long) == 4, meaning any validrb_nodepointer address must be multiples of 4, i.e., the lowest binary 2 bits always00。- On a 64-bit system,
sizeof(long) == 8, the lowest 3 bits always000。In fact, the lowest bit is used here to represent color information. The following operations on the parent pointer and color information are essentially operations on the
rb_parent_color’s bits.
1
2
3
4
5
6
7
8
9
10
11
12
/* 'empty' nodes are nodes that are known not to be inserted in an rbtree */
Linux’s red-black tree implementation is optimized for speed, thus having one less indirection than traditional implementations (better cache locality). Eachstruct rb_nodeinstance of the structure is embedded in the data structure it manages, so there is no need to use a pointer to separaterb_nodeand the data structure it manages.
Users should write their own tree search and insertion functions to call the provided red-black tree functions, rather than using a comparison callback function pointer.
Locking code is also left to the user of the red-black tree to write。
Example
1 | // SPDX-License-Identifier: GPL-2.0 |
radix_tree — Radix Tree
xarraypredecessor, used for mapping integer IDs to pointers. Althoughxarrayhas gradually replaced it, there is still a large amount of code using radix trees in the kernel.
If it is a new project, please use directly<linux/xarray.h>XArray API in . XArray fixes many design flaws of radix_tree (such as preload complexity, index offset issues), and the API is more concise. radix_tree in 5.10.x is just a compatibility layer on top of XArray.
Header file:<linux/radix-tree.h>
1 |
|
As can be seenlinux-5.10.xxarray has already replaced it in
Example
1 | // SPDX-License-Identifier: GPL-2.0 |
xarray — Extensible Array
xarrayIntroduced in Linux 4.20Next-generation radix tree replacement, providing a mapping from integers (unsigned long) to pointers, with a cleaner API and better performance.
Header file:<linux/xarray.h>
Data structure:
1 | struct xarray { |
Core APIs:
| API | Description |
|---|---|
DEFINE_XARRAY(name) | Statically define xarray |
xa_init(xa) | Dynamic initialization |
xa_store(xa, index, entry, gfp) | Store entry |
xa_load(xa, index) | Read entry |
xa_erase(xa, index) | Delete entry |
xa_insert(xa, index, entry, gfp) | Insert (key must not already exist) |
xa_for_each(xa, index, entry) | Iterate over all entries |
xa_find(xa, indexp, max, filter) | Find entries in range |
Usage example:
1 | DEFINE_XARRAY(my_xa); |
plist — Priority Linked List
plistInlist_headadded on the basis ofPriority, the head always points to the node with the highest priority (a smaller prio value represents a higher priority), commonly used in scenarios where “always process the highest priority first” is required.
Header file:<linux/plist.h>
Data structure:
1 | struct plist_node { |
Core APIs:
| API | Description |
|---|---|
plist_head_init(head) | Initialization |
plist_node_init(node, prio) | Initialize node |
plist_add(node, head) | Insert by priority |
plist_del(node, head) | Delete node |
plist_first(head) | Get the node with the highest priority |
plist_head_empty(head) | Check if empty |
llist — lockless linked list
llist(lock-less list) is alockless singly linked list, implemented based on CAS operations, which performs better than spinlock + in specific scenarios (such as interrupts and processes sharing data)list_headhas better performance.
Header file:<linux/llist.h>
Data structure:
1 | struct llist_head { |
Core APIs:
| API | Description |
|---|---|
llist_add(new, head) | Insert at head (lockless) |
llist_del_all(head) | Atomically detach the entire linked list |
llist_del_first(head) | Delete the first node |
llist_empty(head) | Check if empty |
Typical pattern:
1 | // Producer (can be in interrupt context) |
rhashtable — resizable hash table
rhashtableis aCan automatically expand and shrinkhash table implementation that supports RCU lookups, suitable for hash scenarios requiring dynamic growth.
Header file:<linux/rhashtable.h>
Core APIs:
| API | Description |
|---|---|
rhashtable_init(ht, params) | Initialization |
rhashtable_insert_slow(ht, key, obj) | Insert |
rhashtable_lookup(ht, key, params) | Search |
rhashtable_remove(ht, obj, params) | Delete |
rhashtable_free_and_destroy(ht, fn, data) | Destroy |
Typical applications in the kernel:
- Connection tracking table for network namespaces
- XFRM security policy database
- Hash type implementation of BPF map
maple_tree — Maple Tree
A new data structure introduced in Linux 6.1, used to replace the red-black tree + linked list combination in VMA management.maple_treeis a B-tree variant, supportsrange operations(range operations), which is very efficient for VMA lookup, traversal, and gap searching.
Header file:<linux/maple_tree.h>
Data structure:
1 | struct maple_tree { |
Core APIs:
| API | Description |
|---|---|
mt_init(mt) | Initialization |
mtree_lock(mt) | Acquire write lock |
mtree_unlock(mt) | Release write lock |
mas_store(mas, entry) | Store entry |
mas_find(mas, max) | Search |
mas_erase(mas) | Delete |
MTREE_INIT(mt, flags) | Static initialization |
mtree_destroy(mt) | Destroy |
Typical applications in the kernel:
- VMA management: Linux 6.1+ uses
maple_treeto replace red-black tree + doubly linked list managementvm_area_struct - User-space programs (the user-space RCU library URCU also implements maple tree)
interval_tree — Interval tree
Interval tree is an augmented red-black tree used to manageRange([start, last]), supporting fast lookup of all intervals overlapping a given interval. Based onrbtree_augmentedimplementation.
Header file:<linux/interval_tree.h>
Data structure:
1 | struct interval_tree_node { |
Core APIs:
| API | Description |
|---|---|
interval_tree_insert(node, root) | Insert |
interval_tree_remove(node, root) | Delete |
interval_tree_iter_first(root, start, last) | Find first overlapping interval |
interval_tree_iter_next(node, start, last) | Find next overlapping interval |
Typical applications in the kernel:
- VMA interval lookup (finding virtual memory areas overlapping a given address range)
- GEM buffer management for DRM GPU drivers
klist — Kernel object linked list
klistis an alias forlist_headwrapper, which, together withkobjectsystem, provides get/put reference count protection: automatically acquires a reference to the node object while traversing the list, preventing the node from being freed during traversal.
Header file:<linux/klist.h>
Data structure:
1 | struct klist_node { |
Core APIs:
| API | Description |
|---|---|
klist_add_head(n, k) | Add to head |
klist_add_tail(n, k) | Add to tail |
klist_del(n) | Delete |
klist_iter_init(k, i) | Initialize iterator |
klist_next(i) | Get next node (auto get/put) |
klist_iter_exit(i) | Cleanup iterator |
Typical applications in the kernel:
- In the device driver model
bus_type's device list - In the device driver model
driver's device list
IDs, Bitmaps, and DMA
idr — ID allocator
idrProvidesInteger ID to pointermapping, automatically allocates a unique integer ID and associates it with a pointer. Suitable for scenarios where “an integer handle is needed”.
Header file:<linux/idr.h>
Core API (modern interface):
| API | Description |
|---|---|
idr_alloc(idr, ptr, start, end, gfp) | Assign ID and associate pointer |
idr_find(idr, id) | Find pointer by ID |
idr_remove(idr, id) | Delete ID mapping |
idr_for_each(idr, fn, data) | Iterate over all entries |
idr_destroy(idr) | Destroy idr |
idr_init(idr) | Initialization |
Usage example:
1 | DEFINE_IDR(my_idr); |
Typical applications in the kernel:
- Process PID management
- Device minor number allocation
- GPU DRM driver handle management (GEM buffer handle)
ida — IDA allocator
idaisidrA simplified version that only allocates integer IDs without associating pointers (used when only a unique integer ID is needed, with lower memory overhead).
Header file:<linux/idr.h>
Core APIs:
| API | Description |
|---|---|
ida_alloc(ida, gfp) | Allocate an ID |
ida_free(ida, id) | Free an ID |
ida_alloc_range(ida, min, max, gfp) | Allocate an ID within a range |
ida_init(ida) | Initialization |
ida_destroy(ida) | Destroy |
bitmap / cpumask — Bitmap
Used by the kernelunsigned longImplements bitmaps using arrays, providing an efficient set of bit operations.cpumaskA special form of bitmap, specifically describing CPU sets.
Header file:<linux/bitmap.h>/<linux/cpumask.h>
Core API (bitmap):
| API | Description |
|---|---|
bitmap_zero(dst, nbits) | Clear all bits |
bitmap_set(dst, pos, nbits) | Set bit |
bitmap_clear(dst, pos, nbits) | Clear bit |
bitmap_find_next_zero_area(buf, len, start, n, mask) | Find contiguous zero region |
bitmap_and(dst, src1, src2, nbits) | Bitwise AND |
bitmap_or(dst, src1, src2, nbits) | Bitwise OR |
Core API (cpumask):
| API | Description |
|---|---|
cpumask_set_cpu(cpu, mask) | Add CPU to mask |
cpumask_clear_cpu(cpu, mask) | Remove CPU from mask |
cpumask_test_cpu(cpu, mask) | Test if CPU is in mask |
for_each_cpu(cpu, mask) | Iterate over CPUs in mask |
cpumask_of(cpu) | Get mask for a single CPU |
cpu_possible_mask | All possible CPUs in the system |
cpu_online_mask | Currently online CPUs |
cpu_present_mask | Currently present CPUs |
Typical applications in the kernel:
- IRQ affinity setting (specify which CPUs handle interrupts)
- Process’s
cpus_allowed(set which CPUs a process can run on) - DMA mask for memory node
scatterlist — scatter-gather list
scatterlistUsed to describeNon-contiguous memory regions, which is extremely commonly used in DMA (Direct Memory Access) scenarios: linking scattered physical memory fragments into a whole for the DMA engine to process at once.
Header file:<linux/scatterlist.h>
Data structure:
1 | struct scatterlist { |
Core APIs:
| API | Description |
|---|---|
sg_init_one(sg, buf, len) | Initialize a single sg entry |
sg_init_table(sg, nents) | Initialize sg table |
sg_set_buf(sg, buf, len) | Set entry |
sg_set_page(sg, page, len, offset) | Set page entry |
sg_next(sg) | Get next entry |
sg_nents(sg) | Calculate number of entries |
dma_map_sg(dev, sg, nents, dir) | Map sg table for DMA |
dma_unmap_sg(dev, sg, nents, dir) | Unmap DMA |
Usage example:
1 | struct scatterlist sg[2]; |
Typical applications in the kernel:
- Block device I/O (scatter-gather linked list in bio)
- Network drivers (scatter-gather Tx/Rx)
- Any data transfer involving DMA
- Data buffers for encryption/decryption subsystems
Concurrency and synchronization
atomic_t — atomic variable
Atomic operations in the kernel used for simple counting and flags, the foundation of lock-free programming. On 32-bit platformsatomic_tis 32-bit, on 64-bit platforms there is alsoatomic64_t。
Header file:<linux/atomic.h>
Data structure:
1 | typedef struct { |
Core APIs:
| API | Description |
|---|---|
atomic_read(v) | Read value |
atomic_set(v, i) | Set value |
atomic_inc(v) | Increment |
atomic_dec(v) | Decrement |
atomic_add(i, v) | Add |
atomic_sub(i, v) | Subtract |
atomic_inc_return(v) | Increment and return new value |
atomic_dec_and_test(v) | Decrement and test if zero |
atomic_cmpxchg(v, old, new) | CAS operation |
atomic_xchg(v, new) | Swap and return old value |
Typical applications in the kernel:
- Reference counting (open count for drivers)
- Statistical counters (network packet count, interrupt count)
- Simple lock-free flag
kref / refcount_t — reference counting
kref
Encapsulationrefcount_t, provides reference counting management for objects, withreleasecallback to automatically release resources when the count reaches zero.
Header file:<linux/kref.h>
1 | struct kref { |
refcount_t
refcount_tisatomic_tAn enhanced version that provides overflow protection—stops incrementing after reaching the maximum value, preventing use-after-free vulnerabilities caused by reference count overflow.
Header file:<linux/refcount.h>
1 | typedef struct refcount_struct { |
Typical usage pattern:
1 | struct my_object { |
Typical applications in the kernel:
struct kobject's reference countstruct device's lifecycle management- File descriptor (
struct file) - Almost all kernel objects that require lifecycle management
spinlock_t — Spinlock
The most basic in the Linux kernelBusy-wait lock, used for short critical section protection in SMP systems. When the holder spins waiting on one CPU, executors on other CPUs also spin wait.
Header file:<linux/spinlock.h>
Data structure (simplified):
1 | typedef struct spinlock { |
Core APIs:
| API | Description |
|---|---|
spin_lock_init(lock) | Dynamic initialization |
DEFINE_SPINLOCK(lock) | Static definition + initialization |
spin_lock(lock) | Acquire lock (disables kernel preemption) |
spin_unlock(lock) | Unlock |
spin_lock_irq(lock) | Acquire lock and disable local interrupts |
spin_unlock_irq(lock) | Release lock and enable local interrupts |
spin_lock_irqsave(lock, flags) | Acquire lock, save interrupt state |
spin_unlock_irqrestore(lock, flags) | Release lock, restore interrupt state |
spin_lock_bh(lock) | Acquire lock and disable bottom half |
spin_trylock(lock) | Try to acquire lock (non-blocking) |
spin_is_locked(lock) | Check lock state |
While holding spinlockMust not sleep(Cannot callkmalloc(GFP_KERNEL)、copy_from_useroperations that may block). This is one of the most common sources of bugs in the kernel.
Selection guide:
| Scenario | API |
|---|---|
| Between process contexts | spin_lock/spin_unlock |
| Between process and interrupt | spin_lock_irqsave/spin_unlock_irqrestore |
| Between different interrupts | spin_lock_irqsave/spin_unlock_irqrestore |
| Between process and bottom half | spin_lock_bh/spin_unlock_bh |
mutex — mutual exclusion lock
Unlike spinlock,mutexwhen the lock cannot be acquired, it yields the CPU and goes to sleep, suitable forcritical sections that may sleep。
Header file:<linux/mutex.h>
Data structure:
1 | struct mutex { |
Core APIs:
| API | Description |
|---|---|
mutex_init(lock) | Dynamic initialization |
DEFINE_MUTEX(lock) | Static definition |
mutex_lock(lock) | Acquire lock (may sleep) |
mutex_unlock(lock) | Unlock |
mutex_lock_interruptible(lock) | Interruptible acquisition |
mutex_trylock(lock) | Try to acquire (non-blocking) |
mutex_is_locked(lock) | Check status |
mutexThe locker must be responsible for unlocking (lock/unlock in different contexts is not allowed). The kernel strictly checks this.
Choice between spinlock and mutex:
| spinlock | mutex | |
|---|---|---|
| When unable to acquire | Spin wait | Sleep and yield CPU |
| Critical section | Short (nanosecond level) | Can be longer (millisecond level) |
| Can sleep | Absolutely not | can |
| Interrupt context | Available | Unavailable |
| System overhead | Low | Higher (involves scheduling) |
completion — Completion
completionImplemented in the kernelA synchronization mechanism where one thread waits for another thread to complete a taskLighter and has clearer semantics than semaphores.
Header file:<linux/completion.h>
Data structure:
1 | struct completion { |
Core APIs:
| API | Description |
|---|---|
DECLARE_COMPLETION(comp) | Static definition |
init_completion(comp) | Dynamic initialization |
wait_for_completion(comp) | Wait for completion (uninterruptible) |
wait_for_completion_interruptible(comp) | Wait for completion (interruptible by signal) |
wait_for_completion_timeout(comp, timeout) | Wait with timeout |
complete(comp) | Wake up one waiter |
complete_all(comp) | Wake up all waiters |
try_wait_for_completion(comp) | Non-blocking attempt |
Usage example:
1 | // Thread A: Wait |
Typical applications in the kernel:
- Kernel thread creation/destruction wait
- Device initialization completion notification
- Asynchronous I/O completion notification
- Module unload wait
RCU (rcu_head) — Read-Copy-Update
RCU (Read-Copy-Update) is an importantlock-free synchronization mechanism, suitable for read-mostly scenarios. Readers are completely lock-free, writers copy first and then update, and wait for all readers to finish before reclaiming old data.
Header file:<linux/rcupdate.h>/<linux/srcu.h>
Data structure:
1 | struct rcu_head { |
Core APIs:
| API | Description |
|---|---|
rcu_read_lock() | Reader enters critical section |
rcu_read_unlock() | Reader leaves critical section |
call_rcu(head, func) | Register reclamation callback |
synchronize_rcu() | Wait for all readers to complete (blocking) |
rcu_assign_pointer(p, v) | Writer updates pointer |
rcu_dereference(p) | Reader dereferences pointer |
kfree_rcu(ptr, rcu_field) | RCU safe memory release |
Typical pattern:
1 | // Reader — completely lock-free |
Typical applications in the kernel:
- Network routing table lookup
- Filesystem dentry cache
radix_tree/xarraylock-free lookupfdtableExpansion of (file descriptor table)
Waiting and scheduling
wait_queue — wait queue
A wait queue is a more general waiting mechanism: a process puts itself into the wait queue and goes to sleep, and is woken up when the condition is met.
Header file:<linux/wait.h>
Data structure:
1 | struct wait_queue_head { |
Core APIs:
| API | Description |
|---|---|
DECLARE_WAIT_QUEUE_HEAD(wq) | Static definition |
init_waitqueue_head(wq) | Dynamic initialization |
wait_event(wq, condition) | Wait until condition is true |
wait_event_interruptible(wq, condition) | Wait interruptible by signal |
wait_event_timeout(wq, condition, timeout) | Wait with timeout |
wake_up(wq) | Wake up all waiters |
wake_up_interruptible(wq) | Wake up TASK_INTERRUPTIBLE waiters |
wake_up_nr(wq, nr) | Wake up nr waiters |
Usage example:
1 | DECLARE_WAIT_QUEUE_HEAD(wq); |
Typical applications in the kernel:
- Process state switching (TASK_INTERRUPTIBLE / TASK_UNINTERRUPTIBLE)
- Blocking I/O in device drivers (read/write waiting for data)
- Read/write waiting for Pipe, Socket
work_struct / workqueue — Work queue
Work queue deferstasks to process contextfor execution, which is one of the common ways to handle interrupt bottom halves.
Header file:<linux/workqueue.h>
Data structure:
1 | struct work_struct { |
Core APIs:
| API | Description |
|---|---|
DECLARE_WORK(work, func) | Static definition |
INIT_WORK(work, func) | Dynamic initialization |
schedule_work(work) | Dispatch to system workqueue |
schedule_delayed_work(dwork, delay) | Delayed dispatch |
queue_work(wq, work) | Dispatch to specified workqueue |
cancel_work_sync(work) | Cancel and wait for completion |
flush_work(work) | Wait for work completion |
alloc_ordered_workqueue(name, flags) | Create ordered workqueue |
Usage example:
1 | static void my_work_handler(struct work_struct *work) |
Typical applications in the kernel:
- Interrupt bottom-half processing
- Delayed initialization of device drivers
- Packet processing in the network stack
- GPU driver command submission
timer_list — kernel timer
Used to, after a specified timeexecute a callback function(softirq context), with jiffies-level precision (usually 1ms~10ms). For higher precision, usehrtimer。
Header file:<linux/timer.h>
Data structure:
1 | struct timer_list { |
Core APIs:
| API | Description |
|---|---|
timer_setup(timer, callback, flags) | Initialize timer |
mod_timer(timer, expires) | Modify expiration time |
add_timer(timer) | Add timer |
del_timer(timer) | Delete timer |
del_timer_sync(timer) | Synchronous deletion (wait for handler to complete) |
timer_pending(timer) | Check if submitted |
Usage example:
1 | static void my_timer_callback(struct timer_list *t) |
Typical applications in the kernel:
- TCP retransmission timer, keepalive timer
- Watchdog timer
- Device driver polling
- LED blinking control
hrtimer — High-resolution timer
hrtimerProvidesNanosecond levelprecision timer, managed by a red-black tree at the bottom layer. It is the foundation of the modern Linux timer subsystem.
Header file:<linux/hrtimer.h>
Data structure:
1 | struct hrtimer { |
Core APIs:
| API | Description |
|---|---|
hrtimer_init(timer, clock_id, mode) | Initialization |
hrtimer_start(timer, time, mode) | Start timer |
hrtimer_cancel(timer) | Cancel timer |
hrtimer_forward_now(timer, interval) | Advance from current time |
Usage example:
1 | static enum hrtimer_restart my_hrtimer_cb(struct hrtimer *timer) |
Typical applications in the kernel:
- Periodic tick of the process scheduler
- POSIX timer
- High-resolution sleep (
usleep_range) - Precise latency control of network packets
Device model and notifications
kobject / kset — Kernel object model
kobjectis Linux device driver model’s cornerstone, providing unified reference counting, sysfs representation, and hotplug event support for all kernel objects.
Header file:<linux/kobject.h>
Data structure:
1 | struct kobject { |
Core APIs:
| API | Description |
|---|---|
kobject_init(obj, ktype) | Initialize kobject |
kobject_add(obj, parent, fmt, ...) | Add to sysfs |
kobject_init_and_add(obj, ktype, parent, fmt, ...) | Initialize + Add |
kobject_put(obj) | Decrement reference count |
kobject_get(obj) | Increment reference count |
kobject_uevent(obj, action) | Send uevent to userspace |
kset_register(kset) | Register kset |
kobject_create_and_add(name, parent) | Quickly create kobject |
Typical applications in the kernel:
/sysEach directory and file in the file systemstruct device、struct driver、struct bus_typebase classes of the device model, etc.ueventHotplug events (e.g., USB insertion notifying udev)
notifier_block — Notifier Chain
The Notifier Chain is apublish-subscribepattern implementation in the kernel: a subsystem publishes events, and other interested modules register callbacks to receive notifications.
Header file:<linux/notifier.h>
Data structure:
1 | struct notifier_block { |
Core APIs:
| API | Description |
|---|---|
blocking_notifier_chain_register(head, nb) | Register notifier block |
blocking_notifier_chain_unregister(head, nb) | Unregister |
blocking_notifier_call_chain(head, val, v) | Notifier call |
raw_notifier_chain_register(head, nb) | Register (atomic context safe) |
atomic_notifier_chain_register(head, nb) | Register (atomic context) |
Notifier chain types:
| Type | Callback context | Can block |
|---|---|---|
atomic_notifier_chain | Atomic context (interrupt/spinlock) | No |
blocking_notifier_chain | Process context | is |
raw_notifier_chain | Any context (caller’s responsibility) | Depends on the caller |
SRCU_notifier_chain | Process context (SRCU protected) | is |
Typical applications in the kernel:
- Kernel panic notification
- CPU hotplug events
- Network device events (netdev registration/deregistration)
- System reboot/suspend notification
- Out-of-memory (OOM) notification
Memory management and caching
kfifo — kernel FIFO queue
kfifois aLock-free circular buffer(circular buffer), providing lock-free communication for single-reader/single-writer producer/consumer (multi-reader/multi-writer requires external synchronization).
Header file:<linux/kfifo.h>
Core APIs:
| API | Description |
|---|---|
DECLARE_KFIFO(fifo, type, size) | Static definition |
kfifo_alloc(fifo, size, gfp) | Dynamic allocation |
kfifo_put(fifo, val) | Enqueue an element |
kfifo_get(fifo, val) | Dequeue an element |
kfifo_in(fifo, buf, n) | Enqueue multiple bytes |
kfifo_out(fifo, buf, n) | Dequeue multiple bytes |
kfifo_is_empty(fifo) | Is empty |
kfifo_is_full(fifo) | Is full |
kfifo_len(fifo) | Number of used elements |
kfifo_reset(fifo) | Clear queue |
kfifo_free(fifo) | Free memory |
Usage example:
1 | DECLARE_KFIFO(fifo, int, 32); |
Typical applications in the kernel:
- Serial port driver’s transmit/receive buffer
- Audio driver’s PCM buffer
- Kernel log buffer (printk ring buffer)
percpu — Per-CPU variables
Per-CPU variables allocateindependent memory copies for each CPU, CPUs can access their own copies without locking, and cache utilization is extremely high (variables are in the CPU’s local cache).
Header file:<linux/percpu.h>
Core APIs:
| API | Description |
|---|---|
DEFINE_PER_CPU(type, name) | Static definition |
alloc_percpu(type) | Dynamic allocation |
per_cpu(var, cpu) | Access the copy of a specified CPU |
get_cpu_var(var) | Get this CPU’s copy (preemption disabled) |
put_cpu_var(var) | Paired with get_cpu_var, restore preemption |
this_cpu_ptr(ptr) | Get the pointer to this CPU’s copy |
per_cpu_ptr(ptr, cpu) | Get the pointer to a specified CPU’s copy |
for_each_possible_cpu(cpu) | Iterate over all CPUs |
Usage example:
1 | static DEFINE_PER_CPU(int, my_counter); |
Typical applications in the kernel:
- Statistical counters (network packet count, interrupt count)
- Memory allocator per-CPU caches (slab/slub)
- RCU per-CPU state
- Scheduler per-CPU run queues
circ_buf — circular buffer macros
A set of simple macros for using ordinary character arrays as circular buffers. Often used to implement lightweight producer/consumer queues.
Header file:<linux/circ_buf.h>
Data structure:
1 | struct circ_buf { |
Core macros:
| Macro | Description |
|---|---|
CIRC_SPACE(head, tail, size) | Available space |
CIRC_CNT(head, tail, size) | Used bytes |
CIRC_SPACE_TO_END(head, tail, size) | Contiguous space to the end of the buffer |
Typical applications in the kernel:
- TTY driver line discipline buffer
- Simple serial port driver
flex_array — flexible array
flex_arrayAllows creatingarrays spanning multiple pages, but with a fixed element size. Compared to allocating akmalloclarge block of memory at once,flex_arrayit is more tolerant of small memory fragmentation (because each element is distributed across different pages).
Starting from Linux 5.10,flex_arrayit has been marked as deprecated, and it is recommended to use ordinarykmalloc_arrayorkvmalloc_arrayinstead.
Header file:<linux/flex_array.h>(deleted)
Alternative:kvmalloc_array(n, size, GFP_KERNEL)will automatically selectkmallocorvmalloc。
page — physical page descriptor
struct pageis in Linux memory managementthe most important data structure, each physical memory page has a correspondingpagestructure.
Header file:<linux/mm_types.h>
Data structure (greatly simplified):
1 | struct page { |
Core API (partial):
| API | Description |
|---|---|
alloc_pages(gfp_mask, order) | Allocate 2^order pages |
__free_pages(page, order) | Free pages |
get_page(page) | Increment reference count |
put_page(page) | Decrement reference count |
page_to_pfn(page) | Get page frame number |
pfn_to_page(pfn) | Page frame number to page |
kmap(page) | Map to kernel address space |
kunmap(page) | Unmap |
Typical applications in the kernel:
- Page Cache
- Underlying foundation of SLUB/SLAB allocator
- User space page table mapping (page fault handling)
mm_struct / vm_area_struct — Process address space
mm_struct — Memory descriptor
Describes a process’sComplete virtual address space, each process has a uniquemm_struct(can be shared among threads).
Header file:<linux/mm_types.h>
1 | struct mm_struct { |
vm_area_struct — Virtual memory area
Describes a segment ofContiguous virtual address range, each range has the same protection attributes and mapping type.
1 | struct vm_area_struct { |
Typical applications in the kernel:
- Page fault handler
mmap()/munmap()System call/proc/<pid>/mapsinformation source of
Network
sk_buff — Socket buffer
sk_buff(usually abbreviated asskb) is the Linux network subsystem’s core data structure, representing a network packet. It runs through the entire network stack: from network card driver reception to application layer transmission, all usesk_buffas the carrier.
Header file:<linux/skbuff.h>
Data structure (simplified):
1 | struct sk_buff { |
sk_buffManaging data space using a four-pointer model:
1 | head data tail end |
Core APIs:
| API | Description |
|---|---|
alloc_skb(size, gfp) | Allocate skb |
skb_put(skb, len) | Add data to tail |
skb_push(skb, len) | Add data to head (add protocol header) |
skb_pull(skb, len) | Remove data from head (strip protocol header) |
skb_clone(skb, gfp) | Clone skb (shared data) |
skb_copy(skb, gfp) | Deep copy (copy data) |
kfree_skb(skb) | Free skb |
skb_queue_head(list, skb) | Enqueue to head |
skb_dequeue(list) | Dequeue |
Typical applications in the kernel:
- Packet carrier for the entire network stack
- TUN/TAP virtual network card
- Netfilter / iptables packet filtering
