PS:要转载请注明出处,本人版权所有。

PS: 这个只是基于《我自己》的理解,

如果和你的原则及想法相冲突,请谅解,勿喷。

前置说明

  本文作为本人csdn blog的主站的备份。(BlogID=102)

环境说明
  • Ubuntu 18.04
  • gcc version 7.5.0 (Ubuntu 7.5.0-3ubuntu1~18.04)
  • Bochs 2.6
  • As86 version: 0.16.17

前言


  自从我近段时间开始温习一些基础知识以来,其中觉得以前学的很浅的就是OS原理。为啥这样说呢?因为就是浅,知道一些琐碎的知识。以前我自负的认为OS就是硬件的抽象,然后把这些硬件资源合理的分配给用户使用就完了,因为我觉得合理的整合这些硬件资源是非常‘简单’的。

  由于我本身对底层是非常着迷的。带着觉得OS很简单的想法,想着去看看LinuxKernel的源码。在以前,我对LinuxKernel的认知很肤浅,就知道一些驱动移植的事情。如果硬要说一件我在LinuxKernel中玩的很深的事情,那就是自己理解并实现了一个类似Anonymous Shared Memory的Linux驱动,详见以下两篇文章。

  带着这样的想法其实已经很久了,由于现在的LinuxKernel太大了,对新手不友好。我就想着去找一个老一点的版本内核看看。结果去网上一找,就发现了前人已经做了许多许多了,比如这个之前就有了解的《linux 0.11内核完全注释》,还比如其他许许多多前人种的‘树’,看到了许多,最终我决定跟着国内现在比较好和新的资料从‘远古’开始学习它。它就是《Linux内核完全注释(PDF) v5.0 by 赵炯.pdf》。它是基于LinuxKernel0.12 讲述的,它是我在ubuntu1804上编译通过LinuxKernel0.12的主要参考和学习资料,同时也是我在Bochs上运行成功的主要参考和学习资料。

  好的多说无益,直接看运行效果。

  说来也惭愧,利用断断续续的时间,我花了约2月,把LinuxKernel0.12在Ubuntu1804上编译通过,并在1804上通过Bochs运行成功。而且要命的事情是我其实只加了一些打印调试函数,和根据实际的调试情况修改了一些代码,却花了那么久的时间,搞得我很不自信了QAQ。

  我修改好的源码已经开源,立即想要源码的请直接去文末两个rep clone即可。

  本文主要还是简单介绍LinuxKernel从上电到进入sh的中间的简要流程。这些流程网上已经有很多了,可能我会挑选一些我觉得比较重要的来说。

  本文适用于:

  • 会编译和使用bochs的人。不会可以去网上找找,很多这方面的资料。
  • 对Intel AT&T 汇编有点了解的人。
  • 会GDB调试的人。
  • 知道C语言常识的人。
  • 对LinuxKernel感兴趣的人。

搭环境


  工欲善其事必先利其器。本文主要是在Ubuntu1804上编译生成LinuxKernel,然后用Bochs运行我们的内核。

Ubuntu18.04环境安装

我们应该首先安装make,gcc,gcc-multilib,bin86。

  • sudo apt install build-essential cmake make gcc-multilib g++-multilib module-assistant bin86

然后进入源码目录。

  • cd my_src
  • make disk

更多的详情信息查看开源的rep。

编译两个bochs版本备用

  我们首先就得把Linux0.12的运行环境搭建起来,方便我们调试。我们使用的是Bochs2.6 和 GDB远程调试。并编译出两个bochs版本,一个是带本身调试功能(命名为:bochs),一个是和gdb联调(命名为:bochsdbg)。bochs 主要是调试在init/main()函数之前的内容以及查看更多的x86寄存器。 bochsdbg主要是调试进入init/main()函数之后到sh成功执行的事情。

  • 通过 ./configure --enable-debugger 生成bochs。
  • 通过 ./configure --enable-gdb-stub 生成bochsdbg。
运行我们编译的内核

  通过本文介绍生成的文件是Linux内核镜像,稍微懂点行的人都知道还差一个RootFS。这个文件系统我们在网上下载的例如: http://oldlinux.org/Linux.old/bochs/linux-0.12-080324.zip 。本文生成的Linux内核镜像使用的是rootimage-0.12-hd这个文件系统。

  我建议这里自己配置两个.bxrc文件,一个对应bochs,一个对应bochsdbg远程调试。这样在遇到问题的时候我们可以很方便的调试。

LinuxKernel启动简介


  本节简述LinuxKernel的启动流程。根据我近段时间的学习来看,这里包含了许多的历史性的东西,大家不要去细究为啥是这样,很多都是为了兼容。

  此外在整个学习期间,由于涉及到许多的x86 硬件体系知识,除了参考上文我说的文档以外,还必须参考以下Intel官方文档:

  • Intel 64 and IA-32 architectures software developer's manual combined volumes 2A, 2B, 2C, and 2D:Instruction set reference, A-Z
  • Intel 64 and IA-32 architectures software developer's manual combined volumes 3A,

    3B, 3C, and 3D: System programming guide
  • 《Linux内核完全注释(PDF) v5.0 by 赵炯.pdf》 第4章,全篇精华。
boot/bootsect.S 阶段

  当我们的计算机上电以后,IntelCPU进入实模式,并且PC指向了0xfff0整个地址,如下图。什么意思呢?就是开机的时候执行的第一句指令放在0xffff0这个地方,通常这里有一个很重要的东西叫做BIOS。我们可以看到下图,cs=0xf000,base=0xffff0000,在实模式下面,cs:pc 就是真实的指向地址0xffff0。到了这里不知道大家发现没有,这里还差一个东西,那就是bios本来是放在rom里面的,怎么被指向了内存地址0xffff0的地方呢?是谁在之前自动搬运的吗?经过查询后发现,大部分人说开机的时候,对特殊地址的访问会被仲裁器件指向BIOS-ROM器件。仲裁器还可以把地址翻译并指向我们熟悉的MEM和IO。所以这里我理解对0xffff0的访问就是对BIOS-ROM器件的直接访问和执行。

  BIOS主要是做自检,并且在物理地址0x0开始初始化BIOS的中断向量,同时通过BIOS访问存储设备的中断,将可启动设备的第一个扇区512字节给搬运到绝对地址0x7c00(31k)处。然后跳转到0x7c00继续执行,这里被搬运的512字节就是bootsect.S生成的指令。这一段没啥营养,都是一些约定好的,到了CPU执行到绝对地址0x7c00的时候,才是真正的我们能控制的地方。其实这里也能够看到,我们的bootsect.S生成的指令最大只能够512字节,超过了就会出问题。下图为我们的0x7c00处的开始几句指令和bootsect.S的几句指令,同时也能够看到BIOS初始化和自检打印的一些内容:

  在上图的图中,我打印了0x7c00开始的一部分反汇编代码。可以看到和下面的bootsect.S的代码是一致的。

entry start
start:
! start at 0x07c0:0
! add by sky
mov ax,#BOOTSEG
mov es,ax
mov bp,#msg2 ! sky-notes: src-str is es:bp
mov si,#15 ! sky-notes: src-str-len is cx
call pirnt_str
! add by sky mov ax,#BOOTSEG
mov ds,ax
mov ax,#INITSEG
mov es,ax
mov cx,#256
sub si,si
sub di,di
rep
movw
jmpi go,INITSEG

  从0x7c00开始,就是我们自己的可以编程的领域了,也开始有了一些我自己特有的内容。主要是各种方法实现的print语句。这种调试方法简直不要太好。

  下面简要说明一下bootsect.S的功能:

  • 首先用rep movw把自己从0x7c00搬运到0x90000,并跳转cs=0x9000, pc=go 标号的地址。继续执行剩下的内容。
  • 通过读取0x1E号中断向量位置的软驱参数(由BIOS初始化时候通过BIOS中断读取的)到内存,然后修改其中的最大扇区数,并重新写回到0x1E中断向量位置绝对地址0x78去。最后重置软驱,使其加载最新的参数。
  • 使用BIOS INT 0x13的2号功能,将第一个软盘第2,3,4,5扇区读取到0x90200开始的位置。这里读取的就是setup.S的指令内容,最大共2k(4*512)。0x90000-0x90200存放的是bootsect.S, 0x90200-0x90A00 为setup.S。
  • 使用BIOS INT 0x13的8号功能,读取磁盘参数:每磁道扇区数。并保存到变量sectors中。
  • 使用BIOS INT 0x13的2号功能,使用刚刚的参数,读取system模块到0x10000,我们的bootsect.S放在0x90000,所以我们system模块最大只能够占用0x10000~0x8ffff。这里的system模块就是除了bootsect和setup模块之外的所有内核代码。
  • 判断bootsect模块第508,509字节是否为0,来判断我们是否指定根文件系统的设备号。我们的内核定义为0x0301,代表第一个磁盘第一个分区为我们的根文件系统。
  • 然后通过jmpi 0:9020跳转到cs=0x9020,pc=0的地方去执行setup.S的代码。

  在我的bootsect模块,我定义了一个打印字符串的函数,主要是通过使用BIOS INT 0x10的0x13号功能实现。主要还是为了调试,注意,这里不能够随意添加代码,因为生成的代码超过512byte后,链接器会报错。只能够少量的添加我们的调试代码。

  至此,我们就执行完了bootsect模块。本模块的主要内容还是加载setup和system到指定位置。bootsect执行的一些调试日志如下图(在0x90200下断点):

注意:图中话框的部分就是我们上文贴出的call pirnt_str打印的。

boot/setup.S 阶段

  首先我们还是来看一下0x90200的位置是否是setup.S,换句话来说是否加载好了setup模块。

  这里和bootsect一样,我也弄了一个prtstr函数,这个prtstr和bootsect里面的是一样的,原理也是一致的。

  刚刚我们提到,setup是从0x90200开始存放的。那么0x90000~0x901ff中的bootsect已经无用了,于是我们setup中,用这里的内存存放一些参数。下面简要说明一下setup.S的功能:

  • 用BIOS INT 0x15功能号0x88取系统所含扩展内存大小并保存在内存0x90002~0x90003处。共两个字节。
  • 用BIOS INT 0x10功能号0x12读取显卡参数,0x9000A 显存大小,0x9000B 显卡类型(单色/彩色),0x9000C显卡特性参数。
  • 用BIOS中断读取屏幕的行列存放到0x9000E 0x9000F
  • 用BIOS INT 0x10功能号0x03读取当前光标位置存放到0x90000 0x90001
  • 用BIOS INT 0x10功能号0x0f读取当前显示页,显示模式,字符列数。 0x90004~0x90005 存放当前显示页。 0x90006 显示模式, 0x90007 字符列数。
  • 读取第一个硬盘参数表和第二个硬盘参数表,并放到0x90080 0x90090。每个表共16byte。注意,这里和之前的软盘参数一样,在BIOS自检过程中,就被放到了中断向量0x41 0x46 的位置。
  • 用BIOS INT 0x13功能号0x15读取当前硬盘设备情况,如果硬盘2不存在,则把0x90090之后的16byte清零。

  下面我们将使CPU从实模式变更为保护模式,下面继续说明一下setup.S的功能:

  • 禁用中断。
  • 然后我们把system模块0x10000~0x8ffff整体下移到0x0开始的位置。就是把最大0x80000(512k)的system模块向下移动0x10000(64k)。
  • 首先加载LDT和GDT。
  • 开启A20地址线,支持1M以上的内存。
  • 初始化两个8259A中断控制器。
  • 通过lmsw 设置cr0最低位位1,进入保护模式。
  • 通过jmpi 0:0x8跳转到绝对地址0x0开始执行system的代码。system是从boot/head.s开始的。

  这里需要说明几个事情:

  • 我们在下移system模块的时候,覆盖了BIOS中断向量表。所以通过BIOS中断打印字符串是行不通的。
  • 在实模式中,cs:pc就是真实执行的地址。但是在保护模式中,cs是一个选择符号,根据选择符号值不同,分表在GDT或者LDT中查找对应的CS段描述符,其中最重要的就是base地址,当未开启分页的时候,这里的base+pc就是我们真实的执行地址。上面我们加载了LDT和GDT。这里的LDT是空,GDT有3项,第零项是空,第一项是代码段描述符,第二项是数据段描述符,他们的基地址都是0x0。当cs=0x08,ds=0x10时,分别指向这里的第一项和第二项。

  刚刚说了,system下移导致BIOS中断向量表被冲掉了,于是我们不能够通过BIOS打印字符串,于是这里我们使用的是直接操作显存内存地址显示字符,这个原理和LinuxKernel tty显示原理差别不是很大。

  这里我们设计了print_str函数,通过直接操控显存然后写入字符进行显示,这里还使用到了刚刚我们保存的当前光标位置(0x90000 0x90001)。写这个主要还是为了调试。

  到此,我们已经开始去执行system的内容,其中head.s是入口。下图是在0x0下断点得到的setup模块的一些打印日志。

  这里我们可以看到,红框还是BIOS中断打印的,黄框是通过直接操纵显存显示的。注意,我这里设计的直接操作显存的函数,是通过循环在当前显存页显示的,并不是我们常见的整页上移的方式。

boot/head.s 阶段

  首先我们还是来看一下0x0的位置是否是head.s,换句话来说是否加载好了system模块。并且,从这里开始,我们就是进入了真正的LinuxKernel的世界,前面都是做一些环境初始化,都是一些固定的内容。

  这里我们需要说明的是,bootsect.S和setup.S用的是intel汇编,而从head.s开始,我们用的都是AT&T汇编。同理,这里我也弄了一个safe_mode_print_str_no_page,打印字符串,为了调试,还是用的直接操作显存的方式。

  从这里开始,CPU开始工作于保护模式,下面简要介绍一下工作流程:

  • 刚刚我们通过jmpi切换到0x0开始执行,这时cs=0x8,根据setup设置好的GDT,base为0x0,同理我们设置其他段寄存器。
  • 设置堆栈为stack_start,这个就是内核堆栈。此符号定义于kernel/sched.c中,如下文。
long user_stack [ PAGE_SIZE>>2 ] ;

struct {
long * a;
short b;
// } stack_start = { & user_stack [PAGE_SIZE>>2] , 0x10 };
} stack_start = { & user_stack , 0x10 };
  • 设置IDT,所有的中断向量指向ignore_int,一个预定义的中断服务程序。共256项,每项8byte。
  • 重新设置GDT。共256项,每项8byte。重新设置GDT的原因是setup的GDT可能会被冲掉,于是把GDT设置到合理的内存位置。这里设置好的GDT有4个。和setup中类似。第0,3个为0.第1,2项为cs和ds的段描述符。
  • 检查A20是否开通,主要是通过判断0x100000 和 0x0值是否相等。
  • 检查数学协处理器是否存在。

  到这里,我们就开始准备正式进入到init/main.c中的main函数了,但是还差最后一个重要的事情,那就是启用分页机制,下面继续介绍其工作流程:

after_page_tables:
# sky print
push %ebp
lea msg5, %ebp
call safe_mode_print_str_no_page
pop %ebp
#
pushl $0 # These are the parameters to main :-)
pushl $0
pushl $0
pushl $L6 # return address for main, if it decides to.
pushl $main
jmp setup_paging
  • 从上面的代码我们可只,我们在启用分页前,把init/main.c中的main函数地址设置到了堆栈中。
  • 首先我们把从0x0开始的5页内存清零。每页4096字节。其中第一页为页表目录,第2-5页为页表。
  • 设置页表目录的前4项为第2-5页页表地址。注意页表目录为1024项,每项4字节。
  • 倒序设置每一个页表的每一项内容,第5页最后一项为0xfff000。映射之后,2-5页分别映射好了16MB内存的空间。
  • 操作cr0,开启分页
  • 通过ret指令,从堆栈中把main地址弹出去执行。

  到这里,我们正式进入到init/main.c中的main函数中,进入c语言相关代码的地界。下面是进入main之前的一些日志输出。

init/main.c 到进入shell

  这里我们进入了init/main.c中的main函数,可从下图看到。从这里开始,也是我们大家都熟知的Linux内核部分。

void main(void)		/* This really IS void, no error here. */
{ /* The startup routine assumes (well, ...) this */
/*
* Interrupts are still disabled. Do necessary setups, then
* enable them
*/
char _my_msg_buf[100];
sprintf(_my_msg_buf, "kernel main() start, root_dev=%x, swap_dev=%x ... ...\0", ORIG_ROOT_DEV, ORIG_SWAP_DEV);
__asm__ (
"push %%ebp\n\t"
"mov %0, %%ebp\n\t"
"call safe_mode_print_str_after_page\n\t"
"pop %%ebp\n\t"
:
:"p"((char *)&_my_msg_buf)
:); ROOT_DEV = ORIG_ROOT_DEV;
SWAP_DEV = ORIG_SWAP_DEV; sprintf(term, "TERM=con%dx%d", CON_COLS, CON_ROWS); envp[1] = term;
envp_rc[1] = term;
drive_info = DRIVE_INFO; memory_end = (1<<20) + (EXT_MEM_K<<10);
memory_end &= 0xfffff000;//align 4k if (memory_end > 16*1024*1024)//if memory_end > 16MB, set it to be 16 MB
memory_end = 16*1024*1024; if (memory_end > 12*1024*1024)
buffer_memory_end = 4*1024*1024;
else if (memory_end > 6*1024*1024)
buffer_memory_end = 2*1024*1024;
else
buffer_memory_end = 1*1024*1024; main_memory_start = buffer_memory_end; sprintf(_my_msg_buf, "Mem size is %x, buf-mem size is %x, main-mem start %x ... ...\0", memory_end, main_memory_start, buffer_memory_end);
__asm__ (
"push %%ebp\n\t"
"mov %0, %%ebp\n\t"
"call safe_mode_print_str_after_page\n\t"
"pop %%ebp\n\t"
:
:"p"((char *)&_my_msg_buf)
:); #ifdef RAMDISK
sprintf(_my_msg_buf, "ramdisk init ... ...\0");
__asm__ (
"push %%ebp\n\t"
"mov %0, %%ebp\n\t"
"call safe_mode_print_str_after_page\n\t"
"pop %%ebp\n\t"
:
:"p"((char *)&_my_msg_buf)
:);
main_memory_start += rd_init(main_memory_start, RAMDISK*1024);
#endif sprintf(_my_msg_buf, "memory init ... ...\0");
__asm__ (
"push %%ebp\n\t"
"mov %0, %%ebp\n\t"
"call safe_mode_print_str_after_page\n\t"
"pop %%ebp\n\t"
:
:"p"((char *)&_my_msg_buf)
:);
mem_init(main_memory_start,memory_end); sprintf(_my_msg_buf, "trap init ... ...\0");
__asm__ (
"push %%ebp\n\t"
"mov %0, %%ebp\n\t"
"call safe_mode_print_str_after_page\n\t"
"pop %%ebp\n\t"
:
:"p"((char *)&_my_msg_buf)
:);
trap_init(); sprintf(_my_msg_buf, "blk init ... ...\0");
__asm__ (
"push %%ebp\n\t"
"mov %0, %%ebp\n\t"
"call safe_mode_print_str_after_page\n\t"
"pop %%ebp\n\t"
:
:"p"((char *)&_my_msg_buf)
:);
blk_dev_init(); sprintf(_my_msg_buf, "chr init ... ...\0");
__asm__ (
"push %%ebp\n\t"
"mov %0, %%ebp\n\t"
"call safe_mode_print_str_after_page\n\t"
"pop %%ebp\n\t"
:
:"p"((char *)&_my_msg_buf)
:);
chr_dev_init(); sprintf(_my_msg_buf, "tty init ... ...\0");
__asm__ (
"push %%ebp\n\t"
"mov %0, %%ebp\n\t"
"call safe_mode_print_str_after_page\n\t"
"pop %%ebp\n\t"
:
:"p"((char *)&_my_msg_buf)
:);
tty_init(); printk("time init ... ...\n\r");
time_init(); printk("sched init ... ...\n\r");
sched_init();
/*
After sched_init() gdt[0] = NULL
gdt[1] = kernel cs
gdt[2] = kernel ds
gdt[3] = NULL gdt[4] = task0.tss
gdt[5] = task0.ldt tr=task0.tss
ldtr=task0.ldt
*/ printk("buffer init ... ...\n\r");
buffer_init(buffer_memory_end); printk("hd init ... ...\n\r");
hd_init(); printk("floppy init ... ...\n\r");
floppy_init(); printk("enable interrupts ... ...\n\r");
sti(); printk("go to user mode ... ...\n\r");
/*
movl %%esp,%%eax
pushl $0x17
pushl %%eax
pushfl
pushl $0x0f
pushl $1f
iret
1:
movl $0x17,%%eax
mov %%ax,%%ds
mov %%ax,%%es
mov %%ax,%%fs
mov %%ax,%%gs iret instruction will do follow op:
popl eip
popl cs
popl eflag
popl esp
popl ss
*/
move_to_user_mode(); printf("user_mode: fork() task0 ... ...");
if (!fork()) { /* we count on this going ok */ printf("user_mode: task1 call init ... ...");
init();
}
/*
* NOTE!! For any other task 'pause()' would mean we have to get a
* signal to awaken, but task0 is the sole exception (see 'schedule()')
* as task 0 gets activated at every idle moment (when no other tasks
* can run). For task0 'pause()' just means we go check if some other
* task can run, and if not we return here.
*/
printf("user_mode: task0 call sys_pause() in while ... ...");
for(;;)
__asm__("int $0x80"::"a" (__NR_pause):);
}

  注意,这里我们仍然设计了一个函数为safe_mode_print_str_after_page,通过直接操作显存进行显示字符串,知道tty_init之后,我们才能够调用printk类似的函数进行打印。

  下面简要介绍一下main函数主要做的事情:

  • 根据我们在setup中保存到内存中的内存参数初始化高速缓冲区和主存的位置。
  • 然后就是我们常见的初始化mm模块。
  • 初始化中断向量。
  • 初始化块设备。
  • 初始化字符串设备。
  • 初始化tty设备。
  • 初始化时间。
  • 初始化调度模块。
  • 初始化缓冲区。
  • 初始化硬盘。
  • 初始化软盘。
  • 开启中断。
  • 把当前任务切换到用户态。

  当我们切换到用户态之后,并且当前我们的进程是0号进程,我们内核的一些重要初始化基本设置完毕。然后就像我们常见的linux编程那样,通过fork,创建我们的1号进程。然后我们继续进行下面的事情:

  • task0在fork出task1之后,就循环调用sys_pause, 这里主要还是执行schedule()开始执行进程调度。
  • task1成功创建后,调用setup,开始加载根文件系统。然后task1 通过fork创建了task2。
  • task2通过execve开始运行/bin/sh,进入shell。后续就是一些其他的事情。

  到这里,我们已经把kernel跑起来了。在我调试的过程中,主要还是mm模块和schedule模块有些问题,可能和编译器版本有关系,反正我生成的代码,总会报错。哪怕到现在,我开源出来的我修改的内核,也非常的不稳定,经常崩溃。但是好在正常工作了。

  下面给出两种不同打印的日志:

tool/build.c

  此工具是生成LinuxKernel镜像的手段。但是我们在Ubuntu上生成的内核,由于gcc版本变更的原因,需要做一些变更。主要还是把生成的elf格式system模块通过objcopy 生成二进制内存镜像。主要原因就是elf格式需要一个elf加载器进行各个段的重定位,但是由于我们是内核,所以没有。详情,请查看tool/build.c 及 Makefile。

开源


  https://github.com/flyinskyin2013/LinuxKernel-src0.12

  https://gitee.com/sky-X/LinuxKernel-src0.12 (镜像)

后记


  为啥想要在ubuntu1804环境下弄这个东西呢?一方面是想学习一下,通过踩坑的方式加深自己的理解。另一方面还是太懒了,我只想在我的ubuntu1804上编译内核,不想安装其他虚拟机了,我的电脑太卡了(毕竟8年的电脑了QAQ)。

  经过了这一波调试,我对LinuxKernel有了更深的认知,我觉得很不错,如果以后有必要,我还可以分别对这些模块进行详细的查看,在这里,我只是简单的说明了init/main中的内容,其实,还有许多其他的内容是运行在背后的。比如system_call,sys_table等等内容。还有do_fork do_execve等等内容都是我在调试过程中踩过的坑。

  这里还是要说明,深入调试学习这个的原因还是想看看OS是怎么运行起来,虽然不能说已经100%的熟知,但是也可管中窥豹。

  注意,这个版本的内核和现代的2.0,4.0,5.0还缺了一些主要的知识,比如网络栈,VFS等。但是其他的一些内容,在现在的最新内核中,多多少少都能够看到这个版本的一些影子。这也是学习这个内核的原因之一。


打赏、订阅、收藏、丢香蕉、硬币,请关注公众号(攻城狮的搬砖之路)

PS: 请尊重原创,不喜勿喷。

PS: 要转载请注明出处,本人版权所有。

PS: 有问题请留言,看到后我会第一时间回复。

Linux Kernel 0.12 启动简介,调试记录(Ubuntu1804, Bochs, gdb)的更多相关文章

  1. Android HAL层与Linux Kernel层驱动开发简介

    近日稍微对Android中的驱动开发做了一些简要的了解. HAL:Hardware Abstract Layer 硬件抽象层,由于Linux Kernel需要遵循GPL开源协议,硬件厂商为了保护自己硬 ...

  2. linux kernel 0.11 head

    head的作用 注意:bootsect和setup汇编采用intel的汇编风格,而在head中,此时已经进入32位保护模式,汇编的采用的AT&T的汇编语言,编译器当然也就变成对应的编译和连接器 ...

  3. linux 下mysql的启动 、调试、排错

    Linux 下 MySQL 启动与关闭 说明 一.启动 1.1  MySQL 进程 可以用ps 命令查看进程: [root@rac2 ~]# ps -ef|grep mysql root     21 ...

  4. linux kernel 0.11 bootsect

    bootsect作用 ①将自己移动到0x90000处 ②将setup从磁盘读到0x90200处 ③将system从磁盘读到0x10000处 寄存器 汇编代码中存在:数据段data seg 栈段 sta ...

  5. linux kernel 0.11 setup

    setup作用 ①读取参数放在0x90000处. ②将原本在0x10000处的system模块移至0x00000处 ③加载中断描述符表,全局描述符表,进入32位保护模式. 概念 关于实模式和保护模式区 ...

  6. 摩托罗拉SE4500 三星 S3C6410 Wince6.0平台软解码调试记录以及驱动相关问题解释

    虽然S3C6410出来很多年了,甚至于已经停产了,出货的几乎都有依赖于库存,SE4500也出来很多年了,但是网上依旧不会有调试资料帮助你,一切源于自私.希望本文能帮到你,不必感谢.本文来自C.S.D. ...

  7. windows安装SUSE Linux Enterprise Server 12

    一:打开“开发人员模式” 点击开始菜单按钮,选择“设置” 在设置中选择“更新和安全” 在菜单中选择“针对开发人员”,在三个选项中,选中“开发人员模式” 在弹出的警告框中点击“是” 这样开发人员模式就打 ...

  8. Linux kernel ‘qeth_snmp_command’函数缓冲区溢出漏洞

    漏洞名称: Linux kernel ‘qeth_snmp_command’函数缓冲区溢出漏洞 CNNVD编号: CNNVD-201311-423 发布时间: 2013-11-29 更新时间: 201 ...

  9. Linux kernel ‘aac_send_raw_srb’函数输入验证漏洞

    漏洞名称: Linux kernel ‘aac_send_raw_srb’函数输入验证漏洞 CNNVD编号: CNNVD-201311-422 发布时间: 2013-11-29 更新时间: 2013- ...

随机推荐

  1. 后缀数组Da模板+注释 以及 dc3模板

    后缀数组Da模板: 1 /* 2 后缀数组倍增法Da板子 3 */ 4 #include <cstdlib> 5 #include <cstring> 6 #include & ...

  2. ACM International Collegiate Programming Contest, Egyptian Collegiate Programming Contest (ECPC 2015) G. It is all about wisdom (二分,单源最短路)

    题意:有\(n\)个点,\(m\)条边,只有当你的智力值大于这条边的\(w\)才能走,问在花费不超过\(k\)的情况下,从\(1\)走到\(n\)的所需的最小智力值. 题解:这题比赛为什么没想出来呢? ...

  3. 牛客编程巅峰赛S1第6场 - 黄金&钻石&王者 C.星球游戏 (单源最短路,Dijkstra)

    题意:有\(n\)个点,\(m\)条双向边,两个方向的权值都是相等的,可以从\(A\)中的某个点出发走到\(B\)中的某个点,求所有路径中的最短距离,如果A和B中没有点联通,则输出\(-1\). 题解 ...

  4. PowerShell随笔3 ---别名

    上一篇提到了别名,这个有必要说一下,因为我们常常会遇到以下两种情况: 自己写脚本,想快速一些,使用命名 看别人的脚本,发现别人和你想的一样,用了别名,但是你忘记了这个别名是什么意思. 我们可以通过Ge ...

  5. WPF 主动触发依赖属性的 PropertyChanged

    需求背景 需要显示 ViewModel 中的 Message/DpMessage,显示内容根据其某些属性来确定.代码结构抽象如下: // Model public class Message : IN ...

  6. 实战交付一套dubbo微服务到k8s集群(1)之Zookeeper部署

    基础架构 主机名 角色 IP地址 mfyxw10.mfyxw.com K8S代理节点1,zk1 192.168.80.10 mfyxw20.mfyxw.com K8S代理节点2,zk2 192.168 ...

  7. Kerberos原理经典对话

    这是MIT(Massachusetts Institute of Technology)为了帮助人们理解Kerberos的原理而写的一篇对话集.里面有两个虚构的人物:Athena和Euripides, ...

  8. 缓冲区溢出实验 6 exit(0)

    实验环境.代码.及准备 https://www.cnblogs.com/lqerio/p/12870834.html vul6 Vul6和vul2类似,可以覆盖foo的ebp的一字节.而这里有一个ex ...

  9. 51nod-1065 最小正子段和 【贪心 + 思维】

    N个整数组成的序列a[1],a[2],a[3],-,a[n],从中选出一个子序列(a[i],a[i+1],-a[j]),使这个子序列的和>0,并且这个和是所有和>0的子序列中最小的. 例如 ...

  10. 牛客多校第八场E Explorer(左开右闭线段树+可撤回并查集)题解

    题意: 传送门 有\(n\)个点构成一个无向图,每条边有\(L_i,R_i\)表示这条边只能允许编号为\(L_i\dots R_i\)的人通过,现在问你最多有几个人能从\(1\)走到\(n\). 思路 ...