深入理解linux下用户态与核心态切换

Linux将内核程序和基于之上的用户程序分开处理,分别运行在用户态和核心态。以32位x86架构为例,虚拟空间共4G,高地址的1G为系统程序运行的核心栈,低地址的3G空间为用户程序运行的用户栈。

如果一个用户程序需要调用底层的系统接口,比如printf, malloc等等诸如libc里面的系统调用函数,那么就需要牵涉到用户态与核心态的两个栈切换问题。所有的系统调用函数都是运行在核心态。

在系统调用时,由于用户态和核心态是运行两个独立栈上面,所以我们不能仅仅简单的传递函数指针,因为对于核心态空间用户态是不可见的,所以系统调用函数指针对于用户态不可见;另外一个问题是参数传递,由于两个栈之前独立运行的,所以不能用普通的压栈出栈的形式进行参数传递。

所以我们需要分别就1) 系统调用函数的名称转换;2) 系统调用函数的参数传递;两个方面来讨论。

每一个系统调用函数在内核当中都有对应的句柄处理函数,一般以sys_开头,比如sys_restart_syscall, sys_getpid等等,这些句柄函数作为一个“系统调用表”以汇编语言文件形式存在,位于./arch/x86/kernel/syscall_table_32.S,部分内容如下:

1 ENTRY(sys_call_table)
/* 0 - old "setup()" system call, used for restarting */
2 .long sys_restart_syscall
3 .long sys_exit
4 .long ptregs_fork
5 .long sys_read
6 .long sys_write
7 .long sys_open /* 5 */
8 .long sys_close
9 .long sys_waitpid
10 .long sys_creat
11 .long sys_link
12 .long sys_unlink /* 10 */
...
335 .long sys_preadv
336 .long sys_pwritev
337 .long sys_rt_tgsigqueueinfo /* 335 */
338 .long sys_perf_counter_open


每一个系统调用的函数对应着内核里的具体实现,每一个系统函数都有一个对应的数字对应,这个数字事实上是系统调用函数指针的偏移。

当我们调用一个系统函数时,运行时库通过查找这个表来决定对应的函数代码,然后存入到寄存器中,然后当切换到到核心态后,内核同样也是根绝这个函数代码来查找到对应的系统函数名称,从而找到对应的代码入口地址。系统调用切换过程如图所示:




参数的传递

由于当我们调用一个系统函数时候大多数都需要传递参数,但是这些参数传递并不能直接压栈传递,因为用户态和核心态运行在两个独立的栈上面!为了解决这个问题,系统调用函数借助寄存器来传递参数,由于寄存器数量的限制,所以规定参数不超过六个(也有些系统是规定不超过五个,这个和平台相关)。如果参数超过六个,我们可以用其他方法,比如packing方法将有些参数压缩,放在一个结构体里从而传递结构体的指针等方法来进行传递。下面的函数就是x86运行时库用来进行用户态与核心态进行切换时候的函数,(需要注意的是不同的平台的这个系统调用切换函数是不一样的):

http://cristi.indefero.net/p/uClibc-cristi/source/tree/9a2837c77c664d32a1fc9860cb193f25e0f3f37e/libc/sysdeps/linux/i386/syscall.S


/* syscall.S */
1 .text
2 .global syscall
3 .type syscall,%function
4 syscall:
5 pushl %ebp
6 pushl %edi
7 pushl %esi
8 pushl %ebx
9
10 movl 44(%esp),%ebp /* Load the 6 syscall argument registers */
11 movl 40(%esp),%edi
12 movl 36(%esp),%esi
13 movl 32(%esp),%edx
14 movl 28(%esp),%ecx
15 movl 24(%esp),%ebx
16 movl 20(%esp),%eax /* Load syscall number into %eax. */
17 int $0x80
18
19 popl %ebx
20 popl %esi
21 popl %edi
22 popl %ebp
23
24 cmpl $-4095,%eax
25 jae __syscall_error
26 ret /* Return to caller. */
27
28 .size syscall,.-syscall

从代码中可以看出来运行时库首先保存了四个寄存器(5-8行),然后将需要传递的参数(用户栈上)存到六个寄存器中(10-15行),然后将已经查找到的系统函数代码保存到EAX寄存器中(16行),然后通过int $0x80软中断(x86固定的系统调用中断代码)切换到核心态(17行),在核心态运行结束后,核心态返回采用类似的过程切换到用户态,然后恢复保存的寄存器(19-22行)。最后通过比较保存在EAX寄存器里的函数调用返回值来决定是不是调用出错,如果出错则调用出错处理;如果没有出错,则RET返回到原调用函数处。

但是这里有一点不明白的是为什么寄存器保存是四个而不是六个或更多?

Note:

EAX, EBX, ECX, EDX, ESI, EDI, EBP, ESP等是x86 汇编语言中CPU上的通用寄存器的名称,32位的寄存器。这些32位寄存器有多种用途,但每一个都有特定的用处。

EAX 是"累加器"(accumulator), 它是很多加法乘法指令的缺省寄存器。同时在函数调用的时候往往保存的是返回值的结果,如果有多个返回值,那么保存的是返回值结果的指针。

EBX 是"基地址"(base)寄存器, 在内存寻址时存放基地址。

ECX 是计数器(counter), 是重复(REP)前缀指令和LOOP指令的内定计数器。

EDX 则总是被用来放整数除法产生的余数。

ESI/EDI分别叫做"源/目标索引寄存器"(source/destination index),因为在很多字符串操作指令中, DS:ESI指向源串,而ES:EDI指向目标串.

EBP是"基址指针"(BASE POINTER)。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Linux的root是指系统管理员账户,拥有最高的权限,可以对系统进行任何操作,包括修改系统配置、安装软件、删除文件等。在Linux系统中,root账户是非常重要的,但也需要谨慎使用,避免误操作导致系统崩溃或数据丢失。 ### 回答2: Linux的root是指系统中的超级用户,拥有最高的权限和控制权。在Linux系统中,只有root用户才能对系统进行一些特殊的操作和配置,例如安装软件、修改系统设置、管理用户账户等等。 与普通用户相比,root用户不受任何限制,可以访问系统中的所有文件和目录,甚至是系统心文件。这也就意味着,root用户可以对系统进行修改和删除文件,对系统进行危险性操作。因此,使用root权限需要极高的谨慎和责任心。 在Linux系统中,默认情况下是禁止root用户登录的。通常情况下,只有在进行一些需要root权限的重要任务时,才会切换到root用户。一般而言,我们更常使用sudo命令,通过使用sudo命令,普通用户可以有临时获得root权限,以执行需要root权限的命令,但同时也有记录和权限控制的功能。 值得特别注意的是,不应该滥用root权限。因为误操作或者恶意操作可能会导致系统的不稳定性甚至崩溃,对系统安全造成严重威胁。因此,我们应该始终保持警惕,避免滥用root权限。 作为Linux系统的管理员,我们需要深入了解root权限的使用方法和注意事项,仅在必要的时候使用root权限,合理进行权限管理,以确保系统的安全和稳定运行。 ### 回答3: Linux的root是指Linux操作系统中的超级用户,也可以称为系统管理员。root拥有Linux系统中最高的权限,拥有对系统进行全面控制和管理的能力。 root账户具有许多特权和权限,包括对系统的绝对控制、修改系统心文件的权限、创建和删除用户账户、安装和删除软件包、更改系统设置、访问系统日志等。由于这些权限的特殊性,root账户的使用需要谨慎,并且需要谨慎保护账户的安全。 在Linux系统中,root账户通常是在系统安装过程中创建的,默认情况下,只有root账户拥有完全管理员权限。使用root账户登录系统时,可以执行一些需要超级用户权限的操作,并且不受其他限制。 然而,由于root账户的特殊权限,滥用root账户可能导致系统不稳定、数据丢失、潜在的安全风险等问题。因此,在日常使用Linux系统时,应该避免直接使用root账户,而是使用普通用户账户登录系统,并使用sudo命令在需要时获取临时超级用户权限。 总而言之,root是Linux系统中的超级用户,拥有最高的权限和控制能力。使用root账户需要谨慎,应该注意保护账户的安全,并在正常情况下使用普通用户账户完成日常操作。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值