《Linux内核设计与实现》第六周读书笔记——第三章

20135301张忻
估算学习时间:共2.5小时
读书:2.0
代码:0
作业:0
博客:0.5
实际学习时间:共3.0小时
读书:2.0
代码:0
作业:0
博客:1.0
耗时估计的公式:Y=X+X/N ,Y=X-X/N

第3章 进程管理20
3.1 进程20
  • 进程就是处于执行期的程序(目标码存放在某种存储介质上),但进程并不仅仅局限于一段可执行程序代码。通常进程还要包含其他资源,像打开的文件,挂起的信号,内核内部数据,处理器状态,一个或多个具有内存映射的内存地址空间及一个或多个执行线程。当然还包括用来存放全局变量的数据段等,实际上,进程就是正在执行的程序代码的实时结果,内核需要有效而又透明地管理所有细节。
  • 执行线程,简称线程,是在进程中活动的对象,每个线程都拥有一个独立的程序计数器、进程栈和一组进程寄存器,内核调度的对象是线程,而不是进程,在传统的Linux系统中,一个进程只包含一个线程,但现在的系统中,包含多个线程的多线程程序司空见惯。Linux系统的线程实现非常特别:它对线程和进程并不特别区分,对Linux而言,线程只不过是一种特殊的进程罢了。
  • 在现代操作系统中,进程提供两种虚拟机制:虚拟处理器和虚拟内存。有趣的是,注意在线程之间可以共享虚拟内存,但每个都拥有各自的虚拟处理器。
  • 在现代Linux内核中,fork()实际上是由clone()系统调用实现的。
3.2 进程描述符及任务结构 21
  • 内核把进程的列表存放在叫做任务队列的双向循环链表中。
  • 链表中的每项都是类型为task_struct、称为进程描述符的结构,该结构定义在<linux/sched.h>文件中。
  • 进程描述符中包含一个具体进程的所有信息.
  • task_struct相对较大,在32位机器上,它大约有1.7KB。但如果考虑到该结构内包含了内核管理一个进程所需的所有信息,那么它的大小也算相当小了。进程描述符中包含的数据能完整地描述一个正在执行的程序:它打开的文件,进程的地址空间,挂起的信号,进程的状态,还有其他更多信息。
3.2.1 分配进程描述符22
  • Linux以通过slab分配器分配task_struct结构,这样能达到对到对象复用和缓存着色的目的。这样做是为了让那些像x86那样寄存器较少的硬件体系结结构只要通过栈指针就可以估算出他的位置。
  • 每个任务的thread_info结构在他的内核栈的尾端分配。
3.2.2 进程描述符的存放23
  • 内核通过一个唯一的进程标识值或PID来标识每个进程,内核把每个进程的PID存放在它们各自的进程描述符中。
  • 这个最大值很重要,因为它实际上就是系统中允许同时存在的进程的最大数目,尽管32768对于一般的桌面系统足够用了,但是大型服务器可能需要更多进程,这个值越小,转―圈就越快,本来数值大的进程比数值小的进程迟运行,但这样一来就破坏了这一原则,如果确实需要的话,可以不考虑与老式系统的兼容,由系统管理员通过修改来提高上限。
  • 在内核中,访问任务通常需要获得指向其task_struct的指针,实际上,内核中大部分处理进程程描述符的速度就显得尤为重要。硬件体系结构不同,该宏的实现也不同,它必须针对专门的硬件体系结构做处理,有的硬件体系结构可以拿出―个专门寄存器来存放指向当前进程task_struct的指针,用于加快访问速度。
3.2.3 进程状态23
  • 进程描述符中state域描述了进程的当前状态。
  • 五种进程状态:运行、可中断、不可中断、被其他进程跟踪的进程、停止。
3.2.4 设置当前进程状态25
  • 内核需要经常调整某个进程的状态:set_task_state(task,state)函数
3.2.5   进程上下文25
  • 可执行程序代码是进程的重要组成部分。这些代码从一个可执行文件载入到进程的地址空间执行。一般程序在用户空间执行。当一个程序调执行了系统调用(参见第5章)或者触发了某个异常,它就陷入了内核空间。此时,我们称内核“代表进程执行”并处于进程上下文中。在此上下文中current宏是有效的。除非在此间隙有更高优先级的进程需要执行并由调度器做出了相应调整,否则在内核退出的时候,程序恢复在用户空间会继续执行。
  • 系统调用和异常处理程序是对内核明确定义的接口。进程只有通过这些接口才能陷入内核执行——对内核的所有访问都必须通过这些接口。
3.2.6 进程家族树25
  • Unⅸ系统的进程之间存在—个明显的继承关系,在Linux系统中也是如此。所有的进程都是PID为1的init进程的后代。内核在系统启动的最后阶段启动init进程。该进程读取系统的初始化脚本并执行其他的相关程序,最终完成系统启动的整个过程。
3.3 进程创建26
3.3.1 写时拷贝27
  • 传统的fork()系统调用直接把所有的资源复制给新创建的进程,这种实现过于简单并且效率低下,因为它拷贝的数据也许并不共享,更糟的情况是,如果新进程打算立即执行一个新的映像,那么所有的拷贝都将前功尽弃。Linux的fork()使用写时拷贝页实现,写时拷贝是一种可以推迟甚至免除拷贝数据的技术。内核此时并不复制整个进程地址空间,而是让父进程和子进程共享同一个拷贝。
  • 只有在需要写入的时候,数据才会被复制,从而使各个进程拥有各自的拷贝,也就是说资源的复制只有在需要写入的时候才进行,在此之前,只是以只读方式共享,这种技术使地址空间上的页的拷贝被推迟到实际发生写入的时候才进行在页根本不会被写入的情况下它们就无须复制了。
  • fork()的实际开销就是复制父进程的页表以及给子进程创建唯一的进程描述符。在一般情况下,进程创建后都会马上运行一个可执行的文件,这种优化可以避免拷贝大量根本就不会被使用的数据(地址空间里常常包含数十兆的数据)由于Unix强调进程快速执行的能力,所以这个优化是很重要的。
3.3.2   fork()27
copy_process()完成的工作: 
  • 1)调用dup_task_struct()为新进程创建一个内核栈、thread_info结构和task_struct,这些值与当前进程的值相同,此时,子进程和父进程的描述符是完全相同的。
  • 2)检查并确保新创建这个子进程后,当前用户所拥有的进程数目没有超出给它分配的资源的限制
  • 3)子进程着手使自己与父进程区别开来,进程描述符内的许多成员都要被清0或设为初始值,那些不是继承而来的进程描述符成员,主要是统计信息,task_struct中的大多数数据都依然未被修改
  • 4)子进程的状态被设置为TASK_UNINTERRUPTIBLE,以保证它不会投入运行
  • 5) copy_process()调用copy_flags以更新task_struct的flags成员
  • 6)调用alloc_pid()为新进程分配一个有效的PID
  • 7)根据传递给clone()的参数标志,copy_process()拷贝或共享打开的文件、文件系统信息、信号处理函数、进程地址空间和命名空间等,在一般情况下,这些资源会被给定进程的所有线程共享;否则,这些资源对每个进程是不同的,因此被拷贝到这里。
3.3.3 vfork()28
3.4      线程在Linux中的实现28
  • 线程机制是现代编程技术中常用的一种抽象概念,该机制提供了在同―程序内共享内存地址空间运行的―组线程,这些线程还可以共享打开的文件和其他资源,线程机制支持并发程序设计技术,在多处理器系统上,它也能保证真正的并行处理。
  • Linux实现线程的机制非常独特,从内核的角度来说,它并没有线程这个概念,Linux把所有的线程都当做进程来实现,内核并没有准备特别的调度算法或是定义特别的数据结构来表征线程,相反,线程仅仅被视为―个与其他进程共享某些资源的进程,每个线程都拥有唯一隶属于自己task_struct,所以在内核中,它看起来就像是一个普通的进程(只是线程和其他一些进程共享某些资源,如地址空间)。
3.4.1 创建线程29
  • 进程的创建与普通进程的创建类似,只不过在调用clone()时需要传递一些参数标志来指明所需要共享的资源。
3.4.2 内核线程30
  • 内核经常需要在后台执行一些操作,这种任务可以通过内核线程完成——独立运行在内核空间的标准进程。内核线程和普通的进程间的区别在于内核线程没有独立的地址空间。它们只在内核空间运行,从来不切换用户空间去,内核进程和普通进程一样,可以被调度,也可以被抢占。
  • Linux确实会把一些任务交给内核线程去做,像flush和ksofirqd这些任务就是明显的例子,在装有Linux系统的机子上运行ps -ef命令,你可以看到内核线程,有很多!这些线程在系统启动时由另外一些内核线程创建,实际上,内核线程也只能由其他内核线程创建,内核是通过从kthreadd内核进程中衍生出所有新的内核线程来自动处理这一点的,在<linux/kthreadd>中申明有接口。
3.5 进程终结31
  • 当一个进程终结时,内核必须释放它所占有的资源并把这一不幸告知其父进程。
3.5.1 删除进程描述符32
  • 调用release_task函数。
3.5.2 孤儿进程造成的进退维谷32
  • 如果父进程在子进程之前退出,必须有机制来保证子进程能找到一个新的父亲否则这些成为孤儿的进程就会在退出时永远处于僵死状态,白白地耗费内存。前面的部分已经有所暗示于这个问题,解决方法是给子进程在当前线程组内找—个线程作为父亲,如果不行就让init做它们的父进程。
3.6 小结34
  • 在本章中,我们考察了操作系统中的核心概念——进程,我们它为何如此重要,以及进程与线程之间的关系,然也讨论了进程的一般特性,然后,讨论了Linux如何存放和表示进程,如何创建进程,如何把新的执行映像装入到地址空间,如何表示进程的层次关系,父进程又是如何收集其后代的信息以及进程最终如何消亡。
  • 进程是一个非常基础、非常关键的抽象概念,位于每一种现代操作系统的核心位置,也是我们拥有操作系统(用来运行程序)的最终原因。

《Linux内核设计与分析》第六周读书笔记——第三章的更多相关文章

  1. Linux内核设计与实现第六周读书笔记

    第三章 进程管理 3.1 进程 进程是处于执行期的代码.通常进程还要包含其他资源,像打开的文件.挂起的信号.内核的内部数据.处理器状态.一个或多个具有内存映射的内存地址空间及一个或多个执行线程,当然还 ...

  2. 《Linux内核设计与实现》第四周读书笔记——第五章

    <Linux内核设计与实现>第四周读书笔记--第五章 20135301张忻 估算学习时间:共1.5小时 读书:1.0 代码:0 作业:0 博客:0.5 实际学习时间:共2.0小时 读书:1 ...

  3. Linux内核设计与实现第十周读书笔记

    第十七章 设备与模块 关于设备驱动与设备管理,我们讨论四种内核成分. 设备类型 模块 内核对象 sysfs 17.1设备类型 在Linux以及所有Unix系统中,设备被分为以下三种类型: 块设备,块设 ...

  4. Linux内核设计与实现第八周读书笔记

    第四章 进程调度 进程在操作系统看来是程序的运行态表现形式. 4.1多任务 多任务操作系统就是能同时并发地交互执行多个进程的操作系统. 多任务操作系统会使多个进程处于堵塞或者睡眠状态.这些任务尽管位于 ...

  5. Linux内核设计与实现第五周读书笔记

    第十八章 调试 18.1准备开始 需要的只是: 一个确定的bug.大部分bug通常都不是行为可靠而且定义明确的. 一个藏匿bug的内核版本. 相关的内核代码的知识和运气. 18.2内核中的bug 内核 ...

  6. linux内核设计与实现第七周读书笔记

    第七章 链接 链接(linking)是将各种代码和数据部分收集起来并组合成为一个单一文件的过程,这个文件可被加载(或被拷贝)到存储并执行.链接可以执行于编译时(compile time),也就是在源代 ...

  7. 《Linux内核设计与实现》Chapter 1 读书笔记

    <Linux内核设计与实现>Chapter 1 读书笔记 一.Unix的特点 Unix从Multics中产生,是一个强大.健壮和稳定的操作系统. 特点 1.很简洁 2.在Unix系统中,所 ...

  8. 《Linux内核设计与实现》Chapter 5 读书笔记

    <Linux内核设计与实现>Chapter 5 读书笔记 在现代操作系统中,内核提供了用户进程与内核进行交互的一组接口,这些接口的作用是: 使应用程序受限地访问硬件设备 提供创建新进程与已 ...

  9. 《Linux内核设计与实现》Chapter 18 读书笔记

    <Linux内核设计与实现>Chapter 18 读书笔记 一.准备开始 一个bug 一个藏匿bug的内核版本 知道这个bug最早出现在哪个内核版本中. 相关内核代码的知识和运气 想要成功 ...

随机推荐

  1. 薛兆丰吴军何帆曾鸣万维刚李笑来罗永浩等得到APP专栏作者的书23本

    最近看了何帆的<大局观>,是他在得到APP的专栏文章的精选.顺便整理以下最近两三年内看过的得到APP其他专栏与课程作者的得到精选文集和他们写过的其他的书共23本. 薛兆丰 4星|<薛 ...

  2. 【美妙的Python之三】Python 对象解析

    美妙的Python之Python对象         简而言之: Python 是能你无限惊喜的语言.与众不同.         Python对象概念的理解,是理解Python数据存储的前提.Pyth ...

  3. 【Java多线程】线程状态、线程池状态

    线程状态: 线程共包括以下5种状态.1. 新建状态(New) 线程对象被创建后,就进入了新建状态.例如,Thread thread = new Thread().2. 就绪状态(Runnable) 也 ...

  4. luogu P3809 【模板】后缀排序

    嘟嘟嘟 今天学了一个后缀数组,还是挺好理解的. 因为我不会基数排序,所以只会\(O(n \log ^ 2 n)\)的sort版. 首先,后缀数组就是把该字符串的所有后缀按字典序排序得到的一个数组.注意 ...

  5. Git命令的使用_操作远程仓库——详细教程3

    本博文讲述将本地仓库提交到GIT@OSC(或者是GitHub),故而需要用户首先有一个GIT@OSC(或者是GitHub)帐号. 我的GIT@OSC帐号是:https://git.oschina.ne ...

  6. yii2 修改验证码小部件样式

    <?= $form->field($model, 'verifyCode',['labelOptions' => ['class' => 'yanzhengma','style ...

  7. Oracle 11g rac 添加新节点测试

    [转]https://blog.csdn.net/shiyu1157758655/article/details/60877076 前期准备: 操作系统设置OS版本必须相同,检查内核参数,系统内存.C ...

  8. 通过JSP网页连接MySQL数据库,从MySQL数据库中读出一张表并显示在JSP网页中

    1.安装所需软件 ①安装java和tomcat,建立JSP网页最基础的软件②安装MySQL数据库(下载地址:https://www.mysql.com/)③安装Navicat Premium来查看数据 ...

  9. lmbench性能分析工具

    下载地址 http://www.bitmover.com/lmbench/ tar -zxvf lmbench3.tar.gz cd lmbench3 make 此时会报错: make[2]: *** ...

  10. eureka client服务续约源码分析

    必备知识: 1.定时任务 ScheduledExecutorService public class demo { public static void main(String[] args){ Sc ...