首页 > 其他 > 详细

课程学习总结报告

时间:2020-07-09 12:20:57      阅读:67      评论:0      收藏:0      [点我收藏+]
要求

  1.请您根据本课程所学内容总结梳理出一个精简的Linux系统概念模型,最大程度统摄整顿本课程及相关的知识信息,模型应该是逻辑上可以运转的、自洽的,并举例某一两个具体例子(比如读写文件、分配内存、使用I/O驱动某个硬件等)纳入模型中验证模型;

  2.谈谈您对课程的心得体会,改进建议等;

  3.产出要求是发表一篇博客文章,长度不限,只谈自己的思考,严禁引用任何资料造成文章虚长.

一、计算机的基本工作原理

1、冯诺依曼体系结构

技术分享图片

计算机硬件的核?是CPU,它与内存和输?/输出(I/O)设备进?交互,从输?设备接收数据,向输出设备发送数据。CPU由运算器(算术逻辑单元ALU)、控制器和?些寄存器组成。还有?个?常重要的寄存器?般称为程序计数器(Program Counter,PC)。

 2、基本汇编语言

汇编指令包含操作码和操作数,操作码主要?些常?的汇编指令,?如最常?的汇编指令是mov指令,movb中的b是指8位,movw中的w是指16位,movl中的l是指32位,movq中的q是指64位;操作数分为?即数、寄存器和存储器3种:

  ?即数即常数、寄存器,表示某个寄存器中保存的值、存储器,根据计算出的有效地址来访问存储器的某个位置,这就涉及到?种寻址方式:

  寄存器寻址:就是操作的是寄存器,不和内存打交道,如%eax,其中%开头后?跟?个寄存器名称。

  ?即寻址(immediate)是??个$开头后?跟?个数值。

  直接寻址(direct)是直接??个数值,开头没有$符号。开头有$符号的数值表示这是?个?即数;没有$符号表示这是?个地址。

  间接寻址(indirect)就是寄存器加个?括号。举例说明,%ebx这个寄存器中存的值是?个内存地址,加个?括号表示这个内存地址所存储的数据。

  变址寻址(displaced)就是在间接寻址的基础上,在原地址上加上?个?即数。

二、操作系统内核分析

1、mykernel

  mykernel这样一个短小精悍的模拟内核,几乎贯穿整学期的linux内核分析的课程。mykernel已经把保存现场和恢复现场的复杂?作已经帮我们完成了,提供了?个周期性发?的时钟中断,所以我们就有基础写?个时间?轮转调度的操作系统内核。

void __init my_start_kernel(void)
{
    int pid = 0;
    int i;
    /* Initialize process 0*/
    task[pid].pid = pid;
    task[pid].state = 0;/* -1 unrunnable, 0 runnable, >0 stopped */
    task[pid].task_entry = task[pid].thread.ip = (unsigned long)my_process;
    task[pid].thread.sp = (unsigned long)&task[pid].stack[KERNEL_STACK_SIZE-1];
    task[pid].next = &task[pid];
    /*fork more process */
    for(i=1;i<MAX_TASK_NUM;i++)
    {
        memcpy(&task[i],&task[0],sizeof(tPCB));
        task[i].pid = i;
        task[i].state = -1;
        task[i].thread.sp = (unsigned long)&task[i].stack[KERNEL_STACK_SIZE-1];
        task[i].next = task[i-1].next;
        task[i-1].next = &task[i];
    }
    /* start process 0 by task[0] */
    pid = 0;
    my_current_task = &task[pid];
    asm volatile(
    "movq %1,%%rsp\n\t" /* set task[pid].thread.sp to rsp */
    "pushq %1\n\t" /* push rbp */
    "pushq %0\n\t" /* push task[pid].thread.ip */
    "ret\n\t" /* pop task[pid].thread.ip to rip */
    :
    : "c" (task[pid].thread.ip),"d" (task[pid].thread.sp) /* input c or d mean %ecx/%edx*/
);

  mymain.c是mykernel内核代码的??,负责初始化内核的各个组成部分。在Linux内核源代码中,实际的内核??是init/main.c中的start_kernel(void)函数。

2、系统调用

  技术分享图片

 

 宏观上 Linux 操作系统的体系架构分为?户态和内核态。计算机的硬件资源是有限的,为了减少有限资源的访问和使?冲突,CPU 和操作系统必须提供?些机制对?户程序进?权限划分。

技术分享图片

【1】系统调?是?种特殊的中断,而中断分外部中断(硬件中断)和内部中断(软件中断),内部中断?称为异常(Exception),异常?分为故障(fault)和陷阱(trap)。系统调?就是利?陷阱(trap)这种软件中断?式主动从?户态进?内核态的。

【2】?般来说,从?户态进?内核态是由中断触发的,可能是硬件中断,在?户态进程执?时,硬件中断信号到来,进?内核态,就会执?这个中断对应的中断服务例程。也可能是?户态程序执?过程中,调?了?个系统调?,陷?了内核态,叫作陷阱(trap)。

【3】当?户态进程调??个系统调?时,CPU切换到内核态并开始执?system_call(entry_INT80_32或entry_SYSCALL_64)汇编代码,其中根据系统调?号调?对应的内核处理函数。具体来说,在Linux中通过执?int $0x80或syscall指令来触发系统调?的执?,其中这条int$0x80汇编指令是产?中断向           量为128的编程异常(trap)。

3、进程切换

关键代码:

/* schedule */
next = my_current_task->next;
prev = my_current_task;
if(next->state == 0) /* -1 unrunnable, 0 runnable, >0 stopped */
{
    my_current_task = next;
    printk(KERN_NOTICE ">>>switch %d to %d<<<\n",prev->pid,next->pid);
    /* 进程切换关键代码 */
    asm volatile(
        "pushq %%rbp\n\t" /* save rbp of prev */
        "movq %%rsp,%0\n\t" /* save rsp of prev */
        "movq %2,%%rsp\n\t" /* restore rsp of next */
        "movq $1f,%1\n\t" /* save rip of prev */
        "pushq %3\n\t"
        "ret\n\t" /* restore rip of next */
        "1:\t" /* next process start here */
        "popq %%rbp\n\t"
        : "=m" (prev->thread.sp),"=m" (prev->thread.ip)
        : "m" (next->thread.sp),"m" (next->thread.ip)
    );
}

分析如下:

pushq %%rbp 保存prev进程当前RBP寄存器的值到堆栈;

movq %%rsp,%0 保存prev进程当前RSP寄存器的值到prev->thread.sp,这时RSP寄存器指向进程的栈顶地址;

movq %2,%%rsp 将next进程的栈顶地址next->thread.sp放?RSP寄存器,完成了进程间的堆栈切换;

movq $1f,%1 保存prev进程当前RIP寄存器值到prev->thread.ip,这?$1f是指标号1;

pushq %3 把即将执?的next进程的指令地址next->thread.ip?栈,这时的next->thread.ip可能是进程1的起点my_process(void)函数,也可能是$1f(标号1);

ret 就是将压?栈中的next->thread.ip放?RIP寄存器,为什么不直接放?RIP寄存器呢?因为程序不能直接使?RIP寄存器,只能通过call、ret等指令间接改变RIP寄存器;

1: 标号1是?个特殊的地址位置,该位置的地址是$1f;

popq %%rbp 将next进程堆栈基地址从堆栈中恢复到RBP寄存器中。

 进程在执?过程中,当时间??完需要进?进程切换时,需要先保存当前的进程上下?环境,下次进程被调度执?时,需要恢复进程上下?环境,就这样通过虚拟化的进程概念实现了多道程序在同?个物理CPU上并发执?。

技术分享图片

 

 

进程上下?切换时需要保存要切换进程的相关信息(如thread.sp与thread.ip),这与中断上下?的切换是不同的。中断是在?个进程当中从进程的?户态到进程的内核态,或从进程的内核态返回到进程的?户态,?切换进程需要在不同的进程间切换。但?般进程上下?切换是嵌套到中断上下?切换中的,?如前述系统调?作为?种中断先

陷?内核,即发?中断保存现场和系统调?处理过程。其中调?了schedule函数发?进程上下?切换,当系统调?返回到?户态时会恢复现场,?此完成了保存现场和恢复现场,即完成了中断上下?切换。

 4、中断与异常

中断最初?于避免CPU轮询I/O设备,就绪状态发?时让I/O设备主动通过中断信号通知CPU,??提?了CPU在输?输出上的?作效率,这就是硬件中断(外部中断)。后来随着中断适?范围扩?,?如解决机器运?过程出现的异常情况以及系统调?的实现等,这就产?了软件中断(内部中断),也称为异常,软件中断?分为故障(fault)

和陷阱(trap)。

中断包含可屏蔽中断——I/O设备发出的所有中断请求(IRQ)都可以产生可屏蔽中断。非屏蔽中断——只有几个特定的危急事件才引起非屏蔽中断。如硬件故障或是断电。

异常分为:
  · 处理器探测异常
    · 由CPU执行指令时探测到一个反常条件时产生,如溢出、除0错等
  · 编程异常
    · 由编程者发出的特定请求产生,通常由int类指令触发

在中断/异常处理完成后,会执行iret指令:

  1. 将保存在栈中的值装在cs、eip和eflags寄存器,由出错码就弹出
  2. 检查处理程序的特权等级,判断是否运行在内核态。如果是,就进行下一步
  3. 从栈中装载ss和esp,返回原来的栈空间
  4. 检查段寄存器

中断处理:

  当中断发生时,Linux系统会跳转到asm_do_IRQ()函数(所有中断程序的总入口函数),并且把中断号irq传进来。根据中断号,找到中断号对应的irq_desc结构(irq_desc结构为内核中中断的描述结构,内核中有一个irq_desc结构的数组irq_desc_ptrs[NR_IRQS]),然后调用irq_desc中的handle_irq函数,即中断入口函数。我们编写中

  的驱动,即填充并注册irq_desc结构。

从中断/异常返回:

  【1】用保存在栈中的值装载cs、eip和eflags寄存器。如果一个硬件出错码曾被压入栈中,那么弹出这个硬件出错码;

  【2】检查处理程序的特权级是否等于cs中最低两位的值(这意味着进程在被中断的时候是运行在内核态还是用户态)。若是,iret终止执行;否则,转入3;

  【3】从栈中装载ss和esp寄存器。这步意味着返回到与旧特权级相关的栈;

  【4】检查ds、es、fs和gs段寄存器的内容,如果其中一个寄存器包含的选择符是一个段描述符,并且特权级比当前特权级高,则清除相应的寄存器。这么做是防止怀有恶意的用户程序利用这些寄存器访问内核空间。

5、文件系统

在LINUX系统中有一个重要的概念:一切都是文件。 其实这是UNIX哲学的一个体现,而Linux是重写UNIX而来,所以这个概念也就传承了下来。在UNIX系统中,把一切资源都看作是文件,包括硬件设备。UNIX系统把每个硬件都看成是一个文件,通常称为设备文件,这样用户就可以用读写文件的方式实现对硬件的访问。这样带来优势也是

显而易见的:UNIX 权限模型也是围绕文件的概念来建立的,所以对设备也就可以同样处理了。

技术分享图片

 

 

Linux支持多种文件系统,包括ext2、ext3、 vfat、 ntfs、 iso9660、 jffs、 romfs和nfs等,为了对各类文件系统进行统一管理, Linux引入了虚拟文件系统VFS。

虚拟文件系统 VFS:

  • 是一个软件层,用来处理与Unix标准文件系统相关的所有系统调用。
  • 是用户应用程序与文件系统实现之间的抽象层能为各种文件系统提供一个通用的、统一的接口

 6、举例

以用户读一个文件为例:

    当用户点击打开文件时,会调用库函数中的read()函数,而在这个函数中会触发软中断指令,CPU陷入到内核态,进程堆栈也会切换到相应的内核态堆栈。

    接着,进入异常处理的硬件级流程,在这个过程中,CPU在栈中保存eflags、cs、和eip的内容,随后进入异常处理的软件级流程,在这个过程中, CPU会进一步保存上下文.。

    与这个过程相对应的是:CPU读取idtr寄存器指向的IDT表中的第128项,此时可以得到0x80中断门,通过这个中断门CPU可以得到中断处理例程。

    接着, 中断处理例程会到系统调用表中找到read()函数相对应的VFS虚拟文件系统中的系统调用sys_read()。

    到达VFS层次后,sys_read()会根据fd在进程打开文件表中找到相应的系统打开文件表(File数据结构),然后执行系统打开文件表中的file operations中具体的操作。

    文件打开之后,将pt_regs数据结构中描述的寄存器以及cs:eip寄存器恢复, 此时进程又重新恢复到了用户态, 并沿着read()函数的下一条语句继续执行。

 7、总结

通过本次课程既从宏观上了解了linux系统的一般执行过程,然后又具体到每一个细节,比如中断和异常的具体处理过程,定时器的初始化和执行,进程的管理,linux的文件系统等。首先在心中有了框架,然后又深入了解了它们的具体实现过程,从而在宏观和微观上对linux系统有了系统的认知。之前只是知道Linux的基本操作命令,现在对Linux操作系统底层的实现机制已经有了一定的认识,这让我受益良多,非常感谢2位老师的细心教导。

课程学习总结报告

原文:https://www.cnblogs.com/macb/p/13269635.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!