`
lingqi1818
  • 浏览: 253917 次
  • 性别: 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 :)
分享到:
评论

相关推荐

    Process启动进程.rarProcess启动进程.rarProcess启动进程.rar

    为了协调进程间的执行顺序,避免数据竞争,需要进行进程同步,常见的同步机制包括信号量、互斥锁、条件变量等。 八、进程的终止 进程可以正常结束(完成任务),也可以被其他进程强制终止(如使用kill命令),或者...

    android系统从init进程开始到systemserver启动详细流程

    `init.rc`文件是一个配置文件,它定义了`init`进程需要启动的各种服务和进程,以及这些服务的启动条件和依赖关系。`init`进程会读取并解析`init.rc`文件,然后根据文件中的指令启动相应的服务。 - **init.rc文件...

    操作系统实验报告- 进程状态转换及其PCB的变化

    在操作系统中,进程是系统资源分配和调度的基本单位。进程的状态转换是操作系统实现并发执行和资源管理的关键机制。本实验通过编程模拟了进程状态转换的过程,包括创建、就绪、运行、阻塞和退出这五种状态,并利用...

    易语言判断启动进程源码.zip

    在这个场景中,我们要深入探讨易语言的基本概念、启动进程的操作以及源码分析的相关知识点。 首先,易语言的核心特性在于它的汉字编程语法,使得初学者能够快速理解和编写程序,降低了编程的入门门槛。它提供了丰富...

    后台进程与守护进程的区别

    在深入探讨后台进程与守护进程的区别之前,我们先来明确一下两者的基本概念。 #### 后台进程 后台进程(Background Process)是指那些在操作系统后台默默工作,无需用户直接干预的进程。这类进程常见于执行系统...

    进程管理器,实现了对进程的基本管理

    1. **进程创建与销毁**:当用户启动一个应用程序时,操作系统会创建一个对应的进程。进程管理器负责分配必要的资源,如内存、CPU时间等,并在程序结束时回收这些资源,释放进程。 2. **进程调度**:根据不同的调度...

    进程的基本操作C#附源码

    **添加进程(启动进程)** 要启动一个新的进程,可以使用Process类的Start方法。例如,如果你想启动一个浏览器并打开指定的URL,你可以这样做: ```csharp using System.Diagnostics; Process.Start("explorer.exe...

    操作系统:进程管理-作业题目+答案

    - 不可能发生等待态直接转为运行态,因为进程在等待时需要先满足条件才能被调度执行。 3. 进程和程序的区别: - 进程具有动态性,是程序在执行过程中的存在形式,而程序是静态的代码集合。 - 存储位置并不是进程...

    Java通过进程名称杀进程

    - 如果要终止的进程是由其他用户启动的,则可能需要使用管理员权限。 - 在生产环境中,建议增加更多的异常处理逻辑以提高代码的健壮性。 #### 三、进阶实践技巧 1. **处理多个进程**: - 可以使用循环结构配合`...

    操作系统进程管理

    - **创建:** 当用户启动一个应用程序或系统需要执行一个任务时,操作系统会为其创建一个新的进程。 - **撤销:** 当进程完成任务或出现错误时,操作系统会将其撤销。 - **状态转换:** 进程在运行过程中会经历不同...

    计算机操作系统实验一 进程创建模拟 源代码

    5. **启动进程**:操作系统将控制权转移给新创建的进程,开始执行其入口点。 在这个实验中,我们将使用C语言编写代码来模拟这些步骤。C语言是一种底层编程语言,适合进行系统级编程,与操作系统交互。模拟进程创建...

    如何杀excel进程的函数 kill excel进程的函数

    在实际应用中,可能需要更复杂的逻辑来确保正确性,比如检查进程列表是否为空,或者根据特定条件选择需要终止的进程。 ### 四、异常处理与注意事项 在处理进程操作时,应考虑到可能出现的各种异常情况,如进程不...

    用shell脚本监控进程是否存在 不存在则启动的实例

    ### 使用Shell脚本监控进程的存在性与启动实例详解 #### 概述 本文将详细介绍如何使用Shell脚本来监控特定进程的存在性,并在该进程不存在时自动启动它。此方法适用于服务器自动化运维场景,有助于提高系统的稳定性...

    进程管理实验

    - **线程与进程的区别**:进程是资源分配的基本单位,线程是CPU调度的基本单位。一个进程中可以有多个线程,它们共享进程的资源,但有自己的栈和程序计数器。 - **死锁**:当两个或多个进程相互等待对方释放资源而...

    c#守护程序来实现进程保护源码

    Console.WriteLine($"无法启动进程'{processName}': {ex.Message}"); } } ``` 6. **主程序入口**:在`Main`方法中,实例化`ProcessProtector`对象并开始监控。 ```csharp static void Main(string[] args) { ...

    易语言批量判断进程

    4. **条件判断与处理**:在循环中,可以设置条件判断,如若某个进程不存在,可以执行特定的操作,如输出提示信息,或者记录到日志文件中。 5. **源码组织**:在实际编写易语言程序时,应保持代码的清晰性和可读性,...

    易语言主程序源码,易语言子进程源码,易语言进程通讯例程

    学习并理解这些源码,你需要掌握易语言的基本语法和调用API的方法。同时,了解Windows操作系统对进程管理的机制也是必要的。在这个过程中,你将学习到如何定义进程间通信协议,处理同步和异步问题,以及如何防止竞态...

    通过MFC界面实现进程的模拟

    我们将主要关注进程的三种基本状态:就绪、运行和等待,以及如何利用线程技术来实现这些状态的转换。 首先,理解进程的基本概念至关重要。在操作系统中,进程是程序执行时的一个实例,它包含了程序的内存映像、打开...

    进程句柄,参数查看器

    它不仅提供了基本的任务管理功能,如查看和结束进程,还具备了高级特性,例如查看进程打开的句柄、分析进程间的父子关系以及深入探究进程启动的参数。 首先,让我们了解什么是“进程句柄”。在Windows操作系统中,...

Global site tag (gtag.js) - Google Analytics