Linux内核栈初始化的一个小细节

来源:互联网 发布:cocos2d js 源码 编辑:程序博客网 时间:2024/04/28 05:02

今天遇到一个有趣的问题:

内核栈的栈底(高地址)8个字节是预留出来处理bug的,所以在寻找栈底的时候用到这个宏:

#define task_pt_regs(task)                                             /

({                                                                     /

      struct pt_regs *__regs__;                                       /

       __regs__ = (struct pt_regs *)(KSTK_TOP(task_stack_page(task))-8); /

       __regs__ - 1;                                                   /

})

 

KSTK_TOP是实质工作的函数,定义如下:

#define KSTK_TOP(info)                                                 /

({                                                                     /

         unsigned long *__ptr = (unsigned long *)(info);                 /

         (unsigned long)(&__ptr[THREAD_SIZE_LONGS]);                     /

})

 

info就是tsk->thread_info这个地址,就是内核栈的起始地址。

THREAD_SIZE_LONGS = THREAD_SIZE / sizeof(unsigned long),也就是THREAD_SIZE包含多少个unsigned long。

因此这里,&__ptr[THREAD_SIZE_LONGS] = &(*(__ptr + THREAD_SIZE_LONG)) = __ptr + THREAD_SIZE_LONG,即,KSTK_TOP这个宏返回的是内核栈的栈底,回到task_pt_regs,再用KSTK_TOP(task_stack_page(task))-8留出8个字节。

问题出在这里:之前定义的__ptr是unsigned long *类型的指针,即__ptr每次移动1,就会跳过unsigned long(4Bytes)的内存空间,那么,KSTK_TOP(task_stack_page(task))-8不就相当于"-8*sizeof(unsigned long)"了么?

秘密就在(unsigned long)(&__ptr[THREAD_SIZE_LONGS])这条语句中。它将得到的地址转化为unsigned long型,这个转化的作用就在于,得到的&__ptr[THREAD_SIZE_LONGS]这个地址-8相当于减去8个字节,而不是8个unsigned long。

我做了一个简单的实验:

#include <stdio.h>

 

int

main()

{

        int* int_address;

        unsigned long unsigned_long_address = (unsigned long)int_address;

        printf("at the beginning, int_address = 0x%u, and unsigned_long_address = 0x%u.../n", int_address, unsigned_long_address);

        printf("/nthen, int_address + 1 = 0x%u/nunsigned_long_address + 1 = 0x%u/n", int_address + 1, unsigned_long_address + 1);

 

        return 0;

}

 

这里声明了一个int*类型的指针int_address,它是一个指向int大小(4bytes)的地址;

还有一个无符号长整型unsigned_long_address,它的作用是将int_address这个地址转化成一个无符号长整型数。

让这两个变量分别+1,得到如下结果:

at the beginning, int_address = 0x134513723, and unsigned_long_address = 0x134513723...

 

then, int_address + 1 = 0x134513727

unsigned_long_address + 1 = 0x134513724

 

可以看到,开始两个变量的值是相同的,但是分别+1之后,由于int_address是指向一个int大小的地址,所以它每次加1就会自动跳过4个字节,地址变成0x134513727;而unsigned_long_address只不过是一个普通的长整型数,所以它得到正常的+1结果。

 

 

现在回到KSTK_TOP(task_stack_page(task))-8,由于KSTK_TOP(task_stack_page(task))这个地址转换成了unsigned long的无符号长整型数,所以,-8就相当于减去了8个字节,现在明白了将一个地址转换成为整数的巨大作用了!!!

 

在task_pt_regs最后执行__regs__ - 1,由于__regs__转换成了struct pt_regs *类型指针,所以-1操作相当于跳过了

sizeof pt_regs这么多字节的空间了。