Linux_总结

具体博客链接

一、计算机是如何工作的

存储程序计算机工作模型

  • 冯诺依曼体系结构

    • 从硬件角度来看:CPU和内存,由总线连接,CPU中有一个名为IP的寄存器,总是指向内存的某一块:CS,代码段,执行命令时就取IP指向的一条指令,然后IP自加1,就指向下一条指令。
    • 从程序员角度来看:即存储程序计算机,内存存储数据和指令,CPU就是一个for循环,总是在执行下一条指令,CPU负责解释和执行这些指令。

X86汇编基础

  • CPU寄存器

    • 通用寄存器+段寄存器+标志寄存器
  • X86汇编指令
    • mov指令:b,w,l,q分别代表8位,16位,32位,64位
    • 寻址方式:寄存器寻址,立即数寻址,直接寻址,间接寻址,变址寻址
    • 汇编指令:push,pop,call,ret
    • leave
  • 分析汇编指令片段

汇编一个简单的C程序分析其汇编指令执行过程

  • 汇编一个简单的C程序
  • C程序和对应的汇编指令
  • 完整汇编程序执行过程分析

二、操作系统是如何工作的

函数调用堆栈

  • 计算机三大法宝

    • 存储程序计算机工作模型
    • 函数调用堆栈
    • 中断
  • 深入理解函数调用堆栈
    • 函数调用框架
    • 传递参数
    • 保存返回地址
    • 提供局部变量空间
  • 堆栈相关寄存器:esp堆栈指针ebp基址指针,记录当前函数调用基址
  • 堆栈操作:push,pop
  • cs:eip:总是指向下一条的指令地址,执行call的时候,保存下一条指令地址到栈顶,然后cs:eip指向调用函数入口地址。
  • 参数传递与局部变量

借助Linux内核部分源代码模拟存储程序计算机工作模型及时钟中断

  • mykernel实验背后涉及的思想
  • 利用mykernel实验模拟计算机硬件平台
  • 由CPU和内核代码共同实现了保存现场和恢复现场

在mykernel基础上构造一个简单的操作系统内核

  • C代码中嵌入汇编代码的写法
  • 一个简单的操作系统内核源代码
    • mypcb.h:头文件
    • mymain.c: 内核初始化和进程的启动
    • myinterrupt.c:进程的切换
  • 运行这个精简的操作系统内核

三、构造一个简单的Linux系统MenuOS

Linux内核源代码简介

  • 操作系统两把宝剑

    • 中断上下文的切换
    • 进程上下文的切换
  • Linux内核源代码

构造一个简单的Linux系统

  • 构造一个简单的Linux系统MenuOS

    • qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img

      • qemu:启动一个虚拟机平台
      • -kernel:给一个内核,操作系统
      • -initrd:驱动所需的硬盘
      • rootfs.img:放一个可执行文件由menuOS源代码编译成的init

跟踪调试Linux内核的启动过程

  • 使用gdb跟踪调试Linux内核的方法

    • qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img -s -S

      • -S freeze CPU at startup (use ’c’ to start execution)指在CPU初始化之前(刚启动的时候)将其冻结
      • -s shorthand for -gdb tcp::1234 指在1234这个端口上创建的gdb server,若不想使用1234端口,则可以使用-gdb tcp:xxxx来取代-s选项
    • gdb
    • (gdb)file linux-3.18.6/vmlinux # 在gdb界面中targe remote之前加载符号表
    • (gdb)target remote:1234 # 建立gdb和gdbserver之间的连接,按c 让qemu上的Linux继续运行
    • (gdb)break start_kernel # 断点的设置可以在target remote之前,也可以在之后
      • 符号:全局函数名,全局变量名,汇编时没有,不需要符号只是一个地址,但调试时需要,所以有符号表:符号与在内存中地址的一一对应。
  • 简单分析一下start_kernel
    • 全局变量init_task,即手工创建的PCB,0号进程即最终的idle进程
    • trap_init涉及到中断,初始化一些中断向量
    • mm_init:内存管理模块初始化
    • sched_init:调用进程调度初始化
    • rest_init中:
      • kernel_init:创建1号进程
      • 第一个用户态进程:init_process,1号进程,找默认路径下的程序作为1号进程
      • kthreadd:内核线程,用来管理系统资源
      • 当系统没有进程需要执行时就调度idle进程,即0号进程,一直存在。

四、系统调用

用户态、内核态和中断

  • 用户态&内核态(CPU执行级别)

    • 内核态:高级别执行,可以使用特权指令,访问任意的物理地址。对应x86 0级
    • 用户态:低级别执行,代码范围受到限制。对应x86 3级(x86CPU有0-3四个级别)
  • 如何区分用户态&内核态?
    • 0xc0000000以上的地址只能在内核态下访问
    • 0x00000000-0xbfffffff两种状态都行
  • 中断处理是从用户态进入内核态主要的方式
    • 从用户态进入内核态:必须保存用户态的寄存器上下文
    • 中断/int指令在堆栈上保存寄存器的值:用户态/内核态栈顶地址(ss:esp)、状态字(eflags)、cs:eip值(内核态时指向中断服务程序入口)

系统调用概述

  • 系统调用的意义

    • 用户不管硬件编程
    • 提高系统安全性
    • 用户程序可移植
  • API和系统调用
    • API:应用编程接口,是一个函数定义
    • 系统调用:通过软中断向内核发出明确请求
    • 不是每个API都对应一个特定的系统调用
  • 应用程序、封装例程、系统调用处理程序、系统调用服务例程之间的关系
  • 系统调用三层皮:
    • API(xyz)
    • 中断向量(system_call)
    • 中断服务程序(sys_xyz)

使用库函数API和C代码中嵌入汇编代码触发同一个系统调用

  • 使用库函数API获取系统当前时间
  • 使用C代码中嵌入汇编代码触发系统调用获取系统当前时间
    • 传递了一个系统调用号 - eax
    • 传递了参数 - ebx

给MenuOS增加time和time-asm命令

  • 更新menu代码到最新版
  • 在main函数中增加MenuConfig(一个命令一行,与上面的格式一样)
  • 增加对应的time和time-asm函数(就是上周写的两个函数)
  • make rootfs (rootfs是一个脚本,可以自动生成,编译)

使用gdb跟踪系统调用内核函数sys_time

  • c:继续执行,停在断点处
  • n/s:单步运行,s进入函数,n不进入

系统调用在内核代码中的处理过程

  • int 0x80触发一个系统调用 ->执行系统调用处理函数system_call ->返回到用户态

    • 返回到用户态之前有一个进程调度的时机,会跟踪到schedule
  • 中断向量:int 0x80 ->system_call,初始化时绑定
  • 系统调用号:将xyz和中断服务程序sys_xyz关联起来
  • 系统调用机制的初始化
    • main.c中start_kernel函数:trap_init()

      • set_system_trap_gate(SYSCALL_VECTOR,&system_call)
      • SYSCALL_VECTOR:系统调用的中断向量
      • &system_call:汇编代码入口
  • 理解system_call到iret之间的主要代码
    • 从系统调用入口开始:ENTRY(system_call)
    • SAVE_ALL //保存现场
    • system_call中:call *system_ call_table(,%eax,4) //调用了系统调用处理函数,有系统调用号eax中,是实际的系统调用处理程序。
    • 当前任务syscall_ exit_ work里面有work_ pending里面有work_ notifysig //处理pending信号,不用管
    • 重要的是work_ resched:call schedule //决定了进程调度的代码,调用完会跳转到restore_all
    • restore_all //恢复现场
    • INTERRUPT_ RETURN //irp_return宏,中断处理过程在这结束

五、进程的描述和进程的创建

进程的描述

  • 操作系统的三大管理功能:

    • 进程管理(最重要的)
    • 内存管理
    • 文件系统
  • 进程控制块PCB task_struct:
    • 进程状态
    • 进程打开的文件
    • 进程优先级信息
  • 进程描述符task_struct数据结构

进程的创建

  • 进程的创建概览

    • 道生一(start_ kernel....cpu_idle)
    • 一生二(kernel_init和kthreadd)
    • 二生三(即前面0、1和2三个进程)
    • 三生万物(1号进程是所有用户态进程的祖先,2号进程是所有内核线程的祖先)
  • fork创建一个子进程
    • fork在父进程和子进程各返回一次
  • 创建一个新进程在内核中的执行过程
  • 新进程从哪里开始执行?
    • fork出来的子进程是从ret_from_fork开始执行的,然后跳转到syscall_exit,从系统调用中返回。

六、可执行程序的装载

预处理、编译、链接和目标文件的格式

  • 可执行程序是怎么得来的?

    • C代码
    • 编译器预处理
    • 编译成汇编代码
    • 汇编器编译成目标代码
    • 链接成可执行文件
    • 操作系统加载到内存执行
  • 目标文件的格式ELF
  • 静态链接的ELF可执行文件和进程的地址空间
    • 默认加载起始地址是0x8048000
    • ELF头部大小不同,程序入口点也将不同,程序入口在头部有定义:Entry point address,即是可执行文件加载到内存中开始执行的第一行代码

可执行程序、共享库和动态链接

  • 装载可执行程序之前的工作

    • 可执行程序的执行环境
    • 命令行参数和环境变量是如何保存和传递的?
    • 参数传递过程:shell程序 -> execve系统调用 -> sys_execve 内核处理函数在初始化新程序堆栈时拷贝进去
  • 装载时动态链接和运行时动态链接应用举例
    • 准备.so文件
    • 分别以共享库和动态加载共享库的方式使用libshlibexample.so文件和libdllibexample.so文件

可执行程序的装载

  • 可执行程序的装载相关关键问题分析

    • 特殊系统调用:fork、execve
    • sys_execve内部会解析可执行文件格式
    • execve系统调用返回到用户态从哪里开始执行?load_ elf_ binary -> start_thread
  • sys_execve的内部处理过程
  • 使用gdb跟踪sys_execve内核函数的处理过程
  • 可执行程序的装载与庄生梦蝶的故事
  • 浅析动态链接的可执行程序的装载

七、进程的切换和系统的一般执行过程

进程切换的关键代码switch_to分析

  • 进程调度与进程调度的时机分析

    • 中断处理过程(包括时钟中断、I/O中断、系统调用和异常)中,直接调用schedule(),或者返回用户态时根据need_resched标记调用schedule();
    • 内核线程(只有内核态没有用户态的特殊进程)可以直接调用schedule()进行进程切换,也可以在中断处理过程中进行调度,也就是说内核线程作为一类的特殊的进程可以主动调度,也可以被动调度;
    • 用户态进程无法实现主动调度,只能被动调度,仅能通过陷入内核态后的某个时机点进行调度,即在中断处理过程中进行调度。
  • 进程上下文切换相关代码分析
    • 为了控制进程的执行,内核必须有能力挂起正在CPU上执行的进程,并恢复以前挂起的某个进程的执行,这叫做进程切换、任务切换、上下文切换;
    • 挂起正在CPU上执行的进程,与中断时保存现场是不同的,中断前后是在同一个进程上下文中,只是由用户态转向内核态执行;
    • 进程上下文包含了进程执行需要的所有信息
      • 用户地址空间:包括程序代码,数据,用户堆栈等
      • 控制信息:进程描述符,内核堆栈等
      • 硬件上下文(注意中断也要保存硬件上下文只是保存的方法不同)
    • schedule()函数选择一个新的进程来运行,并调用context_switch进行上下文的切换,这个宏调用switch_to来进行关键上下文切换
      • next = pick_ next_task(rq, prev);//进程调度算法都封装这个函数内部
      • context_switch(rq, prev, next);//进程上下文切换
      • switch_to利用了prev和next两个参数:prev指向当前进程,next指向被调度的进程

Linux系统的一般执行过程

  • Linux系统的一般执行过程分析

    • 中断上下文的切换(中断和中断返回时CPU进行上下文切换)
    • 进程上下文的切换(进程调度过程中,从一个进程的内核堆栈切换到另一个进程的内核堆栈)
  • Linux系统执行过程中的几个特殊情况
    • 通过中断处理过程中的调度时机,用户态进程与内核线程之间互相切换和内核线程之间互相切换,与最一般的情况非常类似,只是内核线程运行过程中发生中断没有进程用户态和内核态的转换;
    • 内核线程主动调用schedule(),只有进程上下文的切换,没有发生中断上下文的切换,与最一般的情况略简略;//用户态进程不能主动调用
    • fork:创建子进程的系统调用在子进程中的执行起点(next_ ip = ret_ from_ fork)返回用户态,进程返回不是从标号1开始执行,直接跳转到ret_ from_fork执行然后返回到用户态;
    • 加载一个新的可执行程序后返回到用户态的情况,如execve,只是中断上下文在execve系统调用内部被修改了;

Linux系统架构和执行过程概览

  • Linux操作系统架构概览
  • 最简单也是最复杂的操作--执行ls命令
  • 从CPU和内存的角度看Linux系统的执行

学习心得

收获

还记得第一次看视频的时候是寒假,那时还在家,转眼我已经跟着网络课程学习了两个月,每周固定的看视频、做笔记、写博客、做实验,到现在已经有点习惯这种生活,突然没有视频可学,还有点空虚呢。依稀记得第一次视频老师您还说是除夕,听得到外面的鞭炮声,想想老师也是很辛苦的,很感谢老师能教给我们这么多知识。第一次亲身经历这种学习方式,也体验到了很多好处,比如每周的自评和互评,可以看到很多优秀的博客,加以学习,让自己变得更好,看见差的博客,会指出他的问题,也希望可以帮助到其他人。

通过八周的学习,第一次系统全面的理解了Linux系统:

  • 首先是进程的创建,由0号进程idle创建其子进程1号进程init和2号进程kthreadd,1号进程是所有用户态进程的祖先,2号进程是所有内核线程的祖先。由fork来创建一个新的子进程
  • 系统调用其实就是一种中断,从用户态转到内核态,发生中断时关键点是中断上下文的切换(中断和中断返回时CPU进行上下文切换)。
  • 由中断返回到用户态之前有一个进程调度时机,可能就会发生进程切换,关键点是进程上下文的切换(进程调度过程中,从一个进程的内核堆栈切换到另一个进程的内核堆栈),调用了schedule(),关键切换由switch_to执行。
  • 进程是依附于程序存在的,可执行程序是通过预编译、编译、汇编、链接生成的。由execve装载一个可执行程序。

遗憾

虽然每周都在按部就班的学习,但还是在一些具体的问题上不求甚解,是一直跟着老师的思路在走,老师讲过的就知道,自己去看其他的就蒙圈了,没有更深的去理解,有一些游走于表面,特别是对代码啊堆栈的理解就有些模糊。还有就是总觉得老师的逻辑跟我的有点不太一样,每次跟着听都有些费劲,感觉太散了,自己要看几遍才能理解,然后总结按自己的想法去串一遍,自认效率比较低,所以花的时间就很多。

20135220谈愈敏Linux_总结的更多相关文章

  1. 20135220谈愈敏Blog8_进程的切换和系统的一般执行过程

    进程的切换和系统的一般执行过程 谈愈敏 原创作品转载请注明出处 <Linux内核分析>MOOC课程 http://mooc.study.163.com/course/USTC-100002 ...

  2. 20135220谈愈敏Blog7_可执行程序的装载

    可执行程序的装载 谈愈敏 原创作品转载请注明出处 <Linux内核分析>MOOC课程 http://mooc.study.163.com/course/USTC-1000029000 一. ...

  3. 20135220谈愈敏Blog6_进程的描述和创建

    进程的描述和创建 谈愈敏 原创作品转载请注明出处 <Linux内核分析>MOOC课程 http://mooc.study.163.com/course/USTC-1000029000 进程 ...

  4. 20135220谈愈敏Blog2_操作系统是如何工作的

    操作系统是如何工作的 谈愈敏 原创作品转载请注明出处 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 计 ...

  5. 20135220谈愈敏Blog3_构造一个简单的Linux系统MenuOS

    构造一个简单的Linux系统MenuOS 谈愈敏 原创作品转载请注明出处 <Linux内核分析>MOOC课程 http://mooc.study.163.com/course/USTC-1 ...

  6. 20135220谈愈敏Blog4_系统调用(上)

    系统调用(上) 谈愈敏 原创作品转载请注明出处 <Linux内核分析>MOOC课程 http://mooc.study.163.com/course/USTC-1000029000 用户态 ...

  7. 20135220谈愈敏Blog5_系统调用(下)

    系统调用(下) 谈愈敏 原创作品转载请注明出处 <Linux内核分析>MOOC课程 http://mooc.study.163.com/course/USTC-1000029000 给Me ...

  8. 20135220谈愈敏Linux Book_17

    第17章 设备与模块 关于设备驱动和设备管理的四种内核成分: 设备类型:在所有 Unix 系统中为了统一普通设备的操作所采用的分类. 模块: Linux 内核中用于按需加载和卸载目标码的机制. 内核对 ...

  9. 20135220谈愈敏Linux Book_4

    进程调度 进程:程序的运行态表现形式 进程调度程序:确保进程能有效工作的一个内核子系统,决定将哪个进程投入运行.何时运行以及运行多长时间,在可运行态进程之间分配有限的处理器时间资源. 最大限度的利用处 ...

随机推荐

  1. DP大作战—状态压缩dp

    题目描述 阿姆斯特朗回旋加速式阿姆斯特朗炮是一种非常厉害的武器,这种武器可以毁灭自身同行同列两个单位范围内的所有其他单位(其实就是十字型),听起来比红警里面的法国巨炮可是厉害多了.现在,零崎要在地图上 ...

  2. Java基础之子类父类属性覆盖

    当java的子类和父类具有相同名字的属性时,到底java是怎么处理的. 先看代码: package com.joyfulmath.study.field; public class Person { ...

  3. spring中的 classpath* 存在可移植性问题

    classpath* 的可移植性问题,许多人都应该遇到过了.下面就是一个例子(使用的是spring4.1.5和mybatis3.2.8): <bean id="sqlSessionFa ...

  4. MyCat 学习笔记 第九篇.数据分片 之 数值分布

    1 应用场景 Mycat 自带了多套数据分片的机制,其实根据数值分片也是比较简单,其实这个和数据取摸是类似的实现. 优.缺点同上一篇 2 环境说明 参考  <MyCat 学习笔记>第六篇. ...

  5. .NET 分页

    .net分页 1.存储过程create procedure 存储过程名( @pageIndex int,                         //第几页 @pageSize int     ...

  6. javascript中五种常见的DOM方法

    getElementById将返回一个与那个有着给定id属性值的元素节点对应的对象. <html xmlns="http://www.w3.org/1999/xhtml"&g ...

  7. libstdc++

    一.简介 二.安装 http://www.linuxfromscratch.org/lfs/view/development/chapter05/gcc-libstdc++.html 三.其他 1)l ...

  8. iOS开发-简单解析JSON数据

    什么是JSON   JSON是一种轻量级的数据格式,一般用于数据交互 服务器返回给客户端的数据,一般都是JSON格式或者XML格式(文件下载除外) JSON的格式很像OC中的字典和数组   {“nam ...

  9. zoj 3229 Shoot the Bullet(无源汇上下界最大流)

    题目:Shoot the Bullet 收藏:http://www.tuicool.com/articles/QRr2Qb 把每一天看成一个点,每个女孩也看成一个点,增加源和汇s.t,源向每一天连上[ ...

  10. Android adb 无线调试

    转自:使用WIFI连接android进行调试和adb操作 1. 手机端开启adb tcp连接端口,下载android终端app(终端模拟器) :/$su:/$setprop service.adb.t ...