`
lingqi1818
  • 浏览: 248813 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

【转】启动进程所需要的基本条件

阅读更多
原文地址:
http://blog.csdn.net/richardysteven/article/details/3606398

进程是现代计算机系统运行的最小单位,所以没有进程也不能称之为操作系统。



     当系统启动后,设置了GDT, IDT进入了保护模式后,需要哪些东西才能让进程跑起来呢?其实简单说来进程产生的目的并不是要让程序跑起来,而是要让一个系统上有多个进程一起跑。因为如果一个系统上只有一个所谓的“进程”在跑,那就没有必要保存再恢复进程的运行环境了。



    好,来看看都要加哪些东西才能够让进程跑起来。

1.TSS

2. 进程体本身

3. 进程表





    TSS主要用来保存ring0特权级的ss:esp,且这个ss:esp真好指向了正在运行的进程的进程表。这样在时钟中断产生时,特权级发生切换,正好将eip, cs, eflags, esp, ss保存到进程表中。

    进程表是一个数组,每个进程都有自己的一个表来保存自己的运行状态。其中还可以包含进程自己的LDT描述符,这样就可以和其他进程分开了。

    下面看看代码是怎么搞的。

1. TSS 初始化 及其 设置

typedef struct s_tss {
t_32 backlink;
t_32 esp0;  /* stack pointer to use during interrupt */
t_32 ss0;  /*   "   segment  "  "    "        "     */
t_32 esp1;
t_32 ss1;
t_32 esp2;
t_32 ss2;
t_32 cr3;
t_32 eip;
t_32 flags;
t_32 eax;
t_32 ecx;
t_32 edx;
t_32 ebx;
t_32 esp;
t_32 ebp;
t_32 esi;
t_32 edi;
t_32 es;
t_32 cs;
t_32 ss;
t_32 ds;
t_32 fs;
t_32 gs;
t_32 ldt;
t_16 trap;
t_16 iobase; /* I/O位图基址大于或等于TSS段界限,就表示没有I/O许可位图 */
/*t_8 iomap[2];*/
}TSS;



这是TSS的定义。如前所述,TSS主要保存了ring0的ss:esp,且指向了进程表。

首先要初始化TSS以及GDT中的TSS描述符。

/* 填充 GDT 中 TSS 这个描述符 */
memset(&tss, 0, sizeof(tss));
tss.ss0  = SELECTOR_KERNEL_DS;   这里设置了ss
init_descriptor(&gdt[INDEX_TSS],
   vir2phys(seg2phys(SELECTOR_KERNEL_DS), &tss),
   sizeof(tss) - 1,
   DA_386TSS);  初始化GDT中的TSS描述符
tss.iobase = sizeof(tss); /* 没有I/O许可位图 */





而刚才提到的esp在另一个地方设置。准确的说是在时钟中断的时候设置的。这样就可以根据将要运行哪个进程来该片esp了。

截取一部分来看

mov esp, [p_proc_ready]
lldt [esp + P_LDT_SEL]
lea eax, [esp + P_STACKTOP]
mov dword [tss + TSS3_S_SP0], eax



蓝色部分就是设置了TSS中的ring0 esp。



2.进程表 及 初始化



进程表可谓是重中之重了。先来看看长什么样子。

typedef struct s_stackframe { /* proc_ptr points here    ↑ Low   */
t_32 gs;  /* ┓      │   */
t_32 fs;  /* ┃      │   */
t_32 es;  /* ┃      │   */
t_32 ds;  /* ┃      │   */
t_32 edi;  /* ┃      │   */
t_32 esi;  /* ┣ pushed by save()    │   */
t_32 ebp;  /* ┃      │   */
t_32 kernel_esp; /* <- 'popad' will ignore it   │   */
t_32 ebx;  /* ┃      ↑栈从高地址往低地址增长*/ 
t_32 edx;  /* ┃      │   */
t_32 ecx;  /* ┃      │   */
t_32 eax;  /* ┛      │   */
t_32 retaddr; /* return address for assembly code save() │   */
t_32 eip;  /*  ┓      │   */
t_32 cs;  /*  ┃      │   */
t_32 eflags;  /*  ┣ these are pushed by CPU during interrupt │   */
t_32 esp;  /*  ┃      │   */
t_32 ss;  /*  ┛      ┷High   */
}STACK_FRAME;


typedef struct s_proc {
STACK_FRAME   regs;   /* process' registers saved in stack frame */

t_16    ldt_sel;  /* selector in gdt giving ldt base and limit*/
DESCRIPTOR   ldts[LDT_SIZE];  /* local descriptors for code and data */
        /* 2 is LDT_SIZE - avoid include protect.h */
t_32    pid;   /* process id passed in from MM */
char    p_name[16];  /* name of the process */
}PROCESS;



PUBLIC PROCESS   proc_table[NR_TASKS]; 最后他老人家就长这样了



下面就是初始化了。



PUBLIC int tinix_main()
{
disp_str("-----/"tinix_main/" begins-----/n");

PROCESS* p_proc = proc_table;

p_proc->ldt_sel = SELECTOR_LDT_FIRST; 

下面四句将GDT中的描述符复制到了进程自己的LDT中。 但是改变了属性。
memcpy(&p_proc->ldts[0], &gdt[SELECTOR_KERNEL_CS >> 3], sizeof(DESCRIPTOR));
p_proc->ldts[0].attr1 = DA_C | PRIVILEGE_TASK << 5; // change the DPL
memcpy(&p_proc->ldts[1], &gdt[SELECTOR_KERNEL_DS >> 3], sizeof(DESCRIPTOR));
p_proc->ldts[1].attr1 = DA_DRW | PRIVILEGE_TASK << 5; // change the DPL

初始化进程自身的段寄存器,注意,这些寄存器除了gs都设置了SA_TIL属性,表示都是用的LDT的选择子。
p_proc->regs.cs  = ((8 * 0) & SA_RPL_MASK & SA_TI_MASK) | SA_TIL | RPL_TASK;
p_proc->regs.ds  = ((8 * 1) & SA_RPL_MASK & SA_TI_MASK) | SA_TIL | RPL_TASK;
p_proc->regs.es  = ((8 * 1) & SA_RPL_MASK & SA_TI_MASK) | SA_TIL | RPL_TASK;
p_proc->regs.fs  = ((8 * 1) & SA_RPL_MASK & SA_TI_MASK) | SA_TIL | RPL_TASK;
p_proc->regs.ss  = ((8 * 1) & SA_RPL_MASK & SA_TI_MASK) | SA_TIL | RPL_TASK;
p_proc->regs.gs  = (SELECTOR_KERNEL_GS & SA_RPL_MASK) | RPL_TASK;
p_proc->regs.eip = (t_32)TestA; TestA就是进程体的开始地址啦
p_proc->regs.esp = (t_32) task_stack + STACK_SIZE_TOTAL;
p_proc->regs.eflags = 0x1202; // IF=1, IOPL=1, bit 2 is always 1.

p_proc_ready = proc_table;
restart();


while(1){}
}



上面红色的语句需要再解释一下。

p_proc->ldt_sel 仅仅表示了一个GDT中LDT的选择子。将来会在restart函数中的lldt语句用到。 也就是说告诉系统要用哪一个GDT中的描述符来指定LDT。 实际上 SELECTOR_LDT_FIRST这个段描述符的内容,是在别的地方初始化的。



// 填充 GDT 中进程的 LDT 的描述符
init_descriptor(&gdt[INDEX_LDT_FIRST],
   vir2phys(seg2phys(SELECTOR_KERNEL_DS), proc_table[0].ldts),
   LDT_SIZE * sizeof(DESCRIPTOR) - 1,
   DA_LDT);

蓝色的地方明确表示出了这个LDT是指向proc_table[0]进程的LDT的。



3. 启动进程



好啦,一切都初始化好了,该是让我们的进程跑起来的时候了。



restart:
mov esp, [p_proc_ready]
lldt [esp + P_LDT_SEL]
lea eax, [esp + P_STACKTOP]
mov dword [tss + TSS3_S_SP0], eax
pop gs
pop fs
pop es
pop ds
popad
add esp, 4
iretd


怎么样,这下明白了吧。 进程就这么跑起来了。 good!



4. 忘了贴进程体他自己了



怎么把最关键的东西忘了呢。



void TestA()
{
int i = 0;
while(1){
  disp_str("A");
  disp_int(i++);
  disp_str(".");
  delay(1);
}
}



so easy a :)
分享到:
评论

相关推荐

    winXP常用进程 最基本的系统进程

    最基本的系统进程(也就是说,这些进程是系统运行的基本条件,有了这些进程,系统 就能正常运行) smss.exe Session Manager csrss.exe 子系统服务器进程 winlogon.exe 管理用户登录 services.exe 包含很多系统服务...

    Windows XP 常见的进程列表

     最基本的系统进程(也就是说,这些进程是系统运行的基本条件,有了这些进程,系统  就能正常运行)  smss.exe Session Manager  csrss.exe 子系统服务器进程  winlogon.exe 管理用户登录  services.exe 包含...

    实验二Linux进程编程技术

    实验目的和要求: 进程的基本概念及进程的结构 Linux环境下进程的相关函数的应用 守护进程的概念、启动和建立 进程控制程序的编写 实验条件: 1、装有Linux操作系统的微型计算机; 实验过程 Liunx进程创建

    ARM_Linux启动分析.pdf

    这一部分的启动过程在2.4.x内核中简化了不少,缺省的独立初始化过程只剩下网络 (sock_init())和创建事件管理核心线程,而其他所需要的初始化都使用__initcall()宏 包含在do_initcalls()函数中启动执行。...

    teletap:通过网络套接字为 Telegram (telegram-cli) 守护进程使用 Python 包装库-源码

    Teletap - 电报 cli 的电报包装库一个易于使用的 Python 包装库,用于通过网络套接字的 Telegram (telegram-cli v1.1.1) 守护进程先决条件假设您已经启动并运行了telegram-cli ,并且您可以登录。 如果还没有,请...

    操作系统实验报告

    本实验的目的在于使用高级语言编写和调试一个系统动态分配资源的简单模拟程序,了解死锁产生的条件和原因,并采用银行家算法有效地防止死锁的发生,以加深对课堂上所讲授的知识的理解。 三、实验内容与要求 设计有n...

    Linux Shell高级技巧实战总结

    一、将输入信息转换为大写字符后再进行条件判断 二、为调试信息设置输出级别 三、判断参数是否为数字 四、判断整数变量的奇偶性 五、将Shell命令赋值给指定变量,以保证脚本的移植性 六、获取当前时间距纪元时间...

    UNIX操作系统教程 张红光

    第1章绪论.1 1.1操作系统概述1 1.1.1建立操作系统的目标1 1.1.2操作系统是用户与计算机的接口1 1.1.3操作系统是资源管理器2 1.2UNIX系统的主要...UNIX基本概念及入门技术10 2.1UNIX系统基本常识10 2.1.1两种前端机10 /&gt;...

    Linux C 一站式学习

    4.1. 作为交互登录Shell启动,或者使用--login参数启动 4.2. 以交互非登录Shell启动 4.3. 非交互启动 4.4. 以sh命令启动 5. Shell脚本语法 5.1. 条件测试:test [ 5.2. if/then/elif/else/fi 5.3. case/esac 5.4. ...

    基于javatcpsocket通信的拆包和装包源码-java:Java

    基于java tcp socket通信的拆包和装包源码 #知识点部分 ##计算机专业相关知识点 ###操作系统 进程和线程的区别和联系 进程是分配内存计算机资源的基本单位,线程是CPU调度...一个用户作业或进程所包含的段对应一个二维

    《计算机操作系统》期末复习指导

    三个基本状态是等待、执行和就绪,在一定的条件下,进程的状态将发生转换。 (2)进程调度算法 主要有先来先服务(FCFS)、时间片轮转法、多级反馈轮转法、优先数法。 (3)进程控制块(PCB)是进程...

    Understanding the Linux Kernel

     操作系统基本概念  Unix文件系统概述  Unix内核概述  第二章内存寻址  内存地址  硬件中的分段  Linux中的分段  硬件中的分页  Linux中的分页  第三章进程  进程、轻量级进程和线程  进程描述符  进程...

    [详细完整版]操作系统题库.xls

    虚拟存储器所具有的基本特征是 —— 虚拟扩充,——部分装人 , 一——离散分配和————多次对换." " 在采用树形目录结构的文件系统中,各用户的文件名必须互不相同.( )" (1)用一个执行时间图描述非抢占式优先级...

    CentOS.5系统管理-part1

    12.2.3 管理守护进程的启动脚本 12.2.4 网络服务器的典型配置方法 12.3 xinetd和TCPWrapper 12.3.1 扩展网络守护进程xinetd 12.3.2 TCP Wrappers 12.4 时钟同步守护进程 12.4.1 Linux的时钟 12.4.2 网络时钟同步

    玩转校内&时间观念

    2、2007年8月4日,正式转入了对[Ver 1.1.0.3]的开发进程中,先前所做之事均围绕着[Ver 1.1.0.1]所出现的Bug进行,在完善了最基本的功能后随即推出了[Ver 1.1.0.2]版本。 3、这个版本,重新设置了User ...

    玩转校内&时间观念(终结版Ⅱ)

    2、2007年8月4日,正式转入了对[Ver 1.1.0.3]的开发进程中,先前所做之事均围绕着[Ver 1.1.0.1]所出现的Bug进行,在完善了最基本的功能后随即推出了[Ver 1.1.0.2]版本。 3、这个版本,重新设置了User ...

    玩转校内[精简免安装版] Ver 2.4.27.95

    2、2007年8月4日,正式转入了对[Ver 1.1.0.3]的开发进程中,先前所做之事均围绕着[Ver 1.1.0.1]所出现的Bug进行,在完善了最基本的功能后随即推出了[Ver 1.1.0.2]版本。 3、这个版本,重新设置了User ...

    宋劲彬的嵌入式C语言一站式编程

    4.1. 作为交互登录Shell启动,或者使用--login参数启动 4.2. 以交互非登录Shell启动 4.3. 非交互启动 4.4. 以sh命令启动 5. Shell脚本语法 5.1. 条件测试:test [ 5.2. if/then/elif/else/fi 5.3. case/esac 5.4. ...

    精通Windows.API-函数、接口、编程实例.pdf

    6.2.1 创建进程、获取进程相关信息、获取启动参数 153 6.2.2 编写控制台程序和图形用户界面应用程序 158 6.2.3 获取和设置环境变量 158 6.3 线程、纤程 162 6.3.1 创建线程、退出线程、获取线程信息 162 ...

Global site tag (gtag.js) - Google Analytics