分析概览 文章管理 草稿管理 图片管理 站点管理 主站 关于 登出 手写操作系统项目----进程

大家好,我叫徐锦桐,个人博客地址为www.xujintong.com。平时记录一下学习计算机过程中获取的知识,还有日常折腾的经验,欢迎大家来访。

这里记录了,手写操作系统项目中关于进程的部分。

进程四要素

首先进程有四要素。

  • 有一段程序代其执行
  • 有进程专用的系统堆栈空间
  • 在内核有task_struct数据结构
  • 进程有独立的存储空间,拥有专有的用户空间

如果具备前三条缺少第四条,那就称为线程。如果完全没有用户空间,就称为 内核线程 。如果共享用户空间就称为用户线程

进程初始化

手写操作系统项目的进程初始化的源代码如下:

```cpp
/*** @brief 初始化任务*/
int task_init (task_t * task, const char * name, int flag ,uint32_t entry, uint32_t esp) {ASSERT(task != (task_t *)0);int err = tss_init(task, flag, entry, esp);if (err < 0) {log_printf("init task failed.\n");return err;}// 任务字段初始化kernel_strncpy(task->name, name, TASK_NAME_SIZE);task->state = TASK_CREATED;task->sleep_ticks = 0;task->parent = (task_t *)0;task->heap_start = 0;task->heap_end = 0;task->time_ticks = TASK_TIME_SLICE_DEFAULT;task->slice_ticks = task->time_ticks;task->state = 0;list_node_init(&task->all_node);list_node_init(&task->run_node);list_node_init(&task->wait_node);// 文件相关kernel_memset(task->file_table, 0, sizeof(task->file_table));// 插入就绪队列中和所有的任务队列中irq_state_t state = irq_enter_protection();task->pid = (uint32_t)task;list_insert_last(&task_manager.task_list, &task->all_node);irq_leave_protection(state);return 0;
}

接下来,我为你讲解这个进程初始化函数的各个部分。

初始化TSS

TSS前置知识

TSS是x86系统上的一个结构,保存了当前任务的状态信息,比如运行到了哪,当前任务的寄存器,CPU用来进行任务调度。
当进行任务切换的时候,就把TSS取出来然后恢复要切换的任务的状态。TR寄存器中存储着当前运行进程的TSS结构。进程的task_struct结构中存储着该进程的tss结构,源码如下:

typedef struct _task_t {...tss_t tss;                  // 任务的TSS段int tss_sel;                // tss选择子
}task_t;

TSS过程.webp


下面是TSS的具体结构,具体的各个结构内容我们就不过多叙述了,如果真想要了解各个位所代表的是什么,大家可以看IA-32手册。
TSS结构.webp


注: TSS是x86系统的特性,在当前64位操作系统已经被抛弃。

项目中TSS初始化

我们的项目中并没有用到这么多的,只用到了一部分。具体项目源码如下:

/*** tss描述符*/
typedef struct _tss_t {uint32_t pre_link;  // 没用到uint32_t esp0, ss0, esp1, ss1, esp2, ss2;uint32_t cr3;uint32_t eip, eflags, eax, ecx, edx, ebx, esp, ebp, esi, edi;uint32_t es, cs, ss, ds, fs, gs;uint32_t idt;   // 没用到uint32_t iomap;     // 没用到
}tss_t;

首先给出项目中tss初始化的源码。

static int tss_init (task_t * task, int flag ,uint32_t entry, uint32_t esp) {// 为TSS分配GDTint tss_sel = gdt_alloc_desc();if (tss_sel < 0) {log_printf("alloc tss failed.\n");return -1;}segment_desc_set(tss_sel, (uint32_t)&task->tss, sizeof(tss_t),SEG_P_PRESENT | SEG_DPL0 | SEG_TYPE_TSS);// tss段初始化kernel_memset(&task->tss, 0, sizeof(tss_t));// 分配内核栈,得到的是物理地址uint32_t kernel_stack = memory_alloc_page();   // 分配一页内存    用于中断、系统异常、系统调用if (kernel_stack == 0) {goto tss_init_failed;}// 根据不同的权限选择不同的访问选择子int code_sel, data_sel;if (flag & TASK_FLAGS_SYSTEM) {code_sel = KERNEL_SELECTOR_CS;data_sel = KERNEL_SELECTOR_DS;} else {// 注意加了RP3,不然将产生段保护错误code_sel = task_manager.app_code_sel | SEG_CPL3;data_sel = task_manager.app_data_sel | SEG_CPL3;}task->tss.eip = entry;task->tss.esp = esp ? esp : kernel_stack + MEM_PAGE_SIZE;task->tss.esp0 = kernel_stack + MEM_PAGE_SIZE;// task->tss.ss = data_sel;task->tss.ss0 = KERNEL_SELECTOR_DS;task->tss.eip = entry;task->tss.es = task->tss.ds = task->tss.ss = task->tss.fs = task->tss.gs = data_sel;    // 全部采用同一数据段stask->tss.cs = code_sel;task->tss.eflags = EFLGAGS_IF | EFLGAGS_DEFAULT;task->tss.iomap = 0;// 页表初始化uint32_t page_dir = memory_create_uvm();if (page_dir == 0) {goto tss_init_failed;}task->tss.cr3 = page_dir;task->tss_sel = tss_sel;return 0;
tss_init_failed:// 如果创建页表失败gdt_free_sel(tss_sel);if (kernel_stack) {memory_free_page(kernel_stack);}return -1;
}

首先通过int tss_sel = gdt_alloc_desc()代码分配一个GDT(GDT表是从第一项开始的,第0项不分配)。gdt_alloc_desc()源码如下:

/*** 分配一个GDT推荐表符*/
int gdt_alloc_desc() {mutex_lock(&mutex);// 跳过第0项for (int i = 1; i < GDT_TABLE_SIZE; i ++ ) {segment_desc_t * desc = gdt_table + i;if (desc->attr == 0) {mutex_unlock(&mutex);return i * sizeof(segment_desc_t);}}mutex_unlock(&mutex);return -1;
}

可以看到就是遍历一下GDT表,看哪个表项没有被分出去,然后分配给当前TSS。

初始化task_struct结构

操作系统为每个进程分配一个task_struct结构,用以描述该进程,也就相当于一个进程的简历,写了进程的信息,进程的状态、父进程、进程的pid,进程的名字等等。

/*** @brief 任务控制块结构*/
typedef struct _task_t {// uint32_t * stack;// 这是个枚举数据类型,递增的宏定义,默认第一个为0,每次加1.enum {TASK_CREATED,TASK_RUNNING,TASK_SLEEP,TASK_READY,TASK_WAITTING,   // 等待时间TASK_ZOMBIE,    // 将死状态}state;int pid;                        // 进程的pidstruct _task_t * parent;        // 父进程uint32_t heap_start;            // 堆的顶层地址uint32_t heap_end;              // 堆结束地址int status;                     // 进程执行结果int sleep_ticks;        // 睡眠时间int time_ticks;         // 设置计数器   时间片int slice_ticks;        // 递减时间片计数file_t * file_table[TASK_OFILE_NR];      // 记录进程打开了哪些文件  任务最多打开的文件数量char name[TASK_NAME_SIZE];      // 任务名字list_node_t run_node;           // 运行相关结点list_node_t wait_node;          // 等待队列list_node_t all_node;           // 所有队列结点tss_t tss;                  // 任务的TSS段int tss_sel;                // tss选择子
}task_t;

linux源码中的task_struct描述符,里面包含很多的变量。我这个操作系统知识demo级别的,所以用到的并不多。


可以看到我们是用pid来区分不同的进程,task_struct里面还有该进程的名字,该进程的堆栈空间地址。


进程初始化有一部分就是初始化task_struct这个结构中的信息。

将当前任务插入到所有任务队列中

关于这个代码list_insert_last(&task_manager.task_list, &task->all_node);,这个是将当前任务加入所有任务队列中去。
task_manager是个task_manager_t的结构类型。结构的源码如下:

typedef struct _task_manager_t {task_t * curr_task;     // 当前运行的任务list_t ready_list;      // 就绪队列list_t task_list;       // 保存所有已经创建好的进程 所有已创建任务的队列list_t sleep_list;      // 睡眠队列 延时队列task_t first_task;      // 内核任务task_t idle_task;       // 空闲任务int app_code_sel;       // 任务代码段选择子int app_data_sel;       // 应用任务的数据段选择子
}task_manager_t;

这里面记录了当前运行的任务,就绪队列,已创建好的所有进程队列等等关于进程的队列。

进程切换

进程切换,项目中就只切换了两个进程,一个init_task和一个first_task。就两个进程就好说了,切换的时候传入两个task_struct的地址,然后利用长跳指令跳到新的进程的TSS结构,这个TSS结构包含了新进程的上下文信息,硬件(硬件根据选择子判断是不是TSS结构)会自动将这些信息加载到各个寄存器中。


任务切换中,cpu会把当前寄存器的数据保存到当前(旧的)tr寄存器所指向的tss数据结构里,然后把新的tss数据复制到当前寄存器里。这些操作是通过cpu的硬件实现的

    task_init(&init_task, (uint32_t)init_task_entry, (uint32_t)&init_task_stack[1024]);task_init(&first_task, 0, 0);  // 后面两个参数为0:first_task跑起来后已经运行,不需要从tss中加载初始化的值,因此里面的值无所谓,后面切换的时候也会保存状态。write_tr(first_task.tss_sel);  // 对任务寄存器tr进行初始化int count = 0;for (;;) {log_printf("int main %d", count++);task_switch_from_to(&first_task, &init_task);}

先初始化的两个任务,然后将当前任务的tss选择子存到TR寄存器中。task_switch_from_to(&first_task, &init_task);就是从first_task切换到init_task。

void switch_to_tss (int tss_sel) {far_jump(tss_sel, 0);
}static inline void far_jump (uint32_t selector, uint32_t offset) {uint32_t addr[] = {offset, selector};__asm__ __volatile__("ljmpl *(%[a])"::[a]"r"(addr));
}void task_switch_from_to (task_t * from, task_t * to) {// 简单的用jmp到对应的tss选择子进行任务切换switch_to_tss(to->tss_sel);// simple_switch(&from->stack, to->stack);
}

就是长跳到另一个进程的tss位置,然后硬件会自动将当前TSS中的信息加载到各个寄存器,将tss位置存到TR寄存器中。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/116811.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux软件包和进程管理

一、RPM软件包管理 1、RPM管理工具 &#xff08;1&#xff09;RPM是红帽包管理(Redhat Package Manager)的缩写。 由Red Hat公司提出的一种软件包管理标准。 是Linux各发行版中应用最广泛的软件包格式之一&#xff08;还有debian的发行版deb安装包&#xff09;。 RPM功能通过…

数据结构:选择题+编程题(每日一练)

目录 选择题&#xff1a; 题一&#xff1a; 题二&#xff1a; 题三&#xff1a; 题四&#xff1a; 题五&#xff1a; 编程题&#xff1a; 题一&#xff1a;单值二叉树 思路一&#xff1a; 题二&#xff1a;二叉树的最大深度 思路一&#xff1a; 本人实力有限可能对…

MySQL的索引——索引的介绍及其数据结构B+树 索引的类型 索引的使用及其失效场景 相关名词解释

前言 索引是存储引擎用于快速查找数据纪录的一种数据结构&#xff0c;索引是数据库中经常提及的一个词&#xff0c;究竟什么是索引&#xff0c;索引的数据结构是什么&#xff0c;索引有什么类型&#xff1f; 本篇博客尝试阐述数据库索引的相关内容&#xff0c;涉及什么是索引…

用 Java 在 PDF 中创建和管理图层,实现交互式文档

PDF 图层&#xff08;也称为可见图层或附加图层等&#xff09;是组织和管理 PDF 文档中内容可见性的一种方法。PDF 图层可用于创建交互式文档、隐藏或显示特定信息、创建多语言版本文档等。通过添加和删除图层&#xff0c;用户可以根据需要定制 PDF 文档指定内容的可见性与显示…

适用于物联网的UI设计工具都有哪些?

随着科学技术的飞速发展&#xff0c;“万物相连的互联网”时代逐渐成为现实。如今&#xff0c;物联网已经不是什么新词了。事实上&#xff0c;早在各种屏幕设备诞生之前&#xff0c;人们就与物理世界交织在一起&#xff0c;产生了无数的互动。如何将人们多年积累的互动经验与物…

蓝桥杯 Java 青蛙过河

import java.util.Scanner; // 1:无需package // 2: 类名必须Main, 不可修改/**二分法从大&#xff08;n&#xff09;到小找足够小的步长前缀和记录每个位置的前面有的总石头数&#xff08;一个石头表示可以容纳一个青蛙&#xff0c;一位置有多少个石头hi就是多少&#xff09;&…

025-第三代软件开发-实现需求长时间未操作返回登录界面

第三代软件开发-实现需求长时间未操作返回登录界面 文章目录 第三代软件开发-实现需求长时间未操作返回登录界面项目介绍实现需求长时间未操作返回登录界面实现思路用户操作监控QML 逻辑处理 关键字&#xff1a; Qt、 Qml、 QTimer、 timeout、 eventFilter 项目介绍 欢迎…

秋季期中考复现xj

flow analysis 1 What is the backdoor file name that comes with the server?( Including file suffix) 服务器自带的后门文件是什么&#xff1f;&#xff08;含文件后缀&#xff09; 题目还要求最后把那个文件名MD5一下&#xff0c;再去提交 开始的前三题是流量分析的&…

发挥服务器的无限潜能:创意项目、在线社区和更多

✅作者简介&#xff1a;2022年博客新星 第八。热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 ✨特色专栏&#xff1a…

设计模式:观察者模式(C#、JAVA、JavaScript、C++、Python、Go、PHP)

上一篇《命令模式》 下一篇《策略模式》 简介&#xff1a; 观察者模式&#xff0c;它是一种行为型设计模式&#xff0c;它允许一个对象自动通知其依赖者&#xff08;观察者&#xff09;状态的变化。当被…

Windows Server服务器下的Linux子系统

导读Windows、Linux看似死敌一对&#xff0c;但其实微软对于开源实业还是相当支持的&#xff0c;Windows 10系统内就首次内置了一个Linux子系统(Windows Subsystem for Linux/WSL)&#xff0c;允许开发者模拟Linux环境&#xff0c;而且有需要的可以直接从Windows应用商店下载SU…

顺序栈的实现----数据结构

栈的概念 对于栈&#xff08;Stack&#xff09;&#xff0c;后进先出&#xff08;Last In First Out&#xff0c;LIFO&#xff09;&#xff0c;栈也是一种线性表&#xff0c;只不过是一种操作受限的线性表&#xff0c;只能在一端操作&#xff0c;也就是不允许在中间进行查找、…

华为云全新上线Serverless应用中心,支持一键构建文生图应用

近日&#xff0c;华为云全新上线Serverless应用中心&#xff0c;提供了大量应用模板&#xff0c;让用户能够一键部署函数和周边依赖资源&#xff0c;节省部署时间&#xff0c;快速上手将应用部署到华为云函数计算FunctionGraph&#xff0c;并一键开通周边依赖资源。 本次Serve…

搭建一个windows的DevOps环境记录

边搭建边记录&#xff0c;整个DevOps环境的搭建可能会很久。。。 一、安装Jenkins&#xff1a; 参考&#xff1a;Jenkins基础篇--windows安装Jenkins-CSDN博客 注意上面选择JDK的路径&#xff0c;选择到安装目录&#xff0c;该目录并不一定要在path中配置了&#xff08;就是…

Python中使用cv2.resize()函数批量自定义缩放图像尺寸

目录 常用插值缩放方法缩放示例代码总结 常用插值缩放方法 cv2.resize()函数中的interpolation参数指定了图像缩放时使用的插值方法。以下是常用的插值方法&#xff1a; cv2.INTER_NEAREST&#xff1a;最近邻插值。该方法通过选择最接近目标像素的原始像素来进行插值。它是最…

【Unity地编细节】为什么Unity笔刷在地形上面刷不出来

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! 本文由 秩沅 原创 &#x1f636;‍&#x1f32b;️收录于专栏&#xff1a;unity细节和bug &#x1f636;‍&#x1f32b;️优质专栏 ⭐【…

python+unittest+requests+HTMLRunner编写接口自动化测试集

问题描述&#xff1a; 搭建接口测试框架&#xff0c;执行用例请求多个不同请求方式的接口 实现步骤&#xff1a; ① 创建配置文件config.ini&#xff0c;写入部分公用参数&#xff0c;如接口的基本url、测试报告文件路径、测试数据文件路径等配置项 1 [DATABASE] 2 data_addre…

redis的key超时策略和key淘汰机制(面试题详解)

ChatGPT给出的回答&#xff1a; Redis中的Key超时策略和Key淘汰机制是为了有效管理内存和控制数据的生命周期。 Key超时策略&#xff1a;Redis可以为每个Key设置过期时间&#xff0c;一旦Key过期&#xff0c;它将自动从Redis中删除。可以使用EXPIRE命令为Key设置过期时间&…

【自然语言处理】理解词向量、CBOW与Skip-Gram模型

文章目录 一、词向量基础知识1.1 One-hot表示1.2 Distributed表示 二、word2vec基础知识2.1 CBOW和Skip-gram 三、基于Hierarchical Softmax的 CBOW 模型和 Skip-gram 模型3.1 CBOW 模型3.2 Skip-gram 模型 参考资料 由于计算机不能直接对各种字符进行运算&#xff0c;为此需要…

党建展馆vr仿真解说员具有高质量的表现力和互动性

随着虚拟数字人应用渐成趋势&#xff0c;以虚拟数字人为核心的营销远比其他更能加速品牌年轻化进程和认识&#xff0c;助力企业在激烈的市场竞争中脱颖而出&#xff0c;那么企业虚拟IP代言人解决了哪些痛点? 解决品牌与代言人之间的风险问题 传统代言人在代言品牌时&#xff0…