读书笔记_栈

来源:互联网 发布:美国劳工统计局数据 编辑:程序博客网 时间:2024/05/22 02:17

栈的概念:从数据结构角度来看,栈是一种存储数据的容器(container),放入数据的操作被称为压入(push),从栈中取出数据的操作称为弹出(pop),采用的是后进先出的规则,即LIFO。而从计算机系统的角度而言,栈是存储局部变量和进行函数调用所必须不可少的连续内存区域。编译器再编译时会将函数调用和局部变量存取编译为合适的栈操作。操作系统在创建线程时,会为每个线程创建栈,包括分配栈所需的内存空间和初始有关的数据结构以及寄存器。以x86系统为例,SS(stack Segment)寄存器用来描述栈所在的内存段,ESP(Extended Stack Pointer)寄存器用来记录栈的栈顶地址。

CPU的执行过程:首先假定SS和ESP寄存器已经指向一个设置好的栈,执行PUSH指令时便向ESP指向的内存地址写入数据,然后调整ESP的值,指向新的栈顶。执行POP指令时便从栈顶弹出数据,并调整ESP的值。当CPU执行CALL和RET这样的函数调用指令时,也会使用栈。

从线程的角度看,栈是每个windows线程必备的设施。在Windows系统中,每个线程至少有一个栈,除系统线程之外的每个线程都有两个栈,一个供该线程在用户态下执行时使用,称为用户态栈,另外一个供该线程在内核态执行时使用,称为内核态栈。在一个运行着的多任务的系统中,由于多个线程的存在,所以有很多栈,但对于CPU而言,它只使用当前栈,即SS和ESP寄存器所指向的栈。当系统在不同任务间切换,以及同一任务内的内核态与用户态之间切换时,系统会保证SS和ESP寄存器始终指向合适的栈。在x86系统中,栈是朝向低地址方向生长的。压栈会使ESP值变小,出栈会使ESP值变大,也就是后压入栈的地址更小。

 当一个线程需要调用内核服务,就需要从用户态切换到内核态,这个过程称之为Context Switch。由于存在安全问题,所以不会让用户态的代码和内核服务的代码使用同一个栈,所以x86系统根据不同的特权级有着不同的栈,当夸特权级时,就需切换不同的栈。每个任务的任务状态段(TSS)记录了不同优先级所使用的栈的基本信息(SS段信息和ESP栈指针),CPU通过这些来寻找不同特权级下的不同栈的地址。实际只有两种栈,因为windows只有连个特权级,称之为用户态栈和内核态栈。

在windows系统为每个线程所维护的基本数据结构中,记录了内核态栈和用户态栈的基本信息,内核态栈记录在_KTHREAD结构中,用户态栈记录在_TEB结构中。

每个windows线程都拥有一个名为_KTHREAD的数据结构,该数据结构位于内核空间中,是windows系统管理和记录线程信息和进行线程调度的重要依据。

 在_KTHREAD结构中,有几个成员是专门用于记录栈信息的。

StackBase: 内核态栈的基地址,即栈的起始地址

StackLimit: 内核态栈的边界,因为栈是向下生长的,所以其值等于StackBase减去内核态栈的大小

LargeStack:是否已经切换成大内核态栈

KernelStack:内核态栈的栈顶地址,用于保存栈顶地址(ESP)

KernelStackResident:内核态栈是否位于物理内存中

InitialStack:供内核态代码逆向调用用户态代码时记录本来的栈顶位置

CallbackStack:也是在逆向调用时使用,Vista之前用来记录栈指针值

可以通过windbg的.thread命令得到一个线程的_ETHREAD地址,然后通过dt nt!_KTHREAD [地址] 来得到以上的各个字段。

因为速度和支持多种处理器等,windows没有采用x86CPU的硬件级人物切换机制,但是为了让CPU在模式切换时可以找到合适的栈信息,windows会为系统内的普通线程建立一个共享的TSS段,当进行软件方式的任务切换时,Windows会把当前任务的内核态栈信息复制到TSS段中。用户态栈的基本信息记录在线程信息块(_NT_TIB)结构中,NT_TIB是线程环境(TEB)结构的第一部分,因此可以根据TEB的地址来显示_NT_TIB结构。用户态栈和内核栈基本的功能是相同的,不同之处在于用户态栈是分配在其所在进程的用户空间中,而内核栈是分配在系统空间中的。用户空间是共享的,而系统空间是全局的,也就是说一个系统内的用户空间有很多个,而系统空间只有一个。每个进程都有自己的2GB的用户空间,就是说每个进程都可以使用0~0x7FFFFFFF这一段地址,而系统空间是全局性的,整个系统只有0x8000000~0xFFFFFFFF这个2GB空间,被所有进程所共享。用户态栈可指定大小,其默认大小为1MB,而内核态栈是完全由系统来控制大小的,其大小因处理器结构不同而不同,通常在十几KB到几十KB之间,以下是基于不同处理器的windows系统的默认内核态栈的大小。

在x86CPU的系统中,内核态栈的初始大小事12KB

在x64CPU(Intel64和AMD64)的系统中,内核态栈的大小事24KB,安腾(Itanium)处理器的系统中,内核态栈的初始大小事32KB。

GUI线程在调用GDI等内核服务时通常需要更大的内核态栈,所以在一个线程被转变为GUI线程后,windows会为其创建一个较大的可增长的内核态栈来替换掉原来的栈,称为大内核栈。

 

原创粉丝点击