Не удалось получить отдельные экземпляры класса под 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(), имеет свое собственное пространство виртуальной памяти, которое полностью отличается от других процессов. Они не разделяют память.

Редактировать: Согласно комментариям Брайана ниже, технически они совместно используют память до тех пор, пока ядро ​​не решит их разделить (когда дочерний элемент записывает часть общей памяти). Поведение, однако, фактически такое же.

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

Если вы на самом деле измените содержимое объектов и протестируете их, вы увидите, что хотя расположение в памяти выглядит одинаково, это два совершенно разных объекта, потому что они принадлежат двум разным процессам. На самом деле вы не можете изменить одно из другого (без какого-либо межпроцессного взаимодействия).

person Adam Bellaire    schedule 15.10.2008
comment
Крошечная загвоздка: они на самом деле разделяют память, так как fork() реализуется просто путем пометки общих страниц «Копировать при записи», а не путем их немедленного копирования. Отдельная память будет использоваться только после изменения данных. Однако это полностью прозрачно, поэтому эффективное поведение такое, как вы описываете. - person Brian; 15.10.2008
comment
@ Брайан: Хм, интересно. Однако в моих тестах после изменения значения место в памяти действительно меняется, но оно меняется на то же значение. То есть 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