精华内容
下载资源
问答
  • Linux0.11源码

    2019-04-05 22:57:42
    Linux0.11源码,方便我们熟悉早期的操作系统。 代码来自于实验楼。
  • Linux0.11版本源码

    2017-09-09 22:38:55
    Linux版本是Linux 0.11 版本,linux 0.11版本相对于现在的2.6版本的内核(就是那本经典的《深入理解linux内核》)而言,真简单很多了,代码量少,适合用于深入分析代码以至于深入理解内核,
  • linux0.11-环境搭建

    2017-10-13 09:21:55
    linux0.11环境盘,配合Bochs-2.4.5启动(需另外下载)... 亲测有效。
  • linux0.11-simulation- 模拟一个简单程序运行时linux0.11对进程和内存的处理方式 仿真内容 以linux0.11中进程,内存,系统调用相关的代码为核心,描述了某个简单程序在linux0.11运行时的操作序列。 ,计划,释放,...
  • Linux 0.11

    2018-05-30 13:52:17
    Linux 0.11 系统,可用于了解Linux0.11的内核学习,同时配合赵炯的教材,可以有一个较为理性的认知。
  • Ubuntu 18.04环境下Linux 0.11 内核编译及在bochs 2.6.9中的调试运行
  • Linux 0.11内核完全注释》
  • linux0.11源码完整注释

    2016-08-14 14:15:37
    linux0.11代码完整剖析,赵炯
  • linux0.11源代码

    2016-08-03 12:05:13
    linux0.11源代码,可用sourceInsight这个工具阅读,配合书籍《Linux内核完全剖析基于0.11内核》更佳
  • Linux 0.11源代码_cn.zip

    2020-06-25 18:22:37
    Linux 0.11源码,内核编程入门必备的珍贵资料。供相关的内核编程初学者及资深爱好者用以学习、分析及收藏。
  • linux0.11源码.rar

    2021-07-22 17:37:22
    压缩包里面包含有linux0.11版本的原版代码,是个不错的学习linux的资源
  • 编译linux0.11内核

    千次阅读 2021-10-02 22:24:12
    编译linux0.11内核 一、实验环境 这次照例还是在Vmware虚拟机上进行实验,实验环境为Ubuntu16.04版本。 同时,本次内容也是基于哈尔滨工业大学《操作系统》课程实验的相关内容进行操作。hit-oslab 二、下载文件 1...

    一、实验环境

    这次照例还是在Vmware虚拟机上进行实验,实验环境为Ubuntu16.04版本(32位)。下载地址
    在这里插入图片描述
    image-20211002175332245

    同时,本次内容也是基于哈尔滨工业大学《操作系统》课程实验的相关内容进行操作。hit-oslab

    image-20211002180037128

    二、下载文件

    1、可以直接在hit-oslab-github下载,如果访问不了,或者下载速度慢,这里提供了gitee下载路径:hit-oslab-gitee

    image-20211002202910390

    注意:

    1. 如果VMware实现了文件共享功能,可在解压好之后,直接拖动文件夹放入Ubuntu对应的文件路径中。

    2. 还可以在Ubuntu中安装git,命令如下:sudo apt install git。git安装好之后,可以直接用git命令获取文件。命令如下:git clone https://gitee.com/fayoung/os.git

    2、下载好之后,会得到以下目录

    image-20211002202806690

    3、其中的hit-oslab就是这次我们需要的文件。内容如下:

    image-20211002203050557

    三、配置Linux0.11所需环境

    由于LInux0.11在设计之初是在32位的机器上进行编译的,而现在的各大Linux发行版(例如Ubuntu、redhat、CentOS)都是64位,所以直接对Linux0.11进行编译会出现问题。

    现在要配置Linux0.11所需的环境,在刚刚所下载的文件当中,已经写好了相关的脚本,直接运行即可。

    执行setup.sh脚本:./setup.sh

    image-20211002204253588

    这时候会提示权限不足,需提高一个该文件的权限,命令为:chmod 777 ./setup.sh 。执行之后,会发现setup.sh文件变成绿色,是一个可执行文件。

    image-20211002204535576

    然后,重新执行:./setup.sh

    image-20211002204648190

    完成之后,会提示以下字符:

    image-20211002204736412

    然后,该脚本会在源目录下生成oslab文件夹

    image-20211002191425636

    四、编译内核

    进入oslab文件目录

    image-20211002204956792

    进入到linux-0.11文件夹目录中

    image-20211002205203101

    在当前目录下,使用make命令进行编译

    image-20211002205259620

    等待一段时间,编译成功。

    image-20211002205402183

    五、运行linux0.11

    回到上一级目录当中,执行run文件。

    image-20211002205720756

    提高run文件的权限,运行run文件

    image-20211002214735341

    发现还是报错

    image-20211002214819285

    根据提示,提高当前目录下bochs中bochs-gdb的权限

    image-20211002214939809

    接下来,再执行run文件:./run

    image-20211002215004709

    弹出Bochs界面

    image-20211002215035490

    输入ls命令

    image-20211002215141324

    说明编译成功!

    六、说明

    1、setup.sh脚本里进行了什么操作?

    setup.sh代码如下:

    #!/bin/sh
    
    # Harbin Insitute of Technology
    # Operating System - Setup Script v0.0.1
    #
    # $Author$: Deng Xiongfei<dk@hit.edu.cn>
    # $Date$: 2014-10-10
    export OSLAB_INSTALL_PATH=$HOME/oslab
    cat hit.icon
    echo "|                 Environment Setup Script v0.0.1                   |"
    echo "|                $ \033[34mDeng Xiongfei\033[0m <dk@hit.edu.cn> $                  |"
    echo "+-------------------------------------------------------------------+"
    
    install_gcc34_amd64() {
        echo -n  "* Install gcc-3.4 for x86_64(amd64) arch now......"
        if [ -z `which gcc-3.4` ]; then
            sudo dpkg -i gcc-3.4/amd64/gcc-3.4-base_3.4.6-8ubuntu2_amd64.deb > /dev/null
            sudo dpkg -i gcc-3.4/amd64/cpp-3.4_3.4.6-8ubuntu2_amd64.deb > /dev/null
            sudo dpkg -i gcc-3.4/amd64/gcc-3.4_3.4.6-8ubuntu2_amd64.deb > /dev/null
            echo "\033[34mDone\033[0m"
        else
            echo "\033[32mSipped\033[0m"
        fi
    }
    
    install_gcc34_i386() {
        echo -n  "* Install gcc-3.4 for x86(i386) arch now......"
        if [ -z `which gcc-3.4` ]; then
            sudo dpkg -i gcc-3.4/i386/gcc-3.4-base_3.4.6-8ubuntu2_i386.deb > /dev/null
            sudo dpkg -i gcc-3.4/i386/cpp-3.4_3.4.6-8ubuntu2_i386.deb > /dev/null
            sudo dpkg -i gcc-3.4/i386/gcc-3.4_3.4.6-8ubuntu2_i386.deb > /dev/null
            echo "\033[34mDone\033[0m"
        else
            echo "\033[33mSkipped\033[0m"
        fi
    }
    
    install_dep_i386() {
        echo  "* Install dependencies for x86(i386) arch now......"
        sudo apt-get install bin86
        sudo apt-get install build-essential
        echo  "* Install dependencies for x86(i386) arch now......\033[34mDone\033[0m"
    }
    
    install_dep_amd64() {
        echo  "* Install dependencies for x86_64(amd64) arch now......"
        sudo apt-get install bin86
        sudo apt-get install gcc-multilib
        sudo apt-get install build-essential
        sudo apt-get install bochs bochs-x bochs-sdl
        echo  "* Install dependencies for x86_64(amd64) arch now......\033[34mDone\033[0m"
    }
    
    configure_for_i386() {
        echo -n  "* Copy rest files to oslab......"
        cp -r i386/* $OSLAB_INSTALL_PATH
        echo  "\033[34mDone\033[0m"
    }
    
    configure_for_amd64() {
        # 64-bit version bochs has to show in sdl mode, bochs-sdl required
        echo -n "* Change bochs:display_library into sdl......"
        echo "display_library: sdl" >> $OSLAB_INSTALL_PATH/bochs/bochsrc.bxrc
        echo  "\033[34mDone\033[0m"
    
        echo -n "* Copy run script to oslab......"
        cp -r amd64/* $OSLAB_INSTALL_PATH
        echo "\033[34mDone\033[0m"
    }
    
    # Common Code
    if [ "$1" ] && ([ "$1" = "-s" ] || [ "$1" = "--skip-update" ]); then
        echo -n "* Begin to setup......\033[33m3\033[0m sec to start"; sleep 1
        echo -n "\r* Begin to setup......\033[33m2\033[0m sec to start"; sleep 1
        echo -n "\r* Begin to setup......\033[33m1\033[0m sec to start"; sleep 1
        echo "\r* Begin to setup......                                 \033[0m"
    else
        echo -n "* Update apt sources......\033[33m3\033[31m sec to start"; sleep 1
        echo -n "\r* Update apt sources......\033[33m2\033[31m sec to start"; sleep 1
        echo -n "\r* Update apt sources......\033[33m1\033[31m sec to start"; sleep 1
        echo "\r* Update apt sources......                                 "
        sudo apt-get update
    fi
    
    echo -n "* Create oslab main directory......"
    [ -d $OSLAB_INSTALL_PATH ] || mkdir $OSLAB_INSTALL_PATH
    echo "\033[34mDone\033[0m"
    
    echo -n "* Create linux-0.11 directory......"
    [ -d $OSLAB_INSTALL_PATH/linux-0.11 ] || mkdir $OSLAB_INSTALL_PATH/linux-0.11
    echo "\033[34mDone\033[0m"
    
    # Extract linux-0.11
    echo -n "* Extract linux-0.11......"
    tar zxf common/linux-0.11.tar.gz -C $OSLAB_INSTALL_PATH/linux-0.11
    cp common/linux-0.11.tar.gz $OSLAB_INSTALL_PATH
    echo "\033[34mDone\033[0m"
    
    # Extract bochs and hdc image
    echo -n "* Extract bochs configuration file and hdc image......"
    tar zxf common/bochs-and-hdc.tar.gz -C $OSLAB_INSTALL_PATH/
    echo "\033[34mDone\033[0m"
    
    # Copy common files
    echo -n "* Copy common files......"
    cp -r common/files $OSLAB_INSTALL_PATH
    echo "\033[034mDone\033[0m"
    
    # `getconf LONG_BIT` works better than `uname -a`
    if [ `getconf LONG_BIT` = "64" ]
    then
        install_dep_amd64
        install_gcc34_amd64
        configure_for_amd64
    else
        install_dep_i386
        install_gcc34_i386
        configure_for_i386
    fi
    
    echo "\033[34m* Installation finished. Ciao~\033[0m"
    
    

    总的来说就是:

    • 为所在环境安装gcc3.4,使其可以编译32位的代码文件
    • 配置编译Linux0.11所需要的依赖
    • 下载Linux0.11代码
    • 安装bochs和hdc image
    • 最后将以上文件统一放入在home路径下新建的oslab文件夹中

    2、最后弹出来的窗口bochs是什么?

    Bochs是一个x86硬件平台的开源模拟器。它可以模拟各种硬件的配置。Bochs模拟的是整个PC平台,包括I/O设备、内存和BIOS。

    通过改变配置,可以指定使用的CPU(386、486或者586),以及内存大小等。一句话,Bochs是电脑里的“PC”。根据需要,Bochs还可以模拟多台PC。它还有自己的电源按钮。

    bochs的典型应用是提供x86 PC的完整仿真,包括x86处理器、硬件设备、和存储器。在工作站上的模拟器里运行操作系统和软件,就像你有一台机器内的机器。

    总的来说就是一台模拟器,现在来看就是虚拟机中的虚拟机。

    展开全文
  • 带中文注释可成功编译运行的Linux0.11+Bochs2.62实验环境说明 此注释以网上获得的“linux带中文注释的0.11版本”为基础,对照赵炯博士《Linux内核完全注释(0.11) 》V3.0版...
  • linux0.11源码里面有中文注释。linux内核完全注释根据linux0.11编写的。搭配内核设计的艺术2版,学习linux内核的神组合。学习内核建议从linux0.11看起,初始版本内容比较简单,但包含的都是内核的精髓。先上手,学到...
  • linux0.11 Linux内核0.11完全注释V3.0配套源代码
  • linux0.11 Linux内核0.11完全注释V3.0配套源代码
  • Linux0.11内核

    2012-12-11 18:35:17
    Linux0.11内核代码,对于学习内核的人来说,Linux0.11内核代码确实是个不错的选择。。
  • linux0.11版本源码

    2018-09-19 11:12:49
    linux操作系统源码,可以用来研究操作系统,了解系统的cpu管理,内存管理等
  • linux 0.11

    2012-07-06 18:59:10
    你想见识linux最精简的内核么?你想了解机器是怎么启动的么?看linux0.11版代码。1万行的操作系统内核。
  • linux0.11源码

    千次下载 热门讨论 2010-08-23 13:57:10
    linux0.11的源代码,有对linux内核感兴趣的朋友可以下载看看
  • linux0.11启动与初始化

    2021-05-15 03:10:56
    简单描述Linux0.11的启动与初始化过程。启动过程中需要关注:IDT, GDT, LDT, TSS, 页表, 堆栈这些数据。一:启动过程启动的代码文件为bootsect.s、setup.s、head.sbootsect.s也就是启动扇区的代码。这段代码主要是将...

    简单描述Linux0.11的启动与初始化过程。

    启动过程中需要关注:IDT, GDT, LDT, TSS, 页表, 堆栈这些数据。

    一:启动过程

    启动的代码文件为bootsect.s、setup.s、head.s

    bootsect.s也就是启动扇区的代码。这段代码主要是将setup.s和head.s中的内容读入内存的相应区域。然后开始执行setup.s

    setup.s

    1:使用BIOS中断来获得相关系统信息:内存大小,硬盘分区信息,显示卡信息

    2:将head.s代码拷贝到内存地址为0X0000的地方。

    3:加载idt表和gdt表地址

    4:开启A20地址线,只有开启它了才能访问高于1M地址的内存

    5:重新设定中断控制器。这之后以前的BIOS中断号就没用了。

    6:置位CR0寄存器的最后一位进入保护模式, 然后用jmpi 0, 8指令跳转到地址0x08:0x0000处开始执行,也就是head.s的起始代码处。

    这里设定的idt表全部为空,也即这时并不处理任何中断。

    gdt表中有三个描述符:0--NULL, 1--内核代码段, 2--内核数据段描述符。

    此时内核代码段与内核数据段:基地址为0x00000000, 限长为:8MB

    head.s

    1:将堆栈设定在static_stack处,堆栈大小为1KB

    2:重新设定设定IDT和GDT,此时全部IDT的都设置为ignore_int,即仍然忽略中断。

    GDT中包含256个描述符。

    0--NULL, 1--内核代码段, 2--内核数据段, 3--保留, 4--进程0的TSS段, 5--进程0的LDT段, 6--进程1的TSS段, 7--进程1的LDT段......

    可见系统的GDT中为每个进程都设定了一个TSS和LDT段。

    内核代码段和内核数据段:基地址(0x00000000),限长(16MB)

    3:设定分页(由于内存管理部分目前没看到,因此关于进程的页表如何设定暂不明白,这里是内核的页表)

    在0x00000000处的第一页存放“页目录”,随后存放4页“页表”,每个页表对应于页目录中的一项。

    设定的页表,要求线性地址等于物理地址。

    4个页表能映射16MB的物理空间,因此这16MB的物理内存地址与线性地址是相同的。(0.11的内核没有PAGE_OFFSET)

    4:开始执行init/main.c中的main函数。

    方式如下:

    pushl $_main                          # 将main函数地址入栈

    jmp setup_paging                   # 开始分页

    ......

    setup_paging:

    .......

    ret                                      # 分页完成后,用ret指令弹出堆栈中的main函数入口地址,并开始执行main函数。

    好像内核代码中常用这种弹出堆栈的方式来执行其他的函数

    二:main函数

    在启动过程中设定好GDT和页表后,开始在main函数中设定其他的内容。

    主要是:设定IDT,设备初始化,创建进程0,fork出进程1,用进程1来执行init

    main.c中主要的初始化函数是:trap_init,sched_init

    trap_init中调用set_trap_gate来设定相应的中断描述符表。

    下面以0号中断为例,描述其实现过程

    1:调用set_trap_gate(0, &divide_error)

    这个宏定义用来设定IDT表中的第0项的陷阱门描述符。

    #define set_trap_gate(n, addr)   _set_gate(&idt[n], 15, 0, addr)

    在_set_gate中:&idt[n]为第n个描述符的地址, 15为描述符的类型(陷阱门),0为描述符的权限(最高权限),addr为要调用的代码的地址。

    _set_gate宏会调用相应的汇编指令,在&idt[n]处写入8字节的描述符。

    2:divide_error的实现

    该函数是以汇编码的形式实现。与该函数对应有一个处理函数do_divide_error(用C语言实现)

    对其他的多数陷阱门处理方式也是如此,有一个汇编方式实现的,还有一个C语言实现的处理函数。

    当中断0发生时,先调用divide_error,该函数再调用do_divide_error。

    void do_divide_error(long esp, long error_code)

    上面为函数原型。第一个参数为出错时的代码地址的指针,第二个参数是错误码。(有些中断不产生错误码,则错误码设成0)

    因此在divide_error的汇编代码中,主要的功能就是将出错地址的指针和错误码这两个参数传递给do_divide_error函数,

    同时将目前的数据段设定为内核数据段。

    sched_init函数

    该函数设定了进程0的TSS和LDT描述符,并将它们的选择子加载进了TR和LDTR寄存器。

    另外该函数设定了时钟中断和系统调用

    这里主要说下系统调用的执行,以fork函数为例。

    1:在sched_init中初始化系统调用

    set_system_gate(0x80, &system_call)

    #define set_system_gate(0x80, &system_call)  _set_gate(&idt[n], 15, 3, addr)

    可见系统调用也是一个陷阱门。区别是权限值为3, 因此用户进程能通过int 0x80的中断进入内核,执行系统调用。

    2:每个系统调用都有一个对应的编号,fork为第二个系统调用,因此fork的调用号为2。

    当执行fork函数的时候,它会用int 0x80来调用system_call函数。

    此时fork的调用号被放入eax寄存器中。

    3:全部的系统调用函数指针都保存在数组sys_call_table中。

    在system_call函数中会执行指令

    call _sys_call_table(,%eax, 4)来跳转到eax指定的系统函数代码上,对fork来说就是sys_fork函数。

    4:system_call

    i) system_call首先将相应的寄存器入栈。

    pushl %edx

    pushl %ecx

    pushl %ebx             这三个寄存器对应了相应的系统调用函数的3个参数

    因此0.11中,系统函数最多只能有3个参数。

    ii)将ds和es设定为内核数据段,将fs设定为用户进程的数据段,需要用户进程的数据时,可使用fs来访问

    iii)用call _sys_call_table(,%eax, 4)来执行系统调用

    iii)检查当前进程是否处于可执行状态,检查当前进程的时间片是否用完,相应的执行schedule

    iv)最后是对进程信号的处理。(信号机制没看完)

    三:进入用户态

    在main函数中相关初始化后,main以进程0的身份进入用户态。

    然后调用fork函数,创建进程1,进程1调用init函数

    init函数加载根文件系统,运行初始化配置命令,然后执行shell程序,这样便进入了命令行窗口。

    0.11内核中,每个进程都有一个TSS段和一个LDT段,它们保存在进程描述符strut task_struct结构中。

    相应段的描述符保存在GDT表中。

    在LDT段中,有3个LDT描述符,0--NULL, 1--进程代码段, 2--进程数据段。

    进程n的代码段和数据段:基地址=n*64MB,限长=64MB。(进程0和1的限长为640KB)

    因此系统中最多有64个进程。

    进程0的task_struct为INIT_TASK,进程0的TSS和LDT描述符在sched_init中设定。

    main函数调用move_to_user_mode函数来执行进程0,进入用户态。

    0.11内核中所有进程都是属于用户态,不像之后的Linux内核里有内核线程。

    move_to_user_mode函数

    此函数使用iret返回的方式,从内核态进入用户态。

    +------------+

    +   ss      +             pushl    $0x17

    +------------+

    +   esp    +             pushl %%eax                #eax中保存了esp

    +------------+

    +  eflags  +             pushfl

    +------------+

    +  cs       +             pushl $0x0f

    +------------+

    +   eip     +             pushl $1f                       #目的代码的偏移地址

    +------------+

    首先采用上面的push指令,将相关的数据压入堆栈,然后执行iret将它们弹出堆栈。

    于是进程0从堆栈中的cs:eip指向的代码开始执行。

    四:fork函数

    进程0执行fork函数创建出进程1.

    1:调用get_free_page为进程描述符分配内存。

    p = (struct task_struct *) get_free_page();

    这一页内存,前面保存task_struct内存, 页尾为进程的内核栈,

    当一个用户程序调用系统函数进入内核态后,系统函数执行时使用的栈就是这个。

    2:设定进程的task_struct结构体

    3:内存拷贝,将父亲进程的内存拷贝给新进程。

    4:设定新进程在GDT中的TSS和LDT描述符

    有关fork最主要的是弄明白了,为什么它可以“返回”两次。

    1:调用fork时,CPU自动将父进程的返回地址入栈(即eip寄存器入栈)

    2:创建子进程的task_struct后,将TSS段中的eax字段设成0,eip字段设成父进程的返回地址。

    3:将子进程的状态设成TASK_RUNNING(就绪状态)

    4:fork函数以子进程的pid返回。

    5:等到执行schedule,调度到子进程时。会自动将子进程的TSS内容加载进寄存器。

    因此这时CPU中eax寄存器值为0, eip为父进程的返回地址。所以子进程从fork函数的下一条指令开始执行,返回值在eax中,为0。

    阅读(2793) | 评论(0) | 转发(0) |

    展开全文
  • linux0.11系统调用原理和实验总结》由会员分享,可在线阅读,更多相关《linux0.11系统调用原理和实验总结(11页珍藏版)》请在技术文库上搜索。1、Linux0.11 系统调用原理及实验总结系统调用原理及实验总结1系统调用...

    《linux0.11系统调用原理和实验总结》由会员分享,可在线阅读,更多相关《linux0.11系统调用原理和实验总结(11页珍藏版)》请在技术文库上搜索。

    1、Linux0.11 系统调用原理及实验总结系统调用原理及实验总结1系统调用的原理系统调用的原理1.1概述系统调用是一个软中断,中断号是 0x80,它是上层应用程序与 Linux 系统内核进行交互通 信的唯一接口。通过 int 0x80,就可使用内核资源。不过,通常应用程序都是使用具有标准 接口定义的 C 函数库间接的使用内核的系统调用,即应用程序调用 C 函数库中的函数,C 函数库中再通过 int 0x80 进行系统调用。所以,系统调用过程是这样的:应用程序调用 libc 中的函数libc 中的函数引用系统调用宏系统调用宏中使用 int 0x80 完成系统调用并返回。另外一种访问内核的方式是直。

    2、接添加一个系统调用,供自己的应用程序使用,这样 就不再使用库函数了,变得更为直接,效率也会更高。1.2相关的数据结构在说具体的调用过程之前,这里先要说几个数据结构。1.2.1 系统调用函数表系统调用函数表 sys_call_table 是在 sys.h 中定义的,它是一个函数指针数组,每个元素是 一个函数指针,它的值是各个系统提供的供上层调用的系统函数的入口地址。也就是说通 过查询这个表就可以调用软中断 0x80 所有的系统函数处理函数。1.2.2 函数指针偏移宏这是一系列宏,它们的定义在 unistd.h 中,基本形式为define _NR_name value,name 为 系统函数名字,。

    3、value 是一个整数值,是 name 所对应的系统函数指针在 sys_call_table 中的 偏移量。1.2.3 系统调用宏系统调用宏_syscalln(type,name)在内核的 unistd.h 文件中定义的,对它展开就是: type name(参数列表) 调用过程; ; 其中,n 为参数个数,type 为函数返回值类型,name 为所要调用的系统函数的名字。在 unistd.h 中共定义了 4 个这样的宏(n 从 0 到 3) ,也就是说,0.11 核中系统调用最多可带3 个参数。 那么下面就说这个宏干了什么,也就是说上面的那个“调用过程”是怎么样的呢?在这个宏 中嵌入了汇编代码。

    4、,做的工作就是 int 0x80,其中将字符串“_NR_”和 name 连接,组成一个 宏并将这个宏的值,也就是被调用的系统函数在 sys_call_table 中偏移量送到 eax 寄存器中; 同时指明系统函数将来的返回值放到 eax 中。1.3系统调用处理过程下面我再说一下系统调用的核心软中断 int 0x80 具体干了什么。这条指令会引起 CPU 的软 件中断,cpu 会根据中断号找到中断处理程序。这个中断处理程序是在 System_call.s 中。 在中断处理程序的工作过程大致是这样的:1.3.1 将寄存器 ds,es,fs 以及存有参数的 edx,ecx,ebx 入栈,再 ds,e。

    5、s,指向 内核段,fs 指向用户段。 1.3.2 根据 eax 中的偏移值,在函数表 sys_call_table 中找到对应的系统函数指 针(函数的入口地址) 。并利用 call 指令调用系统函数,返回后,程序把返 回值加入堆栈。 1.3.3 检查执行本次系统调用的进程的状态,如果发现由于某种原因原进程没处 在就绪状态或者时间片到了,就会执行进程调度函数 schedule()。 1.3.4 通过执行这次调用的程序的代码选择符判断它是不是普通用户程序,如果 是就调用信号处理函数。若不是就直接弹出栈内容,并返回2添加一个系统调用的实验添加一个系统调用的实验2.1实验内容在 linux0.11 版。

    6、本中添加两个系统调用,并编写一个简单的应用程序测试它们。第一个系统调用是 iam(),其原型为: int iam(const char * name);完成的功能是将字符串参数 name 的内容拷贝到内核中保存下来。要求 name 的长度不能 超过 23 个字符。返回值是拷贝的字符数。如果 name 的字符个数超过了 23,则返回“-1”, 并置 errno 为 EINVAL。第二个系统调用是 whoami(),其原型为: int whoami(char* name, unsigned int size);它将内核中由 iam()保存的名字拷贝到 name 指向的用户地址空间中,同时确保不会对。

    7、 name 越界访存(name 的大小由 size 说明) 。返回值是拷贝的字符数。如果 size 小于需要 的空间,则返回“-1”,并置 errno 为 EINVAL。2.2代码添加修改步骤2.2.1 在 unistd.h 中添加系统调用接口 #define __NR_whoami 72 #define __NR_iam 73int whoami(void); int aim(void);2.2.2 在 exit.c 文件中添加系统调用处理函数的实现 系统调用的函数可以在其他.c 文件中添加或在新建文件中添加,只要编辑进 image 都是可 以的,这里为了调试方便就在 exit.c 文件中添。

    8、加了。#define MAX 23 char N_MAX26;int sys_whoami(char* name, unsigned int size) if(strlen(N_MAX)size)return -EINVAL; int i;for(i=0;N_MAXi!=0;i+) put_fs_byte(N_MAXi, return strlen(N_MAX); int sys_iam(char *name) char c; char str100; memset(str,0,sizeof(str); int i;for(i = 0; i MAX)return -EINVAL; memset。

    9、(N_MAX,0,sizeof(N_MAX);for(i=0;stri!=0;i+) N_MAXi=stri; return i; 2.2.3 在 system_call.s 汇编代码中修改系统调用的个数 #If 0 nr_system_calls = 72 #else nr_system_calls = 74 #endif 2.2.4 测试代码的编写 test.c 的代码如下:#define __LIBRARY__ #include _syscall1(int,iam,char*,name) _syscall2(int,whoami,char*,name, unsigned int,size。

    10、)int main() int a = 0; char bb26 = “champion“; char cc26 = “;a = iam(bb); printf(“a=%d“, a);a = whoami(cc,8); printf(“iam=%sn“,cc); return(1); 3系统调用相关代码的分析系统调用相关代码的分析3.1初始化软件中断门。3.1.1 函数调用层次初始化软件中断门,就是把 0x80 软件中断的处理函数 system_call 挂载到中断向量表 idt 中, 以确保发生软件中断时会运行 system_call 函数,这个函数在 system_call.s 实现。初始。

    11、化的 流程如下: main() sched_init () set_system_gate (0x80, errno = -__res; return -1; 传入参数说明: 其中 type 表示系统调用的返回值类型,name 表示该系统调用的名称,atype、a 分别表示 第 1 个参数的类型和名称;可以有 n 个系统调用的传入参数,它们的数目和_syscall 后面的 数字一样大。调用接口宏含义说明:它先将宏__NR_#name 存入 EAX,将参数 fd 存入 EBX,然后进行 0x80 中断调用。调用返 回后,从 EAX 取出返回值,存入__res,再通过对__res 的判断决定传给 。

    12、API 的调用者什么 样的返回值。__NR_#name 就是系统调用的编号,在 include/unistd.h 中定义;在上面的例 子中,我们添加了两个自己的系统调用接口,如下:#define __NR_whoami 72 #define __NR_iam 733.3对_system_call 函数的分析处理流程图处理流程分析 _system_call:cmpl $nr_system_calls-1,%eax # 调用号如果超出范围的话就在 eax 中置-1 并退出。 ja bad_sys_callpush %ds # 保存原段寄存器值。 push %es push %fspushl %ed。

    13、x # ebx,ecx,edx 中放着系统调用相应的 C 语言函数的调用参数。 pushl %ecx # push %ebx,%ecx,%edx as parameters pushl %ebx # to the system call movl $0x10, %edx # set up ds,es to kernel spacemov %dx,%ds # ds,es 指向内核数据段(全局描述符表中数据段描述符)。 mov %dx,%es movl $0x17,%edx # fs points to local data spacemov %dx,%fs # fs 指向局部数据段(局部描述符表。

    14、中数据段描述符)。# 下面这句操作数的含义是:调用地址 = _sys_call_table + %eax * 4。参见列表后的说明。 # 对应的 C 程序中的 sys_call_table 在 include/linux/sys.h 中,其中定义了一个包括 72 个 # 系统调用 C 处理函数的地址数组表。 call _sys_call_table(,%eax,4)pushl %eax # 把系统调用号入栈。 movl _current,%eax # 取当前任务(进程)数据结构地址。 # 下面查看当前任务的运行状态。如果不在就绪状态(state 不等于 0) 就去执行调度程序。# 如果该任务在。

    15、就绪状态但 counter 值等于 0,则也去执行调度程序。 cmpl $0,state(%eax) # state jne reschedule cmpl $0,counter(%eax) # counter je reschedule3.4用户态和内核态之间的传递数据在内核中主要提供了四个函数实现内核态和用户态的数据传递: copy_to_user(), copy_from_user(),get_fs_byte(),put_fs_byte();上面测试用例中使用的是对字节的操作 get_fs_byte(),put_fs_byte()。4通过通过 bochs 环境如何验证系统调用环境如何验证。

    16、系统调用1.1Bochs+Linux0.11 调试环境建立。 可以分为两个部分的工作:搭建调试环境和 Bochs 命令的使用;这两部分网上资料较多, 就不在此描述了。 1.2测试程序的修改和添加方法。1.2.1 使用 mount 命令访问文件系统 hdc-0.11.img 要想把测试程序 test.c 运行起来,一定要放入文件系统才行,也就是一定要把 test.c 程序放 进 hdc-0.11.img 中去才行,可以用如下的方法打开文件系统:losetup /dev/loop1 hdc-0.11.img losetup -d /dev/loop1 losetup -o 512 /dev/loo。

    17、p1 hdc-0.11.img mkdir /mnt/tempdir mount -t minix /dev/loop1 /mnt/tempdir 说明:用 losetup 的-d 选项把 hdc-0.11.img 文件与 loop1 的关联解除,用 losetup 的-o 选项,该选项指明关联的起始字节偏移位置。由上面分区信息可知,这里第 1 个分区的起始偏移 位置是 1 * 512 字节。 在把第 1 个分区与 loop1 重新关联后,我们就可以使用 mount 命令来访问其中的文件了。 在对分区中文件系统访问结束后, 最后请卸载和解除关联。 umount /dev/loop1 loset。

    18、up -d /dev/loop11.2.2 编译 test.c 测试程序 把测试程序放到/mnt/tempdir/user/root 目录下,这样就可以任意修改 test.c 文件的内容, 并可以把修改的内容保存到 hdc-0.11.img 文件系统中去了。1.3通过 bochs 调试观察,是如何把 0x80 的中断函数 system_call 的地址挂载上去的。1.3.1 通过添加 do_nothing()函数,然后在此函数设置断点,可以查看 0x80 中断 处理函数是如何放到中断向量表中去的。加入调试辅助代码如下: 因为 set_system_gate 是一个宏,没有办法添加断点,所以就添。

    19、加了一个函数 do_nothing(), 在此处设置断点,以方便观察后面宏的运行情况;并且加入了几个 nop 命令,以方便观察 运行情况。void sched_init(void) do_nothing(); set_system_gate(0x80, 1.3.2 修改_set_gate 宏如下,加入了 nop 命令,以便调试观察。 #define _set_gate(gate_addr,type,dpl,addr) __asm__ (“nop nt“ “nop nt“ “nop nt“ “nop nt“ “movw %dx,%axnt“ “movw %0,%dxnt“ “nop nt“ “n。

    20、op nt“ “movl %eax,%1nt“ “movl %edx,%2nt“ “nop nt“ “nop nt“ “nop nt“ “nop “ : : “i“ (short) (0x8000+(dpl13)+(type8), “o“ (*(char *) (gate_addr), “o“ (*(4+(char *) (gate_addr), “d“ (char *) (addr),“a“ (0x00080000)1.3.3 可以看到运行的效果如下: 在 system.map 中,看到 do_nothing 的线性地址是 0x6c1d,可以在此处设置断点。Figure 1 设置断点Figu。

    21、re 2 程序运行到_set_gate 宏Figure 3 查看此时寄存器中的数值1.4调试测试程序 test.c 和 sys_whoami 和 sys_iam 函数 1.4.1 调试系统调用处理函数 sys_whoami 和 sys_iam.在 system.map 文件中,找到编译 kernel 后,函数 sys_whoami 和 sys_iam 所在的线性 地址。如下所示: 00008dba T sys_iam 00008e57 T sys_whoami在 bochs 启动 kernel 后,在 0x8dba 和 0x8e57 处设置断点,然后运行 test 程序, 就会进入系统调用处理函数,运行到设置的断点处,后面可以单步运行,以调试 sys_whoami 和 sys_iam,达到测试系统调用的目的。如下图所示:1.4.2 在 linux0.11 的系统中编译运行 test 程序 在运行起来的 linux0.11 的系统中,通过 make test 命令编译 test.c 文件,使生产 test 应用。 运行 test 程序后, iam()函数就会通过 get_fs_byte()函数把“champion”字符串存入到内存中; whoami()函数就会把前面存入到内存中的字符串取出,并且通过 put_fs_byte()内核函数返 回到用户层;并且打印出来。如下所示:。

    展开全文
  • linux0.11-master.zip

    2019-09-25 15:22:28
    Linux内核注释,包含了Linux内核源码,Linux内核0.11完全注释V3.0配套源代码
  • linux0.11内存管理

    2021-05-11 12:14:12
    描述linux 0.11的内存管理主要内容。1:内存初始化linux 0.11最大支持16MB的物理内存。main函数和mem_init函数对内存进行了初始化。主要使用数组mem_map[]来标记相应的内存页是否被占用。memory_end是用BIOS中断调用...

    描述linux 0.11的内存管理主要内容。

    1:内存初始化

    linux 0.11最大支持16MB的物理内存。

    main函数和mem_init函数对内存进行了初始化。

    主要使用数组mem_map[]来标记相应的内存页是否被占用。

    memory_end是用BIOS中断调用得到的实际内存大小。

    if (memory_end > 16 * 1024 * 1024)

    memory_end = 16 * 1024 * 1024;           # 因此最大只支持16MB内存

    if (memory_end > 12*1024*1024)

    buffer_memory_end = 4 * 1024 * 1024;   # buffer_memory_end为高速缓存末端地址,其大小于机器总内存大小相关

    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;

    mem_init(main_memory_start, memory_end);

    在mem_init中会对mem_map[]数组进行初始化。

    在1MB~16MB之间,共有(15 * 1024 * 1024) >> 12 = 3840页。

    定义数组mem_map[3840]对应于这段内存的每一页,在main_memory_start和memory_end之间的页,相应的mem_map[i]的值

    初始化为0,表示未使用,其余的项初始化为100,表示被占用。

    2:基本页面分配函数

    有几个基本的页面分配和释放的函数。

    get_free_page():返回一个空闲页面的物理地址, 该函数就是在mem_map数组中查找值为0的项,然后转换成页面物理地址返回。

    free_page(phy_addr):释放phy_addr指向的页面, 释放操作就是将phy_addr在mem_map中对应项的值进行减1。

    get_empty_page(line_addr):该函数的参数指定的是线性地址,要求获得一页物理内存,并用line_addr指向这页内存。

    void get_empty_page(unsigned long address)

    {

    unsigned long tmp;

    if (!(tmp=get_free_page()) || !put_page(tmp, address)) {

    free_page(tmp);

    oom();        # 内存不够

    }

    }

    明显该函数先调用get_free_page获得一页物理内存, 然后用put_page对这页物理内存的物理地址和线性地址之间建立映射。

    建立映射的过程:

    对于32位的线性地址, 高10位表示页目录索引, 中间10位为页表索引, 低12位为页内偏移。

    因此给定一个线性地址,我们就能通过页目录基地址和线性地址高10位来确定它的页目录项。

    在Linux 0.11中,页目录基地址就是0。

    put_page:

    page_table = (unsigned long *) ((line_addr >> 20) & 0xffc)      #  获得页目录项的指针

    如果该页目录项所指向的页表是存在的, 则利用线性地址的中间10位,定位到页表中的相应页表项,

    并将物理地址保存进去即可建立映射。  若页表不存在,则用get_free_page首先分配一页作为页表,再去建立映射。

    不管是建立映射还是进行页表拷贝,都是先考虑页目录,再考虑页表。

    3:sys_fork与copy_page_tables函数

    在sys_fork时会调用copy_process,copy_process中会调用copy_mem函数, 该函数会将父进程的页表拷贝给子进程,

    这是父进程和子进程会共享相应的代码和数据段,只有当父进程或子进程对共享的内存进行写操作时,才会为子进程分配内存,即为写时复制。

    copy_mem是调用copy_page_table进行页表拷贝的。

    copy_page_table(old_data_base, new_data_base, data_limit)

    将父进程的线性地址old_data_base ~ old_data_base+data_limit对应的页表,拷贝给子进程。

    在拷贝过程中,将每个页表项设定为只读, 并且执行相应的mem_map[i]++(相当于添加引用计数)

    4:页出错异常处理

    有两种不同的页出错:

    i)页表项指向的页不存在,即页表项的存在位的值为0.

    ii)页保护机制。 写只读页面时出错。

    但出现页错误时,会发生int 14中断。系统会执行_page_fault:异常处理代码。

    该代码会根据两种不同的页错误,分别执行do_no_page和do_wp_page

    do_wp_page(error_code, address)

    该函数主要是实现了写时复制功能, 在copy_page_table时,将父进程和子进程的页表都设定成了只读,当访问了其中一个页面后,

    会触发中断并执行do_wp_page函数。

    此函数会分配一页新的物理内存, 并将相应的页表项设成可读写。

    do_no_page(error_code, address)

    该函数可处理两种情况:

    i)在应用分配内存时,内核并不会实际分配物理内存, 只有在访问相应内存时,才会分配。

    ii)在执行exec系列函数时,同样只有在访问相应内存时,才会去读文件,

    对于第一种情况, do_no_page直接调用get_empty_page函数,获取一页内存。

    对于第二种情况,有两步过程。

    i)调用share_page函数。 该函数的主要目的是共享代码和数据段。 如果一个可执行文件,已经有一个进程实例在执行,那么

    新进程可以和其他进程共享该可执行文件的代码和数据段。

    ii)如果只执行过一次该文件, 那就先调用get_free_page函数,将文件内容读入内存,然后用put_page函数建立映射。

    阅读(335) | 评论(0) | 转发(0) |

    展开全文
  • LINUX0.11进程调度

    2021-05-10 07:26:08
    LINUX0.11进程调度void schedule(void)是调度函数的原型,在kernel/sched.c文件中定义。一、进程调度的时机1、进程调度发生在时钟中断到来时 2、内核由于等待硬件资源而主动放弃cpu时。由于schedule()本身就是时钟...
  • 实验一 熟悉实验环境 实验报告一、实验目标认识实验平台和实验工具;认识实验环境的目录结构;掌握编译、运行、调试、文件...Bochs :一个 IA-32 (X86) 架构PC机模拟器,可以模拟出多种操作系统的环境,如Linux、DOS...
  • linux0.11内核源码

    2019-01-16 11:47:10
    这个是0.11Linux内核源码,其中每一行都含有中文注释,能够帮且初学者快速入门Linux内核
  • linux0.11内核分析之进程创建 概述 对用户而言,进程创建常用的是fork函数,对于进程创建的分析也主要围绕着该函数执行的一系列操作进行。 源码分析阅读 1、fork 翻libc太麻烦了,所以从大佬的文章中嫖来了下面这段...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 24,805
精华内容 9,922
关键字:

linux0.11

linux 订阅
友情链接: The basic algorithm.zip