软件调试笔记30 - 栈和函数调用 : 栈简介
简介:
从数据结构,栈是一种用来存储数据的容器,遵从后入先出规则。 编译器在编译时会将函数调用和局部变量存取编译为合适的栈操作。操作系统在创建线程时,会为每个线程创建栈,包括分配栈所需的内存空间和初始化有关的数据结构及寄存器。以x86为例,SS(stack segment)寄存器用来描述栈所在的内存段,ESP(extended stack pointer)寄存器用来记录栈的栈顶地址。CPU在执行程序时,会假定SS和ESP已经指向一个设置好的栈,执行PUSH指令就向ESP所指向的内存地址写入数据,然后调整ESP值,使其指向新的栈顶。执行POP就弹出数据,并调整ESP的值。当CPU执行CALL和RET这样的函数调用指令时,也会使用栈。
从线程角度,栈是每个WINDOWS线程必备的设施。每个线程至少有一个栈,系统线程之外的每个线程有两个栈,一个供该线程在用户态在执行使用-用户态栈,一个在内核态下执行使用,称为内核态栈。在一个多任务的系统中,会有多个线程,所以有很多个栈存在。但对CPU而言,它只使用当前栈,即SS和ESP所指向的栈。当在不同任务切换,以及同一个任务内核态和用户态切换,系统会保证SS和ESP始终指向合适的栈。
用户态栈和内核态栈:
WINDOWS会为系统内的普通线程建立一个共享的TSS段,当进行软件方式的任务切换时,WINDOWS会把当前任务的内核态信息复制到TSS段。
用户态栈的信息记录在线程信息块_NT_TIB结构中,它是线程环境块TEB结构的第一部分,因此可以根据TEB地址来显示它。
从功能和使用方式看,用户态栈和内核态栈是相同的。但因为服务对象不同,所以还是有差异的。
1. 首先, 用户态栈是分配在所在线程的用户空间中,而内核态栈是系统空间。而用户空间是共享的,系统空间是全局的。一个系统内用户空间有很多个,但系统空间只有一个。
2. 用户态栈是可指定大小的,默认是1MB,而内核态栈是系统来控制大小的,大小因处理器结构不同而不同,但通常在十几KB到几十KB之间。在一个线程被转变为GUI线程后(创建的时候,所有线程都是非GUI线程),WINDOWS会为他创建一个较大的可增长的内核态栈来替换掉原来的栈,称为大内核态栈。