当前位置:网站首页>Analysis of the characteristics of page owner
Analysis of the characteristics of page owner
2022-07-03 13:56:00 【kaka__ fifty-five】
This analysis is based on linux kernel 4.19.195.
linux Kernel page owner characteristic , Reference resources file , Mainly for Tracking about who allocated each page, Easy to locate memory leaks 、 Memory usage problem . This article simply analyzes from the perspective of source code page owner Implementation principle of .
page owner The overall design idea of the feature is very simple , It is through extension page Structure , Add member variables to store this page Allocated call stack and flag bit , then hack Memory page allocation and release interface , When memory pages are allocated , Save call stack information , Set flag bit ; When the memory page is released , Clear the call stack information , Clear flag bits . then , Through one debugfs The interface of , Pass the call stack information of all memory pages that have been allocated at the time of reading the interface to the user state , And made a tool in user mode , It is used to count the information of these call stacks .
This article focuses on the analysis of kernel state when pages are allocated ,page owner Characteristic behavior .
We know , Before memory pages are allocated , Will walk in post_alloc_hook function , Do some processing ,post_alloc_hook Function will call set_page_owner function , Finish saving the memory page allocation call stack .
static inline void set_page_owner(struct page *page,
unsigned int order, gfp_t gfp_mask)
{
if (static_branch_unlikely(&page_owner_inited))
__set_page_owner(page, order, gfp_mask);
}
noinline void __set_page_owner(struct page *page, unsigned int order,
gfp_t gfp_mask)
{
struct page_ext *page_ext = lookup_page_ext(page);
depot_stack_handle_t handle;
if (unlikely(!page_ext))
return;
handle = save_stack(gfp_mask);
__set_page_owner_handle(page_ext, handle, order, gfp_mask);
}
First step , Call function lookup_page_ext Get the page Corresponding struct page_ext Structure . The kernel for each page The structure is equipped with a struct page_ext Structure , Equivalent to page Extension of structure , I guess it's because I really don't want to expand page Volume of the structure , Just made such a compromise , Otherwise, it can be directly written into page Inside the structure .
The second step , call save_stack function , Save the call stack when the page is allocated , And return a handle like handle, Use in step 3 . This function is mainly analyzed below .
The third step , The information obtained in the second step handle, Write to the corresponding struct page_ext Structure , And set the flag bit , Indicates that the page has been allocated .
The first and third steps are relatively simple , Next, analyze the second step .
static noinline depot_stack_handle_t save_stack(gfp_t flags)
{
unsigned long entries[PAGE_OWNER_STACK_DEPTH];
struct stack_trace trace = {
.nr_entries = 0,
.entries = entries,
.max_entries = PAGE_OWNER_STACK_DEPTH,
.skip = 2
};
depot_stack_handle_t handle;
save_stack_trace(&trace);
if (trace.nr_entries != 0 &&
trace.entries[trace.nr_entries-1] == ULONG_MAX)
trace.nr_entries--;
/* * We need to check recursion here because our request to stackdepot * could trigger memory allocation to save new entry. New memory * allocation would reach here and call depot_save_stack() again * if we don't catch it. There is still not enough memory in stackdepot * so it would try to allocate memory again and loop forever. */
if (check_recursive_alloc(&trace, _RET_IP_))
return dummy_handle;
handle = depot_save_stack(&trace, flags);
if (!handle)
handle = failure_handle;
return handle;
}
First , Call function save_stack_trace Get call stack information ;
then , Using functions depot_save_stack Store the call stack in memory .
depot_save_stack It's a god horse ?
stay lib/stackdepot.c In the document , We found the implementation of this function . From the comments at the beginning of the file , You can see that , This file is a library that stores the call stack , This library , Only memory will be used to store call stack information , This information will not be deleted . also , A hash table will be used , To store the handle representing these call stacks , in addition , Memory for storing call stack information , It is equivalent to a large array , You only need to add data to this memory each time , And record the first address of the call stack information . Let's take a look at the functions depot_save_stack The specific implementation method of .
static void *stack_slabs[STACK_ALLOC_MAX_SLABS]; // Save a large array of call stacks , among , Each element points to a piece of memory , Used to store call stack information ; The reason for this is to save some memory , Instead of applying for all memory at once
static int depot_index;//stack_slabs Of index
static int next_slab_inited; // Indicate the next index Of stack_slabs Whether memory has been allocated
static size_t depot_offset;//stack_slabs[depot_index] Of depot_offset
/** * depot_save_stack - save stack in a stack depot. * @trace - the stacktrace to save. * @alloc_flags - flags for allocating additional memory if required. * * Returns the handle of the stack struct stored in depot. */
depot_stack_handle_t depot_save_stack(struct stack_trace *trace,
gfp_t alloc_flags)
{
u32 hash;
depot_stack_handle_t retval = 0;
struct stack_record *found = NULL, **bucket;
unsigned long flags;
struct page *page = NULL;
void *prealloc = NULL;
if (unlikely(trace->nr_entries == 0))
goto fast_exit;
hash = hash_stack(trace->entries, trace->nr_entries);
bucket = &stack_table[hash & STACK_HASH_MASK];
/* * Fast path: look the stack trace up without locking. * The smp_load_acquire() here pairs with smp_store_release() to * |bucket| below. */
found = find_stack(smp_load_acquire(bucket), trace->entries,
trace->nr_entries, hash);
if (found)
goto exit;
/* * Check if the current or the next stack slab need to be initialized. * If so, allocate the memory - we won't be able to do that under the * lock. * * The smp_load_acquire() here pairs with smp_store_release() to * |next_slab_inited| in depot_alloc_stack() and init_stack_slab(). */
if (unlikely(!smp_load_acquire(&next_slab_inited))) {
/* * Zero out zone modifiers, as we don't have specific zone * requirements. Keep the flags related to allocation in atomic * contexts and I/O. */
alloc_flags &= ~GFP_ZONEMASK;
alloc_flags &= (GFP_ATOMIC | GFP_KERNEL);
alloc_flags |= __GFP_NOWARN;
page = alloc_pages(alloc_flags, STACK_ALLOC_ORDER);
if (page)
prealloc = page_address(page);
}
raw_spin_lock_irqsave(&depot_lock, flags);
found = find_stack(*bucket, trace->entries, trace->nr_entries, hash);
if (!found) {
struct stack_record *new =
depot_alloc_stack(trace->entries, trace->nr_entries,
hash, &prealloc, alloc_flags);
if (new) {
new->next = *bucket;
/* * This smp_store_release() pairs with * smp_load_acquire() from |bucket| above. */
smp_store_release(bucket, new);
found = new;
}
} else if (prealloc) {
/* * We didn't need to store this stack trace, but let's keep * the preallocated memory for the future. */
WARN_ON(!init_stack_slab(&prealloc));
}
raw_spin_unlock_irqrestore(&depot_lock, flags);
exit:
if (prealloc) {
/* Nobody used this memory, ok to free it. */
free_pages((unsigned long)prealloc, STACK_ALLOC_ORDER);
}
if (found)
retval = found->handle.handle;
fast_exit:
return retval;
}
EXPORT_SYMBOL_GPL(depot_save_stack);
First use the function hash_stack Determine which hash bucket the handle information of the call stack is stored in , Used to reduce repeated memory consumption .
then ,find_stack Function to find out whether there is the same call stack in the corresponding hash bucket , Some words , You can go straight back to , After all, the call stack is already stored in memory .
Next, through variables next_slab_inited, To determine whether it is necessary to apply for memory .
Call again find_stack Prevent concurrent problems .
If the call stack has not been stored before , Then use depot_alloc_stack Function stores call stack information .
After storage , Function exit .
Let's continue to see depot_alloc_stack Implementation of function
/* Allocation of a new stack in raw storage */
static struct stack_record *depot_alloc_stack(unsigned long *entries, int size,
u32 hash, void **prealloc, gfp_t alloc_flags)
{
int required_size = offsetof(struct stack_record, entries) +
sizeof(unsigned long) * size;
struct stack_record *stack;
required_size = ALIGN(required_size, 1 << STACK_ALLOC_ALIGN);
if (unlikely(depot_offset + required_size > STACK_ALLOC_SIZE)) {
if (unlikely(depot_index + 1 >= STACK_ALLOC_MAX_SLABS)) {
WARN_ONCE(1, "Stack depot reached limit capacity");
return NULL;
}
depot_index++;
depot_offset = 0;
/* * smp_store_release() here pairs with smp_load_acquire() from * |next_slab_inited| in depot_save_stack() and * init_stack_slab(). */
if (depot_index + 1 < STACK_ALLOC_MAX_SLABS)
smp_store_release(&next_slab_inited, 0);
}
init_stack_slab(prealloc);
if (stack_slabs[depot_index] == NULL)
return NULL;
stack = stack_slabs[depot_index] + depot_offset;
stack->hash = hash;
stack->size = size;
stack->handle.slabindex = depot_index;
stack->handle.offset = depot_offset >> STACK_ALLOC_ALIGN;
stack->handle.valid = 1;
memcpy(stack->entries, entries, size * sizeof(unsigned long));
depot_offset += required_size;
return stack;
}
Um. , Basically is memcpy 了 , Copy the call stack information to " In large array ".
The overall framework is basically like this , Or look at the original author's notes , Can get a lot of information .
边栏推荐
- Go language unit test 4: go language uses gomonkey to test functions or methods
- 金属有机骨架MOFs装载非甾体类抗炎药物|ZIF-8包裹普鲁士蓝负载槲皮素(制备方法)
- php 迷宫游戏
- Another industry has been broken by Chinese chips. No wonder the leading analog chip companies in the United States have cut prices and sold off
- Mastering the cypress command line options is the basis for truly mastering cypress
- [quantitative trading] permanent portfolio, turtle trading rules reading, back testing and discussion
- Installation impression notes
- 又一个行业被中国芯片打破空白,难怪美国模拟芯片龙头降价抛售了
- Implementation of Muduo accept connection, disconnection and sending data
- Ocean CMS vulnerability - search php
猜你喜欢

从零开始的基于百度大脑EasyData的多人协同数据标注

Go language unit test 4: go language uses gomonkey to test functions or methods

Use and design of Muduo buffer class

Complete DNN deep neural network CNN training with tensorflow to complete image recognition cases

Failure of vector insertion element iterator in STL

Screenshot of the operation steps of upload labs level 4-level 9

Go language web development series 25: Gin framework: using MD5 to verify the signature for the interface station

Multi person collaborative data annotation based on Baidu brain easydata from scratch

交联环糊精金属有机骨架负载甲氨蝶呤缓释微粒|金属-有机多孔材料UiO-66负载黄酮苷类药物|齐岳

How to use lxml to judge whether the website announcement is updated
随机推荐
Installation impression notes
Disruptor -- a high concurrency and high performance queue framework for processing tens of millions of levels
[how to earn a million passive income]
Complete deep neural network CNN training with tensorflow to complete picture recognition case 2
Sequence table (implemented in C language)
The solution of Chinese font garbled code in keil5
Brief analysis of tensorboard visual processing cases
金属有机骨架(MOFs)抗肿瘤药载体|PCN-223装载甲硝唑|UiO-66包载盐酸环丙沙星([email protected])
栈应用(平衡符)
Function calling convention
GoLand 2021.2 configure go (go1.17.6)
Comprehensive case of MySQL data addition, deletion, modification and query
树的深入和广度优先遍历(不考虑二叉树)
IBEM mathematical formula detection data set
Replace the GPU card number when pytorch loads the historical model, map_ Location settings
Students who do not understand the code can also send their own token, which is easy to learn BSC
交联环糊精金属有机骨架负载甲氨蝶呤缓释微粒|金属-有机多孔材料UiO-66负载黄酮苷类药物|齐岳
mysql中的字段问题
又一个行业被中国芯片打破空白,难怪美国模拟芯片龙头降价抛售了
Qt学习22 布局管理器(一)