Devres内存管理

目录
Devres(Device Resource Management)分析
1 1. 概念与背景
Devres 是 Linux 内核中用于自动化设备资源管理的机制,旨在简化驱动开发中的资源分配与释放流程。通过 Devres,开发者无需手动跟踪和释放资源(如内存、中断、DMA 缓冲区等),而是将资源绑定到设备生命周期(struct device
),当设备注销或驱动卸载时,内核自动释放相关资源,从而减少内存泄漏风险。
2 2. Devres 的核心原理
- 资源绑定:通过
devm_*
系列函数(如devm_kzalloc()
,devm_request_irq()
)分配资源时,内核会记录资源与设备的关联。 - 自动释放:当设备被移除(调用
device_del()
)或驱动卸载时,内核遍历与该设备关联的资源链表,逐一释放资源。 - 数据结构:资源通过链表(
struct devres_node
)管理,每个节点包含释放资源的回调函数。
3 3. 传统方法与 Devres 对比
传统驱动开发:
probe() {
res1 = kmalloc(...);
res2 = request_irq(...);
if (error) {
free(res1);
free_irq(res2);
return -ERR;
}
}
remove() {
free(res1);
free_irq(res2);
}
使用 Devres:
probe() {
res1 = devm_kzalloc(dev, ...);
res2 = devm_request_irq(dev, ...);
// 无需手动释放
if (error)
return -ERR; // 内核自动回滚已分配资源
}
remove() {
// 无需操作,资源已自动释放
}
4 4. 源码分析
4.1 主要 Devres API
函数 | 作用 |
---|---|
devm_kzalloc() | 分配内存,自动释放 |
devm_request_irq() | 注册中断处理函数 |
devm_ioremap() | 映射物理地址到内核虚拟空间 |
devm_gpio_request() | 请求 GPIO 引脚 |
devm_clk_get() | 获取时钟资源 |
4.2 devm_kmalloc
函数分析
struct devres_node {
struct list_head entry;
dr_release_t release;
const char *name;
size_t size;
};
struct devres {
struct devres_node node;
u8 __aligned(ARCH_KMALLOC_MINALIGN) data[];
};
void *devm_kmalloc(struct device *dev, size_t size, gfp_t gfp)
{
struct devres *dr;
if (unlikely(!size))
return ZERO_SIZE_PTR;
/* use raw alloc_dr for kmalloc caller tracing */
dr = alloc_dr(devm_kmalloc_release, size, gfp, dev_to_node(dev));
if (unlikely(!dr))
return NULL;
set_node_dbginfo(&dr->node, "devm_kzalloc_release", size);
devres_add(dev, dr->data);
return dr->data;
}
在申请资源的时候将释放也加入其中
static __always_inline struct devres * alloc_dr(dr_release_t release,
size_t size, gfp_t gfp, int nid)
{
size_t tot_size;
struct devres *dr;
if (!check_dr_size(size, &tot_size))
return NULL;
dr = kmalloc_node_track_caller(tot_size, gfp, nid);
if (unlikely(!dr))
return NULL;
/* No need to clear memory twice */
if (!(gfp & __GFP_ZERO))
memset(dr, 0, offsetof(struct devres, data));
INIT_LIST_HEAD(&dr->node.entry);
dr->node.release = release;
return dr;
}
devres_add
devres_add
==> add_dr
void devres_add(struct device *dev, void *res)
{
struct devres *dr = container_of(res, struct devres, data);
unsigned long flags;
spin_lock_irqsave(&dev->devres_lock, flags);
add_dr(dev, &dr->node);
spin_unlock_irqrestore(&dev->devres_lock, flags);
}
将dr
加入到设备的devres_head
中
4.3 释放所有资源
int devres_release_all(struct device *dev)
{
unsigned long flags;
LIST_HEAD(todo);
int cnt;
if (list_empty(&dev->devres_head))
return 0;
spin_lock_irqsave(&dev->devres_lock, flags);
cnt = remove_nodes(dev, dev->devres_head.next, &dev->devres_head, &todo);
spin_unlock_irqrestore(&dev->devres_lock, flags);
release_nodes(dev, &todo);
return cnt;
}
那么为什么资源可以自动释放呢?
经过代码分析可知,那就可以说明驱动卸载的时候自动释放资源
device_release --> devres_release_all
4.4 提前释放资源
可以很清楚的弄明白提前释放的原理
void devm_remove_action(struct device *dev, void (*action)(void *), void *data)
{
struct action_devres devres = {
.data = data,
.action = action,
};
WARN_ON(devres_destroy(dev, devm_action_release, devm_action_match,
&devres));
}
int devres_destroy(struct device *dev, dr_release_t release,
dr_match_t match, void *match_data)
{
void *res;
res = devres_remove(dev, release, match, match_data);
if (unlikely(!res))
return -ENOENT;
devres_free(res);
return 0;
}
void * devres_remove(struct device *dev, dr_release_t release,
dr_match_t match, void *match_data)
{
struct devres *dr;
unsigned long flags;
spin_lock_irqsave(&dev->devres_lock, flags);
dr = find_dr(dev, release, match, match_data);
if (dr) {
list_del_init(&dr->node.entry);
devres_log(dev, &dr->node, "REM");
}
spin_unlock_irqrestore(&dev->devres_lock, flags);
if (dr)
return dr->data;
return NULL;
}
void devres_free(void *res)
{
if (res) {
struct devres *dr = container_of(res, struct devres, data);
BUG_ON(!list_empty(&dr->node.entry));
kfree(dr);
}
}