写在前面:
进程管理在任何操作系统上都是最重要的事。高效的进程管理能够确保应用高效稳定地运行。Linux的进程管理方式类似于UNIX的进程管理方式,包含进程调度、中断处理、信号、进程优先级、进程切换、进程状态、进程的内存等。
1.程序与进程2.进程之间的关系3.进程与线程4.进程的优先级和nice级别5.进程的状态
1.程序与进程
程序和进程的区别在于,进程虽然由程序产生,但是它并不是程序;程序是一个进程指令的集合,它可以启用一个或多个进程。同时,程序只占用磁盘空间,而不占用系统运行资源;而进程仅占用系统内存空间,是动态的、可变的,关闭进程,占用的内存资源随之释放。
“程序是存储在磁盘上包含可执行机器指令和数据的静态实体,而进程是在操作系统中执行特定任务的动态实体”。
2.进程之间的关系
当进程要创建一个新的进程时,进程会调用名为fork()的系统函数,它会为新创建的子进程获得一个进程描述符,并设置新的进程ID。同时复制父进程的进程描述符给子进程。这时,不会复制父进程的地址空间,而是父、子进程使用同样的地址空间。
“进程描述符是一个数据结构,里面包含了进程的metadata。通常PID用来索引进程描述符”。
父进程与子进程之间是管理与被管理的关系。当程序执行完成后,子进程使用exit()终止系统调用。exit()会释放进程的大部分数据结构,并且把这个终止的消息通知给父进程。这时候,子进程被称为僵尸进程(zombie progress)。一旦父进程知道子进程已经终止,它会清除子进程的所有数据结构和进程描述符。
如果父进程在子进程退出之前退出,那么其所有的子进程都变成孤儿进程,如果没有相应的处理机制,这些孤儿进程就一直处于僵死状态,无法释放资源。解决的办法是在启动进程内找一个进程作为这些孤儿进程的父进程,或者直接让PID为1的进程作为他们的父进程,进而释放孤儿进程占用的资源。
在CentOS7中PID的默认最大值为131072,PID的范围是0~4194304。如果僵尸进程持续增加可能用占用大量PID,影响到新进程的创建。
3.进程与线程
进程是程序的一次动态执行,它对应着从代码加载、执行至执行完毕的一个完整的过程,是一个动态实体,它有自己的声明周期。进程因创建而产生,因调度而运行,因等待资源或事件而被处于等待状态,因完成任务而被撤销。
线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。一个线程可以创建和撤销另一个线程,同一个进程的多个线程之间可以并发执行。
进程和线程的关系如下所述:
一个线程只能属于一个进程,而一个进程可以由多个线程
线程是资源分配的最小单位,同一个进程的所有线程共享该进程的所有资源
真正在处理器上运行的是线程
进程和线程的区别如下所述:
调度:线程作为调度和分配的基本单位,进程最为拥有资源的基本单位
并发性:不仅进程可以并发执行,同一个进程的多个线程也可以并发执行
拥有资源:进程是拥有资源的基本单位,线程不拥有系统资源,但可以访问隶属于进程的资源
系统开销:在创建和撤销进程时,由于系统都要为之分配和回收资源,导致系统的开销明显大于创建或撤销线程时的开销
4.进程的优先级和nice级别
Linux是一个多用户多任务的操作系统。所有的任务都放在一个队列中,操作系统根据每个任务的优先级为每个任务分配合适的时间片(时间片是进程在处理器中执行时间)。每个时间片很短,用户根本感觉不到是多个任务在运行,从而使所有任务共享系统资源。因此,Linux可以在一个任务还未执行完时,短暂挂起此任务,去执行另一个任务,过一段时间后再回来处理这个任务,直到这个任务完成才从任务队列中去除。这就是单CPU操作系统多任务的概念。
在单CPU的环境下,虽然系统可以运行多个任务,但是在某个时间点,CPU只能执行一个进程(串行执行)。而在多CPU的环境下,某个时间点可以有多个进程同时执行(并行执行)。这就是多核CPU比单核CPU处理性能高的原因。
在CPU执行每个任务的过程中,进程优先级决定了进程在CPU中的执行顺序。优先级越高的进程被处理器执行的的机会越大。
进程优先级由动态优先级和静态优先级决定,根据进程的行为,内核使用启发式算法决定开启或关闭动态优先级。可以通过nice级别直接修改进程的静态优先级,拥有越高的静态优先级的进程会获得更长的时间片。Linux支持的nice级别从19(最低优先级)到-20(最高优先级),默认为0。只有root用户才能把进程的nice级别调整为负数(让其具备较高优先级)。
#调整进程优先级
renice -20 PID
5.进程的状态
进程状态显示进程当前发生的事情,进程执行时进程状态会发生改变。共有如下5种状态:
运行状态:在这个状态中,进程正在CPU中执行,或者在运行队列(run queue)中等待运行
停止状态:进程由于特定信号(如SIGINT、SIGSTOP)而挂起就会处于停止状态,等待恢复信号,如SIGCOUNT(-18)
可中断的等待状态:这类进程处于阻塞状态,一旦达到某种条件,就会变为运行状态。同时该状态的进程也会由于接收到信号而被提前唤醒进入到运行状态。
不可中断的等待状态:处于这个状态的进程对信号不做响应。不可中断的等待状态最典型的例子就是进程等待磁盘I/O操作
僵尸状态:也就是僵尸进程,每个进程在结束后都会处于僵尸状态,等待父进程调用进而释放资源,处于该状态的进程已经结束,但是它的父进程还没有释放其系统资源
一个僵尸进程的产生过程是:父进程调用fork()创建子进程后,子进程运行至其终止,它立即从内存中移除,但进程描述符仍然保留在内存中(进程描述符占有极少的内存空间)。子进程的状态变成EXIT_ZOMBIE,并且向父进程发送SIGCHLD(-17)信号,父进程此时应该调用wait()系统函数来获取子进程的退出状态以及其他信息。在wait()调用之后,僵尸进程就完全从内存中移除了。因此一个僵尸进程存在于其终止到父进程调用wait()函数。如果编程不合理,父进程就不会调用wait()等系统函数来收集僵尸进程,那么这些进程就会一直存在于内存中。最后形成一直滞留的僵尸进程。





