admin 管理员组文章数量: 1184232
Python编程:深入探索进程的技巧
一、进程的基础知识
1.进程的基础知识
a. 进程定义的3种视角
-
进程是计算机中程序关于某些数据集合上的一次运动
- 视角 :动态执行视角
- 解析 : 进程是程序一次动态执行的过程,包含代码、数据、以及执行状态。
-
是系统进行资源分配的基本单位,
- 视角 :资源管理视角
- 解析 :**进程作为独立的实体,操作系统为其分配内存和cpu时间等资源。**进程每个资源的分配都是独立的。
-
是操作系统结构的基础。
- 视角 :系统架构视角
- 解析 : 进程是操作系统调度、同步、通信的核心对象 。操作系统通过管理进程的状态实现(‘就绪、运行、阻塞’)实现多任务处理。
- 早期 面向进程设计的计算机结构: 进程是程序的基本执行实体
- 现代 面向进程设计的计算机机构: 进程是线程的容器
b.引入的原因
- 为了提高系统资源利用率和系统处理能力 。现阶段计算机系统都是多道程序系统(并发执行)
- 优化系统资源 , 方便计算机调度 ,避免系统运算缭乱。
- 进程是一种数据结构 ,能够清晰的刻画动态系统的内在规律,增加程序运行时的动态性
c. 什么是程序
-
程序是
指令
、
数据
及其相关形式的描述。
- 指令:存储在介质中(静态,除非人为不可改变)
-
与进程之间的关系
- 进程是程序的实体
- 同一个程序在 不同的数据集上 运动时,会 构成不同的进程 实例,因为每个进程都有独立的数据空间,即使它们是同样的程序代码。
d.进程的特征
动态性 :进程的实质是程序在多道程序系统中的一次执行过程,进程是动态产生,动态消亡的。
并发性 :任何进程都可以同其他进程一起并发执行。
独立性 :进程是一个能独立运行的基本单位,同时也是系统分配资源和调度的独立单位。
异步性 :由于进程间的相互制约,使进程具有执行的间断性,即进程按各自独立的、不可预知的速度向前推进。
e. 进程的地址空间结构 (扩展)
地址空间的核心作用
每个进程拥有独立的虚拟地址空间 ,防止进程间非法访问,保障安全性和稳定性。
操作系统通过地址空间管理进程的内存使用 (分配、回收、保护)
三个关键区域
- 文本区域: 存储 可执行代码 ,通常是 可读状态 ,在多个进程实例中 可共享 (如运行同一程序的多个副本时节省内存)
- 数据区域 :存储 全局变量 、 静态变量 及 动态分配的内存 (堆)
- 堆栈区域: 管理 函数调用 和局部变量,遵循**LIFO(后进先出)**原则
三个分区实现了进程的
- 代码与数据的隔离
- 动态内存的管理(堆的灵活分配)
- 高效的函数调用(栈的自动管理)
- 进程间内存的保护(独立地址空间)
2.进程的状态
-
创建状态
- 进程在创建的时候需要申请一个 空白PCB(进程控制块 ),进程控制块是用于 描述 和 管理 进程的 数据结构 ,填写与新进程的各种相关信息,如 进程标识符 、 程序计数器 、 寄存器状态 等-
- 完成资源分配
- 初始化后,操作系统会将新的进程插入到就绪队列中,等待着CPU进行调度
- 就绪状态 :进程已经准备好,已分配到所需资源(除了CPU资源),只要分配到CPU就能够立即运行
- 执行状态 :进程处于就绪状态被调度后,进程进入执行状态
- 阻塞状态 :正在执行的进程,由于等待某个事件发生而无法执行时,便放弃处理机而处于阻塞状态。引起进程阻塞的事件可有多种, 例如 ,等待I/O完成、申请缓冲区不能满足、等待信件(信号)等。
- 终止状态 :进程结束,或出现错误,或被系统终止,进入终止状态。无法再执行
3.进程的3种通信方式
进程间通信 (IPC,Interprocess Communication)是操作系统中不同进程之间传递数据或信号的机制。由于进程之间相互隔离,必须依赖操作系统提供的特定方法实现通信。
a.共享存储(shared Memory)
核心机制 :
- 多个进程直接访问 同一块物理内存区域 ,通过读写该共享区域进行交换数据
- 需要 同步工具 (如信号量、互斥锁)控制并发访问, 避免数据冲突
关键特性:
- 高效性 :数据直接在 内存中进行读写 , 无需内核频繁介入 ,适合高频、大数据量场景
- 同步复杂性 :必须显示 处理互斥 (操作独占)和 同步 (读写顺序)
应用场景 :
- 数据库缓存 (如 Redis )、 科学计算 (多进程协作处理同一数据集)
实现方式 (扩展)
-
POSIX共享内存:通过
shm_opn创建内存对象,mmap映射到进程地址空间 -
System V共享内存:使用
shmget创建共享段、shmat附加到进程地址空间
-
POSIX共享内存:通过
b.消息传递
核心机制
- 进程通过 发送/接收信息原语 交换数据,消息通常包含头部(类型,长度)和正文
-
两种模式
:
- 直接通信 :发送方指定接收方,消息挂载到接收方的消息队列(如管道)。
- 间接通信 :通过中间实体(如邮箱、消息队列)传递,发送方和接收方解耦
关键特性
- 灵活性 :一对一,一对多,多对一通信
- 安全性 : 内核负责管理消息缓冲 ,避免数据竞争。
应用场景 :
- 微服务架构中的服务调用
- 即时通讯软件的消息传输。
实现方式
-
POSIX消息队列
:
mq_open创建队列,mq_send和mq_receive操作消息。 -
System V消息队列
:
msgget创建队列,msgsnd和msgrcv发送/接收消息。
-
POSIX消息队列
:
c.管道通信
-
核心机制
- 管道(Pipe) :一种特殊的 单向字节流 通信方式,本质是 内核 维护的 环形缓冲区 。
-
两种类型
:
-
匿名管道
:仅用于
父子进程
或
兄弟进程
(如Shell中的
|)。 - 命名管道(FIFO) :通过 文件系统路径访问 ,允许无关进程通信。
-
匿名管道
:仅用于
父子进程
或
兄弟进程
(如Shell中的
-
关键特性
- 半双工 :数据单向流动,双向通信需创建两个管道。
- 阻塞式读写 :读空管道时阻塞,写满管道时阻塞(除非设为非阻塞模式)。
-
应用场景
-
Shell命令流水线(如
ls | grep .txt)、日志收集系统。
-
Shell命令流水线(如
-
实现方式
-
匿名管道
:
pipe()系统调用创建,返回两个文件描述符(读端和写端)。 -
命名管道
:
mkfifo命令或函数创建FIFO文件。
-
匿名管道
:
d.【三者之间对比】
| 机制 | 性能 | 复杂度 | 同步需求 | 适用场景 |
|---|---|---|---|---|
| 共享存储 | 最高 | 高 | 需显式同步 | 高频数据交换(如GPU计算) |
| 消息传递 | 中等 | 中等 | 由内核管理 | 结构化通信(如微服务) |
| 管道 | 较低 | 低 | 隐式阻塞等待 | 简单数据流(如命令行工具) |
e.IPC其他方式
-
信号(signal)
:内核向进程发送异步通知(如
SIGKILL终止进程)。 - 套接字(socket) :支持跨网络通信(TCP/UDP)
- 内存映射文件 ( Memory-mapped File ):将文件映射到内存,实现进程间共享
4.进程的调度
调度:操作系统负责分配CPU时间给各个进程的过程 , 操作系统的核心机制 ,负责决定 哪个进程 在 何时 使用 CPU资源 ,以优化系统性能(如吞吐量、响应时间)和资源利用率。
- 一个高效的进程调度算法能够显著的提高CPU的利用率,降低系统的响应时间
a.调度目标
- 公平性 :合理分配CPU时间,避免进程饥饿。
- 高效性 :最大化CPU利用率,减少空闲时间。
- 响应速度 :交互式系统需快速响应用户操作(如鼠标点击)。
- 吞吐量 :单位时间内完成的进程数量(适合批处理系统)。
- 可预测性 :确保进程的等待时间和执行时间稳定。
b.调度类型
| 调度类型 | 作用 | 示例场景 |
|---|---|---|
| 长程调度 | 决定哪些作业从磁盘调入内存变为就绪状态 | 批处理系统选择作业加载到内存 |
| 中程调度 | 控制内存中进程数量(通过挂起/激活进程) | 内存不足时,将部分进程换出到磁盘 |
| 短程调度 | 从就绪队列选择进程分配CPU | 时间片用完时切换进程(如分时系统) |
c.经典的调度算法
-
先来先服务
(First-Come First-Served)
- 规则 :按进程到达的顺序分配CPU
-
特点
:
- 简单,无饥饿问题
- 护航效应:长进程阻塞短进程,导致平均等待时间高
- 适用场景 : 批处理系统
-
短作业优先
(SJF,Shortest Job First)
- 规则 :优先调度预计运行时间最短的进程
-
特点
:
- 理论最优:平均等待时间最短(非抢占式)
- 难以预测:实际中进程运行位置
- 变种 : SRTF (Shortest Remaining Time First,抢占式)
-
优先级调度
- 规则 :为每个进程分配优先级,优先调度高优先级进程。
-
问题
:
- 饥饿 :低优先级进程可能长期得不到执行。
- 解决方法 :动态调整优先级(如随等待时间增加提升优先级)。
- 变种 :抢占式优先级调度(更高优先级进程到达时抢占CPU)。
-
轮转调度
- 规则 :每个进程分配固定时间片(如10ms),时间片用完则重新排队。
-
特点
:
- 公平性强,响应时间有保障。
- 时间片过长退化为FCFS,过短增加上下文切换开销。
- 适用场景 :交互式系统(如Linux桌面环境)
-
多级反馈队列(MLFQ, Multilevel Feedback Queue)
-
规则
:
- 设置多个优先级队列,新进程进入最高优先级队列。
- 进程用完时间片未结束则降级到低优先级队列。
- 低优先级队列时间片更长。
-
目标
:
- 短作业快速完成(高优先级队列)。
- 长作业逐步降级,避免饥饿。
- 适用场景 :通用操作系统(如Windows、Linux)。
-
规则
:
5.进程的其他配置
上下文 (Context)是指进程在执行过程中所需的 环境信息 ,当进程被调用执行时,其上下文加载到CPU中;切换的时候上下文被保存起来,以便将来再次执行恢复
CPU寄存器的内容
程序计数器
堆栈内容等
记账信息 : 记账信息 (Accounting Information)是指操作系统用于 跟踪进程执行情况 的统计数据。 对于系统性能的监控、资源分配优化,以及调度有重要的意义
- cpu的使用时间
- I\O操作次数
- 内存的使用量
- 相应时间
- 周转时间等。
二、Python实战案例与代码
1.计算密集型任务(素数统计)
"""
需求:计算1,100000中多少个素数。
如果使用单个进程需要说需要的时间为
"""import functools
import math
import multiprocessing
import time
deffunc_run_time(func):@functools.wraps(func)defwrapper(*args):
start_time = time.perf_counter()
func(*args)
end_time = time.perf_counter()print(f'func {func.__name__} took {(end_time - start_time)}秒')return wrapper
defis_prime(n):"""
最小素数是2:2是最小的素数,也是唯一的偶数素数。
特殊情况处理:处理小于2的整数,因为它们不是素数。
基本检查:2是唯一的偶数素数,因此单独处理。
试除法:对于大于2的整数,检查是否能被2到其平方根之间的任何整数整除
"""if n <2:returnFalsefor i inrange(2,int(math.sqrt(n))+1):if n % i ==0:returnFalsereturnTrue@func_run_timedefcount_primes(start, end, result_queque):# 使用进程的写法"""
输入一定范围,判断其中有多个素数,并将结果放入队列中
:param start:
:param end:
:param result_queque:
:return:count
"""
count =0for num inrange(start, end):if is_prime(num):
count +=1
result_queque.put(count)# 结果放入队列中。if __name__ =='__main__':
result_queue = multiprocessing.Queue()
processes =[]
ranges =[(1,25000),(25000,50000),(50000,75000),(75000,100000)]# 4组范围# 汇总结果
total =0print('============单个进程执行==================')# 启动1个进程进行运算
count_primes(1,100000, result_queue)print(f"Total primes under 100000: {result_queue.get()}")print('\n============4个进程分片执行==================')# 启动4个进程分片计算for start, end in ranges:
p = multiprocessing.Process(target=count_primes, args=(start, end, result_queue))
processes.append(p)
p.start()# 进程开始执行for p in processes:
p.join()# 等待所有进程结束whilenot result_queue.empty():
total = total + result_queue.get()print(f"Total primes under 100000: {total}")"""
结果输出:
============单个进程执行==================
func count_primes took 0.0750123000034364秒
Total primes under 100000: 9592
============4个进程分片执行==================
func count_primes took 0.01507060000585625秒
func count_primes took 0.019340899998496752秒
func count_primes took 0.02227639999910025秒
func count_primes took 0.028007099994283635秒
Total primes under 100000: 9592
"""math.sqrt(n)是用于计算一个非负实数n的平方根的函数multiprocessing.Process类创建一个新的进程。multiprocessing.Queue()多进程之间提供安全的队列进行数据的通信。其中队列中的empty()判断是否为空列表,针对于get() 和put()方法可能会遇到阻塞异常。
2.进程池文件处理
"""
通过进程池完成对文件处理的加速
转换格式;cpu密集型任务
提取数据:根据数量的处理的复杂程度可分为I/O密集型和CPU密集型,例如CPU密集型是在日志中提取有关的信息。I/O密集型:只是简单的提取一部分内容
"""import random
import time
from multiprocessing import Pool
import os
defprocess_file(filename):"""模拟文件处理(格式转换)"""print(f'processing_{filename} in PID {os.getpid()},Parent PID {os.getppid()}')# 实际处理逻辑
time.sleep(random.uniform(1,2))# 防止每个进程执行的速度执行过快,导致PID相同,影响直观的结果returnf'{filename}_processed'# 输出:文件已经处理过if __name__ =='__main__':
files =["file1.txt","file2.txt","file3.txt","file4.txt"]with Pool(processes=4)as pool:
results = pool.map(process_file, files)print("processed files:", results)3.共享内存与锁机制
import multiprocessing
import time
defworker(shared_value, lock):for _ inrange(1000):with lock:# 自动加锁/释放
shared_value.value +=1if __name__ =='__main__':
lock = multiprocessing.Lock()
shared_value = multiprocessing.Value('i',0)
processes =[]for _ inrange(4):
p = multiprocessing.Process(
target=worker, args=(shared_value, lock))
processes.append(p)
p.start()for p in processes:
p.join()print("Final value:", shared_value.value)# 应为40004.常见问题解决
-
僵尸进程
:确保调用
join()或设置daemon属性。 - 死锁 :按固定顺序获取锁,或使用超时机制。
-
调试困难
:用
logging模块替代print,输出到文件。
版权声明:本文标题:Python编程:深入探索进程优化技巧 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.roclinux.cn/b/1774380158a3571072.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论