zoukankan      html  css  js  c++  java
  • 20169219《linux内核原理与分析》第九周作业

    网易云课堂学习

    可执行程序的装载

    可执行程序的产生过程:预处理-----> 编译 ----> 汇编 ----> 链接

    以hello.c文件为例进行分析,编译步骤如下

    
    vi hello.c
    gcc -E -o hello.cpp hello.c -m32
    vi hello.cpp
    gcc -x cpp-outout -S -o hello.s hello.cpp -m32
    vi hello.s
    gcc -x assembler -c hello.s -o hello.o -m32
    vi hello.o
    gcc -o hello hello.o -m32
    vi hello
    gcc -o hello.static hello.o -m32 -static
    
    
    

    结果如下图


    编程使用exec*库函数加载一个可执行文件.

    静态链接和动态链接的区别

    动态库链接时,会在运行时选择需要的部分进行编译,生成的可执行文件会比较小,而且容易后续的更新。静态编译会把所有的函数等都嵌入到可执行文件中,可以直接在任何电脑上直接运行.在静态连接程序中,所有的程序代码包好在一个可执行模块中。因为库程序静态连接到应用程序中,库的引用效率更高。静态连接增加应用程序文件的大小,如果系统同时还运行其他的应用程序时,静态连接也会增加内存中代码的大小。
    加载方式上:
    1.静态库只能使用静态加载方式,静态加载就是编译、链接的时候把静态库代码拷贝到程序中。
    2.动态库可以使用“静态加载”和“动态加载”,静态加载就是在程序启动的时候把库所有的内容加载到内存,动态加载就是用到哪个函数所在的模块就加载对应的库就行。(这是因为,动态库可能也很大,一口气全部加载也是非常耗时,所以运行时用到谁就加载谁,就是动态加载)。

    静态库、共享库以及动态加载库
    (1) 静态库
    静态库是一些目标文件的集合,库文件以”.a”结尾,连接器会将应用程序所需要用到的代码拷贝到应用程序中.
    静态库的优缺点:
    静态库增加了应用程序的大小,另外在处理静态库更新问题上需要花费更多的重编译代价(recompile),但是理论上,静态库应该比共享库或者动态加载库运行更快(1-5%),因为它减少了在程序运行才去加载库的开销。
    (2) 共享库:
    与静态库不同的是,共享库在链接阶段并不需要拷贝所需使用的代码,而只是做些参考标记,然后在程序启动时加载所需要的库文件,因此,对比静态库,链接共享库的应用程序小得多。
    共享库的优缺点:
    对比静态库,共享库在链接阶段只是对所需代码做些标识,在程序启动时才会加载,这样减少了目标应用程序的大小,通过soname,可以做到多版本的兼容,这样每次升级也不需要将原代码全部重新编译,免除了在升级过程中重编译带来的开销;但是,因为需要在程序运行阶段加载共享库,这样势必需要付出运行期间的库加载代价。
    (3)动态加载库 (Dynamically loaded libraries)
    是指在程序运行过程中可以加载的函数库,而不像共享库是在程序启动的时候加载。DLL对实现插件和模块非常实用,因为它们运行程序在允许时等待插件的加载,动态加载库有自己的一套API接口去完成打开、查找符号,处理出错、关闭加载库等功能。

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

    不同类型的进程有不同的调度需求

    第一种分类:

    (1)I/O-bound

    • 频繁的进行I/O
    • 通常会话费很多时间等待I/O操作完成
      (2)CPU-bound
    • 计算密集型
    • 需要大量的CPU时间进行运算
    第二种分类:

    (1)批处理进程

    • 不必与用户交互,通常在后台运行
    • 不必很快响应
    • 典型的批处理程序:编译程序、科学计算
      (2)实时进程
    • 有实时需求,不必被低优先级的进程阻塞
    • 响应时间要短、要稳定
    • 典型实时进程:视频/音频、机械控制等
      (3)交互式进程
    • 需要经常与用户交互,因此要花很多时间等待用户输入操作
    • 相应时间要快,平均延迟要低于50-150ms
    • 典型的交互式进程:shell、文本编辑程序、图形应用程序等

    进程调度算法

    (1)先进先出算法:
    算法总是把处理机分配给最先进入就绪队列的进程,一个进程一旦分得处理机,便一直执行下去,直到该进程完成或阻塞时,才释放处理机。
    (2)短进程优先
    最短CPU运行期优先调度算法(SCBF--Shortest CPU Burst First)
    该算法虽可获得较好的调度性能,但难以准确地知道下一个CPU执行期,而只能根据每一个进程的执行历史来预测。
    (3)轮转法:
    前几种算法主要用于批处理系统中,不能作为分时系统中的主调度算法,在分时系统中,都采用时间片轮转法。
    简单轮转法:系统将所有就绪进程按FIFO规则排队,按一定的时间间隔把处理机分配给队列中的进程。这样,就绪队列中所有进程均可获得一个时间片的处理机而运行。
    多级队列方法:将系统中所有进程分成若干类,每类为一级。
    (4)多级反馈队列
    多级反馈队列方式是在系统中设置多个就绪队列,并赋予各队列以不同的优先权。
    Linux中进程的优先级是动态的,较长时间未分配到CPU的进程,优先级会升高;已经在CPU上运行了较长时间的进程,优先级会下降。

    进程的调度时机与进程的切换

    
    操作系统原理中介绍了大量进程调度算法,这些算法从实现的角度看仅仅是从运行队列中选择一个新进程,选择的过程中运用了不同的策略而已。
    
    对于理解操作系统的工作机制,反而是进程的调度时机与进程的切换机制更为关键。
    
    
    

    进程调度的时机

    • 中断处理过程(包括时钟中断、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指向被调度的进程

    使用gdb跟踪调用schedule函数

    在time系统调用返回前,调用schedule(),在schedule设置断点:

    Linux系统的一般执行过程

    最一般的情况:正在运行的用户态进程X切换到运行用户态进程Y的过程

    1. 正在运行的用户态进程X
    2. 发生中断——save cs:eip/esp/eflags(current) to kernel stack,then load cs:eip(entry of a specific ISR) and ss:esp(point to kernel stack).
    3. SAVE_ALL //保存现场
    4. 中断处理过程中或中断返回前调用了schedule(),其中的switch_to做了关键的进程上下文切换
    5. 标号1之后开始运行用户态进程Y(这里Y曾经通过以上步骤被切换出去过因此可以从标号1继续执行)
    6. restore_all //恢复现场
    7. iret - pop cs:eip/ss:esp/eflags from kernel stack
    8. 继续运行用户态进程Y

    几种特殊情况

    • 通过中断处理过程中的调度时机,用户态进程与内核线程之间互相切换和内核线程之间互相切换,与最一般的情况非常类似,只是内核线程运行过程中发生中断没有进程用户态和内核态的转换;
    • 内核线程主动调用schedule(),只有进程上下文的切换,没有发生中断上下文的切换,与最一般的情况略简略;
    • 创建子进程的系统调用在子进程中的执行起点及返回用户态,如fork;
    • 加载一个新的可执行程序后返回到用户态的情况,如execve;

    虚拟文件系统

    虚拟文件系统中(VFS)有四个主要的对象类型

    超级块对象:代表一个具体的已安装文件系统;
    索引节点对象:代表一个具体文件;
    目录项对象:代表一个目录项,是路径的一个组成部分;
    文件对象:代表由进程打开的文件

    块I/O层

    块设备中最小的可寻址单元是扇区。扇区是设备的最小寻址单元;块是文件系统的最小寻址单元。块包含一个或多个扇区,但大小不能超过一个页面,所以一个页面可以容纳一个或多个内存的块。
    目前内核中块I/O操作的基本容器由bio结构体表示,它代表了正在现场的(活动的)以片段链表形式组织的块I/O操作。
    磁盘寻址是整个计算机系统中最慢的操作之一,所以尽量缩短寻址时间是提高系统性能的关键。
    I/O调度程序通过两种方法减少磁盘寻址时间:合并和排序。

    出现的问题

    在使用gdb跟踪分析一个execve系统调用内核处理函数sys_execve的过程中,需要在执行exec命令的时候同时输出“hello world!”,需要修改Makefile文件里面的代码,使系统在执行exec命令的时候同时执行hello.c文件。

    只需要把Makefile文件里的“cp hello ../rootfs/”这行代码去掉就能解决问题,但是我还是不理解具体原因。!

  • 相关阅读:
    数据持久化的基础知识
    svn常用命令
    关于SVN 目录结构
    linux查看CPU信息
    一个服务器上启动两台tomcat
    centos6.0 配置SVN
    mysql插入表情
    MAC 安装 PIL
    安装freetype
    Hadoop基本文件命令
  • 原文地址:https://www.cnblogs.com/weihua2616/p/6081767.html
Copyright © 2011-2022 走看看