Slab缓存分配没有预定义函数的结构堆栈

Slab cache allocate stack of structures without predefined functions


我正在编写一个使用 kmem_cache_create 和 kmem_cache_alloc 在内核 space 中创建堆栈的模块,但是它不起作用。也许我对指针做了一些不好的事情,或者我不理解 slab 分配的整个概念。我已经把代码给同学和我的实验室负责人看了,遗憾的是没有人能帮助我。

这是我在 "stack"

上分配 5 个结构的代码
#include<linux/module.h>
#include<linux/slab.h>
#include<linux/string.h>

static struct example_struct {
    unsigned int id;
    char example_string[10];
    struct example_struct *next;
} *example_struct_pointer;

static struct example_struct *top = NULL;

static struct kmem_cache *example_cachep;

static void example_constructor(void *argument)
{
    static unsigned int id;
    static char test_string[] = "Stack";
    struct example_struct *example = (struct example_struct *)argument;
    example->id = id;
    strcpy(example->example_string,test_string);
    example->next = top;
    top = example;
    id++;
}

void print_example_struct(struct example_struct *example)
{
    pr_notice("Struct id: %u\n",example->id);
    pr_notice("String field content: %s\n",example->example_string);
}

static int __init slabmod_init(void)
{
    example_cachep = kmem_cache_create("example cache", sizeof(struct example_struct),0, SLAB_HWCACHE_ALIGN|SLAB_POISON|SLAB_RED_ZONE, example_constructor);
    if(IS_ERR(example_cachep)) {
        pr_alert("Error creating cache: %ld\n",PTR_ERR(example_cachep));
        return -ENOMEM;
    }

    for(i=1 ; i<6 ; i++)
    {
        printk(KERN_ALERT "i: %d\n",i);
        example_struct_pointer = (struct example_struct *) kmem_cache_alloc(example_cachep,GFP_KERNEL);
        if(IS_ERR(example_struct_pointer)) {
            pr_alert("Error allocating from cache: %ld\n", PTR_ERR(example_struct_pointer));
            kmem_cache_destroy(example_cachep);
            return -ENOMEM;
        }
    }
    return 0;
}

static void __exit slabmod_exit(void)
{
    struct example_struct *tmp = example_struct_pointer;
    if(example_cachep) {
        while(example_struct_pointer != NULL) {
            print_example_struct(example_struct_pointer);
            tmp = example_struct_pointer;
            example_struct_pointer = tmp->next;
            kmem_cache_free(example_cachep,tmp);
        }
        kmem_cache_destroy(example_cachep);
    }
}

module_init(slabmod_init);
module_exit(slabmod_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("kostek888888");
MODULE_DESCRIPTION("A module demonstrating use of the slab allocator.");
MODULE_VERSION("1.0");

我也有这段代码的调试版本:

#include<linux/module.h>
#include<linux/slab.h>
#include<linux/string.h>

static struct example_struct {
    unsigned int id;
    char example_string[10];
    struct example_struct *next;
} *example_struct_pointer;

static struct example_struct *top = NULL;

static struct kmem_cache *example_cachep;

static unsigned int i;

static void example_constructor(void *argument)
{
    static unsigned int id;
    static char test_string[] = "Stack";
    struct example_struct *example = (struct example_struct *)argument;
    example->id = id;
    strcpy(example->example_string,test_string);
    example->next = top;
    top = example;
    id++;
}

void print_example_struct(struct example_struct *example)
{
    pr_notice("Struct id: %u\n",example->id);
    //pr_notice("String field content: %s\n",example->example_string);
    printk(KERN_ALERT "example_struct_pointer: %p\n",example_struct_pointer);
    //printk(KERN_ALERT "top: %p\n",top);
    printk(KERN_ALERT "i: %d\n",i);
    i++;
}

static int __init slabmod_init(void)
{
    example_cachep = kmem_cache_create("example cache", sizeof(struct example_struct),0, SLAB_HWCACHE_ALIGN|SLAB_POISON|SLAB_RED_ZONE, example_constructor);
    if(IS_ERR(example_cachep)) {
        pr_alert("Error creating cache: %ld\n",PTR_ERR(example_cachep));
        return -ENOMEM;
    }

    for(i=1 ; i<6 ; i++)
    {
        printk(KERN_ALERT "i: %d\n",i);
        example_struct_pointer = (struct example_struct *) kmem_cache_alloc(example_cachep,GFP_KERNEL);
        if(IS_ERR(example_struct_pointer)) {
            pr_alert("Error allocating from cache: %ld\n", PTR_ERR(example_struct_pointer));
            kmem_cache_destroy(example_cachep);
            return -ENOMEM;
        }
    }
    return 0;
}

static void __exit slabmod_exit(void)
{
    struct example_struct *tmp = example_struct_pointer;
    i = 1;
    if(example_cachep) {
        while(example_struct_pointer != NULL) {
            print_example_struct(example_struct_pointer);
            printk(KERN_ALERT "tmp: %p\n",tmp);
            printk(KERN_ALERT "next: %p / %p\n\n",top->next,*(top->next));
            tmp = example_struct_pointer;
            example_struct_pointer = tmp->next;
            kmem_cache_free(example_cachep,tmp);
        }
            printk(KERN_ALERT "tmp: %p\n",tmp);
            printk(KERN_ALERT "next: %p\n\n",top->next);
            printk(KERN_ALERT "example_struct_pointer: %p\n",example_struct_pointer);
        kmem_cache_destroy(example_cachep);
    }
}

module_init(slabmod_init);
module_exit(slabmod_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("kostek888888");
MODULE_DESCRIPTION("A module demonstrating use of the slab allocator.");
MODULE_VERSION("1.0");


在输出中,当我只在 1-5 范围内执行 kmem_cache_alloc 5 次以及消息 "Slab cache still has objects" 时,我在 0-11 范围内得到 12 个元素。
Here is screenshot demonstrating words above
之后我收到呼叫跟踪消息,所以内存有问题。有时它会停止整个虚拟机。
我已经将 __exit 的 while 循环中的指针替换为 "top" 指针以检查它将显示什么 - 我有 122 个元素和相同的消息所以这可能是一个完整的 slab(但是可能因为消息而不完整?)。指针 "next" 在 DEC 中始终具有相同的值 122 - 结构是否为 122 字节宽? Sizeof 结构用 %d 给了我超过 1,000,000 的巨大价值。

原始代码,只分配一个结构并且工作起来很有魅力:

#include<linux/module.h>
#include<linux/slab.h>
#include<linux/string.h>

static struct example_struct {
    unsigned int id;
    char example_string[10];
} *example_struct_pointer;

static struct kmem_cache *example_cachep;

static void example_constructor(void *argument)
{
    static unsigned int id;
    static char test_string[] = "Test";
    struct example_struct *example = (struct example_struct *)argument;
    example->id = id;
    strcpy(example->example_string,test_string);
    id++;
}

void print_example_struct(struct example_struct *example)
{
    pr_notice("Example struct id: %u\n",example->id);
    pr_notice("Example string field content: %s\n",example->example_string);
}

static int __init slabmod_init(void)
{
    example_cachep = kmem_cache_create("example cache", sizeof(struct example_struct),0, SLAB_HWCACHE_ALIGN|SLAB_POISON|SLAB_RED_ZONE, example_constructor);
    if(IS_ERR(example_cachep)) {
        pr_alert("Error creating cache: %ld\n",PTR_ERR(example_cachep));
        return -ENOMEM;
    }

    example_struct_pointer = (struct example_struct *) kmem_cache_alloc(example_cachep,GFP_KERNEL);
    if(IS_ERR(example_struct_pointer)) {
        pr_alert("Error allocating form cache: %ld\n", PTR_ERR(example_struct_pointer));
        kmem_cache_destroy(example_cachep);
        return -ENOMEM;
    }

    return 0;
}

static void __exit slabmod_exit(void)
{
    if(example_cachep) {
        if(example_struct_pointer) {
            print_example_struct(example_struct_pointer);
            kmem_cache_free(example_cachep,example_struct_pointer);
        }
        kmem_cache_destroy(example_cachep);
    }
}

module_init(slabmod_init);
module_exit(slabmod_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Arkadiusz Chrobot <***>");
MODULE_DESCRIPTION("A module demonstrating useing of the slab allocator.");
MODULE_VERSION("1.0");


我已经查过像 Linux 设备驱动程序和类似的书 - 到处都是简单的功能,其中一个元素有示例。
我正在使用内核 3.16.0-4-686-pae 但在内核 4.9 中情况是一样的。
我将不胜感激:)

错误地假设ctor()kmem_cache_create()的最后一个参数)被调用每次你呼叫 kmem_cache_create().

实际上,调用此回调函数只是为了在分配器控制它之前预初始化 "garbage" 内存。

按此顺序:

  1. kmem_cache_alloc(), returns 指针 p.
  2. kmem_cache_free(p)(假设分配器保持对 p 的控制)。
  3. kmem_cache_alloc(), returns 指针 p.

回调函数在步骤 1 中仅 调用 ONCE。分配器假定在步骤 2(释放) p指向的对象已经处于初始化状态,所以第3步不需要初始化。

使用 kmem_cache 实现列表的正确方法是:

static void example_constructor(void *argument)
{
    static unsigned int id;
    static char test_string[] = "Stack";
    struct example_struct *example = (struct example_struct *)argument;
    /*
     *  This only garantee that at any time every element in the stack has unique id.
     * New element may have same id as previously deleted one.
     */
    example->id = id;
    // All elements will have given string.
    strcpy(example->example_string,test_string);
    // Appending into the stack cannot be performed in the constructor.
    // example->next = top;
    // top = example;
    id++;
}

/* Push allocated element into the stack and return that element. */
struct example_struct* push(void)
{
    struct example_struct* example = kmem_cache_alloc(example_cachep,GFP_KERNEL);

    if(example) {
        // Add to the stack here
        example->next = top;
        top = example;
    }

    return example;
}
/* Pop element from the stack and free the element. */
void pop(void)
{
    // Take the first element.
    struct example_struct *example = top;
    // Remove the element from the stack
    top = example->next;
    // In given example re-initialization actions before freeing the element are not needed.
    kmem_cache_free(example_cachep, example);
}

static int __init slabmod_init(void)
{
    ...
    // Test the stack.
    for(i=1 ; i<6 ; i++) {
         printk(KERN_ALERT "i: %d\n",i);
         example_struct_pointer = push();
         if(!example_struct_pointer)) {
             ...
         }
    }
    return 0;
}

static void __exit slabmod_exit(void)
{
    ...
    // Clean the stack
    while(top) pop();
    ...
}