第一次作业:基于orange'sos系统的进程模型分析与心得体会(代码片段)

夏栀 夏栀     2022-11-07     191

关键词:

1一. 操作系统进程概念模型与进程控制块概念浅析

1. 什么是进程?

  

      图 1 - 1 (WIN10系统任务管理器对进程管理的图形化界面)

      计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。

                                          ——百度百科

  应用程序的实例。对正在运行的程序的抽象。 

                                          ——《现代操作系统》

2. 什么是进程控制块?

  进程控制块(Processing Control Block),是操作系统核心中一种数据结构,主要表示进程状态。其作用是使一个在多道程序环境下不能独立运行的程序(含数据),成为一个能独立运行的基本单位或与其它进程并发执行的进程。

                                          ——百度百科

  为了实现进程模型,操作系统维护着一张表格(一个结构数组),即进程表(也称进程控制块)。

                                          ——《现代操作系统》

3. 什么是进程模型?

  在进程模型中,计算机上所有可运行的软件,通常也包括操作系统,被组织成若干顺序进程(sequential process),简称进程(process)。

                                          ——百度百科,《现代操作系统》

  《现代操作系统》补充内容:多道程序设计的设计对进程快速切换,这个过程通过进程模型来实现。

 

4. 我的理解

  1. 进程-CPU ——>伪并行概念 && 多处理器(程序的切换、多道程序系统)
  2. 进程模型
       进程运行在CPU上。CPU在各个进程之间来回切换。(进程的调度:多道程序设计与进程状态的保存)      
       代码+数据+堆栈表示一个进程 (其他细节暂时不考虑) + 进程调度
  3. CPU被若干进程共享(进程通信、进程切换与优先级比较)
  4. PCB可以理解为一个存储进程的所有信息的数据体集合。

运行的进程及一个软件双击打开后,这个程序在操作系统中的具体存在形式。程序是抽象的,进程是具体的。那么进程表也好理解了,管理多个PCB的表,能够实现多个进程在操作系统中的运行的管理与维护。(先不考虑实现方式)

二. Orange OS中进程的实现方式(ring的切换与进程表结构)

进程堆栈信息应该独立成块,防止进程表指针对进程数据/代码读取时破坏其他信息。   
esp的位置出现在3个不同的区域: 
- 进程栈–进程运行时自身的堆栈  
- 进程表–存储进程状态信息的数据结构  
- 内核栈–进程调度模块运行时使用的堆栈   

通过时钟中断来实现对进程的启动。

把大象放在冰箱需要三步,在orange os中,进程的创立也类似于此。其中esp指针的跳转实现了对进程表的管理。

1. 模拟时间中断的实现

1 ALIGN   16
2     hwint00:    ;   /*时间中断时,中断进程*/
3         iretd       /*中断返回*/

 

很简单的汇编代码。模拟时间中断时,通过iretd指令来跳转程序运行的指令。

2. 初始化工作的准备

           

                 图 2 - 1

一个进程刚开始时,指定各个寄存器,就可以直接运行。所以要先完成对寄存器的初始化。

同时,你必须对进程表这个数据结构进行定义。这个是以后要运行的进程的整体框架。

1) 进程结构体的定义

1 typedef struct s_proc
2         STACK_FRAM regs;/*寄存器*/
3         u16 ldt_sel;/*LDT选择子*/
4         DESCRIPTOR  ldts[LDT_SIZE];/*LDTS*/
5         u32 pid;    /*进程id*/
6         char p_name[16];/*进程名*/
7     PROCESS;

上面这个结构体中有对寄存器信息进行保存,这个对于以后进程的管理是相当有用的。

 1 /*准备进程体TestA()*/    
 2 /*充当一个进程执行体的功能函数*/
 3     void TestA()
 4         int i = 0;
 5         while(1)
 6             disp_str("A");
 7             disp_int(i++);
 8             disp_str(".");
 9             delay(1);/*需要再delay中编写简单的运行函数*/
10         
11     

 

2) 进程表的定义与初始化

 1     PROCESS* p_proc=proc_table;
 2 
 3     p_proc--->ldt_sel=SELECTOR_LDT_FIRST;                                              
 4 
 5     //设置进程表中进程的ldt_sel,ldt_sel被赋值SELECTOR_LDT_FIRST,这个宏的定义在代码6-1-中
 6 
 7     memcpy(&p_proc->ldts[0], &gdt[SELECTOR_KERNEL_CS >> 3], sizeof(DESCRIPTOR));  //将SELECTOR_KERNEL_CS所指的描述符拷贝到进程PCB的ldts[0]处
 8     p_proc->ldts[0].attr1 = DA_C | PRIVILEGE_TASK << 5;                              // change the DPL
 9     memcpy(&p_proc->ldts[1], &gdt[SELECTOR_KERNEL_DS >> 3], sizeof(DESCRIPTOR)); //将SELECTOR_KERNEL_DS所指的描述符拷贝到进程PCB的ldts[1]处
10     p_proc->ldts[1].attr1 = DA_DRW | PRIVILEGE_TASK << 5;                          // change the DPL
11 
12     //LDT中共有两个描述符,分别被初始化为内核代码段和内核数据段,只是改变了一下DPL,以让其运行在低特权级下(2)
13     p_proc->regs.cs  = ((8 * 0) & SA_RPL_MASK & SA_TI_MASK) | SA_TIL | RPL_TASK;    //8*0和8*1是选择子,而且是进程PCB中的LDT描述符的选择子。
14 
15     //cs指向LDT中的第一个描述符 (3) 通过cs中的这个描述符跳转到内核代码段                              //LDT选择子是从0开始的。一个描述符相隔8个字节,所以要乘以8
16     p_proc->regs.ds  = ((8 * 1) & SA_RPL_MASK & SA_TI_MASK) | SA_TIL | RPL_TASK;    //根据选择子的属性确定是全局选择子还是LDT选择子。 
17     p_proc->regs.es  = ((8 * 1) & SA_RPL_MASK & SA_TI_MASK) | SA_TIL | RPL_TASK;    //本例会根据cs中的选择子自动到进程PCB中读取LDT描述符
18     p_proc->regs.fs  = ((8 * 1) & SA_RPL_MASK & SA_TI_MASK) | SA_TIL | RPL_TASK;    //本例比较特殊的地方是进程LDT描述符在进程PCB中,没有固定的LDT段
19     p_proc->regs.ss  = ((8 * 1) & SA_RPL_MASK & SA_TI_MASK) | SA_TIL | RPL_TASK;
20 
21     //ds,es,fs,ss指向LDT中的第二个描述符
22     p_proc->regs.gs  = (SELECTOR_KERNEL_GS & SA_RPL_MASK) | RPL_TASK;
23 
24     //gs指向显存,只是其RPL发生改变
25     p_proc->regs.eip = (u32)TestA;
26 
27     //eip指向TestA,这表明进程将从TestA的入口地址开始运行
28     p_proc->regs.esp = (u32) task_stack + STACK_SIZE_TOTAL;
29 
30     //esp指向单独的堆栈,堆栈大小为STACK_SIZE_TOTAL
31     p_proc->regs.eflags = 0x1202; // IF=1, IOPL=1, bit 2 is always 1.
32 
33     //eflags=0x1202,恰巧设置了IF位,并把IOPL设为1,这样,进程就可以使用I/O指令,并且中断会在iretd执行时,被打开。
34 
35     //代码中使用到的宏,基本在protect.h中
36 
37     p_proc_ready = proc_table; 

 

内容很多吧?你只要知道其中完成的工作,1. 寄存器 2. LDTSelector 3. LDT。配置之后,把我们刚才准备的TestA放入运行。

 

其次,必须了解进程表的定义。

 1 //进程表数据结构
 2     typedef struct s_stackframe    /* proc_ptr points here             ↑ Low           */
 3     u32 gs;     /* ┓                        │           */
 4     u32 fs;     /* ┃                        │           */
 5     u32 es;     /* ┃                        │           */
 6     u32 ds;     /* ┃                        │           */
 7     u32 edi;        /* ┃                        │           */
 8     u32 esi;        /* ┣ pushed by save()               │           */
 9     u32 ebp;        /* ┃                        │           */
10     u32 kernel_esp; /* <- \'popad\' will ignore it            │           */
11     u32 ebx;        /* ┃                        ↑栈从高地址往低地址增长*/      
12     u32 edx;        /* ┃                        │           */
13     u32 ecx;        /* ┃                        │           */
14     u32 eax;        /* ┛                        │           */
15     u32 retaddr;    /* return address for assembly code save()  │           */
16     u32 eip;        /*  ┓                       │           */
17     u32 cs;     /*  ┃                       │           */
18     u32 eflags;     /*  ┣ these are pushed by CPU during interrupt  │           */
19     u32 esp;        /*  ┃                       │           */
20     u32 ss;     /*  ┛                       ┷High           */
21     STACK_FRAME;
22 
23 
24     typedef struct s_proc 
25     STACK_FRAME regs;          /* process registers saved in stack frame */
26 
27     u16 ldt_sel;               /* gdt selector giving ldt base and limit */
28     DESCRIPTOR ldts[LDT_SIZE]; /* local descriptors for code and data */
29 
30     int ticks;                 /* remained ticks */
31     int priority;
32 
33     u32 pid;                   /* process id passed in from MM */
34     char p_name[16];           /* name of the process */
35     PROCESS;

 

注意,进程结构和进程表结构完全是不同的概念。进程表用于管理进程。

恢复一个进程时,便将esp指向这个结构体的开始处,然后运行一系列的pop命令,读取寄存器的值。

3.准备GDT和TSS 

 1  restart:
 2         /*设置esp(堆栈指针)的值*/
 3         /*p_proc_ready是一个指向进程表的指针*/
 4         /*p_proc_ready内有s_proc,用于存放进程状态信息*/
 5         mov     esp,[p_proc_ready]  
 6         
 7         lldt    [esp + P_LDT_SEL]
 8         lea     eax,[esp + P_STACKTOP]
 9         mov     dword [tss + TSS3_S_SP0] , eax
10     restart_reenter:
11         dec     dword [k_reenter]
12         pop     gs
13         pop     fs
14         pop     es
15         pop     ds
16         poped
17         add     esp,4
18         iretd

a) 表示把堆栈指针和进程表指针这两者结合,意味着你可以把TestA在进程表中存在这个项目,便于运行。

b) 初始化GDT中的TSS和LDT , 初始化TSS。

  在init_prot中完成

c) 完成跳转,实现ring0->ring1  

  这个过程中需要为下一次ring1->ring0准备,用iretd返回之前要保证tss.esp()指向的地址正确。

 

总结

                

                            图 2 - 2

三. 进程的调度与状态转换

                    

                            图 3 - 1

1. 睡眠态 —— 运行态

所谓进程间切换最主要的就是,进程A运行时,另一个进程B想要占用此时的CPU,那就必须将进程A中止,并将其运行信息保存,这些信息保存到CPU寄存器中(能够恢复) , 然后把CPU的控制权交给进程B,并且通过CPU寄存器读取存放的进程B的信息。这个过程通过进程调度来实现。

esp指针可以决定将进程表中的某个进程占据cpu。此时这个进程从睡眠态->运行态。  

所以,动态把进程从睡眠态变为运行态,只需要esp的指针改变就行了。

 

    ;...
    mov esp, StackTop       ; 切到内核栈
    ;...
    call    clock_handler
    ;...    
    mov esp, [p_proc_ready] ; 离开内核栈
    lldt    [esp + P_LDT_SEL]
    lea eax, [esp + P_STACKTOP] 调整进程表的指针的指向
    mov dword [tss + TSS3_S_SP0], eax
    ;...

2. 运行态 —— 睡眠态

当让一个进程变为运行态时,原先运行的进程需要动态改变,让其睡眠。需要完成对这个进程的信息的保存。(保护机制的使用)  


所以,当时钟中断来临时,切换进程需要保存信息到寄存器中。

 1 hwint00:        ; Interrupt routine for irq 0 (the clock).
 2     sub esp, 4
 3     pushad      ; `.
 4     push    ds  ;  |
 5     push    es  ;  | 保存原寄存器值
 6     push    fs  ;  |
 7     push    gs  ; /
 8     mov dx, ss
 9     mov ds, dx
10     mov es, dx
11 
12     inc byte [gs:0]     ; 改变屏幕第 0 行, 第 0 列的字符
13     ;...
14     mov esp, StackTop       ; 切到内核栈
15     ;...
16     call    clock_handler
17     ;...
18 
19     mov esp, [p_proc_ready] ; 离开内核栈
20     lldt    [esp + P_LDT_SEL]
21     lea eax, [esp + P_STACKTOP]
22     mov dword [tss + TSS3_S_SP0], eax
23     ;...
24     pop gs  ; `.
25     pop fs  ;  |
26     pop es  ;  | 恢复原寄存器值
27     pop ds  ;  |
28     popad       ; /
29     add esp, 4
30 
31     iretd

 

这个时候时在cloc_handler之前,时钟调用之前,即进程切换之前,完成对运行->睡眠的这个进程的信息的保存。包括状态的保存,放到寄存器中。

 

四. 心得

从程序到进程,一个是抽象的,一个是具体的。

期间对于这些应用程序在操作系统中的运行与维护通过对进程的管理来实现。例如怎么停止一个进程的运行,怎么启动一个休眠态的进程。这些都是进程的状态的互相转换及调度来实现的。

进程控制块是进程表中一个相当重要的概念,只有有进程控制块,才能完成对运行的进程的管理。所谓的进程表即是多个进程的集合。

书上介绍的调度算法有多种,orange\'s os 用了最简单的表的管理的思想,称不上是高效,但是是最基本的进程管理与控制的思想。

不过在分析进程模型中,orange os有提到了ring的概念,操作系统内核,有点理解不能。

 

五. 参考资料

1. https://baike.baidu.com/item/%E8%BF%9B%E7%A8%8B/382503?fr=aladdin 百度百科进程词条

2. https://blog.csdn.net/begginghard/article/details/7371754

第一次作业:深入源码分析进程模型

1.简介本文的内容是基于Linux2.6的源码,深入分析进程模型。什么是进程操作系统是怎么组织进程的进程状态如何转换进程是如何调度的谈谈自己对该操作系统进程模型的看法2.什么是进程 进程的概念:进程是处于执行期的程... 查看详情

第一次作业:深入源码分析进程模型(代码片段)

前言:源代码下载地址:https://elixir.bootlin.com/linux/v2.6.39/source  一、操作系统是怎么组织进程的 1.进程是什么?进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位... 查看详情

第一次作业:深入源码分析进程模型(代码片段)

前言:     这是一篇关于linux操作系统的简单介绍。linux本身不能算是操作系统,只是一个内核,基于linux内核的操作系统有很多,比如流行的android,ubuntu,红旗linux等等。Linux以它的高效性和灵活性著称。它能... 查看详情

130292015011朱萍第一章作业

如何看待Android今后的发展    Android一词最早出现于科幻小说《未来夏娃》中,后来这一词即指仿真机器人,同时也是Google于2007年11月5日宣布的基于Linux的开源手机操作系统的名称。现如今,Android在中国甚至是在全... 查看详情

高端运维班第一次作业

1、按系列罗列Linux的发行版,并描述不同发行版之间的联系与区别*Linux发行版=Linux内核+商业软件linux的发行版:RedHat、Fedora、suse(Novell)、红旗、debian、Ubuntu、centos不同发行版之间的联系和区别:RedHat,是redhat自己的发行的企... 查看详情

高端运维班第一次作业

1、按系列罗列Linux的发行版,并描述不同发行版之间的联系与区别*Linux发行版=Linux内核+商业软件linux的发行版:RedHat、Fedora、suse(Novell)、红旗、debian、Ubuntu、centos不同发行版之间的联系和区别:RedHat,是redhat自己的发行的企... 查看详情

第一次作业-(代码片段)

1.作业内容挑选一个开源的操作系统,深入源码分析其进程模型,具体包含如下内容:操作系统是怎么组织进程的进程状态如何转换(给出进程状态转换图)进程是如何调度的谈谈自己对该操作系统进程模型的看法一.操作系统是... 查看详情

基于php023通用作业批改提交系统

...的教学任务十分复杂,工作也很繁琐,在教学任务中,作业的批改也是一个很重要的环节。为了提高老师工作效率,减轻教师的工作强度,提高作业批改的灵活性,《通用作业批改系统》的诞生可以说是事在必行的... 查看详情

实时软件系统设计第一次作业

实时控制软件设计第一次作业ABS系统:简称:制动防抱死系统(antilockbrakesystem)。作用:在汽车制动时,自动控制制动器制动力的大小,使车轮不被抱死,处于边滚边滑(滑移率在20%左右)的状态,以保证车轮与地面的附着力... 查看详情

WinDbg 无法加载 SOS.dll

...难住了。我有使用WinDbg调试非托管代码的经验,但这是我第一次尝试使用托管。当我指示WinDbg加载默认的SOS.dll时,会出现以下错误:sxel 查看详情

物联网第一次作业

一、RFID的原理及系统组成  RFID的原理是利用发射无线电波讯号来传送资料,以进行无接触式的资料辨识与存取,可达到身份及物品识别或信息存储的功能。RFID系统在具体的应用过程中,根据不同的应用目的和应用环境,系统... 查看详情

基于springboot的作业提交系统

1.采用的技术(1)后端技术:SpringBoot、SpringDataJPA(2)前端技术:Layui、html,ajax,jq2.实现的功能(1)管理员:对课程、教师、学生数据进行增删改查(2)教师a.布置作业b. 查看详情

第一次作业

曹迦勒U201310693题目选择一个典型的实时控制系统(CNC/工业机器人/汽车ABS系统......)的软件系统(或其中的一个软件模块)做案例分析,列出:该系统有哪些强实时功能需求?需要对哪些实时事件进行实时响应,对允许的实时延... 查看详情

预备作业03

...这次安装Linux操作系统又是一次不一样的的体验。由于是第一次安装这个系统,缺乏映像文件,我只好去官网下载。但是官网是英文,我只有开了翻译之后才把文件下下来。之前虽然下过虚拟机,但这还是第一次在虚拟机上安装... 查看详情

系统设计与分析第一次作业

1、简单题软件工程的定义(1)将系统化、规范化、可度量的方法应用与软件的开发、运行和维护的过程,即将工程化应用于软件中。(2)对(1)中所述方法的研究。——IEEE[IEE93]软件工程是指导计算机软件开发和维护的工程学科。采用... 查看详情

无法在 WinDbg 中加载 SOS

...:2011-05-2109:29:51【问题描述】:背景:我是WinDbg的新手,第一次尝试让它运行。我想检查从WindowsServer2008(x86)上的IIS7中托管的正在运行的ASP.NET4站点获取并下载到本地计算机的内存转储。我安装了debuggingtools并首次启动了WinDbg,打... 查看详情

管理信息系统第一次作业

1.信息与数据的区别是什么?信息是有关联有结构的数据,数据不能给人们传递有效的内容,而信息可以让人了解被描述的目标物体。例如:18、1530这是两个是数据,不能完整描述目标物体,信息则是姓名李xx,年龄18岁,性别女... 查看详情

第一次作业:深入源码分析进程模型(代码片段)

 1.作业内容   挑选一个开源的操作系统,深入源码分析其进程模型,具体包含如下内容:操作系统是怎么组织进程的进程状态如何转换(给出进程状态转换图)进程是如何调度的谈谈自己对该操作系统进程模型的... 查看详情