Linux课程总结报告

Linux启动过程:

  1. 开机自检:检查硬件是否满足运行基本条件
  2. 启动顺序检查:BIOS需要移交控制权给下一阶段的启动程序,所以需要知道下一阶段的启动程序具体存放在哪个设备,也就是BIOS需要有一个设备的优先级表格,这个表格就是启动顺序自检
  3. 主引导记录:告诉计算机操作系统的位置
  4. 启动管理器Grub:此时可以由用户选择操作系统(一般电脑开机按F12就可以进入启动管理器)
  5. 操作系统:此时cpu控制权移交到操作系统,操作系统也就被载入内核,运行第一个程序systemd,执行默认target配置文件
  6. 1.用户登录:此时若登录shell,操作系统背后的操作是:首先读/etc/profile.d/*.sh,再去读用户的home目录,读~/.bash_profile,然后读~/.bashrc,最后读/etc/bashrc。这一系列步骤是为了初始化环境变量。2.非登录shell,则只需要读取~/.bashrc与/etc/bashr环境变量即可

Linux虚拟化:

  1. 首先,我们要定义 CPU 这种类型的 TypeInfo 和 TypeImpl、继承关系,并且声明它的类初始化函数。在 qemu 的 main 函数中调用 MachineClass 的 init 函数,这个函数既会初始化 CPU,也会初始化内存。CPU 初始化的时候,会调用 pc_new_cpu 创建一个虚拟 CPU,它会调用 CPU 这个类的初始化函数。每一个虚拟 CPU 会调用 qemu_thread_create 创建一个线程,线程的执行函数为 qemu_kvm_cpu_thread_fn。
  2. 在虚拟 CPU 对应的线程执行函数中,我们先是调用 kvm_vm_ioctl(KVM_CREATE_VCPU),在内核的 KVM 里面,创建一个结构 struct vcpu_vmx,表示这个虚拟 CPU。在这个结构里面,有一个 VMCS,用于保存当前虚拟机 CPU 的运行时的状态,用于状态切换。
  3. 在虚拟 CPU 对应的线程执行函数中,我们接着调用 kvm_vcpu_ioctl(KVM_RUN),在内核的 KVM 里面运行这个虚拟机 CPU。运行的方式是保存宿主机的寄存器,加载客户机的寄存器,然后调用 __ex(ASM_VMX_VMLAUNCH) 或者 __ex(ASM_VMX_VMRESUME),进入客户机模式运行。一旦退出客户机模式,就会保存客户机寄存器,加载宿主机寄存器,进入宿主机模式运行,并且会记录退出虚拟机模式的原因。大部分的原因是等待 I/O,因而宿主机调用 kvm_handle_io 进行处理。

Linux一般执行过程

父进程的行为:从shell中执行,假设从shell中执行一个./test文件,此时shell的操作为先fork()出一个子进程,wait()等待这个子进程结束,当子进程运行结束后,又回到了shell等待用户输入,所以shell进程的主要工作就是复制一个进行并等待它执行结束。

子进程的行为:“执行”应用进程

  1. execve(): 在子进程中会调用execve()加载test并开始执行,execve()是操作系统提供的非常重要的一个系统调用,在很多文章中被称为exec()系统调用(注意和shell内部exec命令不一样),其实在Linux中并没有exec()这个系统调用,exec只是用来描述一组函数,它们都以exec开头.exec 函数的作用是在当前进程里执行可执行文件,也就是根据指定的文件名找到可执行文件,用它来取代当前进程的内容,并且这个取代是不可逆的,即被替换掉的内容不再保存,当可执行文件结束,整个进程也随之僵死。因为当前进程的代码段,数据段和堆栈等都已经被新的内容取代,所以exec函数族的函数执行成功后不会返回,失败是返回-1。可执行文件既可以是二进制文件,也可以是可执行的脚本文件,两者在加载时略有差别,这里主要分析二进制文件的运行。
  2. do_execve():在用户态下调用execve(),引发系统中断后,在内核态执行的相应函数是do_sys_execve(),而do_sys_execve()会调用 do_execve()函数。do_execve()首先会读入可执行文件,如果可执行文件不存在,会报错。然后对可执行文件的权限进行检查。如果文件不是当前用户是可执行的,则execve()会返回-1,报permission denied的错误。否则继续读入运行可执行文件时所需的信息
  3. search_binary_handler():接着系统调用search_binary_handler(),根据可执行文件的类型(如shell,a.out,ELF等),查找到相应的处理函数(系统为每种文件类型创建了一个struct linux_binfmt,并把其串在一个链表上,执行时遍历这个链表,找到相应类型的结构。如果要自己定义一种可执行文件格式,也需要实现这么一个 handler)。然后执行相应的load_binary()函数开始加载可执行文件。
  4. load_elf_binary():加载elf类型文件的handler是load_elf_binary(),它先读入ELF文件的头部,根据ELF文件的头部信息读入各种数据 (header information)。再次扫描程序段描述表,找到类型为PT_LOAD的段,将其映射(elf_map())到内存的固定地址上。如果没有动态链接器的描述段,把返回的入口地址设置成应用程序入口。完成这个功能的是start_thread(),start_thread()并不启动一个线程,而只是用来修改了pt_regs中保存的PC等寄存器的值,使其指向加载的应用程序的入口。这样当内核操作结束,返回用户态的时候,接下来执行的就是应用程序了。
  5. load_elf_interp():如果应用程序中使用了动态链接库,就没有那么简单了,内核除了加载指定的可执行文件,还要把控制权交给动态连接器(program interpreter,ld.so in linux)以处理动态链接的程序。内核搜寻段表,找到标记为PT_INTERP的段中所对应的动态连接器的名称,并使用 load_elf_interp()加载其映像,并把返回的入口地址设置成load_elf_interp()的返回值,即动态链接器入口。当 execve退出的时候动态链接器接着运行。动态连接器检查应用程序对共享连接库的依赖性,并在需要时对其进行加载,对程序的外部引用进行重定位。然后动态连接器把控制权交给应用程序,从ELF文件头部中定义的程序进入点开始执行。

操作系统执行过程总结:

整个在shell中键入./test执行应用程序的过程为:当前shell进程fork出一个子进程(子shell),子进程使用execve来脱离和父进程的关系,加载test文件(ELF格式)到内存中。如果test使用了动态链接库,就需要加载动态链接器(或者叫程序解释器),进一步加载 test使用到的动态链接库到内存,并重定位以供test调用。最后从test的入口地址开始执行test。


课程总结:

在操作系统这门课的学习过程中,我逐步认识到了一个系统的运行过程,了解到了文件系统的奥秘,并且通过对Linux的结构和设计原理,各个系统的相互配合和运转有了进一步的了解。之前对于操作系统的了解只是知道怎么操作,但是对于操作的更多的一部分,底层是怎么实现具体的功能,会用到什么方法却没有具体的了解,所以这门课让我对操作系统的内核理解更加深刻!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值