#!/usr/bin/env python
# -*- coding:utf-8 -*-
import os
import time
a = 1
b = 2
pid = os.fork()
if pid < 0:
print "fork error"
elif pid == 0:
a = a+1
print ("it's fork process,a:" + str(a) + ' a address:' + str(id(a)) + " b address:" + str(id(b)))
time.sleep(1)
else:
a = a+2
print ("it's parent process,a:" + str(a) + " a address" + str(id(a)) + " b address:" + str(id(b)))
time.sleep(1)
if pid > 0:
b += 1
time.sleep(1)
print ("it's parent process,a:" + str(a) + " a address" + str(id(a)) + " b address:" + str(id(b)))
else:
print ("it's fork process,a:" + str(a) + ' a address:' + str(id(a)) + " b address:" + str(id(b)))
time.sleep(2)
result:
it's parent process,a:3 a address28298296 b address:28298320
it's fork process,a:2 a address:28298320 b address:28298320
it's parent process,a:3 a address28298296 b address:28298296
it's fork process,a:2 a address:28298320 b address:28298320
分析:fork的时候完全复制父进程空间 变量的虚拟地址及页表都相同 所以b的值及物理地址相同,而a在进程中修改了,内核会复制新的存储空间来存储,所以物理地址及值都不一样
Unix环境高级编程中,fork并不执行一个父进程数据段、栈和堆的完全副本(代码段共享,父子进程代码段映射的是同一块物理内存),作为替代,使用了写时复制技术,父子进程共享的区域内核将它们的访问权限改为只读。如果父或者子进程需要修改某些区域,会复制这部分内存制作一个副本。
fork()创建子进程时继承了父进程的数据段、代码段、栈段、堆,注意从父进程继承来的是虚拟地址空间,同时也复制了页表(没有复制物理块)。因此,此时父子进程拥有相同的虚拟地址,映射的物理内存也是一致的(独立的虚拟地址空间,共享父进程的物理内存)。由于父进程和子进程共享物理页面,内核将其标记为“只读”(类似mmap)的private的方式),父子双方均无法对其修改。无论父进程和子进程何时试图对一个共享的页面执行写操作,就产生一个错误,这时内核就把这个页复制到一个新的页面给这个进程,并标记为可写,同时修改页表,把原来的只读页面标记为“可写”,留给另外一个进程使用——写时复制技术。
哪个进程的数据先修改了 哪个进程的变量的物理地址就会被重置
每个进程可以有不同的映射。两次运行同一个程序就是使用的相同的虚拟地址,但是映射到的物理地却是不一样的。每个进程都有自己的虚拟地址空间,不同进程的相同的虚拟地址显然可以对应不同的物理地址。因此地址相同(虚拟地址)而值不同没什么奇怪。
关于文件描述符:继承父进程的文件描述符时,相当于调用了dup函数,父子进程共享文件表项,即共同操作同一个文件,一个进程修改了文件,另一个进程也知道此文件被修改了