Linux中父进程、子进程的概念与状态(运行、就绪、阻塞)详解
本文详细阐述了Linux系统中父进程和子进程的概念,以及进程状态的分类与转换。首先,文章解释了父进程和子进程的关系,包括它们的创建过程、终止条件和相互之间的通信机制。父进程是创建子进程的进程,而子进程则是被父进程创建出来的进程。文章还探讨了进程之间的层次关系,说明了子进程可以继承父进程的资源并继续执行。接着,文章深入讲解了进程状态的分类,包括运行态、就绪态和阻塞态。运行态是进程正在占用处理器执行代
🐇明明跟你说过:个人主页
🏅个人专栏:《Linux :从菜鸟到飞鸟的逆袭》🏅
🔖行路有良友,便是天堂🔖
目录
一、前言
1、Linux的起源与发展
1991年,芬兰赫尔辛基大学的学生林纳斯·托瓦兹(Linus Torvalds)开始编写一个新的操作系统内核,这个内核最初被称为“Freax”。他的初衷只是想学习操作系统内核的开发,并为自己的个人计算机编写一个操作系统。然而,当他将这个内核发布在Internet上并开放源代码后,全球各地的程序员开始参与到这个项目中来,共同完善这个内核。最终,这个内核被命名为Linux,这个名字是由Linus Torvalds的名字和Unix操作系统的名字组合而成的。
Linux从一开始就坚持源代码的公开和免费使用,任何人都可以对其进行修改和分发。这种开放性和自由性吸引了大量的开发者和用户,使得Linux逐渐成长并成熟起来。如今,Linux已经发展成为一个性能稳定、功能强大的多用户网络操作系统,支持32位和64位硬件,能运行主要的Unix工具软件、应用程序和网络协议。同时,Linux也有上百种不同的发行版,如基于社区开发的Debian、ArchLinux,以及基于商业开发的Red Hat Enterprise Linux、SUSE、Oracle Linux等。
2、进程的概念
在Linux操作系统中,进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。从内核的观点看,进程的目的就是担当分配系统资源(CPU时间、内存等)的基本单位。
进程的特点
- 动态性:进程是程序的一次执行过程,是动态产生和消亡的。
- 并发性:任何进程都可以同其他进程一起并发执行。
- 独立性:进程是一个能独立运行的基本单位,同时也是系统分配资源和调度的独立单位。
- 异步性:由于进程间的相互制约,使进程具有执行的间断性,即进程按各自独立的、不可预知的速度向前推进。
- 结构性:进程由程序、数据和进程控制块三部分组成。
3、进程与线程的区别
进程和线程是操作系统中两个基本的执行单位,但它们有着不同的概念和特性。
以下是进程和线程的主要区别:
进程(Process):
1. 定义:
- 进程是操作系统分配资源和调度的基本单位。每个进程都有自己的地址空间、内存、文件描述符等资源。
2. 内存空间:
- 进程拥有独立的内存空间,进程之间的内存是隔离的。一个进程不能直接访问另一个进程的内存。
3. 资源开销:
- 创建和切换进程的开销较大,因为涉及到内存空间的分配和管理、进程控制块(PCB)的维护等。
4. 数据共享:
- 进程之间的数据共享比较复杂,通常通过进程间通信(IPC)机制实现,如管道、消息队列、共享内存和信号等。
5. 崩溃影响:
- 一个进程崩溃不会影响到其他进程,因为它们是独立的。
6. 创建方式:
- 在 Unix/Linux 系统中,使用 fork() 系统调用创建新进程。
线程(Thread)
1. 定义:
- 线程是进程中的一个执行单位,一个进程可以有多个线程共享其资源(如内存、文件描述符等)。
2. 内存空间:
- 线程共享同一个进程的内存空间和资源,因此线程之间的数据共享和通信非常高效。
3. 资源开销:
- 创建和切换线程的开销较小,因为线程之间共享进程的资源,不需要独立的内存空间。
4. 数据共享:
- 线程之间的数据共享非常方便,因为它们共享进程的地址空间。但这也意味着需要注意线程同步问题,避免数据竞争和不一致。
5. 崩溃影响:
- 一个线程的崩溃可能导致整个进程的崩溃,因为线程共享进程的资源和地址空间。
6. 创建方式:
- 在 Unix/Linux 系统中,使用 pthread_create() 函数创建新线程。在高级语言中,如 Java、Python,也有相应的线程创建机制。
详细对比
特性 | 进程(Process) | 线程(Thread) |
定义 | 操作系统分配资源的基本单位 | 进程中的一个执行单位 |
内存空间 | 独立的内存空间 | 共享进程的内存空间 |
资源开销 | 创建和切换开销大 | 创建和切换开销小 |
数据共享 | 通过IPC机制,较复杂 | 直接共享内存,简单高效 |
崩溃影响 | 独立,不影响其他进程 | 可能导致整个进程崩溃 |
创建方式 | `fork()`系统调用 | 线程函数库 |
适用场景 | 独立的应用或任务 | 需要大量并发操作的场景 |
使用场景
进程:
- 适用于需要强隔离性和独立资源的应用,如不同用户的独立应用程序、服务器上的不同服务等。
- 适用于需要高可靠性,某个任务的崩溃不应影响其他任务的场景。
线程:
- 适用于需要高效并发操作的应用,如高并发的服务器、实时数据处理等。
- 适用于需要共享大量数据和资源的场景,避免频繁的进程间通信开销。
二、Linux进程基础
1、进程ID(PID)和父进程ID(PPID)
在操作系统中,进程ID(PID)和父进程ID(PPID)是用于标识和管理进程的重要概念。
进程ID(PID)
1. 定义:
- 进程ID(PID)是操作系统分配给每个进程的唯一标识符。在同一时刻,每个运行中的进程都有一个唯一的PID。
2. 作用:
- PID 用于标识和管理进程。系统调用、进程间通信、进程控制等操作通常需要使用进程的PID。
3. 分配:
- 当一个进程被创建时,操作系统会为其分配一个唯一的PID。PID 通常是一个正整数,系统会在进程终止后回收其PID,并可能在未来为新的进程重新分配该PID。
4. 查看 PID:
- 在 Unix/Linux 系统中,可以使用 ps、top 等命令查看当前系统中进程的PID。
ps aux
父进程ID(PPID)
1. 定义:
- 父进程ID(PPID)是当前进程的父进程的进程ID。每个进程在创建时都有一个父进程,通常是调用 fork() 系统调用的进程。
2. 作用:
- PPID 用于建立进程间的层级关系和父子关系。操作系统可以通过 PPID 追踪和管理进程的生命周期。
3. 继承关系:
- 当一个进程创建另一个进程时,创建进程(父进程)的PID就成为新创建进程(子进程)的PPID。如果父进程终止,子进程的PPID通常会被重新分配为 init 进程(在现代 Linux 系统中为 systemd 进程,PID 为1)。
4. 查看 PPID:
- 在 Unix/Linux 系统中,可以使用 ps -ef 命令查看每个进程的PPID。
ps -ef
示例
假设我们在 Unix/Linux 系统上创建一个子进程,使用 Python 创建进程并查看它们的PID和PPID:
import os
def child_process():
print(f"Child Process: PID={os.getpid()}, PPID={os.getppid()}")
if __name__ == "__main__":
print(f"Parent Process: PID={os.getpid()}")
pid = os.fork()
if pid == 0:
# Child process
child_process()
else:
# Parent process
print(f"Parent Process: Created Child PID={pid}")
执行上述代码,输出类似如下:
Parent Process: PID=1234
Parent Process: Created Child PID=1235
Child Process: PID=1235, PPID=1234
在这个例子中:
- os.getpid() 返回当前进程的PID。
- os.getppid() 返回当前进程的PPID。
- os.fork() 创建一个新的子进程。对于父进程,fork() 返回子进程的PID;对于子进程,fork() 返回0。
通过这段代码和输出,可以看到父进程和子进程各自的PID和PPID,从而理解进程之间的父子关系。
进程层级关系
- 在操作系统中,进程通常以层级树结构组织,每个进程都有一个父进程,可能有一个或多个子进程。这个层级结构可以通过 pstree 命令查看,以树状图显示所有进程及关系:
pstree
2、进程状态(运行、就绪、阻塞等)
在操作系统中,进程在其生命周期中会经历多个状态。每个状态代表进程在某一时刻的执行情况和资源分配情况。常见的进程状态包括运行、就绪、阻塞等。
进程状态
新建(New):
- 进程刚刚被创建,还未被调度执行。
就绪(Ready):
- 进程已准备好运行,等待被操作系统调度程序分配 CPU 时间。
- 处于就绪状态的进程已分配到所有必要的资源,除了 CPU。
运行(Running):
- 进程正在 CPU 上执行。
- 在单处理器系统中,一次只能有一个进程处于运行状态。在多处理器系统中,可以有多个进程同时运行。
阻塞(Blocked)/等待(Waiting)/睡眠(Sleeping):
- 进程正在等待某些事件完成(如 I/O 操作、信号、资源变得可用等)而无法继续执行。
- 当进程进入阻塞状态,它会被从 CPU 中移除,直到等待的事件发生。
挂起(Suspended):
- 进程被暂停执行,通常为了让位于其他进程或因为等待某个特定事件。
- 进程可以是挂起状态和就绪状态(挂起就绪)或挂起状态和阻塞状态(挂起阻塞)之间的组合。
终止(Terminated)/退出(Exit):
- 进程已完成执行或被强制终止。
- 终止状态的进程将被操作系统回收资源。
状态转换
进程在其生命周期中会经历多个状态转换,主要的状态转换包括:
就绪 -> 运行:
- 调度程序选择就绪队列中的一个进程,并将其分配到 CPU。
运行 -> 就绪:
- 进程时间片用完,调度程序将进程放回就绪队列,等待下次调度。
运行 -> 阻塞:
- 进程等待某个事件(如 I/O 操作)完成,因此进入阻塞状态。
阻塞 -> 就绪:
- 等待的事件完成,进程从阻塞状态变为就绪状态,等待 CPU 分配。
运行 -> 终止:
- 进程完成执行或被强制终止,进入终止状态。
3、进程优先级与调度
在 Linux 中,进程的优先级和调度是操作系统管理进程执行的关键机制。优先级决定了进程在竞争 CPU 时间时的相对重要性,而调度算法决定了哪些进程应该获得 CPU 时间片以执行。
进程优先级
1. Nice 值:
- 在 Linux 中,每个进程有一个所谓的 "nice" 值,它反映了进程对 CPU 的需求程度。
- Nice 值范围从 -20(最高优先级)到 19(最低优先级)。
- 较低的 Nice 值表示更高的优先级,即进程更倾向于获得 CPU 时间。
2. 静态和动态优先级:
- 进程的静态优先级由 nice 值决定,可以通过 nice 命令或 setpriority() 系统调用来设置。
- 动态优先级是由调度器根据进程的历史行为和当前负载动态调整的优先级。在 Linux 中,CFS(完全公平调度器)负责管理动态优先级。
进程调度
1. 调度器:
- Linux 内核提供了多种调度器,其中最常见的是 CFS(完全公平调度器)和实时调度器(例如 SCHED_FIFO 和 SCHED_RR)。
- CFS 旨在提供公平的 CPU 时间分配,而实时调度器则针对实时任务提供低延迟。
2. 调度策略:
- 调度器根据进程的优先级、等待时间和历史执行情况等因素来决定哪个进程应该获得 CPU 时间片。
- CFS 使用红黑树数据结构来管理进程的运行队列,并根据进程的 "虚拟运行时间" 动态调整优先级。
Nice 值和调度策略的关系
1. 静态优先级设置:
- 设置进程的 nice 值可以显式地影响其静态优先级,从而影响进程在调度时的竞争能力。
2. 动态调整:
- 调度器根据进程的行为和负载情况动态调整进程的优先级,以确保系统的整体性能和公平性。
示例
1. 设置 Nice 值:
使用 nice 命令可以为新启动的进程设置 nice 值,例如:
nice -n 10 ./my_program
这个命令将 my_program 进程的 nice 值设置为 10,降低了其优先级。
2. 动态调整:
- 调度器会根据系统的负载情况和进程的行为动态调整优先级,以确保系统的整体性能和公平性。
通过合理设置进程的 Nice 值和使用适当的调度策略,可以有效地管理系统中进程的优先级和调度,从而提高系统的整体性能和响应性。
💕💕💕每一次的分享都是一次成长的旅程,感谢您的陪伴和关注。希望这些关于Linux的文章能陪伴您走过技术的一段旅程,共同见证成长和进步!😺😺😺
🧨🧨🧨让我们一起在技术的海洋中探索前行,共同书写美好的未来!!!
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)