Linux进程管理









进程管理是操作系统的最重要的功能之一。有效率的进程管理能保证一个程序平稳而高效地运行。
        <section style="margin: 0px; padding: 0px; -ms-word-wrap: break-word !important; max-width: 100%; box-sizing: border-box;">
            Linux的进程管理与UNIX的进程管理相似。它包括进程调度、中断处理、信号、进程优先级、上下文切换、进程状态、进度内存等。
        </section>

        <section style="margin: 0px; padding: 0px; -ms-word-wrap: break-word !important; max-width: 100%; box-sizing: border-box;">
            在本节中,我们将描述Linux进程管理的基本原理的实现。它将更好地帮助你理解Linux内核如何处理进程及其对系统性能的影响。
        </section>
    </section>
</section>







什么是进程?









一个进程是一个运行在处理器的程序的一个实例。该进程使用Linux内核能够处理的任何资源来完成它的任务。
        <section style="margin: 0px; padding: 0px; -ms-word-wrap: break-word !important; max-width: 100%; box-sizing: border-box;">
            所有运行在Linux操作系统中的进程都被task_struct结构管理,该结构同时被叫作进程描述。一个进程描述包含一个运行进程所有的必要信息,例如进程标识、进程属性和构建进程的资源。如果你了解该进程构造,你就能理解对于进程的运行和性能来说,什么是重要的。图1-2展示了进程结构相关的进程信息概述。
        </section>
    </section>
</section>







图1-2 task_struct结构体









进程的生命周期









每一个进程都有其生命周期,例如创建、运行、终止和消除。这些阶段会在系统启动和运行中重复无数次。因此,进程的生命周期对于其性能的分析是非常重要的。
        <section style="margin: 0px; padding: 0px; -ms-word-wrap: break-word !important; max-width: 100%; box-sizing: border-box;">
            图1-3展示了经典的进程生命周期。
        </section>
    </section>
</section>







图1-3 经典的进程生命周期
        <section style="margin: 0px; padding: 0px; -ms-word-wrap: break-word !important; max-width: 100%; box-sizing: border-box;">
            当一个进程创建一个新的进程,进程的创建进程(父进程)调用 一个fork()系统调用。当fork()系统调用被调用,它得到该新创建进程(子进程)的进程描述并调用一个新的进程id。它复制该值到父进程进程描述到子进程中。此时整个的父进程的地址空间是没有被复制的;父子进程共享相同的地址空间。
        </section>

        <section style="margin: 0px; padding: 0px; -ms-word-wrap: break-word !important; max-width: 100%; box-sizing: border-box;">
            exec()系统调用复制新的程序到子进程的地址空间。因为父子进程共享地址空间,写入一个新的程序的数据会引起一个分页错误。在这种情况下,内存会分配新的物理内存页给子进程。
        </section>

        <section style="margin: 0px; padding: 0px; -ms-word-wrap: break-word !important; max-width: 100%; box-sizing: border-box;">
            这个推迟的操作叫作写时复制。子进程通常运行他们自己的程序而不是与父进程运行相同的程序。这个操作避免了不必要的开销,因为复制整个地址空间是一个非常缓慢和效率低下的操作,它需要使用大量的处理器时间和资源。
        </section>

        <section style="margin: 0px; padding: 0px; -ms-word-wrap: break-word !important; max-width: 100%; box-sizing: border-box;">
            当程序已经执行完成,子进程通过调用exit()系统调用终止。exit()系统调用释放进程大部分的数据并通过发送一个信号通知其父进程。此时,子进程是一个被叫作僵尸进程的进程(参阅page 7的&ldquo;Zombie processes&rdquo;)。
        </section>

        <section style="margin: 0px; padding: 0px; -ms-word-wrap: break-word !important; max-width: 100%; box-sizing: border-box;">
            子进程不会被完全移除直到其父进程知道其子进程的调用wait()系统调用而终止。当父进程被通知子进程终止,它移除子进程的所有数据结构并释放它的进程描述。
        </section>
    </section>
</section>







线程









一个线程是一个单独的进程生成的一个执行单元。它与其他的线程并行地运行在同一个进程中。各个线程可以共享进程的资源,例如内存、地址空间、打开的文件等等。它们能访问相同的程序数据集。线程也被叫作轻量级的进程(Light Weight Process,LWP)。因为它们共享资源,所以每个线程不应该在同一时间改变它们共享的资源。互斥的实现、锁、序列化等是用户程序的责任。
        <section style="margin: 0px; padding: 0px; -ms-word-wrap: break-word !important; max-width: 100%; box-sizing: border-box;">
            从性能的角度来说,创建线程的开销比创建进程少,因数创建一个线程时不需要复制资源。另一方面,进程和线程拥在调度算法上有相似的特性。内核以相似的方式处理它们。
        </section>
    </section>
</section>









图1-4 进程和线程
        <section style="margin: 0px; padding: 0px; -ms-word-wrap: break-word !important; max-width: 100%; box-sizing: border-box;">
            在现在的Linux实现中,线程支持UNIX的可移植操作系统接口(POSIX)标准库。在Linux操作系统中有几种可用的线程实现。以下是广泛使用的线程库:
        </section>

        <section style="margin: 0px; padding: 0px; -ms-word-wrap: break-word !important; max-width: 100%; box-sizing: border-box;">
            LinuxThreads
        </section>

        <section style="margin: 0px; padding: 0px; -ms-word-wrap: break-word !important; max-width: 100%; box-sizing: border-box;">
            LinuxThreads自从Linux内核2.0起就已经被作为默认的线程实现。LinuxThreads的一些实现并不符合POSIX标准。Native POSIX Thread Library(NPTL)正在取代LinuxThreads。LinuxThreads在将来的Linux企业发行版中将不被支持。
        </section>

        <section style="margin: 0px; padding: 0px; -ms-word-wrap: break-word !important; max-width: 100%; box-sizing: border-box;">
            Native POSIX Thread Libary(NPTL)
        </section>

        <section style="margin: 0px; padding: 0px; -ms-word-wrap: break-word !important; max-width: 100%; box-sizing: border-box;">
            NPTL最初是由红帽公司开发的。NPTL与POSIX更加兼容。通过Linux内核2.6的高级特性,例如,新的clone()系统调用、信号处理的实现等等,它具有比LinuxThreads更高的性能和伸缩性。
        </section>

        <section style="margin: 0px; padding: 0px; -ms-word-wrap: break-word !important; max-width: 100%; box-sizing: border-box;">
            NPTL与LinuxThreads有一些不兼容。一个依赖于LinuxThreads的应用可能不能在NPTL实现中工作。
        </section>

        <section style="margin: 0px; padding: 0px; -ms-word-wrap: break-word !important; max-width: 100%; box-sizing: border-box;">
            Next Generation POSIX Thread(NGPT)
        </section>

        <section style="margin: 0px; padding: 0px; -ms-word-wrap: break-word !important; max-width: 100%; box-sizing: border-box;">
            NGPT是一个IBM开发的POSIX线程库。现在处于维护阶段并且在未来也没有开发计划。
        </section>

        <section style="margin: 0px; padding: 0px; -ms-word-wrap: break-word !important; max-width: 100%; box-sizing: border-box;">
            使用LD_ASSUME_KERNEL环境变量,你可以选择在应用中使用哪一个线程库。
        </section>
    </section>
</section>







进程优先级和nice值









进程优先级是一个数值,它通过动态的优先级和静态的优先级来决定进程被CPU处理的顺序。一个拥有更高进程优先级的进程拥有更大的机率得到处理器的处理。
        <section style="margin: 0px; padding: 0px; -ms-word-wrap: break-word !important; max-width: 100%; box-sizing: border-box;">
            内核根据进程的行为和特性使用试探算法,动态地调整调高或调低动态优先级。一个用户进程可以通过使用进程的nice值间接改变静态优先级。一个拥有更高静态优先级的进程将会拥有更长的时间片(进程能在处理上运行多长时间)。
        </section>

        <section style="margin: 0px; padding: 0px; -ms-word-wrap: break-word !important; max-width: 100%; box-sizing: border-box;">
            Linux支持从19(最低优先级)到-20(最高优先级)的nice值。默认值为0。把程序的nice值修改为负数(使进程的优先级更高),需要以root身份登陆或使用su命令以root身份执行。
        </section>
    </section>
</section>







上下文切换









在进程运行过程中,进程的运行信息被保存于处理器的寄存器和它的缓存中。正在执行的进程加载到寄存器中的数据集被称为上下文。为了切换进程,运行中进程的上下文将会被保存,接下来的运行进程的上下文将被被恢复到寄存器中。进程描述和内核模式堆栈的区域将会用来保存上下文。这个切换被称为上下文切换。过多的上下文切换是不受欢迎的,因为处理器每次都必须清空刷新寄存器和缓存,为新的进程制造空间。它可能会引起性能问题。
        <section style="margin: 0px; padding: 0px; -ms-word-wrap: break-word !important; max-width: 100%; box-sizing: border-box;">
            图1-5 说明了上下文切换如何工作。
        </section>
    </section>
</section>









图1-5 上下文切换









中断处理









中断处理是优先级最高的任务之一。中断通常由I/O设备产生,例如网络接口卡、键盘、磁盘控制器、串行适配器等等。中断处理器通过一个事件通知内核(例如,键盘输入、以太网帧到达等等)。它让内核中断进程的执行,并尽可能快地执行中断处理,因为一些设备需要快速的响应。它是系统稳定的关键。当一个中断信号到达内核,内核必须切换当前的进程到一个新的中断处理进程。这意味着中断引起了上下文切换,因此大量的中断将会引起性能的下降。
        <section style="margin: 0px; padding: 0px; -ms-word-wrap: break-word !important; max-width: 100%; box-sizing: border-box;">
            在Linux的实现中,有两种类型的中断。硬中断是由请求响应的设备发出的(磁盘I/O中断、网络适配器中断、键盘中断、鼠标中断)。软中断被用于处理可以延迟的任务(TCP/IP操作,SCSI协议操作等等)。你可以在/proc/interrupts文件中查看硬中断的相关信息。
        </section>

        <section style="margin: 0px; padding: 0px; -ms-word-wrap: break-word !important; max-width: 100%; box-sizing: border-box;">
            在多处理器的环境中,中断被每一个处理器处理。绑定中断到单个的物理处理中能提高系统的性能。更多的细节,&ldquo;CPU的中断处理亲和力&rdquo;。
        </section>
    </section>
</section>