基于mykernel完成多进程的简单内核

2023-11-04

学号:476
实验资源:https://github.com/mengning/linuxkernel/

1、实验环境准备
使用个人电脑的parallels desktop ubuntu虚拟机
1)安装qemu
sudo apt-get install qemu
sudo ln -s /usr/bin/qemu-system-i386 /usr/bin/qemu
2)下载linux kernel 3.9.4源码以及my kernel补丁
由于在虚拟机直接联网下载速度较慢,直接在宿主机下载
3)解压源码并打补丁
xz -d linux-3.9.4.tar.xz
tar -xvf linux-3.9.4.tar
cd linux-3.9.4
patch -p1 < …/mykernel_for_linux3.9.4sc.patch
4)编译内核代码
在这里插入图片描述
编译过程出现如下问题:
在这里插入图片描述
解决方案:
在这里插入图片描述
5)从qemu窗口看到mykernel在执行
qemu -kernel arch/x86/boot/bzImage
在这里插入图片描述
6)关闭qemu窗口查看mymain.c和myinterrupt.c代码
在这里插入图片描述
mymain.c中有一个my_start_kernel函数,不断输出my_start_kernel here i

在这里插入图片描述
myinterrupt.c中有一个my_timer_handler函数输出>>>>>>>>>my_timer_handler here<<<<<<<<<<<

结合之前的运行结果和两个函数,可以看出mykernel在启动之后会不断调用my_start_kernel函数,并且周期性的调用函数my_timer_handler。
所以当前环境已经实现了cpu运行c代码,并且具有中断处理的功能,现在只需要修改mymain.c和myinterrupt.c的代码就可以实现进程管理和切换。

2、实验内容
1)替换mykernel源码
从老师的github上下载了mymain.c、myinterrupt.c、mypcb.h替换原来mykernel中的文件: https://github.com/mengning/mykernel
2)重新编译
make clean删除原来的make file
make allnoconfig
make
编译之后再次使用qemu查看运行结果:
在这里插入图片描述
3)分析代码:
mypcb.h

#define MAX_TASK_NUM        4
#define KERNEL_STACK_SIZE   1024*2 
/* CPU-specific state of this task */
struct Thread {
    unsigned long		ip;
    unsigned long		sp;
};

typedef struct PCB{
    int pid;
    volatile long state;	/* -1 unrunnable, 0 runnable, >0 stopped */
    unsigned long stack[KERNEL_STACK_SIZE];
    /* CPU-specific state of this task */
    struct Thread thread;
    unsigned long	task_entry;
    struct PCB *next;
}tPCB;

void my_schedule(void);

这是一个描述数据结构定义的头文件
MAX_TASK_NUM定义了进程总数为四个,KERNEL_STACK_SIZE定义了每个进程分配的栈空间为2k,thread里面定义了进程被挂起时的一些状态,包含了程序计数器eip和栈顶指针esp,下面是结构体PCB定义了控制进程运行需要的一些属性,最后是进程调度函数的函数声明。

mymain.c

#include "mypcb.h"

tPCB task[MAX_TASK_NUM];
tPCB * my_current_task = NULL;
volatile int my_need_sched = 0;

这里定义了系统中全部进程的控制信息,my_current_task是指向当前进程控制块的指针,my_need_sched表示调度状态,为1时表示要求内核进行一次进程切换。

void my_process(void);


void __init my_start_kernel(void)
{
    int pid = 0;
    int i;
    /* Initialize process 0*/
    task[pid].pid = pid;
    task[pid].state = 0;/* -1 unrunnable, 0 runnable, >0 stopped */
    task[pid].task_entry = task[pid].thread.ip = (unsigned long)my_process;
    task[pid].thread.sp = (unsigned long)&task[pid].stack[KERNEL_STACK_SIZE-1];
    task[pid].next = &task[pid];

这里初始化了第一个进程控制块的信息,pid为0,进程状态为运行,进程的入口和程序计数器都指向my_process的入口,栈顶指针指向分配的栈空间的地址最高处,最后将下一个控制块的指针指向自己,形成只有一个进程的循环。

for(i=1;i<MAX_TASK_NUM;i++)
    {
        memcpy(&task[i],&task[0],sizeof(tPCB));
        task[i].pid = i;
	(&task[i].stack[KERNEL_STACK_SIZE-1] - 1) = (unsigned long)&task[i].stack[KERNEL_STACK_SIZE-1];
	task[i].thread.sp = (unsigned long)(&task[i].stack[KERNEL_STACK_SIZE-1]);
        task[i].next = task[i-1].next;
        task[i-1].next = &task[i];
    }

这一部分代码是对更多的进程控制块进行初始化,类似于第一个进程的初始化过程,但是是以链的形式连接起来的。

pid = 0;
    my_current_task = &task[pid];
	asm volatile(
    	"movl %1,%%esp\n\t" 	/* set task[pid].thread.sp to esp */
    	"pushl %1\n\t" 	        /* push ebp */
    	"pushl %0\n\t" 	        /* push task[pid].thread.ip */
    	"ret\n\t" 	            /* pop task[pid].thread.ip to eip */
    	: 
    	: "c" (task[pid].thread.ip),"d" (task[pid].thread.sp)	/* input c or d mean %ecx/%edx*/
	);
} 

当前运行进程指向0号进程,接下来的一部分汇编代码是将cpu转向第一个进程控制块的入口函数地址开始执行,即启动了系统。
movl %1,%%esp 将内存控制块中的esp的值赋给esp寄存器,即进程栈空间的最高地址赋给esp
pushl %1 再次访问我们的栈顶地址值,并将这个值保存到栈上;
pushl %0
ret
首先把内存控制块中的eip压入栈,然后用ret指令将该值从栈上弹出并赋值给eip寄存器,由此间接实现了改变程序计数器eip的功能,让我们的CPU跳转到指定的函数地址去执行。而这里我们修改的eip的值,是在初始化阶段设置的函数my_process函数入口地址。因此我们的内核通过上面的四句代码就可以实现进程栈指针初始化,并跳转到该进程的入口函数执行的功能了。

void my_process(void)
{    
    int i = 0;
    while(1)
    {
        i++;
        if(i%10000000 == 0)
        {
            printk(KERN_NOTICE "this is process %d -\n",my_current_task->pid);
            if(my_need_sched == 1)
            {
                my_need_sched = 0;
        	    my_schedule();
        	}
        	printk(KERN_NOTICE "this is process %d +\n",my_current_task->pid);
        }     
    }
}

进程的入口函数实现的是将进程编号输出,每10000000进行一次输出并且检查全局调度的标志看是否需要进行进程切换,如果需要就调用my_schedule()函数,并输出切换后的进程号。

myinterrupt.c

#include "mypcb.h"

extern tPCB task[MAX_TASK_NUM];
extern tPCB * my_current_task;
extern volatile int my_need_sched;
volatile int time_count = 0;

引用外部进程块数组和当前运行进程指针,以及全局的调度标志

void my_timer_handler(void)
{
#if 1
    if(time_count%1000 == 0 && my_need_sched != 1)
    {
        printk(KERN_NOTICE ">>>my_timer_handler here<<<\n");
        my_need_sched = 1;
    } 
    time_count ++ ;  
#endif
    return;  	
}

中断处理函数会被系统时钟周期性的调用,每调用一次time_count就增加一,每增加1000就检查一次全局调度标志,如果不为1就设为1,这时my_process函数就会检测到,并且调用my_schedule函数。


void my_schedule(void)
{
    tPCB * next;
    tPCB * prev;

    if(my_current_task == NULL 
        || my_current_task->next == NULL)
    {
    	return;
    }
    printk(KERN_NOTICE ">>>my_schedule<<<\n");
    /* schedule */
    next = my_current_task->next;
    prev = my_current_task;
    if(next->state == 0)/* -1 unrunnable, 0 runnable, >0 stopped */
    {        
    	my_current_task = next; 
    	printk(KERN_NOTICE ">>>switch %d to %d<<<\n",prev->pid,next->pid);  
    	/* switch to next process */
    	asm volatile(	
        	"pushl %%ebp\n\t" 	    /* save ebp */
        	"movl %%esp,%0\n\t" 	/* save esp */
        	"movl %2,%%esp\n\t"     /* restore  esp */
        	"movl $1f,%1\n\t"       /* save eip */	
        	"pushl %3\n\t" 
        	"ret\n\t" 	            /* restore  eip */
        	"1:\t"                  /* next process start here */
        	"popl %%ebp\n\t"
        	: "=m" (prev->thread.sp),"=m" (prev->thread.ip)
        	: "m" (next->thread.sp),"m" (next->thread.ip)
    	); 
        my_current_task = next; 
    	printk(KERN_NOTICE ">>>switch %d to %d<<<\n",prev->pid,next->pid);   	
    }
    else
    {
        next->state = 0;
        my_current_task = next;
        printk(KERN_NOTICE ">>>switch %d to %d<<<\n",prev->pid,next->pid);
    	/* switch to new process */
    	asm volatile(	
        	"pushl %%ebp\n\t" 	    /* save ebp */
        	"movl %%esp,%0\n\t" 	/* save esp */
        	"movl %2,%%esp\n\t"     /* restore  esp */
        	"movl %2,%%ebp\n\t"     /* restore  ebp */
        	"movl $1f,%1\n\t"       /* save eip */	
        	"pushl %3\n\t" 
        	"ret\n\t" 	            /* restore  eip */
        	: "=m" (prev->thread.sp),"=m" (prev->thread.ip)
        	: "m" (next->thread.sp),"m" (next->thread.ip)
    	);          


    }  
    return;	
}

将当前运行进程指向下一个进程,如果下一个进程的运行状态为0,执行一段汇编代码:
asm volatile(
“pushl %%ebp\n\t” /* save ebp /
“movl %%esp,%0\n\t” /
save esp /
“movl %2,%%esp\n\t” /
restore esp /
“movl $1f,%1\n\t” /
save eip /
“pushl %3\n\t”
“ret\n\t” /
restore eip /
“1:\t” /
next process start here */
“popl %%ebp\n\t”
: “=m” (prev->thread.sp),"=m" (prev->thread.ip)
: “m” (next->thread.sp),“m” (next->thread.ip)
);

如果运行状态不为0,则运行另一段汇编代码:
asm volatile(
“pushl %%ebp\n\t” /* save ebp /
“movl %%esp,%0\n\t” /
save esp /
“movl %2,%%esp\n\t” /
restore esp /
“movl %2,%%ebp\n\t” /
restore ebp /
“movl $1f,%1\n\t” /
save eip /
“pushl %3\n\t”
“ret\n\t” /
restore eip */
: “=m” (prev->thread.sp),"=m" (prev->thread.ip)
: “m” (next->thread.sp),“m” (next->thread.ip)
);

分析两段汇编代码:

将当前的ebp压栈;
将当前的esp值存入被换出的进程的现场状态变量thread.esp中;
将被换入进程的esp值加载到当前的esp寄存器中,也就是切换到被换入的进程的栈空间上去;
1f比较特殊,它其实是一条汇编指令,表示跳转到标号为1的行去继续执行,也就是这段代码的最后两行处,而这条汇编是保存到了被换出进程的保存现场的内存空间thread.eip中,也就是说,这条指令就是下一次prev的进程被换入时将跳转到的代码;
最后两句实现了将eip的值改换为新的进程的eip的值,从而使系统执行新的进程。

1f的作用也体现在了若该进程已经被切换过,当再次被切换的时候就直接从该段汇编代码的后最后两行开始执行,也就是“popl %%ebp”,即将栈上的值出栈并赋值给寄存器ebp,而这正是第一行中保存在栈中的ebp值。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

基于mykernel完成多进程的简单内核 的相关文章

  • ESP32-S2学习记录2-使用sd卡保存日志

    前言 本文旨在记录ESP32 S2的学习研究记录 实现ESP32 S2搭载NB IoT BC26模块进行低功耗网络通信 1 sd card init include
  • Hive的hiveserver2和beeline的使用以及spark thritfserver的启动

    Hive的hiveserver2和beeline的使用以及spark thritfserver的启动 Hive 的hiveserver2介绍 hiveserver2 的配置 beeline连接hiveserver2 配置hiveserver
  • element-ui更改el-table表头背景颜色、字体大小

    秃然发现一个问题 所以记录一下 每个人都知道
  • docker odoo创建模块

    docker启动的如下步骤将创建并安装一个新的插件模块 进入到工作目录即你要操作并放置新建的自定义模块的插件目录中 为新模块选择一个技术名称并使用该名称作为模块名创建目录 本例中 我们使用my library pert PertdeMacB
  • JPA-Specification常用条件查询构造方式

    JPA Specification常用条件查询构造方式 1 toPredicate方法 Predicate toPredicate Root
  • 【TS】Error: Property ‘children‘ does not exist on type ‘X‘

    最近 在做项目模块的迁移 新项目需要使用ts 遇到的问题有点多 记录一下 先来复现一下场景 import React from react type PersonProps name string age number const Pers
  • 阿里云云服务器ECS要怎么绑定域名?

    阿里云云服务器ECS要怎么绑定域名 使用阿里云云服务器ECS 可以轻松地将域名与您的服务器实例进行绑定 以便通过域名来访问您的网站或应用程序 本文将提供详细的步骤和说明 教您如何在阿里云上绑定域名到ECS实例 一 购买域名 首先 在绑定域名
  • 目标检测——Anchor-Based算法的学习笔记

    1 前言 在写这篇笔记之前 首先我们需要确定的是 Anchor Based的思路一定优于Anchor Free的算法 这是毋庸置疑的 你想想 一个事情分成两步做 正负目标分类 目标的精细定位和回归 一个事情一步做 目标分类和回归 负样本被当
  • PixelStreaming数据通信

    PixelStreaming数据通信 简介 H5到UE4通信 H5发送 UE4接收 UE4到H5通信 UE4发送 H5接收 iframe postMessage 父页面 子页面 改造UE4提供的像素流送H5 UE4项目bat启动器 问题 U
  • MySQL生产环境部署架构

    MySQL生产环境部署架构 常用的分库分表架构 按业务id分库分表 建立索引映射表同时进行分库分表 数据同步到ES做复杂搜索 分库分表下如何分页 假设用户现在要查询自己的订单 同时订单要求要支持分页 该怎么做 方案一 因为同一个用户的订单可
  • 黑客突破防火墙常用的几种技术(转)

    一 防火墙基本原理 首先 我们需要了解一些基本的防火墙实现原理 防火墙目前主要分包过滤 和状态检测的包过滤 应用层代理防火墙 但是他们的基本实现都是类似的 路由器 网卡 防火墙 网卡 内部网络 防火墙一般有两个以上的网络卡 一个连到外部 r
  • 使用SSM框架进行Crud时后台出现javax.validation.UnexpectedTypeException: HV000030: No validator could be found

    1 处理之前的效果 2 原因是 Java实体类中属性是Integer类型 用了 NotBlank判断不能为空 而这个注解是判断字符串是否为空 3 解决办法 去掉 NotBlank注解 使用 NotNull 两者的区别 1 NotNull 适
  • ClassLoader.getSystemResource("") 为空的原因

    在idea自带的tomcat中 运行正常 发布到tomcat下获取为空 现修改成类 class getClassLoader getResource 获取方式
  • Vue整体架构分解

    Vue js的整体架构可以分解为以下几个部分 文章目录 1 数据驱动 2 组件化 3 响应式系统 4 虚拟DOM 5 插件系统 6 单文件组件 7 模板编译 总结 1 数据驱动 Vue的一个核心特点是数据驱动 Vue会在初始化的时候给数据提
  • 【image】src相对地址的引入

    src相对地址的引入 前言 相对路径 src引用 前言 在引入图片的时候 突然发现通过相对地址引入项目内图片不会了 特此在这里记录一下 以便学习 相对路径 使用原因 对于放在一个项目包内的图片资源 通过相对路径可直接引用 绝对路径是对于网络
  • ASP.NET Core 8 的 Web App

    Web App Web App 与 Web API 的不同之处在于包含 UI 部分 所谓的 UI 就是 HTML 页面 Web App 支持几种渲染HTML 的方式 服务端渲染 客户端渲染 混合渲染 服务端渲染 服务端渲染UI是在浏览器请求
  • Spyder闪退的解决过程记录

    安装了高版本的 PYQT5 后 原来安装的 老版本的 spyder打开闪退 考虑是版本不兼容的问题 卸载了spyder 重新安装了最新版 Version 5 3 1的spyder 在anaconda prompt中输入如下命令 pip un
  • 学习安卓应用开发一课一得+计算器

    一 在学习安卓应用开之前要掌握语言 本文章以Java为基础的安卓应用开发 二 Android开发环境配置 三 计算器开发 一 在学习安卓应用开之前要掌握语言 本文章以Java为基础的安卓应用开发 1 在学习Android开发之前 我首先需要
  • 网络编程(附代码)

    网络编程 1 网络通信协议 1 1 协议和七层模型 七层模型 也称为OSI Open System Interconnection 参考模型 是国际标准化组织 ISO 制定的一个用于计算机或通讯系统间互联的标准体系 它是一个七层的 抽象的模
  • MIPI信号的分析--结合示波器实际测试波形

    MIPI已经不陌生了 对于现在的设计中 摄像头接口 显示接口都有MIPI 所以有了CSI和DSI接口 MIPI最早是手机里面的协议 为了形成行业统一标准 MIPI联盟发起MIPI 移动行业处理器接口 作为移动应用处理器制定的开放标准 1 M

随机推荐

  • 深度学习—卷积神经网络(Convolutional Neural Networks)

    卷积神经网络 Convolutional Neural Networks 卷积神经网络 convolutional neural network CNN 是一种专门用来处理具有类似网格结构的数据的神经网络 例如时间序列数据 可以认为是在时间
  • sqli-labs通关详解

    sqli labs通关详解 Less1 Less1sqlmap运用 Less2 Less2sqlmap运用 Less3 sqlmap运用 Less4 sqlmap一把梭 Less5 sqlmap直接梭 Less6 Less7 Less8 s
  • Java对象转换最佳方案

    系统变的复杂 系统的层次划分越来越细 边界也越来越明确 然后每一层之间一般都有自己要处理的领域对象 统称为pojo一般在model或者domain包下 类的后缀不能为pojo 常见的一些模型类型 PO DO 持久层对象 一般和数据库直接打交
  • C语言丨快速排序法

    程序员在程序设计时常常需要对存储在数组中的大量数据进行处理 如排序 查找等 排序是把一系列无序的数据按照特定的顺序 如升序或降序 重新排列为有序序列的过程 对数据进行排序是最重要的应用之一 实际生活中的很多问题都需要对数据进行排序 之前我们
  • 多dex合并进apk

    批量重命名 选中所有 dex结尾的文件 右键批量重命名 Zip命令处理APK 删除原有classes dex zip d demo apk classes dex 添加新的脱壳classes dex zip m demo apk class
  • 什么是回调?

    定义 天天说回调回调 真的了解吗 消息异步为什么要传入回调接口 真的了解了吗 前端 axios 发送请求写的箭头函数全都是回调函数 真的理解了吗 其实回调很简单 就是比如 A 中的方法 a1 调用了 B 中的方法 b1 然后 b1 方法执行
  • ros---键盘控制机器人

    一 前言 无非是解决这几个问题问题 1 teleop py的键盘控制文档的书写 2 机器人模型文件xacro和控制xacro的launch文件的书写 二 问题解决思路 1 首先是teleop py键盘控制代码的书写 你有没有这样的问题 为什
  • 如何查计算机电源功率,手把手教你怎么查看电脑耗电量

    大家都知道 电脑只要在运行中就会产生耗电量 就好比电视机 冰箱 电风扇等等之类的啦 在通电的情况下都会产生耗电量 那么该如何计算电脑耗电量呢 别着急 下面小编就给大家介绍电脑耗电量的查询方法 什么是电脑耗电量呢 小编今天来给你们做一个佛系的
  • main函数参数详解

    一 参数形式 1 int main 2 int main void 3 int main int char 4 int main int char 5 int main int argc char argv 6 int main int a
  • (css)background-image设置背景图片大小

    css background image设置背景图片大小 效果 代码 middle li width 100 height 170px background image url assets images applyBj png backg
  • maven中打包项目为war包的pom.xml配置

    maven中打包成war包的pom xml配置 1 完整配置 这个是使用servlet的完整配置 其他的类似
  • Linux用普通用户输入命令报错-fork 资源不可用

    解决办法 修改最大限制打开文件数 vi etc security limits d 修改为65535 修改完之后重启应用服务器 系统正常访问
  • 服务器centos系统相关备注

    Install the UFW package using yum sudo yum y install ufw 开放端口 sudo ufw default allow outgoing sudo ufw allow 7000 tcp su
  • 解决Ubuntu16.04ping www.baidu.com不通的问题

    想要安装一个包 发现Ubuntu16 04不能上网 在终端ping百度 发现不通 问题如下 解决方法 1 检查你是否有IP信息 使用命令ifconfig 如果没有IP 则参照我的上一篇文章 获取IP 2 如果有IP 则进行下一步 执行以下命
  • 其他总结(四)--win10手动一小时四小时一天重新打开wifi

    win10出现这种个情况 是因为你吧笔记本的wifi关了 可以按fn F 这里的 号指你笔记本上的含有wifi信号开关的那个F键 我的是F2 重新开启就可以连接了 还可以用其他方式打开 https jingyan baidu com alb
  • Python语言—爬虫之旅

    活动地址 毕业季 进击的技术er 一 目前是大几 学习的专业是 本专业让你Get到哪些新技能 二 从哪个瞬间开始让你决定学习编程语言 三 进入大学敲下的第一行代码是什么 四 目前学习中最大的收获难是 五 大学期间的学习目标是 对未来的职业规
  • 使用pandas对xlsx文件的基本操作

    起因 因最近实习期间 要求查看 xlsx文件中数据是否有误 由于数据较多 想用python去执行 结果发现网上对xlsx文件操作或是太旧 大多难以应用 所以自己整理了一下 以备自己后用 模拟一个测试数据集data test xlsx文件 文
  • Broken pipe异常分析和常用锁的命令

    错误描述 ClientAbortException java io IOException Broken pipe 这种就是获取不到连接了 连接已经断开了 出现这种问题的可能性 1 连接太多 到了最大连接数 每个连接处理的速度太慢 而导致处
  • COLMAP导出相机外参(bin文件转txt文件)

    官方给出的images txt如下图 Image list with two lines of data per image 每张图像数据占两行 IMAGE ID QW QX QY QZ TX TY TZ CAMERA ID NAME 图像
  • 基于mykernel完成多进程的简单内核

    学号 476 实验资源 https github com mengning linuxkernel 1 实验环境准备 使用个人电脑的parallels desktop ubuntu虚拟机 1 安装qemu sudo apt get inst