Linux内核入门(二)——用户态向内核态切换

那么,程序什么时候使用用户栈,什么时候使用内核栈呢?对,系统调用。也就是执行printf、open、read、write执行C语言库函数时,其最终会用到对应的系统调用,如sys_open、sys_read等。这时候就切换到内核栈。

1 Linux的堆栈切换


我们针对80x86来讨论,其实Linux只在四个地方用了它的堆栈段(由ss+esp指向其栈底地址):
• 系统引导初始化临时实模式下使用的堆栈
• 进入保护模式后提供内核程序始化使用的堆栈,该堆栈也是后来进程0使用的用户态堆栈
• 每个进程通过系统调用,执行内核程序时使用的堆栈,称之为进程的内核态堆栈,每个进程都有自己独立的内核态堆栈
• 进程在用户态执行的堆栈,位于进程逻辑地址空间近末端处

下面简单的介绍一下与普通进程相关的两个堆栈

每个进程都有两个堆栈,分别用于用户态和内核态程序的执行,我们称为用户态堆栈和内核态堆栈。

除了处于不同CPU特权级中,这两个堆栈之间的主要区别还在于任务的内核态堆栈很小,在后面进程管理专题中我们可以看到所保存的数据最多不能超过8096个字节,而进程的用户态堆栈却可以在用户的4GB空间的最底部,并向上延伸。

在用户态运行时,即你看到的那些代码的时候,每个进程(除了进程0和进程1)有自己的4GB地址空间,当一个进程刚被创建时,它的用户态堆栈指针被设置在其地址空间的靠近末端部分,应用程序在用户态下运行时就一直使用这个堆栈,实际物理地址内存则由CPU分页机制确定。

在内核态运行时,每个任务有其自己的内核态堆栈,用于任务在内核代码中执行期间,即执行系统调用以后。其所在的线性地址中位置由该进程TSS段中ss0和esp0 两个字段指定,这两个值来自哪儿呢?

我们的“内存管理”专题中将提到,针对80X86体系,Linux只象征性地使用分段技术,即只是用代码段和数据段。而CPU中的SS寄存器是指向堆栈段的,但是Linux没有使用专门的堆栈段,而是将数据段中的一部分作为堆栈段。所以,当数据段中的CPL字段为3时,SS寄存器就指向该用户数据段中的用户栈;如果数据段中的CPL字段为0时,它就指向内核数据段中的内核栈。注意!这一点很重要,特别是我们以后讲解进程切换的时候,这一个知识你不知道的话,那些内容会让你抓狂的。

除了用户数据段、用户代码段、内核数据段、内核代码段这4个段以外,Linux还使用了其它几个专门的段,下面我们专门来探讨,如图:在单处理器系统中只有一个GDT,而在多处理器系统中每个CPU对应一个GDT。所有的GDT都存放在cpu_gdt_table 数组中,而所有GDT(当初始化gdtr 寄存器时使用)的地址和它们的大小存放在cpu_gdt_descr数组中,这些符号都在文件arch/i386/kernel/head.S中被定义。

我们再把这个知识扩展一下,80x86体系的286以后出现了一个新段,叫做任务状态段(TSS),主要用来保存处理器中各个寄存器的内容。Linux为每个处理器都有一个相应的TSS相关的数据结构,每个TSS相应的线性地址空间都是内核数据段相应线性地址空间的一个小子集。所有的任务状态段都顺序地存放在init_tss数组中;值得特别说明的是,第n个CPU的TSS描述符的Base字段指向init_tss数组的第n个元素。G(粒度)标志被清0,而Limit字段置为0xeb,因为TSS段是236字节长。Type字段置为9或11(可用的32位TSS),且DPL置为0,因为不允许用户态下的进程访问TSS段。

好了,回

  • 2
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 8
    评论
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值