在进入 fork 之前,让我们了解一下什么是过程。计算机术语中的进程是计算机当前正在执行的程序。每个进程都是唯一的,可以通过其 PID 或进程 ID 来标识。

  • 注意:下面显示的所有示例和演示代码均在 Ubuntu 20.04 LTS 和 Python v3.8.5 上进行了尝试。

访问我的 Github 页面以获取所有演示代码片段https://github.com/jaqsparow/fork-demos

我们将在这篇文章中学到什么?

  • 什么是分叉💡

  • 如何在 python 中调用 fork 📗

  • 如何在 Python 中获取进程 ID 或 PID 📙

  • 如何识别父子进程📕

  • 个带有代码片段的示例💻

简介:什么是fork系统调用?

Fork 是 Unix 和 Linux 操作系统中最重要的概念之一。简而言之,fork 只不过是克隆一个进程。这意味着 fork 将创建一个具有调用进程的精确副本的新进程。因此,当程序遇到 fork() 系统调用时,它将创建另一个具有相同内存副本的进程。所以这里出现了父子进程的概念。

调用fork并创建新进程的主进程或第一个进程称为父进程。 fork 创建的新进程称为子进程

如何识别父子进程?

由于子进程和父进程都有完全相同的内存副本,那么问题来了,我们如何识别它们中的哪一个是父进程,哪一个是子进程。正如我上面提到的,每个进程都有唯一的 ID,称为进程 ID 或 PID,可用于区分进程。

为了识别父子进程,我们需要检查fork系统调用的返回码。

fork()的返回码

fork 系统调用的返回码决定了父进程还是子进程。当父进程调用fork时,fork将刚刚创建的子进程的PID返回给父进程,返回0给子进程。所以基本上如果来自 fork 调用的返回码为零,那么它的子进程,如果它是一个正值,那么它必须是父进程。

  • ZERO 如果返回码为0,那么一定是子进程

  • A positive value ,如果返回码是正值(或者子进程的PID),那么它的父进程

  • Negative , 如果返回码为负数,则子进程创建失败且不成功

fork.png

如何使用Python fork?

Python 的 os 模块提供了一个函数 fork() 来创建子进程。要知道任何进程的 PID,请使用 os 模块中的函数 getpid()

import os
os.fork()

现在让我们做一些演示来了解发生了什么

DEMO 1:检查任何进程的进程ID

在下面的示例中,我们只是检查如何使用 getpid() 来获取当前进程的 PID。

import os

def demo1():
    print('I am the only process, My PID:',os.getpid())
demo1()

这是输出:

演示1.png

DEMO 2:使用 fork() 创建一个子进程

在下面的示例中,我们在 fork() 调用之前和之后打印进程 ID。这意味着在 fork 之前,我们有一个进程,而在调用之后,我们有另一个新进程,总共有 2 个进程。

让我们检查以下代码段的输出

import os

def demo2():
    print('Before calling fork(),PID: ', os.getpid())
    os.fork()
    print('After calling fork(), PID: ', os.getpid())

demo2()

这里是输出: -

shaikh@ubuntu:~/Jupyter/fork demos$ python3 demo2.py
Before calling fork(),PID:  6837
After calling fork(), PID:  6837
After calling fork(), PID:  6838
shaikh@ubuntu:~/Jupyter/fork demos$

如上所示,在 fork() 之前,我们只有一个 PID 为 6837 的进程,而在 fork 之后,我们有一个 PID 为 6838 的新进程。

Demo 3:识别父母和孩子

让我们看看,我们如何以编程方式识别父母和孩子。如上一节所述,如果 fork 的返回码为零,则为子进程,如果为正值,则为父进程。让我们在这里检查一下

import os

def demo3():
    print('Before calling fork(),PID: ', os.getpid())
    rc = os.fork()
    if rc == 0:
        print('I am child, PID: ', os.getpid())
        os._exit(0)
    elif rc > 0:
        print('I am parent,PID:',os.getpid())
    else:
        print('Child process creation failed!!')

demo3()

输出

shaikh@ubuntu:~/Jupyter/fork demos$ python3 demo3.py
Before calling fork(),PID:  7316
I am parent,PID: 7316
I am child, PID:  7317

让我们了解上面发生了什么。在 fork 之前,我们只有一个 PID 为 7316 的进程,当它调用 fork() 时,我们得到了另一个进程。这些进程中的每一个都有不同的返回代码副本rc。父进程的 rc 为正值(子进程的 PID),子进程的 rc 等于

Demo 4:让我们创建两个子进程

在下面的示例中,我们调用了 fork() 两次。

import os

def demo4():
    #No fork, only one process
    print('Before any fork,  PID:',os.getpid())
    #First fork
    os.fork()
    print('After first fork, PID:',os.getpid())
    #Second fork
    os.fork()
    print('After second fork,PID:',os.getpid())

demo4()

输出如下:

shaikh@ubuntu:~/Jupyter/fork demos$ python3 demo4.py
Before any fork,  PID: 7471
After first fork, PID: 7471
After first fork, PID: 7472
After second fork,PID: 7471
After second fork,PID: 7473
After second fork,PID: 7472
After second fork,PID: 7474
  • 在第一次分叉之前只有一个进程

  • 第一次分叉后,总进程为两个

  • 第二次调用后,进程总数为四个

如果我们调用 fork 3 次,在评论部分猜猜将创建的进程数🙂

Demo 5: 让我们玩得开心

下面的示例将显示在 fork 调用之后,父子节点将拥有变量 num 的不同副本

import os

def demo5():
    num = 0
    rc = os.fork()
    if rc > 0:
        num = os.getpid()
    print ('num: ',num)    
demo5()

猜猜输出🙂

shaikh@ubuntu:~/Jupyter/fork demos$ python3 demo5.py
num:  7825
num:  0

所以在上面的代码中,只有父进程可以进入 if 语句,因为它有肯定响应代码,即子进程的 PID。由于孩子的 rcZERO,它仍然会有 num 的原始副本

结论

希望这是有趣而有趣的学习🙂。 fork 是在任何 Linux 操作系统中创建子进程的非常常见的方法。它被用于创建多个进程,最常见的用例是 Web 服务器,它在每个 http 请求上分叉一个新进程。

使用 fork 时要小心,并确保在完成任务后成功退出进程,否则内存和 cpu 使用率会很高,并可能造成内存泄漏情况,称为 fork 炸弹

所有的演示都在我的 GitHub 页面中。点击这里访问

参考文献

1.http://www2.cs.uregina.ca/~hamilton/courses/330/notes/unix/fork/fork.html

1.http://people.cs.pitt.edu/~aus/cs449/ts-lecture14.pdf

类似帖子

  • 如何在 Linux 中管理进程所有关于进程

  • 如何在 Linux 中监控 CPU 利用率cpu 利用率

  • 如何使用 crontab 安排作业如何使用 crontab

Logo

更多推荐