网络编程:深入理解进程——基础概念、特征、调度与 Python 实现

目录

引言

一、进程概述

1.1 进程的定义

1.2 进程与程序的区别

二、进程特征

2.1 动态性

2.2 并发性

2.3 独立性

2.4 异步性

2.5 结构特征

三、进程调度

3.1 进程的并行与并发

3.2 进程状态转换

四、同步和异步

4.1 同步

4.2 异步

五、代码实现多进程

5.1 方法一:使用官方提供的进程类

5.2 方法二:创建普通类,继承异常类

六、进程中的常用方法和属性

6.1 常用方法 - join

6.2 常用属性 - daemon

七、总结


引言

        在计算机科学的领域中,进程是操作系统运行的核心概念之一。它如同计算机系统中的舞者,在程序的舞台上演绎着各种精彩的运算和交互。无论是多任务处理、资源分配,还是系统的高效运行,进程都扮演着至关重要的角色。本文将深入探讨进程的各个方面,从基本概念到实际的代码实现,帮助读者全面掌握进程这一关键知识。

一、进程概述

1.1 进程的定义

        进程,简单来说,就是正在执行的程序。它不仅仅是一堆静态的指令和数据,更是程序执行过程中的一次动态集合,涵盖了指令、数据集以及相关的运行环境等要素。可以把它看作是程序的一次 “生命旅程”,从开始到结束,在计算机系统中留下运行的轨迹。

        在早期面向进程设计的计算机结构里,进程是程序执行的基本实体,承载着程序运行的重任。而在当代面向线程设计的计算机结构中,进程又成为了线程的容器,为线程提供了运行的环境和资源支持。

1.2 进程与程序的区别

        程序是指令、数据及其组织形式的描述,它是静态的,就像是一本写好的剧本。而进程是程序的实体,是程序在计算机系统中的一次动态执行过程,如同根据剧本在舞台上实际表演的一场戏。一个程序可以对应多个进程,因为同一个程序在不同的数据集上运行,就构成了不同的进程,也会得到不同的运行结果。但在进程执行过程中,程序本身的代码通常是不会发生改变的。

二、进程特征

2.1 动态性

        进程的实质是程序在多道程序系统中的一次执行过程。它动态产生,在程序开始运行时被创建;动态消亡,当程序执行完毕或者出现异常终止时,进程也随之结束。这种动态性使得计算机系统能够灵活地响应各种任务需求,根据实际情况创建和销毁进程。

2.2 并发性

        任何进程都可以同其他进程一起并发执行。在多道程序环境下,多个进程可以在宏观上同时运行,虽然在微观上,处理器在某一时刻只能执行一个进程的指令,但通过快速切换,使得用户感觉多个进程好像是同时在运行一样。这种并发性大大提高了计算机系统的资源利用率和整体性能。

2.3 独立性

        进程是一个能独立运行的基本单位。它拥有自己独立的地址空间、资源等,与其他进程相互隔离。同时,进程也是系统分配资源和调度的独立单位,操作系统根据进程的需求来分配 CPU 时间、内存空间、I/O 设备等资源,并对进程的执行进行调度,以确保系统的高效运行。

2.4 异步性

        由于进程间的相互制约,进程具有执行的间断性。各个进程按照各自独立的、不可预知的速度向前推进。例如,一个进程可能在等待 I/O 操作完成时被阻塞,而其他进程则继续执行。这种异步性给操作系统的调度和管理带来了挑战,但也正是它使得计算机系统能够充分利用各种资源,实现高效的多任务处理。

2.5 结构特征

        进程由程序、数据和进程控制块(PCB)三部分组成。程序是进程要执行的指令序列;数据是程序执行过程中所需要处理的数据;而进程控制块则是操作系统用于管理进程的核心数据结构,它记录了进程的各种信息,如进程 ID、状态、优先级、资源分配情况等,操作系统通过对 PCB 的操作来实现对进程的控制和管理。

三、进程调度

3.1 进程的并行与并发

        并行是指多个进程在同一时刻真正地同时执行,这需要计算机系统具备多个处理器(多核 CPU),每个处理器可以同时处理一个进程。而并发是指多个进程在一段时间内宏观上同时运行,但在微观上它们是交替执行的,通过处理器的快速切换来实现。在单处理器系统中,进程只能并发执行;而在多处理器系统中,进程既可以并行也可以并发执行。

3.2 进程状态转换

在程序运行过程中,由于被操作系统的调度算法控制,进程会在几个状态之间转换:

  1. 就绪 (Ready) 状态:当进程已分配到除 CPU 以外的所有必要资源,只要获得处理机便可立即执行,这时的进程状态称为就绪状态。处于就绪状态的进程就像是一群准备起跑的运动员,只等发令枪响(获得 CPU)就可以开始执行。
  2. 执行 / 运行(Running)状态:当进程已获得处理机,其程序正在处理机上执行,此时的进程状态称为执行状态。这是进程正在 “全力奔跑” 的阶段,真正地在处理器上执行指令。
  3. 阻塞 (Blocked) 状态:正在执行的进程,由于等待某个事件发生而无法执行时,便放弃处理机而处于阻塞状态。例如,等待 I/O 完成、申请缓冲区不能满足、等待信件 (信号) 等。处于阻塞状态的进程就像是运动员在比赛中遇到了障碍物,不得不停下来等待。
  4. 挂起状态:因为某种原因,进程放弃了 CPU,导致进程无法继续执行,此时进程被踢出内存。这可能是由于系统资源紧张,需要将暂时不执行的进程挂起,以释放内存等资源给更需要的进程。

四、同步和异步

4.1 同步

        同步是一种任务执行方式,当一个任务的完成需要依赖另外一个任务时,只有等待被依赖的任务完成后,依赖的任务才能算完成。这是一种可靠的任务序列,要么两个任务都成功完成,要么都失败,两个任务的状态可以保持一致。例如,在一个数据处理流程中,任务 A 需要先对数据进行预处理,任务 B 才能基于预处理后的数据进行分析,那么任务 B 就必须等待任务 A 完成后才能开始,这就是同步的过程。

4.2 异步

        异步则与同步相反,不需要等待被依赖的任务完成,只是通知被依赖的任务要完成什么工作,依赖的任务也立即执行,只要自己完成了整个任务就算完成。至于被依赖的任务最终是否真正完成,依赖它的任务无法确定,所以它是不可靠的任务序列。比如,在一个网络请求场景中,发起请求的任务不需要等待服务器响应就可以继续执行后续操作,当服务器响应返回时,再通过回调等机制来处理响应结果,这就是异步的操作方式。

五、代码实现多进程

在 Python 中,我们可以方便地使用multiprocessing模块来实现多进程编程。下面介绍两种常见的实现方式。

5.1 方法一:使用官方提供的进程类

  1. 导入进程类
    from multiprocessing import Process
    
  2. 创建进程对象
    • 如果使用没有参数的初始化,则输出默认进程的信息:
    p1 = Process()
    print(p1)  # 
    
    需要注意,进程process初始化中是需要传参的,在Process的父类中有默认的初始化函数:
    def __init__(self, group=None, target=None, name=None, args=(), kwargs={}, *, daemon=None):
        pass
    
     

    参数解释:

    • target: 要执行的目标对象(要执行的目标函数)
    • name: 进程的名称
    • args: 要执行的目标函数的参数,元组类型
    • 携带参数创建进程对象:
    def fun(num):
        print("这是一个普通方法%s" % num)
    
    
    p1 = Process(name="路飞", target=fun, args=(1,))
    
  3. 执行进程(就绪状态)
    p1.start()
    

    运行结果没有顺序。所有进程都在就绪状态,抢 CPU 的执行权,谁抢上谁先运行。例如:
    from multiprocessing import Process
    
    
    def fun(num):
        print("这是一个普通方法%s" % num)
    
    
    if __name__ == '__main__':
        # 普通调用
        # fun()
        # 多进程调用
        # 创建进程对象
        p1 = Process(name="路飞", target=fun, args=(1,))
        p2 = Process(name="乔巴", target=fun, args=(2,))
        p3 = Process(name="丹尼", target=fun, args=(3,))
        p4 = Process(name="张三", target=fun, args=(4,))
        p5 = Process(name="李四", target=fun, args=(5,))
        p6 = Process(name="王五", target=fun, args=(6,))
        # 执行进程    就绪状态
        a = p1.start()
        b = p2.start()
        c = p3.start()
        d = p4.start()
        e = p5.start()
        f = p6.start()
    
  4. 获取进程 pid 编号
    import os
    os.getpid()  # 获取进程pid
    os.getppid()  # 获取父进程的pid
    

5.2 方法二:创建普通类,继承异常类

  1. 导入进程类,创建普通类,继承进程类
    from multiprocessing import Process
    
    
    class MyProcess(Process):
        pass
    
  2. 重写__init__run方法
    class MyProcess(Process):
        def __init__(self, n):
            super().__init__(name=n)
    
        def run(self):
            print("我是子进程")
    
  3. 执行进程
    if __name__ == '__main__':
        p1 = MyProcess("路飞")
        p1.start()
    

六、进程中的常用方法和属性

6.1 常用方法 - join

  join方法可以让父进程等待子进程执行结束,从而使父进程阻塞。正常开启一个子进程时,父进程会等待子进程执行结束,然后父进程再继续执行。例如:

from multiprocessing import Process
import time


def fun():
    print("子进程开始")
    time.sleep(2)
    print("子进程结束")


if __name__ == '__main__':
    p1 = Process(target=fun)
    p1.start()
    print("父进程在子进程开始后立即执行")
    p1.join()
    print("父进程在子进程结束后执行")

6.2 常用属性 - daemon

   daemon属性用于设置进程是否为守护进程,必须在start之前调用。如果一个进程被设置为守护进程,当父进程结束时,子进程也会跟着结束。例如:

from multiprocessing import Process
import time


def fun():
    while True:
        print("守护进程在运行")
        time.sleep(1)


if __name__ == '__main__':
    p1 = Process(target=fun)
    p1.daemon = True
    p1.start()
    time.sleep(3)
    print("父进程结束")

七、总结

        进程作为操作系统的核心概念,贯穿于计算机系统的运行始终。通过对进程的深入理解,我们掌握了它的定义、特征、调度机制以及在 Python 中的实现方式。无论是进行系统开发、优化程序性能,还是处理复杂的多任务场景,对进程的熟练运用都将为我们提供强大的技术支持。希望本文能够帮助读者在进程的学习道路上迈出坚实的一步,不断探索计算机科学的更多奥秘。

你可能感兴趣的:(网络编程,进程,processon,multiprocessing)