前提回顾
Linux 系统经历 BIOS、bootsect.s、setup.s、head.s 一系列执行后, 其从实模式切换到了 32 位保护模式,此时即将运行 init/main.c 中的 main 函数。
内存分布
此时其内存分布如下:
堆栈信息
堆栈信息如下图:
寄存器信息
段选择符
通用寄存器
task[0] 信息
struct task_struct { // ... /* 本任务的局部表描述符。 0 空, 1 代码段 cs, 2 数据段和堆栈段 ds&ss */ struct desc_struct ldt[3]; /* 本进程的任务状态段信息结构 */ struct tss_struct tss; };
task[0] 初始化:
#define INIT_TASK \ /* state etc */ { 0,15,15, \ /* signals */ 0,{{},},0, \ /* ec,brk... */ 0,0,0,0,0,0, \ /* pid etc.. */ 0,-1,0,0,0, \ /* uid etc */ 0,0,0,0,0,0, \ /* alarm */ 0,0,0,0,0,0, \ /* math */ 0, \ /* fs info */ -1,0022,NULL,NULL,NULL,0, \ /* filp */ {NULL,}, \ { \ {0,0}, \ /* ldt */ {0x9f,0xc0fa00}, \ {0x9f,0xc0f200}, \ }, \ /*tss*/ {0,PAGE_SIZE+(long)&init_task,0x10,0,0,0,0,(long)&pg_dir,\ 0,0,0,0,0,0,0,0, \ 0,0,0x17,0x17,0x17,0x17,0x17,0x17, \ _LDT(0),0x80000000, \ {} \ }, \ }
ldt
tss
一、环境初始化
void main(void) /* This really IS void, no error here. */ { /* The startup routine assumes (well, ...) this */ /* * Interrupts are still disabled. Do necessary setups, then * enable them */ ROOT_DEV = ORIG_ROOT_DEV; drive_info = DRIVE_INFO; memory_end = (1 << 20) + (EXT_MEM_K << 10); memory_end &= 0xfffff000; if (memory_end > 16 * 1024 * 1024) memory_end = 16 * 1024 * 1024; if (memory_end > 12 * 1024 * 1024) buffer_memory_end = 4 * 1024 * 1024; else if (memory_end > 6 * 1024 * 1024) buffer_memory_end = 2 * 1024 * 1024; else buffer_memory_end = 1 * 1024 * 1024; main_memory_start = buffer_memory_end; #ifdef RAMDISK main_memory_start += rd_init(main_memory_start, RAMDISK * 1024); #endif mem_init(main_memory_start, memory_end); trap_init(); blk_dev_init(); chr_dev_init(); tty_init(); time_init(); sched_init(); buffer_init(buffer_memory_end); hd_init(); floppy_init(); sti(); move_to_user_mode(); if (!fork()) { /* we count on this going ok */ init(); } /* * NOTE!! For any other task 'pause()' would mean we have to get a * signal to awaken, but task0 is the sole exception (see 'schedule()') * as task 0 gets activated at every idle moment (when no other tasks * can run). For task0 'pause()' just means we go check if some other * task can run, and if not we return here. */ for (;;) pause(); }
代码执行到 move_to_user_mode 函数之前,其进行了一系列初始化操作。跟踪发现除了通用寄存器发生变化外,段寄存器,task[0] 中 ldt ,tss 都未发生变化。
以下为通用寄存器信息:
执行完 move_to_user_mode 函数后,程序由内核态进入用户态(任务 0 的用户态)。
二、move_to_user_mode
move_to_user_mode 函数把进程 0 由内核态转成用户态。其代码如下:
#define move_to_user_mode() \ // 模仿中断硬件压栈,顺序是 ss、esp、eflags、cs、eip __asm__ ("movl %%esp,%%eax\n\t" \ // 保存堆栈指针 esp 到 eax 寄存器中 "pushl $0x17\n\t" \ // 堆栈段选择符 ss 入栈, 0x17 即二进制的 10111(特权级3、LDT、数据段) "pushl %%eax\n\t" \ // 堆栈指针 esp 入栈 "pushfl\n\t" \ // 标志进村器 eflags 入栈 "pushl $0x0f\n\t" \ // Task0 代码段选择符 cs 入栈, 0x0f 即 111 (特权级3、LDT、代码段) "pushl $1f\n\t" \ // 将下面标号 1 的偏移地址 eip 入栈 "iret\n" \ // 出栈恢复现场,翻转特权级从 0 到 3,中断返回,会跳转到下面标号 1处 "1:\tmovl $0x17,%%eax\n\t" \ // 此时开始执行进程 0 。 "movw %%ax,%%ds\n\t" \ // 初始化段寄存器指向 task0 的局部表的数据段 "movw %%ax,%%es\n\t" \ // 下面的代码使 es、fs、gs 与 ds 一致 "movw %%ax,%%fs\n\t" \ "movw %%ax,%%gs" \ :::"ax")
1、寄存器
此时寄存器的信息如下:
ldt:
tss 未发生变化
2、分析
typedef struct desc_struct { unsigned long a,b; } desc_table[256];
以下为 GDT、LDT、TSS 间的关系:
值的形式为:b:a,其位于地址 0x5cb8 处。
0 | 1 | 2 | 3 | 4 | 5 |
NULL | 内核CS | 内核DS | NULL | TSS0 | LDT0 |
0:0 | C09A00:FFF | C09300:FFF | 0:0 | 8B01:F4480068 | 8201:F4300068 |
6 | 7 | 8 | 9 | … |
TSS1 | LDT1 | TSS2 | LDT2 | … | |
0:0 | 0:0 | 0:0 | 0:0 | … |
2.1 段选择符
段选择符结构如下:
2.2 段描述符
2.3 Task0 处于用户态
CS
此时段寄存器 CS 为 0x0F。指定了 LDT 中具有 RPL=3 的段 1,其索引字段值是 1,TI 位是 1,指定 LDT 表。其指向Task0的局部描述符,其值为:C0FA00:0x9F。
备注:GDT 中第 5 个值 LDT0 存储了 LDT 描述符地址,其值为:8201:F4300068,其段基址为 0x1F430,段限长为 104 个字节。段基址正好指向 task[0] 中成员变量 ldt 的地址。而 task[0].ldt[1] 为 LDT 描述符,其值正是上文说的 C0FA00:0x9F
其他段寄存器 SS、DS、ES、FS、GS
此时其他段寄存器 SS、DS、ES、FS、GS 值为 0x17。指定了 LDT 中具有 RPL=3 的段 2,其索引字段值是 2,TI 位是 1,指定 LDT 表。其指向Task0的局部描述符,其值为:C0F300:0x9F。
备注:GDT 中第 4 个值 TSS0 存储了 TSS 描述符地址,其值为:8B01:F4480068,其段基址为 0x1F448,段限长为 104 个字节。段基址正好指向 task[0] 中成员变量 tss 的地址。而 task[0].tss 为 TSS 描述符,其值正是上文说的 C0F300:0x9F
2.4 Task0 处于内核态
CS
此时段寄存器 CS 为 0x08。其指定了 GDT 中具有 RPL=0 的段 1,其索引字段值是 1,TI 位是 0,指定 GDT 表。其指向的描述符值为:0xC09A00:FFF。
其他段寄存器 SS、DS、ES、FS、GS
此时其他段寄存器 SS、DS、ES、FS、GS 值为 0x10。其指定了 GDT 中具有 RPL=0 的段 2,其索引字段值是 2,TI 位是 0,指定 GDT 表。其指向的描述符值为:0xC09300:FFF。
Linux0.11 系统调用进程创建与执行(九)(下):https://developer.aliyun.com/article/1597313