本文是个人分析《Linux内核设计与实现》而写的总结,欢迎转载,请注明出处:

                                                                                 http://blog.csdn.net/dlutbrucezhang/article/details/12139579

        第一章--进程管理

1.什么是进程     

       第一章首先从进程开始讲起,进程是操作系统最基本的东西,我们依靠进程提供服务,利用进程完成我们需要完成的工作。首先需要明白什么是进程。它是一个动态实体,是程序的一次执行过程。一个进程需要一些必须的资源才能够运行,而最基本的资源就是 CPU 和内存。进程获得 CPU 它就处于运行状态,进程的运行肯定需要程序段,数据段,堆,栈这样的资源,而这些就是内存提供的。在Linux中的实现是两种虚拟机制,被称作是“虚拟处理器”和“虚拟内存”。虚拟处理器的概念就好像是每个进程独占CPU,虚拟内存的概念是每个进程拥有自己的内存地址空间,而这个地址空间涵盖了整个内存空间。进程的创建工作是通过 fork() 函数,进程的终止是通过 wait4() 函数,这些内容的详细介绍将在下文提及。


2.进程描述符以及进程的结构

      进程描述符用于描述一个进程,在其中存储了一个进程的所有信息,每个进程都有一个进程描述符。而所有的进程描述符都被存储在一个双向循环链表中。由此,我们可以想到,从链表中的任何一个进程开始,我们可以遍历系统的所有进程。进程描述符比较重要的字段有 :进程PID,进程的父进程,进程的状态等等。进程描述符是由 slab 分配器分配的,它是作为页高速缓存特殊存在的一种结构。当一个进程创建时,slab 分配器分配一个进程描述符给它,当进程消亡时,进程描述符被 slab 回收,可以循环利用。

      和进程描述符密切相关的是一个结构--thread_info。这个结构的定义如下:

 union thread_union {
      struct thread_info thread_info;
     unsigned long stack[THREAD_SIZE/sizeof(long)];
  };

      我们可以看到,它是和一个栈存储在一起的,这个栈被称作是内核栈。thread_info 的位置在内核栈的尾端(往低地址方向增长)。thread_info 中存储了进程描述符的地址,所以,我们可以很方便的获得当前进程的描述符,这对于 X86 架构的计算机是非常优良的设计,因为我们可以不占用昂贵的寄存器来存储地址。

      说了这么多,好吧,下面这张图更能显示它们之间的关系:


3.进程状态

      进程是一个活动的实体,当然,它是有生命的,所以,它就必然会有生命中的各个状态。一般情况下,进程的状态会被划分为五种。

      1.TASK_RUNNING 进程处于就绪态或者是运行态。这个状态很有意思,它可以代表进程的两种很相似的状态。这两种状态的区别是进程时候获得 CPU 的使用权,如果获得了,那么进程处于运行态,反过来,如果没有,那么进程就处于就绪态。

       2.TASK_INTERRUPTIBAL 进程处于可中断状态,此时进程由于某些情况不能运行而被挂起,这时会引起进程的切换,当进程希望的事件到来或者接收到信号时,进程就有可能会被唤醒,等待重新被调度执行。

      3.TASK_UNINTERUPTIBLE 进程处于不可中断状态,从命名方式我们就可以看出,它和上一种进程状态是很相似的,的确,它们在表现上也是极为相似的,都是由于进程期待的事情没有发生而被挂起,不能运行。但是,这种状态的进程不能被信号唤醒,所以,我们不能发送一个终止信号给进程使进程死掉,所以,这种状态的进程并不多,一个常见的例子就是,进程等待磁盘的数据传输。

      4.TASK_PTRACED 进程处于被跟踪的状态,这时,会发生一些很有意思的事,进程的父进程会变成跟踪进程,当然,这只是临时的,此时,进程描述符的 parent 字段会被修改。

      5.TASK_STOPPED 进程处于死亡状态,这时的进程不能再被投入运行,它此刻拥有的资源也仅仅是 进程描述符,内核栈,thread_info 结构,此时,进程会发送信号给父进程,等待父进程收回它的资源。

4.进程上下文

      进程上下文其实就是进程切换的过程,就是指进程的所有信息。

5.进程家族树

      我们都知道,类UNIX系统组织系统中的文件都是以树的形式完成的,根则是文件系统的根目录,之后是子目录,接着是子目录的子目录。。。,最后才是文件。由于这种思想能够很好的管理计算机资源,所以,系统中的进程也是以这种方式来管理的。进程被组织成一棵树,树的根是进程1 ,也就是 init 进程。

6.进程的创建

      熟悉 Linux 编程的朋友都知道一个函数 -- fork(),没错,这就是著名的创建进程的函数,其实,其内部实现是 clone() 函数,并给它传递相应的参数完成所需要的进程或者线程。

      fork() :如果我们使用的是这个函数,那么 shell 会创建一个普通的进程,并给它分配进程描述符等资源。Linux 中有一项非常著名的技术,被称之为写时拷贝,它的意思是,父进程和新创建的子进程共享父进程的地址空间,但是这时,地址空间是只读的,也就是说,此时,如果有一个进程往地址空间中写,那么系统就会拷贝这一页,并让需要写的那个进程在拷贝的那一页完成写操作。这项技术的原因是因为,进程创建的子进程一般用于其他的功能,所以,它会很快的调用 exec() 函数,这时会创建新的地址空间,如果,之前创建时直接拷贝,那么原来拷贝的东西就需要被丢弃,所以,浪费了许多资源和空间。但是,它还是需要拷贝父进程的页表。

      vfork():这个函数和 fork 函数是非常相似的,它只是没有拷贝父进程的页表,但是,在函数返回后,父进程被阻塞,子进程会运行,直到它有了自己的地址空间或者是调用 exit 退出执行,才能恢复父进程的运行。

7.线程在 Linux 中的实现

      在Linux系统中,线程其实和进程没有很多的区别,无非是线程没有自己的地址空间,线程是调度执行的基本单位,而进程是资源拥有和分配的基本单位。Linux 在创建线程时,无非是给新创建的线程分配一个进程描述符,并把它的 mm 字段指向创建它的那个进程,由此,完成线程共享进程的地址空间。

8.内核线程

      有些情况,需要内核提供功能并定期的执行,这时,内核线程肯定是最好的选择。既然被称为是内核线程,所以,它肯定是在内核空间运行,而且只是在内核空间运行,所以,我们可以得出结论,它的 mm 字段被赋予 NULL (由于也是线程,所以,肯定存在进程描述符)。在Linux系统上,我们可以利用命令  ps -ef 查看你的系统上存在多少内核线程,看看它们是什么,在这里告诉你,有很多。

9.进程终结

      进程在执行了所需要完成的功能,或者是显示的调用了 exit 之后,就会走向一条面向死亡的不归路。这时,操作系统会收回进程的地址空间,CPU,信号,打开的文件等资源。此时,进程的状态变成了 TASK_ZOMBIE,僵尸进程。这时,由于进程还有一些资源没有被系统回收,而且没有被父进程收回,变成了没人管的进程。这时,会有两种情况会发生。

      1.如果,进程的父进程存在,那么子进程会给父进程发送信号,父进程要么收回子进程的资源,要么不在意子进程的状态,直接丢弃。

      2.如果,进程的父进程已经死了,那么这时的子进程又成了孤儿进程,这时,就需要给子进程寻找一个父进程,这时,又分为两种情况:

                  1.如果,子进程所在的线程组还有进程存在,那么就从线程组中寻找一个进程,让它成为子进程的父进程 

                  2.如果,子进程所在的线程组中没有存活的进程,那么,就让 init 进程成为它的父进程

        我们此时让然需要考虑一种情况,就是进程如果被跟踪了呢?那么这个进程的父进程是临时的,而它真实的父进程已经死了,所以,我们又得为被跟踪的子进程寻找父进程,依然是重复上述的两步。

Linux内核探讨-- 第一章相关推荐

  1. Linux内核探讨-- 第二章

          本文是个人分析<Linux内核设计与实现>而写的总结,欢迎转载,请注明出处:                                                   ...

  2. linux内核参数分析,linux内核启动第一阶段分析

    linux内核启动第一阶段分析 http://blog.csdn.net/aaronychen/article/details/2838341 本文的很多内容是参考了网上某位大侠的文章写的<&l ...

  3. linux内核启动第一个进程,linux内核启动流程

    描述 Linux的启动代码真的挺大,从汇编到C,从Makefile到LDS文件,需要理解的东西很多.毕竟Linux内核是由很多人,花费了巨大的时间和精力写出来的.而且直到现在,这个世界上仍然有成千上万 ...

  4. Linux内核探讨-- 第五章

    本文是个人分析<Linux内核设计与实现>而写的总结,欢迎转载,请注明出处: http://blog.csdn.net/dlutbrucezhang/article/details/123 ...

  5. Linux内核探讨-- 第七章

    本文是个人分析<Linux内核设计与实现>而写的总结,欢迎转载,请注明出处: http://blog.csdn.net/dlutbrucezhang/article/details/136 ...

  6. Linux内核探讨-- 第六章

    本文是个人分析<Linux内核设计与实现>而写的总结,欢迎转载,请注明出处: http://blog.csdn.net/dlutbrucezhang/article/details/130 ...

  7. Linux内核探讨-- 第四章

    本文是个人分析<Linux内核设计与实现>而写的总结,欢迎转载,请注明出处: http://blog.csdn.net/dlutbrucezhang/article/details/122 ...

  8. Linux内核探讨-- 第三章

    本文是个人分析<Linux内核设计与实现>而写的总结,欢迎转载,请注明出处:                                                         ...

  9. 【正点原子Linux连载】第一章 应用编程概念-摘自【正点原子】I.MX6U嵌入式Linux C应用编程指南V1.1

    1)实验平台:正点原子阿尔法Linux开发板 2)平台购买地址:https://item.taobao.com/item.htm?id=603672744434 2)全套实验源码+手册+视频下载地址: ...

最新文章

  1. C语言 递归实现分解质因数
  2. 电机编码器调零步骤_各种编码器的调零方法
  3. 第二十二讲 对角化分解和幂公式
  4. 初建FreeMarker工程
  5. Hadoop权威指南 _01前言感悟
  6. python 线程池_Python线程池及其原理和使用(超级详细)
  7. [转载]使用.net 2003中的ngen.exe编译.net程序
  8. HTTP协议中返回代码302的情况
  9. python raise用处_python基础教程python raise的基本使用
  10. css网页favicon_自用代码css获取任意网址的/favicon.ico的方法教程
  11. LINUX编译Android doubango
  12. 电脑控制安卓手机的工具神器Scrcpy GUI
  13. cwRsync-windows下的rsync工具
  14. Android 加载SDCard中so库
  15. vue-生成随机颜色
  16. 0x0000001a蓝屏代码是什么意思 0x0000001a蓝屏代码怎么解决
  17. 《『EcmaScript』之理顺Js中的四种继承
  18. 数字通信世界杂志数字通信世界杂志社数字通信世界编辑部2022年第6期目录
  19. 【Scratch3.0案例教学】手把手教小朋友Scratch3.0制作消灭新冠病毒游戏 scratch案例教学
  20. 3d效果图全景难制作吗?制作3d全景图的目的是什么

热门文章

  1. zabbix 自动发现和注册
  2. 利用源代码搭建lnmp环境
  3. IPsec NAT穿越
  4. 每天要问自己的十个问题
  5. 小坑记录:get_cmap参数区分大小写
  6. 无线网络会杀死固网? 不可能的事情
  7. 美甲帮:玩转指甲上的大数据平台
  8. phoenixframework自动化测试平台架构图
  9. Javascript-稳妥构造函数模式
  10. javascript获取网页URL地址及参数等