我只是把active_obj的数量基本凑齐了,细节还没有去研究,可能有些地方是错的
之前遇到了一个设备跑了一段直接之后内存降低无法回收的问题。当时通过slabinfo看到某些slab池子里面active_objs和num_objs差距很大。slub分配器无法自己回收
(无法回收的原因有种这个说法,如果你申请了一个obj,实际上它会给去申请n个,然后拿一个给你。经过一段时间后n个都被申请完了。如果slub分配器想回收,只能是n个都被释放了才能回收,只要有其中一个未被释放,都不行。这个说法还没有去验证过。)
当时就想知道究竟是哪些人申请了内存没有释放 。所以就产生了去找active_objs的内存申请轨迹的想法
突然想到个问题。在设备启动时slabinfo如下:
# name <active_objs> <num_objs> <objsize>
xxx 6000 6000 yyyy
全部业务停掉,slabinfo如下
# name <active_objs> <num_objs> <objsize>
xxx 6000 10000 yyyy
active的数量都恢复了。只是总的数量增加了,无法回收。这种情况还会是因为部分人持有buffer,导致其他人无法被回收嘛?难道是这6000只是数量没有变化,申请者发生了变化??
我的环境里面使用的是slub分配器 。每一类object都会拥有一个cache(struct kmem_cache)。这个cache里面可以管理多个slab(代码里面其实是没有slab这个东西的,slab由2^order个连续的page组成,每个page里面又可以有多个object)
理论上我们只需要遍历所有slab里面所有的page,我们就可以找到所有的object(包含已分配和位未分配的)。
struct kmem_cache {
struct kmem_cache_cpu __percpu *cpu_slab;
............................
struct kmem_cache_node *node[MAX_NUMNODES];
};
struct kmem_cache_node {
spinlock_t list_lock;
.......................
#ifdef CONFIG_SLUB
unsigned long nr_partial;
struct list_head partial;
#ifdef CONFIG_SLUB_DEBUG
atomic_long_t nr_slabs;
atomic_long_t total_objects;
struct list_head full;
#endif
#endif
};
可以看到 kmem_cache里面有个node,这个node里面的partial是一个链表,里面挂的是部分空闲的page,链表full里面的page是全部都被分配出去的page(active obj主要在这个里面)。cpu_slab里面我不知道有没有用(感觉应该是有的,但是我本地试过都是空的,不用遍历也行),我遍历的时候还是去检查了它
最后代码如下
linux-3.16\mm\slub.c(需要开启slub debug)
account_active_obj里面的循环不需要,是我理解错了
print_tracking就可以打印内存申请释放信息
unsigned long account_active_obj(struct page *slab, struct kmem_cache *s, int order)
{
void *start;
void *last;
void *p;
struct page *page;
unsigned long alloc_cnt = 0;
int i = 0;
/*
最开始我以为slab是连续的几个page,
链表里面只有起始的page,我们需要向后遍历2^order个page
其实所有的page都在里面了
*/
//int num = 1 << order;
int num = 1 << 0;
if (NULL == slab)
{
//printk(KERN_EMERG "\r\n slab null\n");
return 0;
}
unsigned long pfn = page_to_pfn(slab);
start = page_address(slab);
last = start;
while (i < num)
{
page = pfn_to_page(pfn);
//printk(KERN_EMERG "\r\n page->objects %d, page->inuse %d\n", page->objects, page->inuse);
for_each_object(p, s, start, page->objects) {
/*
判断当前obj是否是空闲的,不是空闲的打印出申请者信息
*/
if (!on_freelist(s, page, last))
{
//print_tracking(s, last);
alloc_cnt++;
}
last = p;
}
++i;
++pfn;
}
return alloc_cnt;
}
unsigned long showinfo(struct kmem_cache *s)
{
unsigned long alloc_cnt = 0;
unsigned long flags;
int cpu;
struct page *page;
int order = oo_order(s->oo);
struct kmem_cache_node *n = s->node[0];
spin_lock_irqsave(&n->list_lock, flags);
list_for_each_entry(page, &n->partial, lru)
{
//printk(KERN_EMERG "\r\n n->partia\n");
alloc_cnt +=account_active_obj(page, s, order);
}
list_for_each_entry(page, &n->full, lru)
{
//printk(KERN_EMERG "\r\n n->partial\n");
alloc_cnt +=account_active_obj(page, s, order);
}
spin_unlock_irqrestore(&n->list_lock, flags);
for_each_possible_cpu(cpu) {
struct kmem_cache_cpu *c = per_cpu_ptr(s->cpu_slab, cpu);
//printk(KERN_EMERG "\r\n cpu_slab\n");
alloc_cnt +=account_active_obj(c->page, s, order);
alloc_cnt +=account_active_obj(c->partial, s, order);;
}
//printk(KERN_EMERG "\r cache %s used cnt %lu, order %d\n", s->name, alloc_cnt, order);
return alloc_cnt;
}
下面这个代码是修改显示的地方
int cache_show(struct kmem_cache *s, struct seq_file *m)
{
......................................
alloc_cnt = showinfo(s);
seq_printf(m, "%-17s %6lu(%lu) %6lu %6u %4u %4d",
cache_name(s), sinfo.active_objs, alloc_cnt, sinfo.num_objs, s->size,
sinfo.objects_per_slab, (1 << sinfo.cache_order));
.......................
return 0;
}
实际效果展示:
cat /proc/slabinfo
基本数量能对的上文章来源:https://www.toymoban.com/news/detail-807800.html
文章来源地址https://www.toymoban.com/news/detail-807800.html
到了这里,关于遍历slub分配器申请的object(linux3.16)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!