抄写Linux源码(Day19:读取硬盘前的准备工作有哪些?)

news2024/11/19 3:22:05

回忆我们需要做的事情:
为了支持 shell 程序的执行,我们需要提供:
1.缺页中断(不理解为什么要这个东西,只是闪客说需要,后边再说)
2.硬盘驱动、文件系统 (shell程序一开始是存放在磁盘里的,所以需要这两个东西)
3.fork,execve, wait 这三个系统调用,也可以说是 进程调度 (否则无法 halt shell 程序并且启动另外的程序)
4.键盘驱动、VGA/console/uart 驱动、中断处理 (支持键盘输入和屏幕显示)
5.内存管理 (shell 启动其它进程时,不能共用内存,而是切换其它进程的页表) — 完成内核内存管理
6.为了写代码方便,我们需要从 MBR 进入到 main 函数,这也是从 汇编 切换到 C 语言 — 已经完成
7.应用程序申请内存的接口

看闪客文章 “第16回 | 按下键盘后为什么屏幕上就会有输出”

书接上回,上回书咱们说到,继内存管理结构 mem_map 和中断描述符表 idt 建立好之后,我们又在内存中倒腾出一个新的数据结构 request。

在这里插入图片描述
并且把它们都放在了一个数组中。

在这里插入图片描述
这是块设备驱动程序与内存缓冲区的桥梁,通过它可以完整地表示一个块设备读写操作要做的事。

我们继续往下看,tty_init。

void main(void) {
    ...
    mem_init(main_memory_start,memory_end);
    trap_init();
    blk_dev_init();
    chr_dev_init();
    tty_init();
    time_init();
    sched_init();
    buffer_init(buffer_memory_end);
    hd_init();
    floppy_init();
    
    sti();
    move_to_user_mode();
    if (!fork()) {init();}
    for(;;) pause();
}

这个方法执行完成之后,我们将会具备键盘输入到显示器输出字符这个最常用的功能。

打开这个函数后我有点慌。

void tty_init(void)
{
    rs_init();
    con_init();
}

看来这个方法已经多到需要拆成两个子方法了。

打开第一个方法,还好。

void rs_init(void)
{
    set_intr_gate(0x24,rs1_interrupt);
    set_intr_gate(0x23,rs2_interrupt);
    init(tty_table[1].read_q.data);
    init(tty_table[2].read_q.data);
    outb(inb_p(0x21)&0xE7,0x21);
}

这个方法是串口中断的开启,以及设置对应的中断处理程序,串口在我们现在的 PC 机上已经很少用到了,所以这个直接忽略,要讲我也不懂。

看第二个方法,这是重点。代码非常长,有点吓人,我先把大体框架写出。

void con_init(void) {
    ...
    if (ORIG_VIDEO_MODE == 7) {
        ...
        if ((ORIG_VIDEO_EGA_BX & 0xff) != 0x10) {...}
        else {...}
    } else {
        ...
        if ((ORIG_VIDEO_EGA_BX & 0xff) != 0x10) {...}
        else {...}
    }
    ...
}

可以看出,非常多的 if else。

这是为了应对不同的显示模式,来分配不同的变量值,那如果我们仅仅找出一个显示模式,这些分支就可以只看一个了。

啥是显示模式呢?那我们得简单说说显示,一个字符是如何显示在屏幕上的呢?换句话说,如果你可以随意操作内存和 CPU 等设备,你如何操作才能使得你的显示器上,显示一个字符‘a’呢?

我们先看一张图。

在这里插入图片描述
内存中有这样一部分区域,是和显存映射的。啥意思,就是你往上图的这些内存区域中写数据,相当于写在了显存中。而往显存中写数据,就相当于在屏幕上输出文本了。

没错,就是这么简单。

如果我们写这一行汇编语句。

mov [0xB8000],'h'

后面那个 h 相当于汇编编辑器帮我们转换成 ASCII 码的二进制数值,当然我们也可以直接写。

mov [0xB8000],0x68

其实就是往内存中 0xB8000 这个位置写了一个值,只要一写,屏幕上就会是这样。

在这里插入图片描述
简单吧,具体说来,这片内存是每两个字节表示一个显示在屏幕上的字符,第一个是字符的编码,第二个是字符的颜色,那我们先不管颜色,如果多写几个字符就像这样。

mov [0xB8000],'h'
mov [0xB8002],'e'
mov [0xB8004],'l'
mov [0xB8006],'l'
mov [0xB8008],'o'

此时屏幕上就会是这样。

在这里插入图片描述
是不是贼简单?那我们回过头看刚刚的代码,我们就假设显示模式是我们现在的这种文本模式,那条件分支就可以去掉好多。

代码可以简化成这个样子。

#define ORIG_X          (*(unsigned char *)0x90000)
#define ORIG_Y          (*(unsigned char *)0x90001)
void con_init(void) {
    register unsigned char a;
    // 第一部分 获取显示模式相关信息
    video_num_columns = (((*(unsigned short *)0x90006) & 0xff00) >> 8);
    video_size_row = video_num_columns * 2;
    video_num_lines = 25;
    video_page = (*(unsigned short *)0x90004);
    video_erase_char = 0x0720;
    // 第二部分 显存映射的内存区域 
    video_mem_start = 0xb8000;
    video_port_reg  = 0x3d4;
    video_port_val  = 0x3d5;
    video_mem_end = 0xba000;
    // 第三部分 滚动屏幕操作时的信息
    origin  = video_mem_start;
    scr_end = video_mem_start + video_num_lines * video_size_row;
    top = 0;
    bottom  = video_num_lines;
    // 第四部分 定位光标并开启键盘中断
    gotoxy(ORIG_X, ORIG_Y);
    set_trap_gate(0x21,&keyboard_interrupt);
    outb_p(inb_p(0x21)&0xfd,0x21);
    a=inb_p(0x61);
    outb_p(a|0x80,0x61);
    outb(a,0x61);
}

别看这么多,一点都不难。

首先还记不记得之前汇编语言的时候做的工作,存了好多以后要用的数据在内存中。就在 第五回 | 进入保护模式前的最后一次折腾内存

在这里插入图片描述
所以,第一部分获取 0x90006 地址处的数据,就是获取显示模式等相关信息。

第二部分就是显存映射的内存地址范围,我们现在假设是 CGA 类型的文本模式,所以映射的内存是从 0xB8000 到 0xBA000。

第三部分是设置一些滚动屏幕时需要的参数,定义顶行和底行是哪里,这里顶行就是第一行,底行就是最后一行,很合理。

第四部分是把光标定位到之前保存的光标位置处(取内存地址 0x90000 处的数据),然后设置并开启键盘中断。

开启键盘中断后,键盘上敲击一个按键后就会触发中断,中断程序就会读键盘码转换成 ASCII 码,然后写到光标处的内存地址,也就相当于往显存写,于是这个键盘敲击的字符就显示在了屏幕上。

在这里插入图片描述
这一切具体是怎么做到的呢?我们先看看我们干了什么。

  1. 我们现在根据已有信息已经可以实现往屏幕上的任意位置写字符了,而且还能指定颜色。
  2. 并且,我们也能接受键盘中断,根据键盘码中断处理程序就可以得知哪个键按下了。

有了这俩功能,那我们想干嘛还不是为所欲为?

好,接下来我们看看代码是怎么处理的,很简单。一切的起点,就是第四步的 gotoxy 函数,定位当前光标。

#define ORIG_X          (*(unsigned char *)0x90000)
#define ORIG_Y          (*(unsigned char *)0x90001)
void con_init(void) {
    ...
    // 第四部分 定位光标并开启键盘中断
    gotoxy(ORIG_X, ORIG_Y);
    ...
}

这里面干嘛了呢?

static inline void gotoxy(unsigned int new_x,unsigned int new_y) {
   ...
   x = new_x;
   y = new_y;
   pos = origin + y*video_size_row + (x<<1);
}

就是给 x y pos 这三个参数附上了值。

其中 x 表示光标在哪一列,y 表示光标在哪一行,pos 表示根据列号和行号计算出来的内存指针,也就是往这个 pos 指向的地址处写数据,就相当于往控制台的 x 列 y 行处写入字符了,简单吧?

然后,当你按下键盘后,触发键盘中断,之后的程序调用链是这样的。

_keyboard_interrupt:
    ...
    call _do_tty_interrupt
    ...
    
void do_tty_interrupt(int tty) {
   copy_to_cooked(tty_table+tty);
}

void copy_to_cooked(struct tty_struct * tty) {
    ...
    tty->write(tty);
    ...
}

// 控制台时 tty 的 write 为 con_write 函数
void con_write(struct tty_struct * tty) {
    ...
    __asm__("movb _attr,%%ah\n\t"
      "movw %%ax,%1\n\t"
      ::"a" (c),"m" (*(short *)pos)
      :"ax");
     pos += 2;
     x++;
    ...
}

前面的过程不用管,我们看最后一个函数 con_write 中的关键代码。

asm 内联汇编,就是把键盘输入的字符 c 写入 pos 指针指向的内存,相当于往屏幕输出了。

之后两行 pos+=2 和 x++,就是调整所谓的光标。

你看,写入一个字符,最底层,其实就是往内存的某处写个数据,然后顺便调整一下光标。

由此我们也可以看出,光标的本质,其实就是这里的 x y pos 这仨变量而已。

我们还可以做换行效果,当发现光标位置处于某一行的结尾时(这个应该很好算吧,我们都知道屏幕上一共有几行几列了),就把光标计算出一个新值,让其处于下一行的开头。

就一个小计算公式即可搞定,仍然在 con_write 源码处有体现,就是判断列号 x 是否大于了总列数。

void con_write(struct tty_struct * tty) {
    ...
    if (x>=video_num_columns) {
        x -= video_num_columns;
        pos -= video_size_row;
        lf();
  }
  ...
}

static void lf(void) {
   if (y+1<bottom) {
      y++;
      pos += video_size_row;
      return;
   }
 ...
}

相似的,我们还可以实现滚屏的效果,无非就是当检测到光标已经出现在最后一行最后一列了,那就把每一行的字符,都复制到它上一行,其实就是算好哪些内存地址上的值,拷贝到哪些内存地址,就好了。

这里大家自己看源码寻找。

所以,有了这个初始化工作,我们就可以利用这些信息,弄几个小算法,实现各种我们常见控制台的操作。

或者换句话说,我们见惯不怪的控制台,回车、换行、删除、滚屏、清屏等操作,其实底层都要实现相应的代码的。

所以 console.c 中的其他方法就是做这个事的,我们就不展开每一个功能的方法体了,简单看看有哪些方法。

// 定位光标的
static inline void gotoxy(unsigned int new_x, unsigned int new_y){}
// 滚屏,即内容向上滚动一行
static void scrup(void){}
// 光标同列位置下移一行
static void lf(int currcons){}
// 光标回到第一列
static void cr(void){}
...
// 删除一行
static void delete_line(void){}

内容繁多,但没什么难度,只要理解了基本原理即可了。

OK,整个 console.c 就讲完了,要知道这个文件可是整个内核中代码量最大的文件,可是功能特别单一,也都很简单,主要是处理键盘各种不同的按键,需要写好多 switch case 等语句,十分麻烦,我们这里就完全没必要去展开了,就是个苦力活。

到这里,我们就正式讲完了 tty_init 的作用。

在此之后,内核代码就可以用它来方便地在控制台输出字符啦!这在之后内核想要在启动过程中告诉用户一些信息,以及后面内核完全建立起来之后,由用户用 shell 进行操作时手动输入命令,都是可以用到这里的代码的!

让我们继续向前进发,看下一个被初始化的倒霉鬼是什么东东。

欲知后事如何,且听下回分解。

看完闪客文章 “第16回 | 按下键盘后为什么屏幕上就会有输出”

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

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

相关文章

如何在VMware workstation虚拟机中安装ensp(完美运行),解决报错40

如何在VMware workstation虚拟机中安装ensp&#xff08;完美运行&#xff09; 效果如图&#xff1a; 问题体现是ensp的路由器报错40&#xff0c;这是vbox相关的问题 在虚拟机中安装ensp的关键其实是vbox&#xff08;ensp依赖vbox&#xff09; ensp自带的vbox版本太低&#x…

想要精通算法和SQL的成长之路 - 存在重复元素

想要精通算法和SQL的成长之路 - 存在重复元素 前言一. 存在重复元素II二. 存在重复元素III2.1 基于红黑树增删改查 前言 想要精通算法和SQL的成长之路 - 系列导航 一. 存在重复元素II 原题链接 思路&#xff1a; 我们用HashSet存储元素&#xff0c;做到去重的效果。同时存储…

防火墙的相关技术

安全技术&#xff1a;&#xff08;市场上常见的防御&#xff09; 1.入侵检测机制 阻断&#xff0c;量化&#xff0c;定位来自内外的网络的威胁情况 提供报警和事后监督。类似于监控。 2.入侵防御 以透明模式工作&#xff0c;分析数据包的内容&#xff0c;一切进入本机的内容…

windows 任务计划自动提交 笔记到github 、gitee

一、必须有个git仓库托管到git上。 这个就不用说了&#xff0c;自己在github或者码云上新建一个仓库就行了。 二、创建自动提交脚本 这个bat脚本是在windows环境下使用的。 注意&#xff1a;windows定时任务下 调用自动提交git前&#xff0c;必须先进入该git仓库目录&#x…

Windows11 安全中心页面不可用问题(无法打开病毒和威胁防护)解决方案汇总(图文介绍版)

本文目录 Windows版本与报错信息问题详细图片&#xff1a; 解决方案:方案一、管理员权限&#xff08;若你确定你的电脑只有你一个账户&#xff0c;则此教程无效&#xff0c;若你也不清楚&#xff0c;请阅读后再做打算&#xff09;方案二、修改注册表(常用方案)方案三、进入开发…

服务器or虚拟机安装SSH和虚拟机or服务器设置远程服务权限

第一步 服务器/虚拟机安装SSH工具,这是外部SSH终端连接服务器/虚拟机的第一步! sudo apt update && sudo apt upgrade#更新apt sudo apt install openssh-server#安装SSH工具 service ssh status#查看SSh运行状态 sudo systemctl enable --now ssh#运行SSH工具第二步…

基于SpringBoot的信息化在线教学平台的设计与实现

目录 前言 一、技术栈 二、系统功能介绍 学生信息管理 教师信息管理 学生成绩管理 留言板 学生注册管理 留言反馈 三、核心代码 1、登录模块 2、文件上传模块 3、代码封装 前言 随着信息技术在管理上越来越深入而广泛的应用&#xff0c;管理信息系统的实施在技术上已…

各平台更新根证书

windows7 windows7根证书更新 https://support.microsoft.com/en-us/topic/support-for-urgent-trusted-root-updates-for-windows-root-certificate-program-in-windows-a4ac4d6c-7c62-3b6e-dfd2-377982bf3ea5 Linux 查看证书 ls -l /etc/ssl/certs/更新根证书 update-ca…

Cocos Creator3.8 项目实战(七)Listview 控件的实现和使用

滚动列表在游戏中也很常见&#xff0c;比如排行榜 、充值记录等&#xff0c;在这些场景中&#xff0c;都有共同的特点&#xff0c; 那就是&#xff1a;数据量大 &#xff0c; 结构相同。 在cocoscreator 中&#xff0c;没有现成的 Listview 控件&#xff0c; 无奈之下&#xff…

[python 刷题] 4 Median of Two Sorted Arrays

[python 刷题] 4 Median of Two Sorted Arrays 题目&#xff1a; Given two sorted arrays nums1 and nums2 of size m and n respectively, return the median of the two sorted arrays. The overall run time complexity should be O ( l o g ( m n ) ) O(log (mn)) O(lo…

lv8 嵌入式开发-网络编程开发 14

目录 1 I/O基本概念 1.1 IO概念 1.2 同步和异步 1.3 阻塞和非阻塞 2 五种I/O模型 2.1 阻塞IO 2.2 非阻塞I/O 2.3 多路复用I/O ​编辑 2.4 信号驱动式I/O ​编辑 2.5 异步I/O模型​编辑 3 五种I/O模型比较 4 练习 1 I/O基本概念 1.1 IO概念 I/O即数据的读取&#x…

功能定义-前方交通穿行提示制动

功能概述 前方交通穿行提示(Front Cross Traffic Alert)&#xff0c;简称FCTA&#xff0c;其功能表现为在车辆低速前进时&#xff0c;实时监测车辆前部横向接近的其他道路使用者&#xff0c;并在可能发生碰撞风险时发出警告信息 前方交通穿行制动(Front Cross Traffic Braking…

算法题:分发饼干

这个题目是贪心算法的基础练习题&#xff0c;解决思路是排序双指针谈心法&#xff0c;先将两个数组分别排序&#xff0c;优先满足最小胃口的孩子。&#xff08;本题完整题目附在了最后面&#xff09; 代码如下&#xff1a; class Solution(object):def findContentChildren(se…

【Unity ShaderGraph】| 快速制作一个实用的 模型溶解效果

前言 【Unity ShaderGraph】| 快速制作一个实用的 模型溶解效果一、效果展示二、简易溶解效果三、进阶溶解效果四、应用实例 前言 本文将使用ShaderGraph制作一个模型溶解的效果&#xff0c;可以直接拿到项目中使用。对ShaderGraph还不了解的小伙伴可以参考这篇文章&#xff1…

【Docker内容大集合】Docker从认识到实践再到底层原理大汇总

前言 那么这里博主先安利一些干货满满的专栏了&#xff01; 首先是博主的高质量博客的汇总&#xff0c;这个专栏里面的博客&#xff0c;都是博主最最用心写的一部分&#xff0c;干货满满&#xff0c;希望对大家有帮助。 高质量博客汇总https://blog.csdn.net/yu_cblog/categ…

Android SurfaceFlinger导读(04)理解BufferQueue

该系列文章总纲链接&#xff1a;Android GUI系统之SurfaceFlinger 系列文章目录 说明&#xff1a; 关于导读&#xff1a;导读部分主要是方便初学者理解SurfaceFlinger代码中的机制&#xff0c;为后面分析代码打下一个更好的基础&#xff0c;这样就可以把更多的精力放在surfac…

使用opencv及FFmpeg编辑视频

使用opencv及FFmpeg编辑视频 1.融合两个视频2.为视频添加声音2.1 安装ffmpy Python包2.2 下载ffmpeg2.3 代码实现 3.效果参考文献 帮朋友做了一个小作业&#xff0c;具体实现分为几个过程&#xff1a; 将两个mp4格式视频融合到一起为新视频添加声音 1.融合两个视频 其中一个…

AI智能创作系统ChatGPT商业运营源码+AI绘画系统/支持GPT联网提问/支持Midjourney绘画+支持国内AI提问模型+Prompt应用

一、AI创作系统 SparkAi创作系统是基于OpenAI很火的ChatGPT进行开发的Ai智能问答系统&#xff0c;支持国内AI提问模型。本期针对源码系统整体测试下来非常完美&#xff0c;可以说SparkAi是目前国内一款的ChatGPT对接OpenAI软件系统。那么如何搭建部署AI创作ChatGPT&#xff1f…

VSCode Intellij IDEA CE 数据库连接

VSCode & Intellij IDEA CE 数据库连接 大概记一下现在正在用的几个工具/插件 VSCode VSCode 里面的工具我下载了很多&#xff0c;如果只是链接 MySQL 的话&#xff0c;可能用 Jun Han 这位大佬的 MySQL 就好了&#xff1a; 使用这个插件直接打开 .sql 文件单击运行就能…

VMware16.1.2安装及密钥

文章目录 一、VMware 16 虚拟机下载二、安装步骤三、VMware 各版本注册密钥1 、VMware 16密钥2 、VMware 14密钥3 、VMware 15密钥4 、VMware 17密钥 一、VMware 16 虚拟机下载 VMware 16 下载地址&#xff1a; https://www.vmware.com/cn/products/workstation-pro/workstati…