目录
进程
概念
第一,进程是一个实体。每一个进程都有它自己的地址空间,一般情况下,包括
- 文本区域(text region)----存储处理器执行的代码
- 数据区域(data region)----存储程序执行期间的一些数据变量
- 堆栈(stack region)----存储动态分配的内存和本地变量及指令
第二,进程是一个“执行中的程序”。程序是一个没有生命的实体,只有在运行时处理器才会赋予它生命,才能成为一个活动的实体,我们称其为“进程”。
每一个进程都会有一个独一无二的编号,被称为进程标识码,简称PID(Process identifier),它是一个取值为1-32768。但是init是一个特殊的进程。所谓的init进程,是一个内核启动的用户级进程,也是系统上运行的所有其他进程的父进程,它会观察子进程,并在需要的时候启动,停止,重新启动它们,init进程主要完成系统各项的配置。
linux系统中,init从根文件夹系统目录里的/etc/inittab文件里获取信息。是所有进程的发起者和控制者,内核启动后,便由init进程来进行各项配置。
特点
- 独立性:每个进程都有独立的地址空间和资源,互相之间不会直接影响。
- 资源拥有者:进程拥有操作系统分配的资源,包括内存、文件句柄、信号等。
- 动态性:进程的状态会随着执行过程的变化而变化,进程可以创建、终止、等待等。
生命周期
进程通常经历以下几种状态:
- 新建(New):进程被创建,尚未开始执行。
- 就绪(Ready):进程已准备好,可以被调度执行,但由于 CPU 资源的限制,暂时不能执行。
- 运行(Running):进程正在 CPU 上执行。
- 阻塞(Blocked):进程因某种原因(如等待 I/O 操作)而不能继续执行。
- 终止(Terminated):进程完成执行或被强制终止,其资源被回收。
其中,
就绪(Ready)状态:当进程分配到除CPU以外的必要资源后,只要再获得CPU,便可以立即执行,进程这时的状态为就绪状态。在一个系统中处于就绪状态的进程可能有多个,通常将它们排成一个队列,称为就绪队列。
阻塞(Blocked)状态:正在执行的进程由于发生某事件或接受某消息无法继续执行时,便放弃处理机而处于暂停状态,也即进程的执行收到阻塞,把这种暂停状态称为阻塞状态,有时也称为等待状态和封锁状态。通常使进程处于阻塞的原因有:请求I/O,申请缓冲空间。也会产生一个相应的阻塞队列。
运行(Running)状态:进程已获得CPU,其程序正在执行。在单处理机系统中,只有一个进程处于执行状态,在多处理机系统中,则有多个进程处于执行状态。
其关系如下图所示:
进程的通信
进程间的通信通常称为进程间通信(IPC),常见的方式包括:
- 管道(Pipes):允许一个进程将输出传递给另一个进程。
- 消息队列(Message Queues):允许进程发送和接收消息。
- 共享内存(Shared Memory):允许多个进程访问同一块内存区域。
- 信号(Signals):用于通知进程某些事件的发生。
应用场景
- 服务器应用:使用多进程来处理多个并发客户端请求,例如 Web 服务器。
- 桌面应用:一些应用程序会创建多个进程来提高性能和安全性,例如浏览器。
- 计算密集型任务:在科学计算和数据处理等领域,通过多个进程并行处理任务以提高效率。
线程
概念
线程是进程中的 执行运算的最小单位,是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点在运行中必不可少的资源( 程序计数器,一组寄存器和栈),但它可与同属一个进程的其他线程 共享进程所拥有的全部资源。
特点
- 轻量级:相对于进程,线程创建和管理的开销较小,因为它们共享进程的资源。
- 共享资源:同一进程中的所有线程可以访问相同的内存空间,这使得线程间的通信更加高效。
- 独立性:虽然线程共享资源,但每个线程有自己的栈空间和程序计数器,独立执行。
类型
- 用户级线程(User-Level Threads):在用户空间管理的线程,操作系统并不直接感知这些线程的存在,所有调度和管理都在用户级完成。
- 内核级线程(Kernel-Level Threads):由操作系统内核管理的线程,操作系统知道这些线程的存在,并为它们提供调度和管理支持。
状态
线程的状态通常包括以下几种:
- 新建(New):线程被创建,但尚未开始执行。
- 就绪(Ready):线程准备好,可以被调度执行。
- 运行(Running):线程正在执行。
- 阻塞(Blocked):线程在等待某种条件(如 I/O 操作完成)时暂时停止执行。
- 终止(Terminated):线程执行完成,资源被释放。
调度
操作系统使用调度算法来决定哪个进程在何时获得 CPU 的使用权。常见的调度算法包括:
- 先来先服务(FCFS):按照进程到达的顺序进行调度。
- 短作业优先(SJF):优先调度需要时间最短的进程。
- 轮转调度(Round Robin):为每个进程分配固定的时间片,轮流调度。
应用场景
- 服务器:在网络服务器中,使用多线程可以同时处理多个客户端请求,提高并发处理能力。
- 图形用户界面(GUI):在 GUI 应用中,使用线程可以使界面保持响应,同时处理后台任务。
- 计算密集型任务:利用多线程可以分担任务,提高计算效率。
线程和进程的关系与区别
线程和进程的关系与区别如下:
关系
-
包含关系:进程是资源分配的基本单位,每个进程可以包含多个线程。也就是说,线程是进程中的执行单元。
-
生命周期:线程的生命周期依赖于它所处的进程。当一个进程结束时,所有属于该进程的线程都会被终止。
-
资源共享:同一个进程内的多个线程可以共享该进程的资源(如内存、文件描述符等),而不同进程之间的资源是隔离的。
-
一个进程中可以有多个线程,多个线程共享进程的堆和方法区 (JDK1.8 之后的元空间)资源,但是每个线程有自己的程序计数器、虚拟机栈 和 本地方法栈。
-
区别
线程和进程的区别如下,
总结
- 关系:线程是进程的组成部分,多个线程可以在同一进程内并发执行。
- 区别:进程是资源分配的单位,具有独立的地址空间和资源,而线程是执行的单位,线程之间可以共享资源。
总结来说,线程更轻量,适合并发操作,而进程提供了更高的隔离性和安全性。选择使用进程还是线程取决于具体的应用场景和需求。
僵尸进程
僵尸进程(Zombie Process)是指已经完成执行但仍然存在于操作系统进程表中的进程。这种进程会保留一些信息(如进程ID和退出状态),以便父进程读取这些信息。僵尸进程不再占用系统资源(如内存),但仍然占用一个进程表项。
产生原因
-
父进程未回收子进程的状态:当一个子进程执行完毕后,它的状态(如退出状态和资源使用情况)会被保留在操作系统中,以便父进程通过
wait()
/waitpid()
系统调用获取这些信息。如果父进程没有调用这些系统调用,子进程就会变成僵尸进程。 -
父进程提前终止:如果父进程在子进程结束之前就退出,子进程会被操作系统的
init
进程(通常是进程ID为1的进程)收养,并且它的状态仍会保留,因此在某些情况下也可能会形成僵尸进程。 -
错误的进程管理:如果程序没有正确处理子进程的退出状态,例如忽略了
SIGCHLD
信号的处理,子进程也可能会成为僵尸进程。
解决方法
僵尸进程是指已经完成执行但未被父进程回收的进程。通过适当的进程管理和信号处理,可以有效防止僵尸进程的产生。
-
使用
wait()
/waitpid()
:在父进程中调用这些系统调用来回收子进程的状态,从而避免僵尸进程的产生。 -
设置信号处理程序:可以设置信号处理程序来处理
SIGCHLD
信号,以便在子进程结束时及时处理它的退出状态。 -
检查和清理:使用
ps
命令可以查看系统中的僵尸进程(状态为 Z),并定期检查和清理它们。