Неуспешно получаване на отделни екземпляри на клас под mod_python

Опитвам се да стартирам някакъв код на Python под Apache 2.2 / mod_python 3.2.8. В крайна сметка кодът прави os.fork() и ражда 2 отделни дългосрочни процеса. Всеки от тези процеси трябва да създаде отделен екземпляр на клас, за да се избегне евентуален сблъсък в паралелния поток.

class Foo(object):
   pass

kidprocs = []

for kid in ('kid1', 'kid2'):

  pid = os.fork()
  if pid:
    # parent
    kidprocs.append(pid)
    time.sleep(5)
  else:
    # child

    fooobj = Foo() 
    print "Starting %s in sub-process %s" % (kid, os.getpid())
    print "Kid fooobj: %s" % repr(fooobj) 
    os._exit(0)

for kidproc in kidprocs:
  os.waitpid(kidproc, 0)

Тези резултати за печат изглеждат така:

Starting kid1 in sub-process 20906
    foo obj: <__main__.Foo instance at 0xb7da5fec>

Starting kid2 in sub-process 20909
    foo obj: <__main__.Foo instance at 0xb7da5fec>

Както можете да видите, получих един и същ обект и за двата подпроцеса. Имате ли идея защо става така под mod_python и има ли начин все пак да получите отделни екземпляри? Благодаря много.


person victorz    schedule 15.10.2008    source източник


Отговори (1)


Мястото в паметта, дадено от функцията repr(), е адрес във виртуалната памет, а не адрес в глобалната памет на системата. Всеки от вашите процеси, върнат от fork(), има собствено виртуално пространство в паметта, което е напълно различно от другите процеси. Те не споделят памет.

Редактиране: Според коментарите на Brian по-долу, технически те споделят памет, докато ядрото не реши да ги раздели (когато дете пише в част от споделената памет). Поведението обаче на практика е същото.

Структурата на вашите програми е една и съща, така че Python използва едно и също място във виртуалната памет в отделното хранилище на виртуална памет на всеки процес за всеки от вашите идентични обекти за всяко дете.

Ако действително промените съдържанието на обектите и ги тествате, ще видите, че въпреки че местоположението на паметта изглежда същото, двата са напълно различни обекти, защото принадлежат на два различни процеса. В действителност не можете да модифицирате едно от друго (без някакъв вид междупроцесна комуникация за посредничество).

person Adam Bellaire    schedule 15.10.2008
comment
Малка придирчивост: Те всъщност споделят памет, тъй като fork() се реализира само чрез маркиране на общи страници Copy On Write, вместо да ги копира веднага. Отделна памет ще се използва само след промяна на данните. Това обаче е напълно прозрачно, така че ефективното поведение е такова, каквото описвате. - person Brian; 15.10.2008
comment
@Brian: Хм, интересно. Въпреки това, в моите тестове, след промяна на стойността местоположението на паметта се променя, но се променя на същата стойност. Това означава, че repr() все още показва същия адрес като другото дете дори след като всяко дете задава различна стойност. - person Adam Bellaire; 15.10.2008
comment
Все още ще бъде същият виртуален адрес - всичко е просто оптимизация зад кулисите. Ядрото маркира страницата като само за четене и когато записът причини грешка, тя копира физическата памет, пренасочва виртуалния адрес към това ново физическо местоположение и след това продължава с приложението, без да е по-разумно. - person Brian; 15.10.2008
comment
Единствената разлика ще бъде използването на несподелена памет от приложението, което няма да се увеличи до първата модификация, и леко забавяне при първото записване (което иначе би се случило, когато се извърши fork()). Изгодата е, че ядрото никога не трябва да копира страници, които не са докоснати. - person Brian; 15.10.2008
comment
Супер, благодаря за изчерпателното обяснение. Това е една от причините да харесвам StackOverflow. :) - person Adam Bellaire; 15.10.2008