Размер памяти занимаемый форком

Столкнулся со странным поведением при форке через pcntl_fork

Возьмем простой скрипт вечно живущего демона

pcntl_fork();
while (true) {
    sleep(1);
}

И сравним потребление памяти у родителя и ребенка memory-usage

Видно, что дочерний процесс занимет примерно на треть меньше операвтивной памяти. Если копнуть немного глубже и сравнить вывод pmap -x 1172 1173 для обоих процессов увидим, такую картину memory-allocated-diff

Если я правильно понимаю, то при старте php аллоцирует памяти с запасом на себя, а так же на все свои модули и этот запас довольно ощутимый, особенно если у вас на одном инстансе запускается множество демонов.

Получается таким хитрым способом можно урезать потребление памяти вашим демоном

if (pcntl_fork() > 0) {
    die();
}
while (true) {
    sleep(1);
}

В чем подвох, только в том, что придется тратить время на аллокацию памяти при необходимости?


Ответы (1 шт):

Автор решения: Sergei Kirjanov

(обобщая комментарии выше)

При форке процесса используется copy-on-write страниц памяти между родителем и ребенком. Если php инициализируется 1 раз, а потом форкается, все форки разделяют (совместно используют) с родителем эту память. На этом можно экономить память и время на инициализацию (если она тюжелая).

Но тут надо учесть, что как только процесс-ребенок начнет активно работать с памятью, страницы памяти снова будут становиться раздельными. В какой мере память станет раздельной -- предсказать невозможно, только написать реальный код, запустить и сравнить.

/* О средсвах, позволяющих подробно узнать, какие страницы физической памяти используется совместно какими процессами из-за copy-on-write, мне не известно. */


Нашел достаточно простой способ:

pmap -X содержит столбик Pss -- "proportional set size" -- Объем страниц в памяти поделенный на количество процессов разделячщих страницы. Так если у процесса 1000 страниц, которые пользует лично он, и еще 1000 страниц, общих с другим процессом, то Pss будет 1500. https://www.kernel.org/doc/Documentation/filesystems/proc.txt

Запускаем скрипт, который инициализирует на куче большой массив, делает форк, и постепенно меняет случайные значения в массиве:

perl -e '$max=1000000; @c=1..$max; $pid=fork; @c[map{rand()*$max}1..100]=() while sleep 1'

Находим пару процессов скрипта и наблюдаем pmap -X:

perl -e 'while(sleep 1){$pids=join" ",map{/([0-9]+)/ && $1!=$$?"$1":()}`ps -ef|grep perl|grep sleep`; print `pmap -X $pids`}'

Видно, что Pss на строке heap стартует с половины Rss и постепенно увеличивается с убывающей скоростью.

Еще пишут, что /proc/<pid>/pagemap содержит соответствие между виртуальной и физической памятью (в бинарном виде), и имея CAP_SYS_ADMIN, можно его читать: https://www.kernel.org/doc/Documentation/vm/pagemap.txt Но с этим я эксперементировать уже не стал.

→ Ссылка