ARM Linux系统中的用户栈与内核栈

发布者:qazwsx007最新更新时间:2016-06-22 来源: eefocus关键字:ARM  Linux系统  用户栈  内核栈 手机看文章 扫描二维码
随时随地手机看文章
在Linux系统上,一个进程有两种不同的栈,一种是用户栈,另一种是内核栈。

用户栈

用户栈就是应用程序直接使用的栈。如下图所示,它位于应用程序的用户进程空间的最顶端。

当用户程序逐级调用函数时,用户栈从高地址向低地址方向扩展,每次增加一个栈帧,一个栈帧中存放的是函数的参数、返回地址和局部变量等,所以栈帧的长度是不定的。

用户栈的栈底靠近进程空间的上边缘,但一般不会刚好对齐到边缘,出于安全考虑,会在栈底与进程上边缘之间插入一段随机大小的隔离区。这样,程序在每次运行时,栈的位置都不同,这样黑客就不大容易利用基于栈的安全漏洞来实施攻击。

用户栈的伸缩对于应用程序来说是透明的,应用程序不需要自己去管理栈,这是操作系统提供的功能。应用程序在刚刚启动的时候(由fork()系统调用复制出新的进程),新的进程其实并不占有任何栈的空间。当应用程序中调用了函数需要压栈时,会触发一个page fault,内核在处理这个异常里会发现进程需要新的栈空间,于是建立新的VMA并映射内存给用户栈。

内核栈

内核栈对于应用程序是不可见的,因为它位于内核空间中。在应用程序执行过程中,如果发生异常、中断或系统调用的话,应用程序会被暂停,系统进入内核态,转去执行异常响应等代码,这个时候所使用的栈就是内核栈。

与用户栈相比,内核栈的尺寸要小得多。在32位Linux系统上,用户栈最多可以扩展到64M,但内核栈最多也只有8K字节,而且有时为了提高内存利用率还常常把内核栈配置成4K。其实即使是只有4K,在绝大多数情况下也仍然是够用的,因为这里只是给内核代码使用的,栈不会很大。

每个进程在内核空间中都拥有一个对应的内核栈,而且这个栈是在进程fork的时候就预留出来的。以下是创建内核栈的代码(Kernel 2.6.35 版本):

   [c]

static struct task_struct *dup_task_struct(struct task_struct *orig)
{
 struct task_struct *tsk;
 struct thread_info *ti;
 ......
 ti = alloc_thread_info(tsk);
 ......
 tsk->stack = ti;
 ......
}

[/c] 

内核栈的结构比较精巧,内核使用一个联合体来定义内核栈:

   [c]

union thread_union {
 struct thread_info thread_info;
 unsigned long stack[THREAD_SIZE/sizeof(long)];
};

[/c] 

其中thread_info中存放了进程/线程(内核不大区分进程与线程)的一些数据,其中包括指向task_struct结构的指针。数组stack即内核栈,stack占据8K/4K(依配置不同)空间,是这个联合体的主要部分。

这样,一个实际的内核栈的结构将如下图所示。由于栈总是由高地址向低地址延伸的,所以栈底位于thread_union联合体的最末端,而thread_info结构则位于thread_union联合体的开始处,而且所占用的空间比较少。只要不出现内核栈特别大的极端情况,栈与thread_info可以互不干扰。

为什么要设计成这样的结构呢?原因就在于,使用这种结构可以在系统进入内核态时很方便地取得当前进程的信息。如果不用这种方式的话,取得task_struct将是一个比较麻烦的事情。

不管系统因为什么原因进入内核态,最后都要切换到SVC模式做主要的异常处理。在进入SVC模式时,SP/R13寄存器所指向的位置就正好是当前进程的内核栈。通过简单的对齐操作,就可以拿到thread_union即thread_info结构的指针,从中又可以得到最重要的task_struct的指针,这个进程的所有信息就都有了。

以下两个函数即分别用于从SP寄存器取得当前进程的thread_info,以及进一步取得task_struct结构的内容。

   [c]

static inline struct thread_info *current_thread_info(void)
{
 register unsigned long sp asm ("sp");
 return (struct thread_info *)(sp & ~(THREAD_SIZE - 1));
}

static inline struct task_struct *get_current(void)
{
 return current_thread_info()->task;
}

[/c] 

关于SP寄存器,这里有一个问题值得澄清一下,前面提到“在进入SVC模式时,SP/R13寄存器所指向的位置就正好是当前进程的内核栈”,原因是什么呢?

在当前进程即当前被异常或中断所暂停的这个进程,是在上一次发生进程调度(schedule())的时候被调入的,当时在“上下文切换”(context_switch())完成的时候,当前这个进程可以说已经被调入了CPU,系统当时所处的模式也是SVC模式。当进程高度完成,CPU从SVC模式切换到USR模式时候,SVC模式下的SP寄存器已经指向了当前进程的内核栈。所以当再次切换到SVC模式时,进程还是这个进程,SP也还是指向这个内核栈。

其实ARM处理器的每一种模式下都有自己独立的SP/R13寄存器。当CPU在不同的模式间切换的时候所看到的寄存器内容都是不同的。Linux对于各种模式的使用策略是:SVC和USR两种模式是可以稳定工作的模式;在其它的模式下都是不稳定的,会尽快切换到稳定的模式去工作。在SVC模式下,SP寄存器总是指向内核栈;在USR模式下,SP寄存器总是指向用户栈;那么,其它模式下,SP又指向哪里呢?

其它模式下,Linux对于SP寄存器的维护很简单。在系统启动阶段,cpu_init()函数会被调用,其中有对其它模式下SP寄存器的初始化操作:

   [c]

struct stack {
 u32 irq[3];
 u32 abt[3];
 u32 und[3];
} ____cacheline_aligned;

static struct stack stacks[NR_CPUS];

void cpu_init(void)
{
 unsigned int cpu = smp_processor_id();
 struct stack *stk = &stacks[cpu];


 __asm__ (
 "msr cpsr_c, %1\n\t"
 "add r14, %0, %2\n\t"
 "mov sp, r14\n\t"
 "msr cpsr_c, %3\n\t"
 "add r14, %0, %4\n\t"
 "mov sp, r14\n\t"
 "msr cpsr_c, %5\n\t"
 "add r14, %0, %6\n\t"
 "mov sp, r14\n\t"
 "msr cpsr_c, %7"
 :
 : "r" (stk),
 PLC (PSR_F_BIT | PSR_I_BIT | IRQ_MODE),
 "I" (offsetof(struct stack, irq[0])),
 PLC (PSR_F_BIT | PSR_I_BIT | ABT_MODE),
 "I" (offsetof(struct stack, abt[0])),
 PLC (PSR_F_BIT | PSR_I_BIT | UND_MODE),
 "I" (offsetof(struct stack, und[0])),
 PLC (PSR_F_BIT | PSR_I_BIT | SVC_MODE)
 : "r14");
}

[/c] 

可以看到,Linux为IRQ\ABT\UND3种模式的SP寄存器指定了相应的栈,但是这个栈很小很小,只有12个字节。但这已经足够了,在中断处理最初那部分相应模式的代码vector_XXX中,linux只保存r0, lr和spsr三个32位的数据,这正好需要12个字节。

关键字:ARM  Linux系统  用户栈  内核栈 引用地址:ARM Linux系统中的用户栈与内核栈

上一篇:ARM Linux (S3C6410架构/2.6.35内核)的内存映射(四)
下一篇:ARM Linux (S3C6410架构/2.6.35内核)的内存映射(三)

推荐阅读最新更新时间:2024-03-16 14:58

Arm架构是如何一步步成为全球计算的基石?
以前,提起Arm,人们更多想到的是手机和嵌入式,然而自从2018年开始,Arm宣布推出Neoverse并进军高性能计算市场,至今已过去了四年,如今Arm架构的基础设施已经成为了一个明显趋势,正如Arm 首席执行官 Rene Haas所说:“目前世界上所有主要的公有云服务提供商现在都在使用 Arm 架构。” 盘点2022年Arm Neoverse的里程碑 Arm高级副总裁兼基础设施事业部总经理 Chris Bergey 盘点了2022年Arm Neoverse的重要事件,其中包括: 在全球范围内,Arm 现已被用于各个主要公有云,包括 AWS、微软、谷歌、阿里巴巴、甲骨文等科技巨头。值得一提的是AWS,在一个月前,亚马
[网络通信]
<font color='red'>Arm</font>架构是如何一步步成为全球计算的基石?
ARM Cortex-M3的SRAM单元故障软件的自检测研究
   引言   目前,对于存储单元SRAM的研究都是基于硬件电路来完成,而且这些方法都是运用在生产过程中,但是生产过程并不能完全杜绝SRAM的硬件故障。在其使用过程中,如果SRAM硬件出错,将导致程序出错而且很难被发现。因此在运用的阶段,为防止存储单元损坏而导致系统出错,通过软件的方式对SRAM进行检测是必要的。   1 SRAM运行状态分析   SRAM是存储非CONSTANT变量(如RW),它具有掉电即失的特点。由Cortex—M3的启动步骤可知,系统上电后,首先执行复位的5个步骤:   ①NVIC复位,控制内核;   ②NVIC从复位中释放内核;   ③内核配置堆栈;   ④内核设置PC和LR;   ⑤运行
[单片机]
<font color='red'>ARM</font> Cortex-M3的SRAM单元故障软件的自检测研究
在消费领域,Arm瞄向了这四大领域
近日,在Arm Tech Day 2019上,Arm市场营销资深副总裁Ian Smythe做了题为《从小屏到大屏,Arm全面提升高端消费体验》的主题演讲。 盘点一年间Arm的创新产品 Ian表示,为了帮助合作伙伴交付完美的方案,Arm在近一年的时间内做了多项全新举措,聚焦消费者体验。 其中包括发布Cortex-A75、Cortex-A72以及全新的DynamIQ技术架构,为消费者提供最佳的计算平台。此外,在如火如荼的AI领域,Arm则发布了Project Trilium 以及OD目标检测IP,实现边缘计算与机器学习的高级别结合。 而在GPU领域,Arm为主流市场发布了Mali-G72,结合了机器学习,增强了移动设备设备的VR体
[手机便携]
在消费领域,<font color='red'>Arm</font>瞄向了这四大领域
IAR Embedded Workbench for Arm 9.40版本通过集成PACBTI来提升代码安全性
IAR Embedded Workbench 9.40版本引入了与指针验证和分支目标识别(PACBTI)扩展的无缝兼容性,保护嵌入式应用程序免受各种安全攻击。 瑞典乌普萨拉–2023年6月7日-嵌入式软件和服务的全球领导者IAR发布了备受欢迎的IAR Embedded Workbench for Arm v9.40版本,最新版本引入了针对代码安全的 增强功能: 添加了针对Armv8.1-M专用的指针验证和分支目标识别(PACBTI)扩展。 通过PACBTI,用户应用程序可以通过加密签名来增强防护,有效防止攻击者控制整个系统。新版本还提供了更强大、更智能的IDE Build Actions,可为软件工程师带来更好的开发体验。
[嵌入式]
概述十一种基于ARM的嵌入式操作系统
  嵌入式操作系统(Embedded OperaTIon System,EOS)是指用于嵌入式系统的操作系统。嵌入式系统分为4层,硬件层、驱动层、操作系统层和应用层。嵌入式操作系统是负责嵌入式系统的全部软、硬件资源的分配、任务调度,控制、协调并发活动。它必须体现其所在系统的特征,能够通过装卸某些模块来达到系统所要求的功能,是一种用途广泛的系统软件。   嵌入式LINUX   嵌入式Linux 是将日益流行的Linux操作系统进行裁剪修改,使之能在嵌入式计算机系统上运行的一种操作系统。Linux做嵌入式的优势,首先,Linux是开放源代码;其次,Linux的内核小、效率高,可以定制,其系统内核最小只有约134KB;第三,Linu
[单片机]
ARM7串口9位方式多机通信的编程技术
   1 主从式多机通信   所谓主从式多机系统,即在数个ARM(或单片机)中,有一个是主机,其余的为从机。从机要服从主机的调度、支配,其拓扑结构如图1所示。   主机信息可以发到各个从机,从机发送的信息只能被主机接收,从机之间不进行通信。   51单片机串口不同寻常的特征是包括第9位方式(在串口模式2和模式3下)。它允许把在串行口通信增加的第9位用于标志特殊字节的接收。一般约定第9位为高时表示该字节为地址字节,第9位为低时为数据字节。第9位方式允许接收单片机信息,仅当字节具有一个第9位时才能被中断。用这种方式,主机首先广播1字节,并让其第9位为高,同时收到该字节的各个从机,只有地址相符的打开,以接收后面的数据字节。所接
[单片机]
arm Linux系统启动之start_kernel函数
head-common.S ---具体做了哪些动作 ---跳转到init/main.c ---b start_kernel //关于start_kernel的强文深入理解linux内核,第八章 main.c asmlinkage void __init start_kernel(void) { char * command_line; extern struct kernel_param __start___param , __stop___param ; //来设置smp process id,当然目前看到的代码里面这里是空的 smp_setup_processor_id(); /* * Need to r
[单片机]
首届“ST-EMBEST杯”嵌入式电子设计大赛正式拉开帷幕
中国 – 全球知名的半导体供应商意法半导体公司与深圳市英蓓特信息技术有限公司主办的 2006 年首届“ ST-EMBEST 杯”嵌入式电子设计大赛”在全国范围内正式拉开帷幕。主办双方希望通过本次嵌入式设计大赛,搭建一个便于沟通的嵌入式设计平台,为广大电子设计工程师和 ARM 爱好者提供嵌入式方案设计的竞技和学习机会,从而提高国内嵌入式技术的整体设计应用水平。同时,大赛还得到 ARM 中国,中国微机学会嵌入式系统分会及北京航天航空大学出版社等相关机构的鼎力支持。 本次大赛参赛作品主要是基于意法半导体公司的 STR7 和 STR9 进行应用设计开发,英蓓特公司将免费提供开发工具。大赛欢迎嵌入式领域的企业或研究所的软、
[焦点新闻]
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved