操作系统实现:断点切换原理及实现

本文参考书:操作系统真像还原、操作系统原型xv6分析与实验、其中图主要来自linux内核完全注释

本文针对断点切换迷茫的问题。

详解内核态-用户态的栈变化, 了解用户态-内核态的实现原理和代码分析

为帮助大家理解,我将模拟断点切换时的栈变化过程。

首先要知道几个基础概念

①调用约定:

C语言是用cdecl 约定,

函数参数从右到左入栈,

参数在栈中传递,EAX、ECX、EDX 寄存器由调用者保存,

其余寄存器由被调用者保存,

函数返回值存储在EAX中。

调用者清理栈空间

②内核态上下文context结构 context {

uint edi;

uint esi;

uint ebx ;

unit ebp ;

uint eip ; }

这个结构的作用在于进程在内核态中执行系统调用时可能出现的进程切换操作调用swtch函数 “后”的 被调用者保存,也就是说swtch是被调用者。

还有内核态执行调度器函数 scheduler,每个CPU都有自己独立的context,用于执行 swtch 函数 “后”的 被调用者保存,可以理解为CPU执行流的上下文。

当内核加载到内存,所有都相关数据等初始化完毕后每个CPU只是无限循环 scheduler 调度函数中的for(;;)死循环,直到调度0号进程,才切换到用户态。(有些操作系统实现不是无限循环,都可以总体上都一样细节有差别)

③ 当用户态切换入内核态出现特权级变换时,会在tss中找到当前进程的内核栈地址并进行切换,在当前进程的内核栈中,会压入 cs :ip、flags、ss:sp 等(这是硬件帮忙完成的)。

操作系统实现:断点切换原理及实现

④ 中断入口,执行任何中断时,优先执行的一段代码

ntr%1entry:         ; 每个中断处理程序都要压入中断向量号,所以一个中断类型一个中断处理程序,自己知道自己的中断向量号是多少

   %2                 ; 中断若有错误码会压在eip后面
; 以下是保存上下文环境
   push ds
   push es
   push fs
   push gs
   pushad             ; PUSHAD指令压入32位寄存器,其入栈顺序是: EAX,ECX,EDX,EBX,ESP,EBP,ESI,EDI

操作系统实现:断点切换原理及实现

执行汇编语言后将栈变成类似上图的形式,每个操作系统都是类似的处理方式但是也稍有不同,本文不需要扣这种细节,只需要知道为了保存用户态上下文 需要压入所有寄存器。 从ss 一直到 esp 这段栈空间也可以称为 用户态断点,用trapframe结构体描述。

一、

我们现在模拟第一个场景,内核已经加载入内存CPU开始执行scheduler 调度函数,此时进程链表中有A 、B 2个进程(实际的操作系统应该是调度init进程)。

操作系统实现:断点切换原理及实现

上图为进程刚刚创建好,但是还没进行调度时,由内核创建的进程内核栈。如果是init进程,那是内核一条条写进去的,否则是fork系统调用所创建的。

为什么新创建的进程要变成这样?

这样是模拟由用户态切换入内核态时的栈情况。

这里要注意,如果是正常的用户态切换入内核态,一般是要执行系统调用, 上图的中断退出函数地址和context结构之间会有很多调用帧,并且中断退出函数地址不会出现在栈中,而且被中断入口函数替换,中断入口函数是第一个调用帧,他在下几条指令会执行中断退出函数。

我们所要关注的就是如何由内核态切换到用户态,就是其中所加入的中断退出函数。

当内核scheduler函数,选中上图的进程pcb,会执行swtch

 1 scheduler(void)
 2 {
 3   struct proc *p;
 4
 5   for(;;){
 6     // Enable interrupts on this processor.

 7     sti();
 8
 9     // Loop over process table looking for process to run.

10     acquire(&ptable.lock);
11     for(p = ptable.proc; p < &ptable.proc[NPROC]; p++){
12       if(p->state != RUNNABLE)
13         continue;
14
15       // Switch to chosen process.  It is the process's job
16       // to release ptable.lock and then reacquire it
17       // before jumping back to us.

18       proc = p;
19       switchuvm(p);
20       p->state = RUNNING;
21       swtch(&cpu->scheduler, p->context);
22       switchkvm();
23
24       // Process is done running for now.

25       // It should have changed its p->state before coming back.

26       proc = 0;
27     }
28     release(&ptable.lock);
29
30   }

swtch 的参数1 old_context ,参数2 new_context

1 .globl swtch
 2 swtch:
 3   movl 4(%esp), %eax    //获得参数old_context
 4   movl 8(%esp), %edx    //获得参数new_context
 5
 6   # Save old callee-save registers
 7   pushl %ebp
 8   pushl %ebx
 9   pushl %esi
10   pushl %edi
11
12   # Switch stacks
13   movl %esp, (%eax)
14   movl %edx, %esp
15
16   # Load new callee-save registers
17   popl %edi
18   popl %esi
19   popl %ebx
20   popl %ebp
21   ret

问题:为什么要调度进程仅仅需要context?

答:因为context是放在栈顶的,通过栈顶就可以得到出内核栈的位置。

老的系统或者一些简单的开源系统,pcb和内核栈是放在同一个页中,可以通过屏蔽有效位计算内核栈位置,现代linux系统pcb是有单独分配器分配,不过也有类似的结构和操作计算偏移。

代码的 7-10行对于本次swtch调用,是压入context到内核的内核栈,为什么没有压入ip?

在内核调度器scheduler → swtch 时,ip作为返回地址已经压入栈中了。 此时ip指向scheduler的第22行代码。

代码13-14 行 重点是这里,此时的栈顶是new_context的地址,那弹出了 17-20后,ret 返回的ip 地址是上图的中断退出函数。由此完成了内核态到用户态的切换。

中断退出函数的作用如下图

操作系统实现:断点切换原理及实现

而此时的内核的内核栈

操作系统实现:断点切换原理及实现

我们现在模拟一个场景,2个进程 A , B,其中A在执行,B就绪。

叮叮叮,第一个时钟中断来啦 。

1 static void intr_timer_handler(void) {
 2    struct task_struct* proc = running_thread();   //获取进程或线程pcb
 3
 4    ASSERT(proc->stack_magic == 0x19870916);         // 检查栈是否溢出
 5
 6    proc->elapsed_ticks++;      // 记录此线程占用的cpu时间嘀
 7    ticks++;      //从内核第一次处理时间中断后开始至今的滴哒数,内核态和用户态总共的嘀哒数
 8
 9    if (proc->ticks == 0) {      // 若进程时间片用完就开始调度新的进程上cpu
10       swtch(proc->context,cpu->context)
11    } else {                  // 将当前进程的时间片-1
12       proc->ticks--;
13    }
14 }

从整体上看调用过程,如下图,中断入口拿中会拿到中断号,然后执行相应的中断也就是时钟中断的中断号。通过swtch 将当前栈顶切换回内核的内核栈,在swtch中执行ret时,下一条程序会执行 scheduler的第22行代码。然后重新调度下一个进程。

操作系统实现:断点切换原理及实现

此时的进程A的内核栈如下图

操作系统实现:断点切换原理及实现

当然如果调用其他中断函数,那中断入扣帧到context结构中间会替换为其他栈帧。

Original: https://www.cnblogs.com/thotf/p/16251401.html
Author: thotf
Title: 操作系统实现:断点切换原理及实现

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/582278/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • 【git】合并分支到主干master

    分支合并到master主干上 1.当前为其他分支切换到主分支上: git checkout [&#x4E3B;&#x5206;&#x652F;&#x…

    Linux 2023年6月13日
    0121
  • Sublime Text 左侧不显示目录树如何解决

    方法一: 依次点击 View – Side Bar – Show Side Side点击后左侧的目录树就会显示出来了,如下图所示方法二:快捷键 Mac : …

    Linux 2023年6月13日
    099
  • 015 Linux 标准输入输出、重定向、管道和后台启动进程命令

    1 三种标准输入输出 2 什么是重定向?如何重定向? (1)什么是重定向? (2)如何重定向? 3 管道符以及和它容易混淆的一些符号使用 (1)管道符 | (2)&和&am…

    Linux 2023年5月27日
    0115
  • 通过过滤器实现前后端分离的跨域问题

    跨域指的是浏览器不能执行其他网站的脚本。它是由浏览器的同源策略造成的,是浏览器对JavaScript施加的安全限制。在做前后端分离项目的时候就需要解决此问题。 创建过滤器解决跨域问…

    Linux 2023年6月7日
    0103
  • 【MQTT】在Linux使用MQTT上报温度到阿里云

    MQTT上报温度到阿里云 * – 前言 – iniparser配置文件 – cJSON – sqlite3数据库 – 流…

    Linux 2023年6月13日
    0107
  • 解决微信Windows客户端无法播放视频问题

    问题描述 我的Windows端微信版本是3.6.0,更新后点开视频,没有播放按钮出现,并且过一会就会卡死,并且整个微信程序崩掉。 问题解决 后来发现,是微信客户端的 播放器插件问题…

    Linux 2023年6月14日
    0377
  • 磁盘操作指令 dd

    dd if=/home/thotf/PaperOS/boot/mbr.bin of=/home/thotf/bochs/hd60M.img bs=512 count=1 conv=…

    Linux 2023年6月7日
    0111
  • NoteOfMySQL-12-备份与还原

    一、备份概述 备份不是单纯的复制数据,因为这样无法留下历史记录和系统的DNS或Registry等信息。完整的备份应包括自动化的数据管理与系统的全面恢复,即备份=复制+管理。 1. …

    Linux 2023年6月14日
    076
  • Git

    什么是Git Git是用C语言开发的分布式版本控制系统,所谓版本控制系统,就是可以储存一个文件在不同时间的版本,记录每次文件的改动,可以根据需要,随时切换到之前的版本(比如在编写W…

    Linux 2023年6月7日
    099
  • Nginx/Tengine安装配置详解

    1 概念 Nginx (“engine x”) 是一个高性能的 HTTP 和 反向代理 服务器,也是一个 IMAP/POP3/SMTP 代理服务器。官方测试…

    Linux 2023年5月27日
    0155
  • Linux 常用命令总结(三)

    一、实用命令 1、crontab(定时任务) (1)基本概念crontab 是用来管理定时任务的命令。系统启动后,将会自动调用 crontab,如果存在任务,则根据相关定义去执行。…

    Linux 2023年5月27日
    0119
  • 最小容器内常用命令安装方法

    首先改成中科大的源 echo “deb http://mirrors.ustc.edu.cn/debian stable main contrib non-free\ndeb ht…

    Linux 2023年6月13日
    0108
  • 【Example】C++ 标准库多线程同步及数据共享 (std::future 与 std::promise)

    否则你会像听天书一样懵。(…) ==================================== 在任何语言的多线程编程当中,必然涉及线程的同步及数据的共享,方…

    Linux 2023年6月13日
    0116
  • 搭配色轮播(CSS进阶版本)

    html;gutter:true; Color</p> <pre><code> /* 自定义颜色 */ /* :root { */ /* –t…

    Linux 2023年6月13日
    079
  • Windows 是最安全的操作系统

    建了一个用户交流群,我在群里说:”Windows 是最安全的操作系统。” 立刻引发了很多有意思的观点。我在群里一个人说不过大家,先篇文章把自己的论点罗列一下…

    Linux 2023年6月14日
    094
  • 相关powerLink教程、配置方法等

    openPowerLink的开发小组早已经解散,所以有些资料都可以在官网上下载到; 这也是最后一次更新了。其中相关powerlink的教程均放在百度网盘里,链接:https://p…

    Linux 2023年6月14日
    0109
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球