首页 > 其他分享 >xv6操作系统启动过程

xv6操作系统启动过程

时间:2023-04-20 19:14:39浏览次数:40  
标签:定时器 操作系统 启动 中断 xv6 start init mode 寄存器

  1. 当按下系统电源按键后,做一些硬件层面的配置和初始化:

上电复位:在开机时,计算机进行硬件复位,确保寄存器和其他硬件组件处于初始状态。
检查和测试硬件:计算机进行一系列硬件检查和自检操作,以确保硬件组件功能正常。这可能包括内存检测、CPU测试等。
初始化硬件组件:初始化和配置计算机上的各种硬件组件,例如内存控制器、输入输出设备和其他外设。这包括为这些组件提供电源、设置时钟信号等。
读取配置数据:从非易失性存储器(例如CMOS)读取系统配置数据,如BIOS设置、引导设备顺序等。
这些操作通常由计算机的固件(例如BIOS或UEFI)完成。完成硬件和固件的初始化后,

  1. 计算机将运行引导加载程序(boot loader),该程序存储在只读存储器(ROM)中。引导加载程序负责将xv6内核加载到内存,并将执行权交给内核。
    在M模式中,cpu执行内核的起点是 _entry,这个时候虚拟内存还没有工作,所以此时虚拟地址是和物理地址直接映射的
	# qemu -kernel loads the kernel at 0x80000000
        # and causes each CPU to jump there.
        # kernel.ld causes the following code to
        # be placed at 0x80000000.
.section .text
.global _entry
_entry:
	# set up a stack for C.
        # stack0 is declared in start.c,
        # with a 4096-byte stack per CPU.
        # sp = stack0 + (hartid * 4096)
        la sp, stack0
        li a0, 1024*4
	csrr a1, mhartid # 将当前CPU的硬件线程ID(hartid)读取到寄存器a1中。这个ID是唯一的,用于区分不同的CPU。
        addi a1, a1, 1
        mul a0, a0, a1
        add sp, sp, a0
	# jump to start() in start.c
        call start
spin:
        j spin

这段汇编代码中,设置了stack0这个栈,这样就可以调用C代码了。sp栈指针指向stack0 + 4096这个地址(栈顶)。然后调用start(call start)

  1. start函数
// entry.S jumps here in machine mode on stack0.
void
start()
{
  // set M Previous Privilege mode to Supervisor, for mret.
  unsigned long x = r_mstatus();
  x &= ~MSTATUS_MPP_MASK;
  x |= MSTATUS_MPP_S;
  w_mstatus(x);

  // set M Exception Program Counter to main, for mret.
  // requires gcc -mcmodel=medany
  w_mepc((uint64)main);

  // disable paging for now.
  w_satp(0);

  // delegate all interrupts and exceptions to supervisor mode.
  w_medeleg(0xffff);
  w_mideleg(0xffff);
  w_sie(r_sie() | SIE_SEIE | SIE_STIE | SIE_SSIE);

  // configure Physical Memory Protection to give supervisor mode
  // access to all of physical memory.
  w_pmpaddr0(0x3fffffffffffffull);
  w_pmpcfg0(0xf);

  // ask for clock interrupts.
  timerinit();

  // keep each CPU's hartid in its tp register, for cpuid().
  int id = r_mhartid();
  w_tp(id);

  // switch to supervisor mode and jump to main().
  asm volatile("mret");
}

先做一些只能在M mode做的配置,然后进入S mode.
riscv提供了mret这个指令来实现这个操作。
mret指令的功能是:从S mode进入M mode时,在M mode调用这个mret指令,将会返回到S mode。
此时,虽然在M mode,但是并不是从S mode进来的。

所以,将mstatus设置为S mode,表示此前的特权模式是 supervisor。
然后,把返回地址设置为main,通过把main的地址写到mepc
然后,禁止虚拟地址翻译,通过把0这个值写入satp(页表寄存器)
然后,把所有的中断和异常交给S mode
然后,对时钟芯片进行编程,以便产生定时器中断。

对时钟芯片编程意味着配置时钟芯片的相关寄存器,以便在特定时间间隔后产生定时器中断。定时器中断在操作系统中非常重要,因为它们使操作系统能够实现多任务调度、计时等功能。操作系统可以根据需要设置定时器中断的间隔,从而控制时间片长度或其他相关参数。

在xv6操作系统中,对时钟芯片编程主要涉及以下几个步骤:

选择一个合适的时间间隔,用于触发定时器中断。这通常基于处理器的时钟频率和期望的中断频率来计算。
将计算出的时间间隔值写入时钟芯片的相关寄存器。在RISC-V平台上,这通常涉及编程mtimecmp寄存器。mtimecmp寄存器存储了下一个定时器中断的时间值。每当mtime寄存器的值达到mtimecmp寄存器的值时,时钟芯片就会产生一个定时器中断。
配置处理器以接收时钟芯片产生的定时器中断。这可能涉及设置中断控制器的相关寄存器,例如启用定时器中断并配置优先级等。
完成上述操作后,时钟芯片将定期产生定时器中断。操作系统会在每个中断发生时执行特定的中断处理程序,例如更新系统计时器、调度进程等。这使得操作系统能够有效地进行多任务处理、资源管理和时间跟踪。

设置时钟芯片的代码(暂时不重要)

// set up to receive timer interrupts in machine mode,
// which arrive at timervec in kernelvec.S,
// which turns them into software interrupts for
// devintr() in trap.c.
void
timerinit()
{
  // each CPU has a separate source of timer interrupts.
  int id = r_mhartid();

  // ask the CLINT for a timer interrupt.
  int interval = 1000000; // cycles; about 1/10th second in qemu.
  *(uint64*)CLINT_MTIMECMP(id) = *(uint64*)CLINT_MTIME + interval;

  // prepare information in scratch[] for timervec.
  // scratch[0..2] : space for timervec to save registers.
  // scratch[3] : address of CLINT MTIMECMP register.
  // scratch[4] : desired interval (in cycles) between timer interrupts.
  uint64 *scratch = &timer_scratch[id][0];
  scratch[3] = CLINT_MTIMECMP(id);
  scratch[4] = interval;
  w_mscratch((uint64)scratch);

  // set the machine-mode trap handler.
  w_mtvec((uint64)timervec);

  // enable machine-mode interrupts.
  w_mstatus(r_mstatus() | MSTATUS_MIE);

  // enable machine-mode timer interrupts.
  w_mie(r_mie() | MIE_MTIE);
}

最后,返回到main

  1. main
    main函数初始化设备和子系统;main调用userinit创建第一个进程;第一个进程执行initcode.S中的RISC-V汇编代码。
// Set up first user process.
void
userinit(void)
{
  struct proc *p;

  p = allocproc();
  initproc = p;
  
  // allocate one user page and copy init's instructions
  // and data into it.
  uvminit(p->pagetable, initcode, sizeof(initcode));
  p->sz = PGSIZE;

  // prepare for the very first "return" from kernel to user.
  p->trapframe->epc = 0;      // user program counter
  p->trapframe->sp = PGSIZE;  // user stack pointer

  safestrcpy(p->name, "initcode", sizeof(p->name));
  p->cwd = namei("/");

  p->state = RUNNABLE;

  release(&p->lock);
}

initcode.S加载SYS_EXEC到寄存器a7。
initcode.S调用ecall指令,使CPU重新进入内核。

# Initial process that execs /init.
# This code runs in user space.

#include "syscall.h"

# exec(init, argv)
.globl start
start:
        la a0, init
        la a1, argv
        li a7, SYS_exec
        ecall

# for(;;) exit();
exit:
        li a7, SYS_exit
        ecall
        jal exit

# char init[] = "/init\0";
init:
  .string "/init\0"

# char *argv[] = { init, 0 };
.p2align 2
argv:
  .long init
  .long 0

当ecall指令被执行时,CPU将重新进入内核,处理发起的exec系统调用。exec系统调用用于加载和运行一个用户程序,替换当前进程的内存映像。在这种情况下,第一个进程将加载和运行xv6操作系统中定义的初始用户程序。

在这段代码中,exec读取到的file是init,这就是初始化程序,创建了一个新的console,和一些fd。打开fd 0,1,2,打开shell。

标签:定时器,操作系统,启动,中断,xv6,start,init,mode,寄存器
From: https://www.cnblogs.com/ijpq/p/17337986.html

相关文章

  • hive 使用JDBC访问 和启动远程端口
      0:hiveservice开启远程访问端口: hive使用jdbc进行访问,要求我们必须开启远程可以访问的端口,其中端口11000是随机的,可以是10002或者别的端口,这设定好好java代码链接时候就要使用开启好的端口。  linuxshell下执行hive--servicehiveserver-p11000>/dev/null2>&1......
  • 数据中心厂商超云加入龙蜥社区,多款服务器完成与龙蜥操作系统适配
    近日,长城超云(北京)科技有限公司(以下简称“超云”)签署了CLA(ContributorLicenseAgreement,贡献者许可协议),正式加入龙蜥社区(OpenAnolis)。超云是由CEC中国电子、云基地产业基金联合注资成立的国家高新技术企业,其专注于云基础架构、云系统、云服务三大产业布局,现已形成了覆盖X86服......
  • 如何让 Windows 应用程序在 Parallels Desktop 中启动得更快
    经过十多年的发展,ParallelsDesktop在性能方面得到了突飞猛进的提升。但俗话说,“没有最快只有更快。”因此,在这篇文章中,小编将分享您一个小技巧:如何使您的Windows应用程序启动得更快。下图显示了视频中的同一台M1MacBookPro和在InteliMac上测试个过的启动时间。“第......
  • tomcat启动时,加载类
    有时候在开发Web应用的时候,需要tomcat启动后自动加载一个用户的类,执行一些初始化方法,如从数据库中加载业务字典到内存中,因此需要在tomcat启动时就自动加载一个类,或运行一个类的方法。可以采用在WEB-INF/web.xml中添加一个监听程序(ServletContextListener配置......
  • GitLab 服务的启动、停止和重启命令
    GitLab服务的启动、停止和重启命令 一、问题现象在使用GitLab管理项目代码时,有时候可能因为服务出现异常,导致无法正常访问GitLab上的代码。报错如下: 二、解决方案重启GitLab服务,命令如下:gitlab-ctlrestart命令执行效果如下: 三、补充说明 1、GitLab......
  • tomcat6启动报错java.lang.ClassNotFoundException: 1catalina.org.apache.juli.FileH
    评:tomcat6启动报错在apache-tomcat-6.0.26/logs/catalina.out日志里面报错:java.lang.ClassNotFoundException:1catalina.org.apache.juli.FileHandler这个是由于apache-tomcat-6.0.26/bin/catalina.sh文件被修改过了,应该把下面的一行放在-Djava.util.logging.manager的前......
  • 无图形界面的Linux操作系统如何安装在飞机导航系统上?
    支持Linux操作系统的飞机型号是比较有限的像V型飞机只能支持嵌入式操作系统,Linux相对来说太大了更老的2型飞机则更加不可能,它们还在使用批处理系统呢射线型飞机的计算机硬件已经支持了特权级切换,能够运行像xv6这样的操作系统最新的A型飞机为了计算导弹轨迹,能够支持Linux......
  • IDEA 启动tomcat6项目
    目录IDEA启动tomcat6项目导入项目配置项目配置tomcatIDEA启动tomcat6项目接到一个老项目要做达梦数据库适配,老项目用的spring+hibernate,前端用jsp展示,之前用的eclipse开发,eclipse好多年没用了,用IDEA启动。导入项目一路next到底,注意选择对应jdk,我这用的是jdk6配置项目添加......
  • Linux安装nacos并配置开机启动
    一、Nacos的安装、启动1.解压tar-zxvfnacos-server-1.4.1.tar.gz-C/usr/local注意:nacos的启动需要java的jdk环境支持才能成功运行。2.启动进入目录nacos解压包的bin目录:cd/usr/local/nacos/bin开启./startup.sh非集群模式开启:./startup.sh-mstandalone关闭......
  • jar启动文件
    #!/usr/bin/envbashAPP_NAME=pt-resource-web.jarLOG_FILE=/log/console_log/pt-resource-web.logFILE_PATH=/home/application/oceandata/pt_resource/#使用说明,用来提示输入参数usage(){echo"Usage:sh执行脚本.sh[start|stop|restart|status]"exit1}#检查程序......