Linux Kernel Mode与User Mode的实质

本文详细解析了Linux操作系统如何在ARM架构上运行,探讨了Kernel Mode与User Mode的区别及其实现原理,包括不同处理器模式间的切换机制。


Linux Kernel Mode与User Mode的实质

     ---Linux 在ARM Architecture上的实现

 

Linux Kernel作为一种操作系统有别与一般的用户程序,即所谓的kernel mode和user mode。Kernel mode下运行的程序能够访问所有的memory,能够处理interrupt;而user mode的程序只能访问有限的memory,不能直接处理中断。那么kernel mode和user mode的这些差别是由谁来决定的呢?归根结底,所有的程序都是CPU的指令,同样的指令有的时候能做某件事,有的时候不能,这都是由指令执行时CPU的Mode决定的。简而言之就是说,执行Kernel指令的时候,CPU处于特权模式,可以访问所有Memory;执行user program的指令时,CPU处于user mode,读写memory受限制;而当Interrupt发生时,因为要操作硬件,所以CPU会从原来工作的Mode切换到特权模式来执行Interrupt handler。下面将详细阐述Linux操作系统是怎样在ARM architecture上运转的。

 

  1. ARM的Processor Mode

ARM有以下7种Processor Mode,当前处于的模式有程序状态寄存器Current Program Status Register (CPSR)的bit4:0决定:

  • User: 用户模式User Mode

  • FIQ : 快速中断Fast Interrupt Request

  • IRQ: 普通中断Normal Interrupt Request

  • SVC: 管理模式Supervisor Mode

  • Abort: 异常模式 Abort Mode

  • Undefined: 未定义模式 Undefined Mode

  • System: 系统模式

其中User Mode是一种受限制的模式,其他6种都是特权模式(Privilege Mode)。

 

这些模式间可以相互切换,切换方法可以归结为以下两种:

 

第一种:由CPU本身硬件完成切换

CPU本身主动进行模式切换是因为在执行指令的时候发生了某种“意外“,这种”意外“就是Exception。一旦Exception发生,CPU就要立即切换到这种Exception相对应的模式去处理该Exception。所有Exception都在Privilege Mode下处理。ARM CPU共有6种Exception,他们发生的条件和发生后进入的CPU模式如下:

 

 

Exception

触发条件

进入CPU模式

Reset

CPU的Reset pin被Assert (CPU Power on时,Reset pin会被Assert)

SVC

Undefined Instruction:

企图执行一条不认识的指令

Undefined

Software Interrupt (SWI)

执行指令SWI

SVC

Prefetch Abort

获取指令失败

Abort

Data Abort

获取数据失败

Abort

IRQ

普通中断发生,CPU的IRQ pin被Assert

IRQ

FIQ

快速中断发生,CPU的FIQ pin被Assert

FIQ

 

请注意Exception和CPU模式中都有IRQ和FIQ,但他们的意义是不同的。前者指一个Exception的事件,后者指一种CPU运行的模式。

 

第二种:程序修改CPSR

某些指令是可以修改CPSR的,在程序中执行这些指令就能切换CPU的模式。但是这些模式两两之间并不是都可以通过修改CPSR来实现切换的,如User Mode下不可以修改CPSR切换到其他Mode。通常从Exception中返回时要修改CPSR,从而恢复到Exception发生时的CPU Mode继续执行。


  1. Linux的Kernel mode和User mode


     Linux Kernel工作在Privilege mode下,它需要处理所有在privilege mode下完成的工作。如IRQFIQ Exception发生时,进入相应的Privilege Mode,由Kernel来处理相应的IRQ, FIQ事件。也就是说,Kernel 对应CPU6Privilege Mode。正是因为privilege mode可以对系统资源“特权“的访问,所以kernel能够控制所有的软硬件资源,因而被称为”操作系统“。



     User mode运行在CPUUser processor mode下,所以在访问memory等方面都有限制,用户的各种Application都运行在User Mode下。User Mode下的程序想要访问只有Privilege Mode才能访问的资源就必须使用系统调用(System Call),进入Kernel Mode来处理。从User Mode进入Priviledge mode的唯一方式是swi指令,这条指令执行会触发Software Interrupt Exception, 从而进入SVC模式。所以System call的实质就是通过swi指令进入Kernel Mode。在kernel中处理完毕后,kernel会将CPU切回到User Mode继续执行,同时将调用结果返回到User Mode

 

下表说明了CPU Mode与Linux Mode之间的对应关系:

 

User

FIQ

IRQ

Supervisor

Abort

Undefined

System

Kernel Mode

 

User Mode

 

 

 

 

 

 

 

  1. CPU Mode切换的场合在Linux中的实现

如 前所述,所有CPU privilege模式下的工作都对应于OS的Kernel mode,那么所谓Linux Kernel mode和User mode之间的切换映射到CPU层面来说就是CPU的User mode怎样切换到其他6种mode,每种privilege mode又怎样切换到User mode的。同时,6中Privilege Mode之间当Exception发生时也会切换。下面我们将对照Linux kernel 2.6.33的代码来逐一分析每种Exception发生时Linux Kernel是怎样处理的。


在ARM Architecture中,Exception发生时指令将从固定的地址开始执行。所以必须在这些地址上放置正确的代码来处理相应的Exception。这些固定的地址也称向量,由CP15 Reg1中的V bit控制,可以在虚拟内存的底部或顶部。注意是虚拟内存,也就是说这些向量的固定地址是虚拟地址,是要经过MMU转换的。各Exception对应的向量地址(虚拟地址)如下:



Low Vector

High Vector

Reset

0

0xFFFF0000

Undefined Instruction:

0x04

0xFFFF0004

Software Interrupt (SWI)

0x08

0xFFFF0008

Prefetch Abort

0x0C

0xFFFF000C

Data Abort

0x10

0xFFFF0010

IRQ

0x18

0xFFFF0018

FIQ

0x1C

0xFFFF001C


在Linux kernel中向量位于高地址,由CONFIG_VECTORS_BASE控制,Default为0xFFFF0000,在__arm926_setup中将CP15 control register 1的V bit置为1(High Vector)。向量表在源代码里位于entry-armv.S中,从__vector_start开始,在FIQ, IRQ等被enable前,必须要copy到0xFFFF0000开始的高地址。Copy的时点在start_kernel ()->setup_arch()->early_trap_init()中。在setup_arch()之后的local_irq_enable()才是真正clear CPSR的I-bit,至此,IRQ才Enable了。

下面逐一讲述各Exception Handler的实现。

  1. Reset

CPU Power on后即触发Reset Exception。Reset发生后,CPU进入SVC Mode, PC被设置为0,即从内存地址0开始执行代码。这是系统启动的开端。对应arm Linux中,arch/arm/kernel/head.S中的“__HEAD"开始即是系统执行的Kernel的第一行代码(2.6.33中是设置CPSR,disable FIQ, IRQ)。用objdump vmlinux也可以看到此行代码是Kernel的第一条指令。详细Linux启动的过程在”Linux Kernel的启动过程“一文中阐述。

在向量表中Reset Exception的向量处是执行swi SYS_ERROR0,这条指令将触发SWI Exception,在vector_swi中通过判断参数,最终将执行到die(),系统停止运行。也就是说,Linux在运行过程中发生Reset Exception,将被认为是系统错误而停止。

  1. 发生IRQ

IRQ Exception发生时将从__vector_irq开始执行。这部分代码稍显晦涩。源代码中__vector_irq这个符号是由__vector_stub这个宏产生的。__vector_stub这个宏实现了IRQ和Abort Exception的共用部分,主要是保存SPSR,然后根据发生Exception时 的Program Mode跳转到相应的处理分支上。处理分支的table是紧跟在_vector_stub之后,可以看到每一种Program mode下发生IRQ对应于table中的一个entry。SVC下发生IRQ的时候就进入__irq_svc,User Mode下发生IRQ的时候就进入__irq_user。关于__irq_usr和__irq_svc中具体是怎样处理IRQ的,详见“Linux中IRQ的处理“一文。

  1. 发生Prefetch Abort

与IRQ一样,发生Prefrech Abort时将跳转到__pabt_usr和_pabt_svc的分支上处理。

  1. 发生Data Abort

同样,发生Data Abort时将跳转到__dabt_usr和_dabt_svc的分支上处理。

  1. 发生Undefined Instruction Abort

同样,发生Undefined InstructionAbort时将跳转到__und_usr和_und_svc的分支上处理。

__irq_usr,__pabt_usr, __dabt_usr, __und_usr都是从User Mode发生Exception进入到相应 Mode,完成相应处理后在返回User Mode,所以在入口处有宏user_entry来处理User Mode->相应exception Mode的模式切换,__irq_usr处理完IRQ之后需要返回User Mode,宏ret_to_user来完成各exception Mode>User Mode的切换。

__irq_svc,__pabt_svc, __dabt_svc, __und_usr都是从SVC Mode发生Exception进入到相应 Mode,完成相应处理后在返回SVC Mode,所以在入口处有宏svc_entry来处理SVC Mode->P相应exception Mode的模式切换,exception handler处理完之后需要返回SVC Mode,宏svc_exit来完成各exception Mode>User Mode的切换。

  1. 发生SWI

     SWI是由swi指令触发的exception,目的是从user mode进入SVC mode。可想而知所有User模式下软件的系统调用(system call)都是通过swi指令进入Kernel模式的。System call的实质就是swi指令。Glibc中包含了一系列System call,如最常用open, read, write, ioctl,每个system call对应一个system call number。每个system call在kernel中都有相对应函数来完成。在__vector_start开始的向量表中SWI exception handle将从vector_swi开始执行。实际vector_swi的代码在entry-common.S中。vector_swi主要做得事情就是根据User Mode执行swi时存在寄存器中的system call number找到kernel中对应的handler routine,并跳转到该routine执行。System call带有参数,具体这些参数怎样存放在寄存器中由Application Binary Interface的版本规定。所以在vector_swi中可以看到很多OABI,EABI的#ifdef判断。vector_swi最重要的地方是根据system call number (scno)跳转的相应的handler routine执行。Handler routine的table在calls.S中,可以看到handler routine都是以sys_开头的,总共有360多个system call。sys_read对应于User Mode下的系统调用函数read,sys_socket对应于socket。执行完handler routine后,通过ret_fast_syscall返回User Mode。这里还有一点要说明,在User ModeSystem call是通过swi指令进入Kernel,在Kernel里同样也可以执行swi指令触发SWI Exception,同样也是从vector_swi开始执行。这种情况下会跳转到arm_syscall,这是Kernel内部用于对arm进行某些操作,如cache flush等。

  1. 发生FIQ

    向量表上FIQ exception将跳转到vector_fiq,但vector_fiq实际只是简单的返回了。应该可以根据需要在这里添加代码来处理FIQ

<think>嗯,用户问的是内核模式(Kernel Mode)和用户模式(User Mode)的区别及联系。我之前回答过关于malloc为什么会导致切换到内核模式的问题,里面提到了内存管理和系统调用的部分。现在需要更全面地比较这两者的区别,同时还要说明它们的联系。 首先,我需要回顾之前的引用资料,尤其是引用[2]和[3],里面提到内核模式有完全访问硬件的权限,而用户模式需要通过系统API来访问。用户模式下的崩溃可以恢复,内核模式的崩溃会导致系统崩溃。另外,CPU的保护环概念,比如x86的四个环,通常只用0环(内核)和3环(用户)。 然后,用户可能想知道权限、安全性、资源访问、稳定性等方面的区别。需要分点列出,结构清晰。同时,联系部分可能需要提到系统调用、异常处理、中断这些机制如何让两者协同工作。 还要注意是否需要举例,比如系统调用的过程,或者CAS操作不涉及切换这样的例子。之前的回答提到CAS不涉及切换,这里可以引用作为例子,说明用户态和内核态切换的具体情况。 可能还需要解释为什么需要两种模式,主要是为了保护系统安全和稳定性,防止用户程序直接操作硬件导致系统崩溃。联系部分可以强调操作系统通过这两种模式实现资源管理和隔离,用户程序通过系统调用请求内核服务。 需要确保回答符合用户的要求,结构清晰,分点说明区别,再讲联系,最后可能总结或给出应用场景。同时,检查是否有遗漏的重要点,比如中断处理、异常事件等触发切换的情况,是否在区别或联系中提及。 另外,用户提供的引用中有提到应用程序切换到内核模式的三种情况:系统调用、异常、设备中断。这些可以作为切换机制的实例,补充在联系部分,说明两者如何通过特定机制互动。 最后,生成相关问题时要围绕主题,比如切换机制、设计原因、实际应用中的例子等,帮助用户进一步理解。</think>#### 一、内核模式(Kernel Mode用户模式(User Mode)的核心区别 1. **权限级别** - **内核模式**: - 运行在 CPU 的 **最高特权级**(如 x86 的 Ring 0),可直接执行所有指令(包括特权指令),无限制访问硬件(如内存、I/O 端口、设备控制器)[^2][^3]。 - 例如:直接修改页表、操作中断控制器、分配物理内存。 - **用户模式**: - 运行在 **最低特权级**(如 x86 的 Ring 3),禁止执行特权指令(如 `in/out` 访问端口、`cli` 关闭中断),只能通过系统调用(如 `read`, `write`)间接访问硬件[^2]。 2. **内存访问** - **内核模式**: - 可访问 **全部物理内存和内核地址空间**(如内核数据结构、设备映射内存)。 - 例如:内核可以直接读写进程的页表或调度队列。 - **用户模式**: - 仅能访问 **进程自身的虚拟地址空间**,且受内存保护机制限制(如页表权限位、ASLR)[^2]。 - 例如:用户程序无法直接访问其他进程的内存或内核代码。 3. **稳定性安全性** - **内核模式**: - 代码错误可能导致 **系统崩溃**(如内核 panic、Windows 蓝屏),因其直接操作硬件和关键数据结构[^3]。 - **用户模式**: - 代码错误通常仅导致 **进程崩溃**(如段错误),操作系统可通过终止进程恢复系统稳定。 4. **典型应用场景** - **内核模式**:操作系统核心功能(进程调度、内存管理、设备驱动)、系统调用实现。 - **用户模式**:普通应用程序(浏览器、文本编辑器)、用户态服务(如数据库、Web 服务器)。 --- #### 二、内核模式用户模式的联系 1. **协作机制** - **系统调用**:用户程序通过 `int 0x80`(x86)或 `syscall` 指令触发软中断,切换到内核模式执行特权操作(如文件读写、网络通信)后返回用户模式。 ```plaintext 用户程序 → 调用 open() → 触发系统调用 → 切换至内核模式 → 内核处理文件打开 → 返回结果 → 恢复用户模式 ``` - **中断异常**: - **硬件中断**(如键盘输入、定时器):强制切换到内核模式处理中断服务程序(ISR)。 - **异常**(如除零错误、缺页异常):由内核接管处理(终止进程或修复状态)[^2]。 2. **资源共享** - 内核通过 **虚拟内存机制** 为用户进程提供独立的地址空间,同时管理物理内存的全局分配。 - 用户程序通过 **共享内存、管道、信号量** 等 IPC 机制内核或其他进程交互,但需内核协调权限。 3. **性能隔离的权衡** - 用户模式通过 **权限隔离** 提升系统安全性,但系统调用涉及上下文切换(保存寄存器、切换页表),带来性能开销。 - 内核通过 **缓冲区管理**(如网络协议栈)和 **延迟处理**(如 write-back 磁盘缓存)优化用户模式硬件的交互效率。 --- #### 三、关键设计原因实例 1. **为何需要两种模式?** - **安全**:防止用户程序错误或恶意操作破坏系统(如直接修改中断向量表)。 - **稳定性**:隔离用户程序崩溃的影响,保证操作系统核心功能持续运行。 - **抽象硬件**:通过系统调用提供统一的硬件访问接口,简化应用程序开发。 2. **实例分析** - **CAS(Compare-and-Swap)操作**: - 在 x86 架构中,通过 `lock cmpxchg` 指令实现原子操作,无需切换到内核模式[^2]。 - 用户模式直接使用 CPU 指令完成原子更新,避免了系统调用开销。 - **内存分配**: - `malloc` 在用户模式管理空闲内存块,仅当堆空间不足时通过 `brk`/`mmap` 系统调用进入内核模式扩展内存[^1]。 --- #### 四、总结对比表 | **特性** | **内核模式** | **用户模式** | |------------------|---------------------------------------|---------------------------------------| | 特权级别 | Ring 0(最高) | Ring 3(最低) | | 硬件访问 | 直接访问 | 需通过系统调用 | | 内存范围 | 全部物理内存 + 内核空间 | 进程虚拟地址空间 | | 崩溃影响 | 系统崩溃 | 进程终止 | | 典型代码 | 操作系统内核、驱动程序 | 应用程序、用户态服务 | --- #### 相关问题 1. 系统调用过程中,CPU 如何实现从用户模式到内核模式的切换? 2. 用户模式能否绕过系统调用直接操作硬件?为什么? 3. 为什么现代 CPU 设计多个特权级(如 Ring 0-3),但操作系统通常只使用 0 和 3 环?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值