Browsed by
Category: Kernel

[Linux 0.11] Draft 6 IA32架构下多任务的硬件支持

[Linux 0.11] Draft 6 IA32架构下多任务的硬件支持

Overview 支持多任务的硬件结构为 Task Register (TR) Task State Segment (TSS), LDT 以及 Task Gate。而最核心的,存储任务上下文信息的就是 Task State Segment, 下面对其进行详细的说明 TSS基本数据结构 GDT, LDT TSS(Task State Segment)  has its own descriptor called TSS Descriptor Structure of 32 bit TSS, store the context and link to previous task, and 3 different privileged Stack 下面对一些比较关键的部分进行介绍 Previous Task Link: 存储的是上一个任务的选择符 LDT Segment Selector: 存储的是这个Task使用的LDT I/O Map Base Address: I/O Map的基地址(要对 I/O Map 是什么进行进一步的解读: I/O Map 包含一个权限Map和一个Interrupt redirect Map) TSS 描述符的一些说明 TSS Descriptor 用于描述 Task State Segment 的描述符,当选择符的TI…

Read More Read More

[Linux 0.11] Draft 2 80386 System Architecture

[Linux 0.11] Draft 2 80386 System Architecture

此文对 Intel 80386 的系统架构进行一定程度的说明 80386 在系统层面支持下面的功能 保护 内存管理 多任务 IO 异常和中断 初始化 协处理器,多处理技术 调试功能 上述均通过系统寄存器以及指令来支持,下面会对这些进行介绍 系统寄存器(Registers) 系统寄存器分为如下几类 EFLAGS 内存管理寄存器 控制寄存器 (Control Registers) 调试寄存器 (Debug Registers) 测试寄存器 (Test Registers) 下面对每一类寄存器进行说明 System FLAGS EFLAGS 中的 System FLAG 可以控制如 IO, 可屏蔽中断,任务切换,调试等功能。 IF: 用来控制系统是否接受(可屏蔽的)外部中断请求 IF = 0 为屏蔽 NT: 此位用于控制 Chain interrupted 以及 called tasks, 该位会影响 IRET 指令的操作方式 RF: debug 有关的 flag TF: debug 有关的 flag,将处理器设置为单步执行模式,在这个模式下,所有的指令CPU都会产生一个异常,可以在每一个指令处停下来进行调试 VM: 用于进入 Virtual 8086 Mode 的 flag 这里不做解释 内存管理寄存器 与内存分段管理相关的寄存器,如全局描述符表,中断描述符表,也就是 GDT, LDT, IDT 和 Task 的描述信息存储用的寄存器 GDTR LDTR…

Read More Read More

[Linux 0.11] Draft 1 Assembly

[Linux 0.11] Draft 1 Assembly

REP前缀 对 源 和 目的 地址的数据进行某些操作 汇编指令形式为 rep <INSTRUCTION> INSTRUCTION 可以是 mov 系列(movb, movw, …) 也可以是 movs 系列,  还可以是cmps系列 该指令涉及到的寄存器有 标志寄存器(DF位) ECX ESI EDI ECX是计数器,用来指定操作的次数 ESI 指定源地址 EDI 指定目的地址 (很好记辣 Source Index (SI) Destination Index (DI)) 当处理 movs 的时候,如果 DF = 0 就是表示 ESI 指向要复制的块的开头,EDI 指向目的块的开头,DF = 0 的时候这两个就变成指向末尾了   JMPI 间接跳转 在实模式下,指定段地址和偏移量,将会设置IP以及CS 磁盘参数表 磁盘参数表存在中断向量0x41中 第二个磁盘的参数表存在0x46中   开启A20地址线 A20地址线关闭状态县 限制最多仅能寻址到1MB空间,更大的空间将会循环 开启方法如下:   字符串比较指令 SCAS, SCASB, SCASW, SCASD AX, ES, DI FLAGS(DF) 比较 AL(AX)(EAX) 与 ES:[DI] 的值,不保留结果,仅保留FLAGS。 根据后缀(B,W,D)的不同,比较的字节数也不同 根据方向位(D FLAG)决定比较之后DI寄存器是增长还是减少 可以与REP前缀一起使用…

Read More Read More

Eudyptula Challenge 1 — 8 总结

Eudyptula Challenge 1 — 8 总结

TL;DR (PS: 昨天竟然在一个非计算机领域的朋友口中听到TLNR好神奇) 从开始做eudyptula-challenge已经有一个多月了,也从原来的连第一关都会卡关好久到现在第八关只用了不到四十分钟的时间就写好了(虽然第八关的coding要求很简单,主要是要练习git send-mail的使用方法)随着Challenge的进行,之前关卡学到的一些东西难免会遗忘,因此在继续进行之前,现将前面学到的知识进行总结。注意: 本文不是Eudyptula Challenge的攻略,Eudyptula Challenge明确禁止了对答案代码的公布和分享,个人也赞同这种做法,毕竟是Challenge,需要你自己研究,而不是直接上网就能搜到答案(尽管现在的确能搜到答案了,比如某章鱼猫(( Intro What is Eudyptula Challenge? 这里就引用官网的介绍了 The Eudyptula Challenge is a series of programming exercises for the Linux kernel, that start from a very basic “Hello world” kernel module, moving on up in complexity to getting patches accepted into the main Linux kernel source tree. How does it work? 官网上也给了相应的介绍,不过窝认为从一个例子来介绍更为形象: Eudyptula-Challenge的评判系统是由”A set of convoluted shell scripts that are slow to anger and impossible to debug.”(摘自官网)来进行的,也就是全部自动评测(不过有的时候它的只能程度让我猜想后面有人在操作*****(大雾 以一个Task为例, 当你注册成功Eudyptula Challenge之后,会收到一封说明邮件以及你的第一个任务,之后你所有的任务提交都将通过在第一个任务里给你分配的ID以plain-text mail的形式进行。 当你收到一个Task的时候,根据相应的知识完成任务要求的代码之后,你需要通过纯文本邮件客户端(如mutt)将proof运行结果的输出作为正文,将写好的代码作为附件(这里不能用gmail的plain-text模式,因为它会把邮件附件转为base64格式)通过客户端发送给[email protected],之后你会收到一封通知邮件,表示系统已经成功收到你的提交(有可能收不到,因为脚本也许会卡住,这时候你如果等了一天还没收到,那你可以再次发送一封,不要频繁重发,小企鹅会生气的(不要问我怎么知道的Orz)) 通知邮件的格式是这样的 blabla,…

Read More Read More

ULK Chapter2 总结

ULK Chapter2 总结

本章的知识结构如下 地址转换过程 –Logical Address–> [Segment Unit] –Linear Address–> [Paging Unit] –Physical Address–> 谈到内存地址,具体来讲有三种不同类型的地址,逻辑地址,线性地址,物理地址。 逻辑地址对应内存的分段 线性地址对应分页 物理地址对应到硬件芯片上的内存单元的地址 MMU通过Segment Unit与Paging Unit两个硬件电路将一个逻辑地址转为物理地址, 具体转换过程如上代码段所示 下面对整个转换过程进行概述 段选择符 15 0 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +T+ R + + index +I+ P + + + + L + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ [0-1]RPL: 请求者特权级 [2]TI: Table Indicator, 指明是从GDT还是LDT中取出段描述符(Segment Descriptor) [3-15]index:用来指定从GDT中第index项取出Segment Descriptor 段描述符 对段描述符的解释参考 [不是科普向?] RE: 从零开始的操作系统开发 第二集 中相应内容即可 Logical Addr ===> Linear Addr 逻辑地址的高16位为段选择符(Segment Selector)其余32位(或者64位)为偏移量(Offset) 流程如下 检查TI确定是从GDT(TI=0)还是从LDT(TI =1)中选择段描述符 从Segment Selector的index字段计算出段描述符的地址,计算方法 index * 8 (一个Segment Descriptor大小为8) 并与gdtr/ldtr中的内容相加得到Segment Descriptor 把逻辑地址的offset字段与Segment Descriptor中的Base字段相加,得到Linear Address   而Linux中的分段只是一个兼容性考虑,分段和分页的作用是重复的,分页能以更精细的粒度对内存进行管理,因而在Linux中分页是主要的手段,Linux分段中的用户代码段,数据段,内核代码段,数据段,都是以Base = 0…

Read More Read More

将内核编译到自定义的目录下

将内核编译到自定义的目录下

参考wiki: https://wiki.archlinux.org/index.php/Kernels/Traditional_compilation 使用ArchLinux作为构建Linux的环境 方法如下 假设我们的customized directory为kernel-build 我们必须在kernel-tree目录结构下对内核进行编译,假设kernel-tree目录名为linux-kernel 下面是具体的操作: make menuconfig # Change the local version in General Settings make -j8 make modules -j8 sudo make modules_install 在 make menuconfig 的时候,最好对内核版本进行修改,使之不会在make modules_install的时候覆盖掉现有内核的modules 修改途径为General Setup里的Local Version项 这样执行之后,应该在arch/ARCH/boot/下面存在bzImage内核镜像,并且modules应该被安装在了/lib/modules/<linux-version>-<localversion> 下 下一步操作, 修改linux.preset文件,并创建initrd文件 ## Change to kernel-build directory cp ../arch/<arch>/boot/bzImage [image-name] cp /etc/mkinitcpio.d/linux.preset . 修改linux.preset文件, 修改完毕后的内容类似下面 # mkinitcpio preset file for the ‘linux’ package ALL_config=”/etc/mkinitcpio.conf” ALL_kver=”/home/void001/linux-kernel/kernel-build/vmlinuz” PRESETS=(‘default’ ‘fallback’) #default_config=”/etc/mkinitcpio.conf” default_image=”/home/void001/linux-kernel/kernel-build/initramfs-linux.img” #default_options=”” #fallback_config=”/etc/mkinitcpio.conf” fallback_image=”/home/void001/linux-kernel/kernel-build/initramfs-linux-fallback.img” fallback_options=”-S autodetect” 我们需要修改ALL_kver, default_image, fallback_image三个选项,使其将image保存到指定的目录下 然后使用 mkinitcpio -p ./linux.preset 创建initrd, initrd-fallback,目前kernel-build目录应该有如下文件: ╰─➤ ls…

Read More Read More

[科普向?] Re: 从零开始的操作系统开发 第一集

[科普向?] Re: 从零开始的操作系统开发 第一集

Hmm, 果然还是开坑了~! 在学校智障的操作系统课设的发起下, 再加上每个程序员都有一个写一个自己的操作系统的公主梦(雾), 我们愉(作)快(死)地开坑啦~ 以前曾经跟着 “30天自制操作系统” 玩过DIY操作系统, 不过那个书更像面向小白, 讲的东西也不够系统, 而且使用的是自己改过的nas汇编器, 因而不能算写过, 这一次则是真正的开坑啦~ (虽然课设时间很短写出一个完整的根本不可能不过慢慢写总会写完的你说对不喵~) 我们的开发过程在Bearychat上直播 neugeek.bearychat.com 的Toy-OS频道, 我们的git-repo 为 https://github.com/VOID001/toy-os 菊苣们不要喷, 既然挖了坑窝就不会不填(….你都已经挖了多少个坑了啊喂! (逃)) 这个系列的文章将会记录在开发操作系统的整个过程中的一些经验&心得&吐槽 不知道会有多少集(   参考资料们: MIT 的 XV6 源代码 & handbook University of Birmingham 的 Writing an simple operating system from scratch Quora, StackOverflow Jiong Zhao Linux 0.11内核完全注释   我们使用的工具链: GNU Assembler & GNU C Compiler Qemu Gdb objcopy, objdump, binutils, elfutils GNU Makefile   操作系统编写总览 什么是BIOS 参考阅读: http://whatis.techtarget.com/definition/BIOS-basic-input-output-system 我们的定位是写一个操作系统,那么首先我们应该了解,整个操作系统都应该由哪些模块构成, 那么就让我们从操作系统的启动说起, 说到这里就不得不说一下BIOS, BIOS是Basic Input and Output System, 是你的计算机加电运行后加载的第一个程序, 它是固化在你的EPROM内的一个程序片段…

Read More Read More

[Linux C]ELF Trick — 获取程序内的函数名及地址

[Linux C]ELF Trick — 获取程序内的函数名及地址

这一切的起源来自于一个群的对话: B 23:27:38 我定义了一个 C 的函数 abc_hello_world() 然后我想通过命令行通过参数 xxxxx abc hello world 把三个参数,自动作为函数名给弄进去 然后输出 那个函数的执行结果 在 C 里怎么搞啊 首先我们的第一想法都是 “宏” 网络部 R 籓绽23:29:13 我记得宏定义有一种方式可以拼接字符串吧 网络部 R 籓绽23:29:25 我没弄过 窝想想看 A 23:30:05 好像没办法实现这个,编译之后都是二进制了,函数名在优化过程中都没了 A 23:30:26 我试试宏 网络部 R 籓绽23:30:29 宏定义的确可以拼接字符串的说 网络部 R 籓绽 23:30:32 你试试看 然后, 这些抽风的程序员们感觉, 宏这种用法太无趣了(笑) 于是就有了下面这段对话 网络部 R 籓绽23:37:30 知道这个原理我们c就可以实现了吧 A 23:38:09 他是解释器,变量名会一直保存,但是 C 编译后就没有变量名了 网络部 R 籓绽 23:38:47 有的 c可以留符号表的 A  23:39:03 (⊙o⊙)…怎么做 网络部 R 籓绽 23:39:04 我们可不可以查符号表 找地址呢 网络部 R 籓绽 23:39:12 调试信息呀 B 23:39:15 查符号表地址那不就是函数指针了? 网络部 R 籓绽23:39:21 恩 网络部 R 籓绽23:39:31 先查函数名然后 网络部 R 籓绽 23:39:36 找到函数的地址…

Read More Read More

[Linux C内核] 信号

[Linux C内核] 信号

下面对Linux 下的信号的产生, 响应机制, 以及可重入(reentrant)进行介绍~ [首先再次给网易云音乐点个赞~\(≧▽≦)/~, 在写博客的时候听着歌 而且还有歌词滚动, 真的太棒了 ~~~~] [不行, 我要附上一张截图] 恩,心满意足的窝开始写文章了 ~~ 什么是信号 在Interprocess Communication(IPC)的情形中,如何让不同的process进行通信是一个很重要的需要解决的问题, 而信号就是不同的进程之间通信的一种方式, 而在Linux下运行的每个程序, 都会受到内核的控制, 内核就通过信号告知进程一些控制信息, 如中断执行, 停止执行, 进程和进程之间也会进行信号的传递, 比如 父进程fork出的子进程, 子进程退出的时候会发送SIGCHLD信号给父进程, 告诉父进程, 我运行完啦~, 那么Linux是如何实现信号的捕捉,并且送达到相应的程序的呢? 下面我们举个具体例子来看一下 我们以最简单的情景: 用户在terminal下执行一个程序,然后由用户通过Ctrl+C打断程序的执行,返回到terminal下的整个过程中, 信号的流向进行说明  输入命令运行程序, 在Shell下启动一个前台进程, 分配了相应的PCB 程序运行过程中,用户按下 Ctrl+C, 因为有键盘输入的介入, 产生了硬件中断, CPU从用户态切换到内核态,去处理中断 终端的相应驱动程序,将Ctrl+C解释为 SIGINT信号, 并记录在当前运行在终端中的程序的PCB中 当要从内核态切换回用户态之前, 会对PCB中记录的信号, 进行处理, 检察到PCB中有 SIGINT信号, 于是对信号进行处理, 而目前这个信号的处理动作是终止进程执行, 于是这个程序终止了 信号分为实时信号和非实时信号, kill 指令后面可以接信号常量,  根据信号常量对应的数值区分实时/非实时信号 > 34的是实时信号 我们这里也值讨论 < 34的信号(即非实时的信号) 关于信号的更详细介绍, 参考 signal(7)即可 产生信号的条件 上面只说了通过Ctrl+C产生一个信号发送给进程, 那么还有什么方式产生一个信号呢? 下面列出主要的几种方式  用户在终端进行某些按键的时候,  终端驱动程序 会发送信号给相应的进程 硬件异常也会产生信号, 如当前进程执行了除以0指令, CPU的运算单元会产生异常 一个进程调用 kill 系统函数可以发送信号给某个进程, 注意这里的kill不仅仅能够kill掉某个进程, kill可以用来发送各种信号给其他的进程 当内核检测到某种软条件可以通过信号通知进程,…

Read More Read More