2019最新《尚硅谷SpringBoot项目实战(内含Docker)附代码+笔记+课件》

命令(输入 m 获取帮助):g
 
已创建新的 GPT 磁盘标签(GUID: 3A60FB68-50C7-FC43-A633-6F603EA6DE7A)。
The old gpt signature will be removed by a write command.
 
命令(输入 m 获取帮助):n
分区号 (1-128, 默认  1): 
第一个扇区 (2048-100663262, 默认 2048): 
Last sector, +/-sectors or +/-size{K,M,G,T,P} (2048-100663262, 默认 100663262): +512M
 
命令(输入 m 获取帮助):n
分区号 (2-128, 默认  2): 
第一个扇区 (1050624-100663262, 默认 1050624): 
Last sector, +/-sectors or +/-size{K,M,G,T,P} (1050624-100663262, 默认 100663262): +24G
 
创建了一个新分区 2,类型为“Linux filesystem”,大小为 24 GiB。
 
命令(输入 m 获取帮助):l
  1 EFI System                     C12A7328-F81F-11D2-BA4B-00A0C93EC93B
 19 Linux swap                     0657FD6D-A4AB-43C4-84E5-0933C84B4F4F
 20 Linux filesystem               0FC63DAF-8483-4772-8E79-3D69D8477DE4
 
命令(输入 m 获取帮助):t
分区号 (1,2, 默认  2): 1
分区类型(输入 L 列出所有类型):1
 
已将分区“Linux filesystem”的类型更改为“EFI System”。
 
命令(输入 m 获取帮助):w
帮助:
 
  DOS (MBR)
   a   开关 可启动 标志
   b   编辑嵌套的 BSD 磁盘标签
   c   开关 dos 兼容性标志
 
  GPT
   M   进入 保护/混合 MBR
 
  常规
   d   删除分区
   F   列出未分区的空闲区
   l   列出已知分区类型
   n   添加新分区
   p   打印分区表
   t   更改分区类型
   v   检查分区表
   i   打印某个分区的相关信息
 
  杂项
   m   打印此菜单
   x   更多功能(仅限专业人员)
 
  脚本
   I   从 sfdisk 脚本文件加载磁盘布局
   O   将磁盘布局转储为 sfdisk 脚本文件
 
  保存并退出
   w   将分区表写入磁盘并退出
   q   退出而不保存更改
 
  新建空磁盘标签
   g   新建一份 GPT 分区表
   G   新建一份空 GPT (IRIX) 分区表
   o   新建一份的空 DOS 分区表
   s   新建一份空 Sun 分区表
#国内非官方用户仓库,两个可以任选或都开
[archlinuxcn] 
SigLevel = Optional TrustedOnly
Server = https://mirrors.tuna.tsinghua.edu.cn/archlinuxcn/$arch
#Server = http://repo.archlinuxcn.org/$arch
 
 
#infinality是一个字体美化软件源,包含了美化过的大量字体(已更换清华源旧源不能用)
[infinality-bundle] 
Server = https://mirrors.tuna.tsinghua.edu.cn/infinality-bundle/$arch
[infinality-bundle-multilib]
Server = https://mirrors.tuna.tsinghua.edu.cn/infinality-bundle/multilib/$arch
[infinality-bundle-fonts]
Server = https://mirrors.tuna.tsinghua.edu.cn/infinality-bundle/fonts/
 
 
#antergos linux是一个基于arch linux的衍生版系统,它的软件源包含了很多常用但是未包含在arch官方源中的工具和软件。
[antergos]
#SigLevel = PackageRequired
SigLevel = TrustAll
Usage = All
Server = http://mirrors.antergos.com/$repo/$arch

Executes a fast call to a level 0 system procedure or routine. SYSENTER is a companion instruction to SYSEXIT. The instruction is optimized to provide the maximum performance for system calls from user code running at privilege level 3 to operating system or executive procedures running at privilege level 0.

When executed in IA-32e mode, the SYSENTER instruction transitions the logical processor to 64-bit mode; otherwise, the logical processor remains in protected mode.

Prior to executing the SYSENTER instruction, software must specify the privilege level 0 code segment and code entry point, and the privilege level 0 stack segment and stack pointer by writing values to the following MSRs:

• IA32_SYSENTER_CS (MSR address 174H) — The lower 16 bits of this MSR are the segment selector for the privilege level 0 code segment. This value is also used to determine the segment selector of the privilege level 0 stack segment (see the Operation section). This value cannot indicate a null selector.

• IA32_SYSENTER_EIP (MSR address 176H) — The value of this MSR is loaded into RIP (thus, this value references the first instruction of the selected operating procedure or routine). In protected mode, only bits 31:0 are loaded.

• IA32_SYSENTER_ESP (MSR address 175H) — The value of this MSR is loaded into RSP (thus, this value contains the stack pointer for the privilege level 0 stack). This value cannot represent a non-canonical address. In protected mode, only bits 31:0 are loaded.

These MSRs can be read from and written to using RDMSR/WRMSR. The WRMSR instruction ensures that the IA32_SYSENTER_EIP and IA32_SYSENTER_ESP MSRs always contain canonical addresses.

主要信息有:

  (1)sysenter与sysexit指令配套,可以以比较高的执行效率在用户态执行要在系统态执行的系统调用。

  (2)在IA-32e模式下执行时,sysenter指令将逻辑处理器转换为64位模式,否则逻辑处理器保持在保护模式。

  (3)执行sysenter指令之前,需要将下列MSR(Model Specific Registers)中写入值来指定Ring0代码段、代码入口点、Ring0堆栈段和堆栈指针:

    - IA32_SYSENTER_CS(174H):指定要执行Ring0代码的代码段选择符,也能得出目标Ring0所用堆栈段的段选择符

    - IA32_SYSENTER_EIP(176H):指定要执行的Ring0代码的起始地址

    - IA32_SYSENTER_ESP(175H):指定要执行的Ring0代码所使用的栈指针

  (4)使用rdmsr/wrmsr读取和写入MSR

下面基于linux-2.6.39内核进行分析:

3.1 系统调用初始化

  从linux内核启动流程入手:start_kernel() -> chenk_bugs() -> identify_boot_cpu() -> sysenter_setup() & enable_sep_cpu()

3.1.1 页面初始化和映射

首先执行sysenter_setup()函数来支持之前提到的vDSO机制,

将vdso32-sysenter.so动态链接库装载进vsyscall页中,在arch/x86/vdso/vdso32-setup.c可以找到sysenter_setup()函数:

    

 

int __init sysenter_setup(void)

{   

    void *syscall_page = (void *)get_zeroed_page(GFP_ATOMIC);    

    const void *vsyscall;    

    size_t vsyscall_len;

   vdso32_pages[0] = virt_to_page(syscall_page);

#ifdef CONFIG_X86_32

   gate_vma_init();

#endif

   if (vdso32_syscall()) {

        vsyscall = &vdso32_syscall_start;

        vsyscall_len = &vdso32_syscall_end - &vdso32_syscall_start;    } else if (vdso32_sysenter()) {         vsyscall = &vdso32_sysenter_start;         vsyscall_len = &vdso32_sysenter_end - &vdso32_sysenter_start;    } else {         vsyscall = &vdso32_int80_start;         vsyscall_len = &vdso32_int80_end - &vdso32_int80_start;

   }    

    memcpy(syscall_page, vsyscall, vsyscall_len);

    relocate_vdso(syscall_page);  

    return 0;

}

sysenter_setup()函数的主要工作:

  (1)调用get_zeroed_page()获得一个被填充为0的物理页,返回该页在内核地址空间的线性地址。

  (2)调用宏virt_to_page得到syscall_page地址对应的page管理结构地址并赋值给vdso32_page[0]。

  (3)随后判断支持哪些指令,从而做不同处理,可以看到优先级是syscall > sysenter > int80。

  (4)将vdso32_sysenter_start地址赋给vsyscall,然后用memcpy()将vsyscall拷贝到对应的页,最后用relocate_vdso()进行重定向。

arch/x86/vdso/vdso32.S中可以看到vdso32_sysenter_start就是vdso32-sysenter.so:

 

vdso32_sysenter_start:

    incbin "arch/x86/vdso/vdso32-sysenter.so"

  即将vdso32-sysenter.so拷贝到对应的页中,在《系统调用分析(1)》的vDSO介绍中提到的arch_setup_additional_pages函数便是把拷贝到的页的内容映射到用户空间。

3.1.2 相关MSR寄存器的初始化

  在arch/x86/vdso/vdso32-setup.c中的enable_sep_cpu()函数完成相关MSR寄存器的初始化:

 

void enable_sep_cpu(void)

{    

    int cpu = get_cpu();    

    struct tss_struct *tss = &per_cpu(init_tss, cpu);

       if (!boot_cpu_has(X86_FEATURE_SEP)) {        put_cpu();        eturn;    }    tss->x86_tss.ss1 = __KERNEL_CS;    tss->x86_tss.sp1 = sizeof(struct tss_struct) + (unsigned long) tss;    wrmsr(MSR_IA32_SYSENTER_CS, __KERNEL_CS, 0);    wrmsr(MSR_IA32_SYSENTER_ESP, tss->x86_tss.sp1, 0);    wrmsr(MSR_IA32_SYSENTER_EIP, (unsigned long) ia32_sysenter_target, 0);    put_cpu(); }

主要内容:

 (1) MSR_IA32_SYSENTER_*的声明在arch/x86/include/asm/msr-index.h中,可以看到对应的MSR寄存器地址:

 

#define MSR_IA32_SYSENTER_CS     0x00000174

#define MSR_IA32)SYSENTER_ESP    0x00000175

#define MSR_IA32_SYSENTER_EIP    0x00000176    

(2)将__KERNEL_CS设置到MSR_IA_SYSENTER_CS中。

(3)将tss->x86_tss.sp1栈地址设置到MSR_IA32_SYSENTER_ESP中。

(4)将ia32_sysenter_target(sysenter指令的接口函数)设置到MSR_IA32_SYSENTER_EIP。

3.2 sysenter和sysexit的指令操作

在Ring3的代码调用sysenter指令之后,CPU做出如下操作:

  1. 将SYSENTER_CS_MSR的值装在到cs寄存器。

  2. 将SYSENTER_EIP_MSR的值装在到eip寄存器。

  3. 将SYSENTER_CS_MSR的值加8(Ring0的堆栈段描述符)装载到ss寄存器。

  4. 将SYSENTER_ESP_MSR的值装载到esp寄存器。

  5. 将特权级切换到Ring0。

  6. 如果EFLAGS寄存器的VM标志被置位,则清除该标志。

  7. 开始执行指定的Ring0代码。

在Ring0代码执行完毕,调用sysexit指令退回Ring3时,CPU会做出如下操作:

  1. 将SYSENTER_CS_MSR的值加16(Ring3的代码段描述符)装载到cs寄存器。

  2. 将寄存器edx的值装载到eip寄存器。

  3. 将SYSENTER_CS_MSR的值加24(Ring3的堆栈段描述符)装载到ss寄存器。

  4. 将寄存器ecx的值装载到esp寄存器。

  5. 将特权级切换到Ring3。

  6. 继续执行Ring3的代码。

3.3 sysenter的系统调用处理

3.3.1 linux2.6.39内核sysenter系统调用

正如刚才对IA32_SYSENTER_EIP寄存器中传入sysenter的系统调用函数入口地址ia32_sysenter_target。

  在arch/x86/ia32/ia32entry.S中可以看到sysenter指令所要执行的系统调用处理程序ia32_sysenter_target的代码,其中执行系统调用的代码是:

 

sysenter_dispatch:    

    call    *ia32_sys_call_table(,%rax,8)

   

...

ia32_sys_call_table:    

    .quad sys_restart_syscall

   .quad sys_exit    .quad stub32_fork    .quad sys_read    .quad sys_write    .quad compat_sys_open       /* 5 */

可以看到sysenter指令会直接到系统调用表中找到相应系统调用处理程序去执行。

3.3.2 linux4.20内核sysenter系统调用

在linux4.20内核中,对IA32_SYSENTER_EIP寄存器中传入的是entry_SYSENTER_32函数。

  在arch/x86/entry/entry_32.S中可以看到entry_SYSENTER_32()函数:

 
  1. ENTRY(entry_SYSENTER_32)

  2.    pushfl

  3.    pushl   %eax

  4.    BUG_IF_WRONG_CR3 no_user_check=1

  5.    SWITCH_TO_KERNEL_CR3 scratch_reg=%eax

  6.    popl    %eax

  7.    popfl

  8.    movl    TSS_entry2task_stack(%esp), %esp

  9.    

  10. .Lsysenter_past_esp:

  11.    pushl   $__USER_DS      /* pt_regs->ss */

  12.    pushl   %ebp            /* pt_regs->sp (stashed in bp) */

  13.    pushfl              /* pt_regs->flags (except IF = 0) */

  14.    orl $X86_EFLAGS_IF, (%esp)  /* Fix IF */

  15.    pushl   $__USER_CS      /* pt_regs->cs */

  16.    pushl   $0          /* pt_regs->ip = 0 (placeholder) */

  17.    pushl   %eax            /* pt_regs->orig_ax */

  18.    SAVE_ALL pt_regs_ax=$-ENOSYS    /* save rest, stack already switched */

  19.    testl   $X86_EFLAGS_NT|X86_EFLAGS_AC|X86_EFLAGS_TF, PT_EFLAGS(%esp)

  20.    jnz .Lsysenter_fix_flags

  21.    

  22. .Lsysenter_flags_fixed:

  23.    TRACE_IRQS_OFF

  24.    movl    %esp, %eax

  25.    call    do_fast_syscall_32

  26. ...

  27.    sysexit

  28.  

猜你喜欢

转载自blog.csdn.net/weixin_44944853/article/details/89671707