zoukankan      html  css  js  c++  java
  • 课本学习笔记4:第三章 20135115臧文君

    进程管理

    注:作者:臧文君,原创作品转载请注明出处。

    一、进程

    1、进程管理是所有操作系统的心脏所在。

    2、进程:是处于执行期的程序以及相关的资源的总称,实际上,进程就是正在执行的程序代码的实时结果。

    3、执行线程:简称线程thread,是在进程中活动的对象。

    4、内核调度的对象是线程,而不是进程。

    5、对Linux而言,线程是一种特殊的进程。

    6、进程提供两种虚拟机制:虚拟处理器和虚拟内存。

    同一个进程中的线程之间可以共享虚拟内存,但每个都拥有各自的虚拟处理器。

    7、Linux系统中,调用fork()系统调用,通过复制一个现有进程来创建一个全新的进程。

    调用fork()的进程称为父进程,新产生的进程称为子进程。

    fork()系统调用从内核返回两次:一次回到父进程,另一次回到新产生的子进程。

    调用exec()这组函数可以创建新的地址空间,并把新的程序载入其中。

    fork()实际上是由clone()系统调用实现的。

    程序通过exit()系统调用退出执行。

    二、进程描述符及任务结构

    1、内核把进程的列表存放在叫做任务队列的双向循环链表中。

    2、链表中的每一项都是类型为task_struct、称为进程描述符的结构,其中包含一个具体进程的所有信息。

    进程描述符相对较大:

    3、分配进程描述符

    Linux通过slab分配器分配task_struct结构,这样能达到对象复用和缓存着色的目的。

    4、进程描述符的存放

    (1)内核通过一个唯一的进程标识值或PID来标识每个进程。

    (2)PID的最大默认设置为32768,它实际上就是系统中允许同时存在的进程的最大数目,可以由系统管理员通过修改/proc/sys/kernel/pid_max来提高上限。

    (3)内核把每个进程的PID存放在它们各自的进程描述符中,通过current宏查找到当前正在运行进程的进程描述符。

    5、进程状态:进程描述符中的state域。

    6、内核调整某个进程的状态,使用set_task_state(task,state)函数。

    7、可执行代码是进程的重要组成部分。

    系统调用和异常处理程序是对内核明确定义的接口。

    8、进程之间存在继承关系,所以的进程都是PID为1的init进程的后代。

    三、进程创建

    1、Unix进程的创建通过fork()和exec()。

    fork()通过拷贝当前进程创建一个子进程,exec()函数负责读取可执行文件并将其载入地址空间开始运行。

    2、Linux的fork()使用写时拷贝(copy-on-write)页实现。

    fork()的实际开销就是复制父进程的页表以及给子进程创建唯一的进程描述符。

    3、fork()、vfork()和_clone()库函数都根据各自的参数标志去调用clone(),然后由clone()去调用do_fork(),do_fork()调用copy_process()函数,然后让进程开始运行。

    4、vfork()除了不拷贝父进程的页表项外,与fork()的功能相同。

    四、线程在Linux中的实现

    1、线程机制提供了在同一程序内共享内存地址空间运行的一组线程。

    线程机制支持并发程序设计技术,在多处理器系统上,它也能保证真正的并行处理。

    2、Linux把所有的线程都当做进程来实现,线程仅仅被视为一个与其他进程共享某些资源的进程。

    3、线程的创建与进程类似,只是在调用clone()的时候需要传递一些参数标志来指明需要共享的资源。

    父子进程共享地址空间、文件系统资源、文件描述符和信号处理程序。

    4、内核线程

    (1)内核经常需要在后台执行一些操作(例:flush,ksofirqd),这些任务通过内核线程完成。

    (2)内核线程与普通进程的区别:内核线程没有独立的地址空间,只在内核空间运行,不切换到用户空间。

    相同:可以被调度,可以被抢占。

    (3)内核线程也只能有其他的内核线程创建,内核通过从kthreadd内核进程中衍生出所有新的内核线程来自动处理这一点。

    (4)新创建的进程处于不可运行状态,如果不通过调用wake_up_process()明确地唤醒它,它不会主动运行。

    创建一个进程并让它运行起来,可以通过调用kthread_run()来实现。

    五、进程终结

    1、当一个进程终结时,内核必须释放它所占有的资源,并告知父进程。

    2、大部分的进程终结都要靠do_exit()。

    3、在父进程获得已终结的子进程的信息后,或者通知内核它并不关注那些信息后,子进程的task_struct结构才被释放。

    当最终需要释放进程描述符是,release_task()会被调用。

    4、如果父进程在子进程之前退出,必须有机制来保证子进程能找到一个新的父亲,否则这些成为孤儿的进程就会在退出时永远处于僵死状态,白白地耗费内存。

    解决方法:给子进程在当前线程组内找一个线程作为父亲,如果不行,就让init作为它们的父进程。

    5、init进程会例行调用wait()来检查其子进程,清除所有与其相关的僵死进程。

  • 相关阅读:
    织梦内容模型自定义字段设置一个随机数
    网页禁止右键查看源码屏蔽键盘事件
    面试官:如何防止 Java 源码被反编译?我竟然答不上来。。
    Elastic Job 同城主备、同城双活,高可用必备~
    再见,Spring Security OAuth!!
    怎么让 Linux 进程在后台运行?
    30 个 ElasticSearch 调优知识点,都给你整理好了!
    Spring Boot 2.5.4 发布,2.2.x 正式结束使命!
    移动端与服务器端之间的 token 怎么设计?
    最新数据库排行出炉,SQL Server 暴跌。。
  • 原文地址:https://www.cnblogs.com/CatherineZang/p/5339608.html
Copyright © 2011-2022 走看看