简单驱动管理,内核链表宏及container_of宏演示代码
author: hjjdebug
date: 2026年 05月 05日 星期二 07:03:19 CST
descrip: 简单驱动管理,内核链表宏及container_of宏演示代码
文章目录
1. 3个内核宏, LIST_HEAD, container_of, list_for_each
看内核代码时,碰到的三个个典型宏,
LIST_HEAD, container_of, list_for_each
它们到处在用,到底什么意思呢?
这些宏,不容易看懂, 因为宏本身是一堆代码的组合.
2. 3个宏的定义
下面是定义,看了几乎还是不懂, 尽管代码很少,算是宏中比较简单的了.
#define LIST_HEAD_INIT(name) { &(name), &(name) }
#define LIST_HEAD(name)
struct list_head name = LIST_HEAD_INIT(name)
// 内核核心宏:通过成员指针找回结构体指针
#define container_of(ptr, type, member)
(type *)((char *)(ptr) - (unsigned long)&((type *)0)->member)
// 遍历链表
#define list_for_each(pos, head)
for (pos = (head)->next; pos != (head); pos = pos->next)
3. 3个宏的预处理展开
怎么才能看懂, 写小测试程序,看宏的预处理过程, 则可以秒懂!
后面附程序.
main 函数中
LIST_HEAD(tty_drivers);
展开后为 struct list_head tty_drivers = { &(tty_drivers), &(tty_drivers) };
原来是定义了一个局部变量. 宏就是变戏法!
list_for_each(pos, &tty_drivers)
宏展开后为:
for (pos = (&tty_drivers)->next; pos != (&tty_drivers); pos = pos->next)
宏展开后易懂,展开前却不易懂,因为被包起来了,
宏的存在只是方便书写而以.
drv = container_of(pos, tty_driver_t, list);
// 宏展开后为,
drv = (tty_driver_t *)((char *)(pos) - (unsigned long)&((tty_driver_t *)0)->list);
一眼看懂是赋值语句,移动指针pos
用这些宏,写一小段测试代码,看其展开后的结果,跟踪调试,就能掌握了.
宏怎样展开,可以直接看预处理输出.
4. 附测试代码: 简单的内核管理驱动
调试一下代码,更有收获.
p$ cat list_demo.c
#include <stdio.h>
#include <string.h>
// ====================== 模拟内核链表基础定义 ======================
struct list_head {
struct list_head *next, *prev;
};
//这些宏,不容易看懂, 而看了预处理, 则可以秒懂!
#define LIST_HEAD_INIT(name) { &(name), &(name) }
#define LIST_HEAD(name) \
struct list_head name = LIST_HEAD_INIT(name)
// 内核核心宏:通过成员指针找回结构体指针
#define container_of(ptr, type, member) \
(type *)((char *)(ptr) - (unsigned long)&((type *)0)->member)
// 遍历链表
#define list_for_each(pos, head) \
for (pos = (head)->next; pos != (head); pos = pos->next)
// 添加节点到链表,在head后插队, 代码只有4行,但步步关键,次序也不要变(防止出错).
static inline void list_add(struct list_head *new, struct list_head *head)
{
//在head后插队,需要调整3个节点的指针,自己(new),head的next指针,和head后面那个的prev指针
new->next = head->next; //在head 处断开,让head后面的人做自己后面的人
new->prev = head; //让head做自己前面的人,这样,自己节点就处理好了,前后指针正常
head->next->prev = new; //修复下一个人的前向指针,原来它指向head,现在要指向我.
head->next = new; //修复head的后向指针,原来它指向下一个人,现在要指向我.
}
// ====================== 我们自己的设备结构(模拟 tty_driver) ======================
// 不管什么驱动,总得要有名称和设备号
typedef struct {
int major; // 主设备号
char name[20]; // 驱动名
struct list_head list; // 内核链表节点(内嵌)
} tty_driver_t;
// ====================== 测试代码 ======================
int main()
{
// 1. 创建链表头:内核里的 LIST_HEAD(tty_drivers)
// 不看预处理真是难懂,看了预处理秒懂! 原来是定义了一个局部变量.
// 展开后为 struct list_head tty_drivers = { &(tty_drivers), &(tty_drivers) };
LIST_HEAD(tty_drivers);
// 2. 模拟创建3个TTY驱动(像ACM、tty、ttyS驱动)
tty_driver_t driver1 = {.major = 4, .name = "tty"};
tty_driver_t driver2 = {.major = 5, .name = "tty_ctrl"};
tty_driver_t driver3 = {.major = 166, .name = "acm_tty"};
// 3. 把驱动链入链表(内核 tty_register_driver 干的事,驱动管理)
list_add(&driver1.list, &tty_drivers);
list_add(&driver2.list, &tty_drivers);
list_add(&driver3.list, &tty_drivers);
// 4. 遍历链表 + container_of 找回完整结构体(内核最常用写法)
struct list_head *pos;
tty_driver_t *drv;
printf("==== 遍历内核 tty_drivers 链表 ====\n");
// 宏展开后为for语句: 宏语句就像变戏法.
// for (pos = (&tty_drivers)->next; pos != (&tty_drivers); pos = pos->next) {
list_for_each(pos, &tty_drivers) {
// 核心:通过链表节点 pos → 找回整个 tty_driver_t
// 宏展开后为计算pos位置,展开后一眼看懂
// drv = (tty_driver_t *)((char *)(pos) - (unsigned long)&((tty_driver_t *)0)->list);
drv = container_of(pos, tty_driver_t, list);
printf("驱动:%-10s 主设备号:%d\n", drv->name, drv->major);
}
return 0;
}
5 附Makefile
make preprocess 可生成.i 预处理文件
$ cat Makefile
# 编译器
CC = gcc
# 编译参数
CFLAGS = -Wall -g
# 目标程序名
TARGET = list_demo
# 源文件
SRC = list_demo.c
# 预处理输出文件
PREPROC = $(SRC:.c=.i)
# 默认:编译
all: $(TARGET)
# 编译可执行文件
$(TARGET): $(SRC)
$(CC) $(CFLAGS) -o $@ $^
# ==================== 新增:生成预处理文件 ====================
preprocess: $(SRC)
$(CC) -E $(SRC) -o $(PREPROC)
@echo "✅ 预处理完成,输出到: $(PREPROC)"
# 运行
run: $(TARGET)
./$(TARGET)
# 清理
clean:
rm -f $(TARGET) $(PREPROC)
6. 程序执行结果
附结果
$ make run
./list_demo
==== 遍历内核 tty_drivers 链表 ====
驱动:acm_tty 主设备号:166
驱动:tty_ctrl 主设备号:5
驱动:tty 主设备号:4
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐
所有评论(0)