遍历slub分配器申请的object(linux3.16)

我只是把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

基本数量能对的上

  • 10
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值