Python:How os.fork() 有效吗?
Python:How os.fork() works?
我正在 python 学习多处理。我尝试了 multiprocessing,在阅读 multiprocessing 模块的源代码后,我发现它使用 os.fork()
,所以我写了一些代码来测试 os.fork()
,但我卡住了。我的代码如下:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import os
import time
for i in range(2):
print '**********%d***********' % i
pid = os.fork()
print "Pid %d" % pid
我认为每个打印都会执行两次,但它们执行了三次。我不明白这是怎么回事?
我读了这个 Need to know how fork works?
从这篇文章说它也会被执行两次,所以我很卡...
为了直接回答问题,os.fork()
通过调用底层 OS 函数 fork()
。
但你肯定对它的作用感兴趣。那么这将创建另一个进程,它将在与这个进程完全相同的位置恢复。所以在第一个循环 运行 中,你得到一个 fork,之后你有两个进程,"original one"(它获得 child 进程的 PID 的 pid
值)和分叉的(它的 pid
值为 0
)。
他们都打印他们的 pid
值并继续第二个循环 运行,他们都打印。然后他们都分叉,给你留下 4 个进程,它们都打印各自的 pid
值。其中两个应该是0
,另外两个应该是他们刚刚创建的child的PID。
将代码更改为
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import os
import time
for i in range(2):
print '**********%d***********' % i
pid = os.fork()
if pid == 0:
# We are in the child process.
print "%d (child) just was created by %d." % (os.getpid(), os.getppid())
else:
# We are in the parent process.
print "%d (parent) just created %d." % (os.getpid(), pid)
你会更清楚地看到发生了什么:每个进程都会告诉你它自己的 PID 以及分叉上发生的事情。
首先,删除 print '******...'
行。这只会让每个人感到困惑。相反,让我们试试这个代码...
import os
import time
for i in range(2):
print("I'm about to be a dad!")
time.sleep(5)
pid = os.fork()
if pid == 0:
print("I'm {}, a newborn that knows to write to the terminal!".format(os.getpid()))
else:
print("I'm the dad of {}, and he knows to use the terminal!".format(pid))
os.waitpid(pid, 0)
好吧,首先,什么是"fork"? Fork 是现代和 standard-compliant 操作系统的一个特征(M$ 除外 Windows:OS 的笑话几乎是现代的 standard-compliant) 允许进程 (a.k.a: "program", 并且包括 Python 解释器!) 完全复制自己,有效地创建一个新进程 (另一个"program" 的实例)。一旦魔术完成,两个进程都是独立的。更改其中一个中的任何内容都不会影响另一个。
负责拼写这个黑暗而古老的咒语的过程被称为parent过程。这种对生命本身的不道德憎恶的无情结果被称为 child 过程。
众所周知,包括那些并非如此的人,您可以成为 select 一群通过 os.fork()
出卖灵魂的程序员的一员。此函数执行一个 fork 操作,从而导致第二个进程凭空创建。
现在,这个功能有什么作用 return,或者更重要的是, 它甚至 return 是如何工作的?如果你不想变得疯狂,请不要去阅读Linux内核的/kernel/fork.c
文件!一旦内核做了我们知道它必须做的事情,但我们不想接受它,os.fork()
returns 在 两个 进程中!是的,连调用栈都复制上去了!
那么,如果它们是完全相同的副本,那么如何区分 parent 和 child?简单的。如果 os.fork()
的结果为零,那么您正在 child 中工作。否则,您正在 parent 中工作,并且 return 值是 child 的 PID(进程标识符)。无论如何,child 可以从 os.getpid()
获得自己的 PID,不是吗?
现在,考虑到这一点,并且在循环中执行 fork()
是导致混乱的原因,这就是发生的情况。我们称原始过程为 "master" 过程...
- 大师:
i = 0
,叉入child-#1-of-master
- Child-#1-of-master:
i = 1
分支到 child-#1-of-child-#1-of-master
- Child-#1-of-child-#1-of-master:
for
循环结束,退出
- Child-#1-of-master:
for
循环结束,退出
- 大师:
i = 1
,叉入 child-#2-of-master
- Child-#2-of-master:
i = 1
分支到 child-#1-of-child-#2-of-master
- Child-#1-of-child-#2-of-master:
for
循环结束,退出
- Child-#2-of-master:
for
循环结束,退出
- 大师:
for
循环结束,退出
如您所见,共有 6 parent/child 个打印来自 4 个独特的进程,导致 6 行输出,类似于...
I'm the dad of 12120, and he knows to use the terminal!
I'm 12120, a newborn that knows to write to the terminal!
I'm the dad of 12121, and he knows to use the terminal!
I'm 12121, a newborn that knows to write to the terminal!
I'm the dad of 12122, and he knows to use the terminal!
I'm 12122, a newborn that knows to write to the terminal!
但这只是任意的,它本可以输出这个...
I'm 12120, a newborn that knows to write to the terminal!
I'm the dad of 12120, and he knows to use the terminal!
I'm 12121, a newborn that knows to write to the terminal!
I'm the dad of 12121, and he knows to use the terminal!
I'm 12122, a newborn that knows to write to the terminal!
I'm the dad of 12122, and he knows to use the terminal!
或者除此之外的任何东西。 OS(和你的主板的时髦时钟)全权负责进程获取时间片的顺序,所以如果你不喜欢内核管理你的进程的方式,请转到 blame on Torvalds (and expect no self-steem when back);)。
我希望这对您有所启发!
因为这两个答案几乎涵盖了有关 os.fork() 工作原理的所有内容。我想添加一些关于此案的更具体的信息。
Fork系统调用用于创建一个新进程,称为子进程,它与进行fork()调用的进程(父进程)同时运行。创建新的子进程后,两个进程将执行(执行状态;堆、堆栈和处理器寄存器)fork() 系统调用之后的下一条指令(创建新进程的系统调用与
调用者复制文本、数据、堆栈和堆)。 fork() 不带参数和 returns 一个整数值。成功调用 fork() 后,子进程基本上是父进程的完全复制。 fork 是 Unix 系统调用,用于创建新进程。向内核请求服务的系统调用方式。 fork returns 一个值,如果该值大于0,则表示fork成功,但你也可能低于0,这可能是该进程内存不足或其他错误,无法创建,它会引发错误。如果它等于 0 则它是一个大于 0 的子进程,这意味着父进程。所以进程是并发运行的,我们这里存档是并行的,但是fork调用之后的一切都会被执行。
调用 fork 复制引用父进程的进程。 fork 表示地址基址的两个不同的相同副本,一个用于子进程,一个用于父进程。除了 PID 和其他一些文章外,这两个散文都是彼此的精确副本。由于两个进程具有相同但独立的地址 spaces,因此在 fork() 调用之前初始化的那些变量在两个地址 spaces 中具有相同的值。由于每个进程都有自己的地址 space,任何修改都将独立于其他进程。也就是说,如果父进程改变了它变量的值,修改只会影响父进程地址space中的变量。由 fork() 调用创建的其他地址 space 不会受到影响,即使它们具有相同的变量名。
在 fork() 之后,父级拥有的所有资源都被复制,并将副本提供给子级,在 Linux 中,fork() 是通过使用 Copy-on-Write 页面实现的。 Copy on Write 或简称 CoW 是一种资源管理技术。更多关于 CoW
你可以使用 fork,以防你想创建具有多个进程的并行程序。
我正在 python 学习多处理。我尝试了 multiprocessing,在阅读 multiprocessing 模块的源代码后,我发现它使用 os.fork()
,所以我写了一些代码来测试 os.fork()
,但我卡住了。我的代码如下:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import os
import time
for i in range(2):
print '**********%d***********' % i
pid = os.fork()
print "Pid %d" % pid
我认为每个打印都会执行两次,但它们执行了三次。我不明白这是怎么回事?
我读了这个 Need to know how fork works?
从这篇文章说它也会被执行两次,所以我很卡...
为了直接回答问题,os.fork()
通过调用底层 OS 函数 fork()
。
但你肯定对它的作用感兴趣。那么这将创建另一个进程,它将在与这个进程完全相同的位置恢复。所以在第一个循环 运行 中,你得到一个 fork,之后你有两个进程,"original one"(它获得 child 进程的 PID 的 pid
值)和分叉的(它的 pid
值为 0
)。
他们都打印他们的 pid
值并继续第二个循环 运行,他们都打印。然后他们都分叉,给你留下 4 个进程,它们都打印各自的 pid
值。其中两个应该是0
,另外两个应该是他们刚刚创建的child的PID。
将代码更改为
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import os
import time
for i in range(2):
print '**********%d***********' % i
pid = os.fork()
if pid == 0:
# We are in the child process.
print "%d (child) just was created by %d." % (os.getpid(), os.getppid())
else:
# We are in the parent process.
print "%d (parent) just created %d." % (os.getpid(), pid)
你会更清楚地看到发生了什么:每个进程都会告诉你它自己的 PID 以及分叉上发生的事情。
首先,删除 print '******...'
行。这只会让每个人感到困惑。相反,让我们试试这个代码...
import os
import time
for i in range(2):
print("I'm about to be a dad!")
time.sleep(5)
pid = os.fork()
if pid == 0:
print("I'm {}, a newborn that knows to write to the terminal!".format(os.getpid()))
else:
print("I'm the dad of {}, and he knows to use the terminal!".format(pid))
os.waitpid(pid, 0)
好吧,首先,什么是"fork"? Fork 是现代和 standard-compliant 操作系统的一个特征(M$ 除外 Windows:OS 的笑话几乎是现代的 standard-compliant) 允许进程 (a.k.a: "program", 并且包括 Python 解释器!) 完全复制自己,有效地创建一个新进程 (另一个"program" 的实例)。一旦魔术完成,两个进程都是独立的。更改其中一个中的任何内容都不会影响另一个。
负责拼写这个黑暗而古老的咒语的过程被称为parent过程。这种对生命本身的不道德憎恶的无情结果被称为 child 过程。
众所周知,包括那些并非如此的人,您可以成为 select 一群通过 os.fork()
出卖灵魂的程序员的一员。此函数执行一个 fork 操作,从而导致第二个进程凭空创建。
现在,这个功能有什么作用 return,或者更重要的是, 它甚至 return 是如何工作的?如果你不想变得疯狂,请不要去阅读Linux内核的/kernel/fork.c
文件!一旦内核做了我们知道它必须做的事情,但我们不想接受它,os.fork()
returns 在 两个 进程中!是的,连调用栈都复制上去了!
那么,如果它们是完全相同的副本,那么如何区分 parent 和 child?简单的。如果 os.fork()
的结果为零,那么您正在 child 中工作。否则,您正在 parent 中工作,并且 return 值是 child 的 PID(进程标识符)。无论如何,child 可以从 os.getpid()
获得自己的 PID,不是吗?
现在,考虑到这一点,并且在循环中执行 fork()
是导致混乱的原因,这就是发生的情况。我们称原始过程为 "master" 过程...
- 大师:
i = 0
,叉入child-#1-of-master- Child-#1-of-master:
i = 1
分支到 child-#1-of-child-#1-of-master - Child-#1-of-child-#1-of-master:
for
循环结束,退出 - Child-#1-of-master:
for
循环结束,退出
- Child-#1-of-master:
- 大师:
i = 1
,叉入 child-#2-of-master- Child-#2-of-master:
i = 1
分支到 child-#1-of-child-#2-of-master - Child-#1-of-child-#2-of-master:
for
循环结束,退出 - Child-#2-of-master:
for
循环结束,退出
- Child-#2-of-master:
- 大师:
for
循环结束,退出
如您所见,共有 6 parent/child 个打印来自 4 个独特的进程,导致 6 行输出,类似于...
I'm the dad of 12120, and he knows to use the terminal!
I'm 12120, a newborn that knows to write to the terminal!
I'm the dad of 12121, and he knows to use the terminal!
I'm 12121, a newborn that knows to write to the terminal!
I'm the dad of 12122, and he knows to use the terminal!
I'm 12122, a newborn that knows to write to the terminal!
但这只是任意的,它本可以输出这个...
I'm 12120, a newborn that knows to write to the terminal!
I'm the dad of 12120, and he knows to use the terminal!
I'm 12121, a newborn that knows to write to the terminal!
I'm the dad of 12121, and he knows to use the terminal!
I'm 12122, a newborn that knows to write to the terminal!
I'm the dad of 12122, and he knows to use the terminal!
或者除此之外的任何东西。 OS(和你的主板的时髦时钟)全权负责进程获取时间片的顺序,所以如果你不喜欢内核管理你的进程的方式,请转到 blame on Torvalds (and expect no self-steem when back);)。
我希望这对您有所启发!
因为这两个答案几乎涵盖了有关 os.fork() 工作原理的所有内容。我想添加一些关于此案的更具体的信息。
Fork系统调用用于创建一个新进程,称为子进程,它与进行fork()调用的进程(父进程)同时运行。创建新的子进程后,两个进程将执行(执行状态;堆、堆栈和处理器寄存器)fork() 系统调用之后的下一条指令(创建新进程的系统调用与 调用者复制文本、数据、堆栈和堆)。 fork() 不带参数和 returns 一个整数值。成功调用 fork() 后,子进程基本上是父进程的完全复制。 fork 是 Unix 系统调用,用于创建新进程。向内核请求服务的系统调用方式。 fork returns 一个值,如果该值大于0,则表示fork成功,但你也可能低于0,这可能是该进程内存不足或其他错误,无法创建,它会引发错误。如果它等于 0 则它是一个大于 0 的子进程,这意味着父进程。所以进程是并发运行的,我们这里存档是并行的,但是fork调用之后的一切都会被执行。
调用 fork 复制引用父进程的进程。 fork 表示地址基址的两个不同的相同副本,一个用于子进程,一个用于父进程。除了 PID 和其他一些文章外,这两个散文都是彼此的精确副本。由于两个进程具有相同但独立的地址 spaces,因此在 fork() 调用之前初始化的那些变量在两个地址 spaces 中具有相同的值。由于每个进程都有自己的地址 space,任何修改都将独立于其他进程。也就是说,如果父进程改变了它变量的值,修改只会影响父进程地址space中的变量。由 fork() 调用创建的其他地址 space 不会受到影响,即使它们具有相同的变量名。
在 fork() 之后,父级拥有的所有资源都被复制,并将副本提供给子级,在 Linux 中,fork() 是通过使用 Copy-on-Write 页面实现的。 Copy on Write 或简称 CoW 是一种资源管理技术。更多关于 CoW
你可以使用 fork,以防你想创建具有多个进程的并行程序。