当前位置: 首页 > article >正文

【python】os.fork进程创建

文章目录

  • 基本使用
  • ps.从fork理解虚拟内存管理与写时复制技术

基本使用

API:https://docs.python.org/zh-cn/3.9/library/os.html#os.fork

fork,叉子、分叉的意思。python的os模块中,fork用来从主进程中复制出来一个子进程副本,复制出来的子进程和主进程基本完全相同,除一些进程特有的标识外(如进程id、父进程id),其余的像环境变量、文件句柄、内部对象的虚拟内存地址等都一样,仅适用类unix系统平台(对于开发来说基本可以认为两个进程相同)。

在fork调用后,父进程和子进程会在同一个地方继续执行下去。类似于:
在这里插入图片描述
如果fork调用成功,在父进程中,fork将返回新创建的子进程的进程ID;而在子进程中,fork将返回0,也就是一些资料中说的“一次调用,两次返回”,可以通过这个返回的值来区分子进程与父进程。如果调用失败,会抛出OSError异常。

注意这个返回的值跟os.getpid()返回的pid含义不同,os.getpid()返回的是当前进程的进程号,而fork调用返回的值更像是一个进程内部的标识变量,只不过这个变量的值在父进程中定义为子进程的进程号(便于控制子进程)。

可以通过下面代码验证:

import os

if __name__ == '__main__':
    print(f'fork前 进程号:{os.getpid()} 父进程号:{os.getppid()}')
    try:
        pid = os.fork()
    except OSError as e:
        print(f'fork error, msg:{e}')
    else:
        if pid == 0:  # 子进程执行
            print(f'fork后 子进程执行 进程号:{os.getpid()} 父进程号:{os.getppid()}')
        elif pid > 0:  # 父进程执行
            print(f'fork后 父进程执行 进程号:{os.getpid()} 父进程号:{os.getppid()}')
        else:
            raise ValueError('pid is not 0 or an integer greater than 0')

在这里插入图片描述

ps.从fork理解虚拟内存管理与写时复制技术

从下面这个demo开始了解:

import os

if __name__ == '__main__':
    data = list(range(10))
    try:
        pid = os.fork()
    except OSError as e:
        print(f'fork error, msg:{e}')
    else:
        if pid == 0:
            print(f'子进程输出 data:{data} id:{id(data)}')
            data.append(10)
            print(f'子进程输出 data:{data} id:{id(data)}')
            data = [1, 2, 3]
            print(f'子进程输出 data:{data} id:{id(data)}')
        elif pid > 0:
            os.waitpid(pid, 0)  # 等待子进程执行完毕
            print(f'父进程输出 data:{data} id:{id(data)}')
        else:
            raise ValueError('pid is not 0 or an integer greater than 0')

在这里插入图片描述
上面代码的执行顺序:主进程创建data对象 -> 主进程fork一个子进程 -> 子进程执行完毕 -> 主进程执行。

开始的不理解在于,fork出来的子进程data指向的内存地址和父进程相同,但子进程对该对象修改后对父进程的data对象又没有影响?或者说,不同进程有各自的内存空间,为什么父子进程中data对象的内存地址相同?

整个问题可以把虚拟内存管理和写时复制技术结合来看:

在类unix系统中,fork系统调用一般采用了写时复制(Copy on Write)技术,fork出来的子进程会复制一份父进程的内存页表作为自己的内存页表,因为页表项中虚拟内存页指向的实际物理内存页还是原来的,所以进程间内存也是共享的。只有当其中某个进程触发写操作时,操作系统会给这个触发写的进程申请复制一个相关内存页的副本,修改内存页中所指向的实际物理内存页后(此处保证了内存的物理隔离)并在页表中替换原页表项。

这里还有一个容易混淆的点就是,对于不同的进程来说,虚拟内存地址是可以重复的,因为虚拟内存地址的主要作用就是:页目录索引定位页表物理地址 -> 页表索引定位页目录项物理地址 -> 页偏移量定位页目录项中物理页的页内偏移量。在页目录项映射实际物理页这一步,不同进程中有各自独立的映射。虚拟内存更像是充当一个标识符的作用。

虚拟内存地址寻址实际物理内存地址的过程参考:虚拟内存地址寻址实际物理内存地址。页目录属于操作系统级别,页表属于进程级别,可以把页目录理解成为一本书,书中的每一页是一个页表,属于一个进程。页表中每一行记录了一个实际的物理内存页号。


http://www.kler.cn/a/372413.html

相关文章:

  • 爬虫代理服务要怎么挑选?
  • xinput1_3.dll放在哪里?当xinput1_3.dll丢失时的应对策略:详细解决方法汇总
  • Gmsh有限元网格剖分(Python)---点、直线、平面的移动
  • 五、Swagger 介绍(Flask+Flasgger的应用)
  • 数据库管理-第274期 Oracle Enterprise Manager 24ai新特性一览(20241223)
  • 强力巨彩租赁屏技术更新,适用多种户外应用场景
  • SCSI驱动与 UFS 驱动交互概况
  • Maven 下载与安装详细教程,新手也很适用!
  • unity3d——Time
  • QModbus使用时出现内存增加的问题
  • 5G RAN
  • 跨平台实现实时通讯
  • SpringCloudAlibaba实战入门之Nacos注册中心(四)
  • 为什么STM32的HAL库那么难用,ST还是要硬推HAL库?
  • 方法+数组
  • 每天五分钟深度学习:逻辑回归和神经网络
  • 电子电气架构 --- 车载以太网的未来已经来临
  • 解决Eclipse中’Run As’菜单缺少’Run on Server’选项的问题
  • appium+mumu模拟器+python 嚼碎菜鸟教程
  • Flutter登录界面使用主题
  • 完全透彻了解一个asp.net core MVC项目模板2
  • 【WebDriver】浏览器驱动下载及其配置
  • ​​Linux已经走向分裂
  • 代码随想录:从中后/中前遍历序列构造二叉树
  • nascpolarssh
  • 【JavaEE初阶】网络原理(2)