ffutop

概述

之所以想要了解 Linux Kernel,与前一个月的 JVM ClassFile 的学习有这很大的关联。

说实话,刨除 JVM 的具体实现,Sun (或者现在该说是 Oracle) 确实将 Java 的底层逻辑设计得相当简单。

  1. 有限而统一的指令集(不超过 256 个,可以用 1 字节表示)
  2. 操作数栈+局部变量表共同实现的指令运算
  3. 高度封装的成员变量/方法的寻址方式
  4. ... (见识短浅,想不到了...以后再补充吧)

但是,与 JVM 模拟的虚拟机不同,实体机器有着更为复杂的结构。 最根本的,不同厂商的机器就带来了不同的 CPU 指令集,这就已经让人难以接受了。

其实最初,是想要继续去看看 Hotspot 虚拟机的。但是,混杂的代码(C++, Java, 平台相关各种实现) 带来了很大的阅读障碍。 最根本的,我甚至找不到一个学习的基本立足点,出发点(当然,也可能我根本就没仔细去看,哈哈)。

借着一次机会,我开始看《程序员的自我修养——链接、装载与库》一书。确实无论是 ELF 格式,静态链接与动态链接甚至 Linux 的内建函数 都给了我比较深刻的印象,向我展示了 C 更深入的一面。但是,macOS 给我带来了比较大的客观阻碍(即使用 Docker 容器得到了一个可用的 Linux 环境,但是否与直接建立在机器上的系统有所区别,此处还得打个问号)。 同时,书中的部分内容上下不统一,缺少前后文也是一个重大的问题。总之,这并不完全适合我这种初学者逐一进行书中所描述的全部实验。

最后,我决定展开对 Linux Kernel 的学习,试图通过对直接构架在硬件上的操作系统进行一番比较深入的学习。

期间,找过一些资料,也了解到 Linus Torvals 直接领导着的 Kernel 项目的官网;甚至,找到了各个版本的 kernel 源码(虽然确实地丢失了最早期 0.XX 的若干版本)。 不得不说,就目前来讲,我觉得《Linux内核完全注释》是最适合(打个问号,至少暂时是的)我学习的一书。

之后的若干博文,将都以《Linux内核完全注释》中的内容为基础,结合目前的实际需要,从而进行实操性的认知与学习。

软硬件描述

进行这方面的描述,是因为 Linux Kernel 将直接与底层硬件打交道(当然,这方面使用的 Bochs 仿真器)。 但是,准备诸如 .img (磁盘映像文件), 机器指令文件等都不得不借助于现有的平台。

  • 操作系统: macOS 10.13.6 , Ubuntu 16.04 (Docker 虚拟机容器)
  • 仿真器 : Bochs 2.6.9_2 (macOS 与 Ubuntu 上相同)
  • 更多 : 将直接在正文首次使用到时进行说明

Bochs

Bochs 官网

Bochs 下载链

说实话这一周都差不多要被 Bochs 折腾死,用源码进行编译出现各种各样的问题。(我猜新版本根本就没考虑对 macOS 做兼容)

最后,不得不使用经编译后的二进制分发版。 brew install bochs 当然了,反正也能用,就不纠结非得自己编译了。(如果哪位能成功编译个 macOS 可用的应用,希望能把踩得坑整理整理,供后来者参考)

目录结构

.
├── CHANGES
├── COPYING
├── INSTALL_RECEIPT.json
├── LICENSE
├── README
├── TODO
├── bin
│   ├── bochs                           // bochs 可执行文件
│   └── bximage                         // 制作磁盘映像文件的工具
├── lib                                 // 动态库目录
│   └── bochs
│       └── plugins
│           ├── libbx_acpi.0.0.0.so
│           ├── ... 略
│           └── libbx_vga.so -> libbx_vga.0.0.0.so
└── share                               // 与体系结构无关的文件放在此目录下
    ├── bochs
    │   ├── BIOS-bochs-latest
    │   ├── BIOS-bochs-legacy
    │   ├── SeaBIOS-README
    │   ├── VGABIOS-elpin-2.40
    │   ├── VGABIOS-elpin-LICENSE
    │   ├── VGABIOS-lgpl-README
    │   ├── VGABIOS-lgpl-latest
    │   ├── VGABIOS-lgpl-latest-cirrus
    │   ├── VGABIOS-lgpl-latest-cirrus-debug
    │   ├── VGABIOS-lgpl-latest-debug
    │   ├── bios.bin-1.7.5
    │   └── keymaps
    │       ├── sdl-pc-us.map
    │       ├── ... 略
    │       └── x11-pc-us.map
    ├── doc
    │   └── bochs
    │       ├── CHANGES
    │       ├── COPYING
    │       ├── LICENSE
    │       ├── README
    │       ├── TODO
    │       ├── bochsrc-sample.txt      // bochsrc 配置文件的示例样板
    │       └── slirp.conf
    └── man
        ├── man1
        │   ├── bochs-dlx.1.gz
        │   ├── bochs.1.gz
        │   └── bximage.1.gz
        └── man5
            └── bochsrc.5.gz

简单使用

Bochs 下载链提供了 Bochs 不同版本的源码的下载,同时也提供了一些可用的磁盘映像文件(都是一些已经经过处理,可用使用进行仿真运行的 OS )

说实话,这是绝对有必要进行的一步,不仅仅是了解配置文件 bochsrc 的配置方式,更可用借此了解一下仿真器的使用方式。(在启动系统上,我也被坑了一天,后面细说)

首先,这里演示的将是 DLX Linux 。

  1. 下载,解压,进入目录。

  2. 目录下文件如下

.
├── bochsrc.txt         // bochs 启动时将默认通过这个文件读取仿真器配置
├── hd10meg.img         // 磁盘映像文件
├── readme.txt
└── testform.txt
  1. 启动仿真器

在 DLX Linux 目录下键入命令 bochs, 观察到命令行输出:

========================================================================
                       Bochs x86 Emulator 2.6.9
               Built from SVN snapshot on April 9, 2017
                  Compiled on May  2 2018 at 13:26:32
========================================================================
00000000000i[      ] LTDL_LIBRARY_PATH not set. using compile time default '/usr/local/Cellar/bochs/2.6.9_2/lib/bochs/plugins'
00000000000i[      ] BXSHARE not set. using compile time default '/usr/local/Cellar/bochs/2.6.9_2/share/bochs'
00000000000i[      ] lt_dlhandle is 0x7fef2252a500
00000000000i[PLUGIN] loaded plugin libbx_usb_common.so
00000000000i[      ] lt_dlhandle is 0x7fef2252a8a0
00000000000i[PLUGIN] loaded plugin libbx_unmapped.so
00000000000i[      ] lt_dlhandle is 0x7fef2252ad00
00000000000i[PLUGIN] loaded plugin libbx_biosdev.so
00000000000i[      ] lt_dlhandle is 0x7fef2252b1e0
00000000000i[PLUGIN] loaded plugin libbx_speaker.so
00000000000i[      ] lt_dlhandle is 0x7fef2252ba50
00000000000i[PLUGIN] loaded plugin libbx_extfpuirq.so
00000000000i[      ] lt_dlhandle is 0x7fef2252be70
00000000000i[PLUGIN] loaded plugin libbx_parallel.so
00000000000i[      ] lt_dlhandle is 0x7fef238013c0
00000000000i[PLUGIN] loaded plugin libbx_serial.so
00000000000i[      ] lt_dlhandle is 0x7fef22706cd0
00000000000i[PLUGIN] loaded plugin libbx_iodebug.so
00000000000i[      ] reading configuration from bochsrc.txt
------------------------------
Bochs Configuration: Main Menu
------------------------------

This is the Bochs Configuration Interface, where you can describe the
machine that you want to simulate.  Bochs has already searched for a
configuration file (typically called bochsrc.txt) and loaded it if it
could be found.  When you are satisfied with the configuration, go
ahead and start the simulation.

You can also start bochs with the -q option to skip these menus.

1. Restore factory default configuration
2. Read options from...
3. Edit options
4. Save options to...
5. Restore the Bochs state from...
6. Begin simulation
7. Quit now

Please choose one: [6]

bochs 默认读取当前目录下 bochsrc.txt 文件,因此不需要其他配置。

选择 6 或者直接 回车

00000000000i[      ] lt_dlhandle is 0x7fef22707270
00000000000i[PLUGIN] loaded plugin libbx_sdl2.so
00000000000i[      ] installing sdl2 module as the Bochs GUI
00000000000i[SDL2  ] maximum host resolution: x=2880 y=1800
00000000000i[      ] using log file bochsout.txt
Next at t=0
(0) [0x0000fffffff0] f000:fff0 (unk. ctxt): jmpf 0xf000:e05b          ; ea5be000f0
<bochs:1>

终端出现更多输出,同时 Bochs 开启了一个可视化终端(当然,现在还没有任何内容)。

在这儿被坑了好久。本来以为 Bochs 会直接执行 BIOS 指令,启动操作系统,但是自始至终,停在这里就没动过...

这边是由于 Bochs 本身是支持调试的(T_T,见鬼),就像是 GDB 和 LLDB 一样,进入调试后,需要指令 c (continue) 来继续执行(当然,还有其它调试命令)

  1. 键入 c
<bochs:1> c

仿真器开始引导程序的加载

OS Kernel 启动,等待用户登录 (默认的用户是 root , 没有密码)。 之后就和普通的 Linux 机器类似,不过支持的命令比较少。毕竟是一个精简后的系统。

  1. 关机

仿真器仿真了许多实体机案件,右上角最后一个就是关机键

Docker 容器中运行 Ubuntu

这部分不详述,作为一个热门的技术,这方面的资料很多。Ubuntu 系统只是为了来执行一些 macOS 上没有的命令的(比如 as86, ld86)

简述两个 docker 容器和宿主机间复制文件的命令

在宿主机下执行命令:

docker cp [OPTIONS] CONTAINER:SRC_PATH DEST_PATH|-
docker cp [OPTIONS] SRC_PATH|- CONTAINER:DEST_PATH [flags]

例如:

docker cp linux:/root/hello.c ./hello.c         # 这里 linux 是 docker 容器名
docker cp 92dfc8ad70e1:/root/hello.c ./hello.c  # 这里 92dfc8ad70e1 是 docker 容器 ID

参考

[1]. 俞甲子. 程序员的自我修养[M]. 电子工业出版社, 2009. [2]. 赵炯. Linux内核完全注释 内核版本0.11 V3.0[EB/OL]. http://www.oldlinux.org/, 2007

  __                    __                  
 / _| __ _ _ __   __ _ / _| ___ _ __   __ _ 
| |_ / _` | '_ \ / _` | |_ / _ \ '_ \ / _` |
|  _| (_| | | | | (_| |  _|  __/ | | | (_| |
|_|  \__,_|_| |_|\__, |_|  \___|_| |_|\__, |
                 |___/                |___/