Это безопасный способ разделить доступную только для чтения память с дочерними процессами? - PullRequest
7 голосов
/ 09 февраля 2010

Я хочу выделить и инициализировать довольно большой кусок непрерывной памяти (~ 1 ГБ), затем пометить его как доступный только для чтения и обработать несколько (скажем, несколько десятков) дочерних процессов, которые будут его использовать, не создавая своих собственных копий память (машине не хватит памяти для этого).

Правильно ли я считаю, что если я malloc памяти, как обычно, пометить ее как доступную только для чтения с помощью mprotect(addr, size, PROT_READ), а затем fork , это позволит дочерним процессам безопасно использовать память, не вызывая ее копирование? (При условии, что я ничего не пытаюсь записать в выделенную память после вызова mprotect).

edit: Спасибо за все ответы.

Следующий вопрос - я планировал использовать shmget, но я подумал, что он использовал mm и, таким образом, будет ограничен меньшими выделениями (см. Ограничения раздел этой страницы ). Например, /proc/sys/kernel/shmmax - 32 МБ на сервере, которым я пользуюсь. Но я хочу 1 ГБ непрерывной памяти. Я не прав насчет этого ограничения?

Ответы [ 6 ]

7 голосов
/ 09 февраля 2010

man mprotect

Реализация потребует, чтобы addr был кратным размеру страницы, который возвращается sysconf () .

Поведение этой функции не определено, если сопоставление не было установлено при вызове mmap () .

  1. mprotect работает только на страницах, а не на произвольных диапазонах байтов, поэтому в целом malloc не подходит. posix_memalign может помочь, но ...
  2. Хотя это может работать в вашей системе в настоящее время, вы должны не mprotect ничего, что вы не mmap сами. Вместо этого используйте mmap(0, pages*sysconf(_SC_PAGESIZE), PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0).
3 голосов
/ 09 февраля 2010

Вы не правы по той причине, что любой из дочерних процессов может вызвать mprotect(), чтобы снять защиту и начать писать там. Если страницы не были скопированы, это нарушило бы принципы fork().

Даже если это работает таким образом, что копирование при записи используется для разветвленных процессов, я не считаю, что в стандартах говорится, что это так (POSIX не говорит, что это копирование на напиши, например).

Вместо использования нестандартного поведения вы можете использовать стандартные меры для совместного использования памяти. Например, POSIX совместно использовал память с shm_open и, следовательно, mmap (как было указано в комментарии и объяснено в его посте ephemient ). Дескриптор файла будет сохранен при разветвлении.

2 голосов
/ 10 февраля 2010

Нет необходимости отмечать его только для чтения, просто попросите дочерние процессы оставить его в покое.

Если ни родитель, ни ребенок не пишет в него, он должен оставаться общим.Если вы не хотите его менять, это нормально.

Если вы хотите написать в него, вы захотите использовать mmap с MAP_SHARED.

1 голос
/ 09 февраля 2010

Вы предполагаете, что ядро ​​выполнит оптимизацию копирование при записи и не будет копировать mprotect страницы. Я бы на это не рассчитывал. malloc -еданная память содержит всевозможные метаданные - защитные страницы и т. Д. И т. Д., И только Ульрих Дреппер знает, что происходит внутри libc:)

Вероятно, было бы проще и безопаснее подготовить данные в файле на диске и передать их mmap во все процессы или просто пойти по обычному маршруту POSIX shm_open.

0 голосов
/ 09 февраля 2010

Вы могли бы сделать это таким образом.

Альтернативой является использование mmap () .

Другой альтернативой является использование разделяемой памяти POSIX ( shm_open () ); другой основной альтернативой является разделяемая память System V ( shmget () , shmat () ). Одно из преимуществ формальных систем разделяемой памяти состоит в том, что ваш родительский процесс может создать память, а затем к ней может подключиться несвязанный процесс - если это было выгодно.

0 голосов
/ 09 февраля 2010

Насколько я понимаю, да, поскольку Linux использует механизм копирования при записи для страниц памяти, передаваемых дочернему процессу.

...