LINUX.ORG.RU

Архитектура демона


0

0

Привет!

Есть проблема с выбором архитектуры демона.

Вкраце опишу задачу:
Демон при старте считывает из БД Oracle информацию и строит
по ней дерево поиска (цифровое деверо).
Критичным ресурсом является именно время поиска информации, а
в случае дерева всё что нужно - просто пройти по нему в глубину.
После построения дерева в памяти демон переходит в бесконечный цикл
обработки запросов клиентов (т.е. блокируется в accept, принимает
соединение, создает дочерний процесс на обработку запроса и снова
блокируется в accept).
Т.о. имеем тривиальную process per connection архитектуру.

Теперь опишу проблему:
Дерево в памяти занимает около 100Mb, т.е. очень много.
Известно, что при создании процесса через fork используется
оптимизация copy-on-write.
Казалось бы, при read-only доступе к дереву в дочерних процессах 
никакого копирования быть не должно и все дочерние процессы должны
разделять дерево.
Однако, во-первых, COW опцимизация в Linux постраничная (per page).
Во-вторых, демон написан на Perl'е, который может творить "за кулисами"
всё что угодно.
И последнее, я не уверен на 100%, но скорее всего это так - при
своппинге разделяемых страниц памяти на диск они перестают разделяться
между процессами и система уходит в глубочайший своп (это то, что
я вижу после какого-то времени работы демона при большом количестве 
подключений, но не таком большом чтобы была fork-бомба).

Соответсвенно, при такой простой архитектуре и большом объеме дерева
демон не работает корректно.

Сразу на ум приходят 2 решения:
1) Потоки вместо процессов (явное разделение адресного пространства).
К сожалению, использование потоков невозможно (по ряду причин, которые
от меня не зависят).
2) Разделяемая память.
Я пробовал модуль Shareable, но он валится при заполнении дерева.

Может кто предложить грамотный способ решения задачи?
Может есть другие подходы?

Спасибо!

мультиплексировать? в смысле, не форкать ничего, а работать на подобии select

swizard
()

>1) Потоки вместо процессов (явное разделение адресного пространства).

>К сожалению, использование потоков невозможно (по ряду причин, которые от меня не зависят).

Означает ли это, что нужен имено форкающийся демон на perl? Так можно, конечно, нагородить огород, что один демон принимает соединения и форкается, а другой, который хранит дерево, взаимодействует с первым через сокет, занимается только поиском и не форкается...

mky ★★★★★
()
Ответ на: комментарий от mky

>взаимодействует с первым через сокет, занимается только поиском и не форкается...
Но поиск тогда принципиально последователен.

Krivenok_Dmitry
() автор топика

IMHO полагаться на COW не корректно. Если данные хранятся в одном месте - добивай Shared memory. Честно говоря не представляю, какие косяки могут вылазить в перле при использовании Shared memory, поэтому возможный совет - перейти на более контроллируемый язык - С\C++\D . Кстати в перле может быть достаточно большой оверхед по памяти и возможно, что в С память может расходоваться в разы меньше.

Если указанные решения неприемлемы - как вариант - ограничить кол-во клиентов соответственно оперативной памяти, и лишних ставить в очередь.

anonymous
()
Ответ на: комментарий от anonymous

> IMHO полагаться на COW не корректно. Если данные хранятся в одном месте - добивай Shared memory. Честно говоря не представляю, какие косяки могут вылазить в перле при использовании Shared memory, поэтому возможный совет - перейти на более контроллируемый язык - С\C++\D . Кстати в перле может быть достаточно большой оверхед по памяти и возможно, что в С память может расходоваться в разы меньше.

ну если есть возможность перейти на C/C++ то и проблемы с COW/fork()/shm и пр. элементарно решаются использованием потоков :)

// wbr

klalafuda ★☆☆
()
Ответ на: комментарий от krum

> Хм, если есть Oracle, почему бы в нём не организовать поиск? Это будет наиболее правильным решением.

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

// wbr

klalafuda ★☆☆
()
Ответ на: комментарий от krum

> Хм, если есть Oracle, почему бы в нём не организовать поиск? Это будет наиболее правильным решением.

у нас очень похожая задача. плохо себе представляю как оракл будет выполнять, например, поиск ближайшего элемента удовлетворяющего определенным условиям. Он умеет строить k-d деревья?

dilmah ★★★★★
()

http://lwn.net/1999/0121/a/vmreview.html

Swapped-out pages are also represented by pte's and also marked for copy-on-write as appropriate.

Чего то не так в датском королевстве. Может все же чего то творишь плохого, в смысле правишь в дереве?

Попробуй ради интереса 100 последовательных коннектов, будет ли рост потребляемой памяти.

roller ★★★
()
Ответ на: комментарий от roller

>Чего то не так в датском королевстве. Может все же чего то творишь плохого, в смысле правишь в дереве?

Я не правлю точно.
А вот Perl может делать всё что угодно (с физической, а не логической
точки зрения конечно) в операциях, которые с виду read-only.

P.S.
Мог бы я потоки использовать ...

Krivenok_Dmitry
() автор топика
Ответ на: комментарий от roller

>Swapped-out pages are also represented by pte's and also marked for copy-on-write as appropriate.

Тогда не понимаю с чем ещё может быть связано то, что система
уходит в даун.

Ясно, что СOW перестает работать.
И ясно, что полагаться на него в общем случае нельзя, но как же
быть в условии тех ограничений, в которых я нахожусь.

Попробую разобраться с shared memory.
Нужно заставить её работать.

Krivenok_Dmitry
() автор топика
Ответ на: комментарий от Krivenok_Dmitry

Вот вывод pmap'а для родительского процесса:

Address   Kbytes Mode  Offset           Device    Mapping
08048000     944 r-x-- 0000000000000000 008:00005 perl5.8.8
08134000      16 rw--- 00000000000eb000 008:00005 perl5.8.8
08138000   91068 rw--- 0000000008138000 000:00000   [ anon ]
b6986000     216 rw--- 00000000b69b6000 000:00000   [ anon ]
b69e5000    2772 rw--- 00000000b69e5000 000:00000   [ anon ]
b6c9a000      56 r-x-- 0000000000000000 008:00005 libresolv-2.5.so
...
...
...
b7fb1000     104 r-x-- 0000000000000000 008:00005 ld-2.5.so
b7fcb000       4 r---- 0000000000019000 008:00005 ld-2.5.so
b7fcc000       4 rw--- 000000000001a000 008:00005 ld-2.5.so
bfdb4000      80 rwx-- 00000000bfdb4000 000:00000   [ stack ]
bfdc8000      12 rw--- 00000000bfdc8000 000:00000   [ anon ]
ffffe000       4 ----- 0000000000000000 000:00000   [ anon ]
mapped: 114688K    writeable/private: 95276K    shared: 28K

а это для дочернего:

Address   Kbytes Mode  Offset           Device    Mapping
08048000     944 r-x-- 0000000000000000 008:00005 perl5.8.8
08134000      16 rw--- 00000000000eb000 008:00005 perl5.8.8
08138000   91068 rw--- 0000000008138000 000:00000   [ anon ]
b6986000     216 rw--- 00000000b69b6000 000:00000   [ anon ]
b69e5000    2772 rw--- 00000000b69e5000 000:00000   [ anon ]
b6c9a000      56 r-x-- 0000000000000000 008:00005 libresolv-2.5.so
...
...
...
b7fb0000       4 rw--- 00000000b7fb0000 000:00000   [ anon ]
b7fb1000     104 r-x-- 0000000000000000 008:00005 ld-2.5.so
b7fcb000       4 r---- 0000000000019000 008:00005 ld-2.5.so
b7fcc000       4 rw--- 000000000001a000 008:00005 ld-2.5.so
bfdb4000      80 rwx-- 00000000bfdb4000 000:00000   [ stack ]
bfdc8000      12 rw--- 00000000bfdc8000 000:00000   [ anon ]
ffffe000       4 ----- 0000000000000000 000:00000   [ anon ]
mapped: 114688K    writeable/private: 95276K    shared: 28K

Очень интересно, что соотношение приватной и шаред памяти 
такое.

Krivenok_Dmitry
() автор топика
Ответ на: комментарий от Krivenok_Dmitry

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

dilmah ★★★★★
()
Ответ на: комментарий от dilmah

>просто как идея -- ведь перл использует подсчет ссылок, таким образом он похерит тебе всю шаренность

А можно поподробнее?

Я не совсем понимаю как это происходит, ведь COW постраничный и чтобы
снять COW флаг со страницы должен случиться page fault (write fault).

Krivenok_Dmitry
() автор топика
Ответ на: комментарий от Krivenok_Dmitry

> Я не совсем понимаю как это происходит, ведь COW постраничный и чтобы снять COW флаг со страницы должен случиться page fault (write fault).

ну идет подсчет ссылок. Счетчик ссылок изменился -- вот тебе и write fault

dilmah ★★★★★
()
Ответ на: комментарий от dilmah

>ну идет подсчет ссылок. Счетчик ссылок изменился -- вот тебе и write fault

в момент форка в форкнутой программе различны только результат возврата функции ну и переменные типа содержащей PID. Итого всего 1-2 страницы вирт. памяти будут различны. Далее ты меняешь в чилде чего то там, но основной массив данных(дерево 100мб например) НЕ МЕНЯЕТСЯ, и существует в одном экземпляре. Да, твоя переменная, которую ты поменял, может попасть в одну страницу с ЧАСТЬЮ дерева, но в этом случае счет пойдет лишь на килобайты. Все дерево размещенно на нескольких страницах.

вроде бы так (с)

roller ★★★
()
Ответ на: комментарий от roller

AFAIU у любой перловской переменной есть счетчик ссылок. Каждый раз когда используешь дерево (просто читаешь) ты увеличиваешь число ссылок на его составные части -- это ведет к записи в те части дерева которые ты читаешь.

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

dilmah ★★★★★
()
Ответ на: комментарий от dilmah

Ага, и этот счетчик хранится в некой странице памяти. И у родителя и форка эта область будет раздельной в общем случае. Но на сами данные этот счетчик не влияет.

Т.е. некие служебные страницы памяти раздвоятся, но те где лежит само дерево - нет.

roller ★★★
()
Ответ на: комментарий от roller

> Ага, и этот счетчик хранится в некой странице памяти.

во первых это не один счетчик. Это куча счетчиков на разные части дерева (грубо говоря на каждую строку которая в дереве лежит). Во вторых что-то мне подсказывает что счетчики лежат не отдельно, а рядом с соответствующими данными.

dilmah ★★★★★
()
Ответ на: комментарий от dilmah

а провести маленький тест?
Я там дал ссылочку на модулек :)

У меня к сожалению второй комп ща нерабочий, а линукс там.

http://search.cpan.org/~opi/Linux-Smaps-0.01/lib/Linux/Smaps.pm#Example:_The_...

Только данные не менять а ссылок на них понаделать.

roller ★★★
()
Ответ на: комментарий от roller

Вот smaps для родителя:
08138000-0bc05000 rw-p 08138000 00:00 0          [heap]
Size:             60212 kB
Rss:              60140 kB
Shared_Clean:         0 kB
Shared_Dirty:     46280 kB
Private_Clean:        0 kB
Private_Dirty:    13860 kB

А вот для потомка (сразу после fork'a до посылки запроса):
08138000-0bc26000 rw-p 08138000 00:00 0          [heap]
Size:             60344 kB
Rss:              60144 kB
Shared_Clean:         0 kB
Shared_Dirty:     46280 kB
Private_Clean:        0 kB
Private_Dirty:    13864 kB

Krivenok_Dmitry
() автор топика
Ответ на: комментарий от Krivenok_Dmitry

вот сцуко...

а если увеличить размер дерева(ну например 2-4 раза получи его) а потом форкнуть насколько вырастет Private_Dirty

roller ★★★
()

Происки архитектуры:

1)

>Критичным ресурсом является именно время поиска информации, а
в случае дерева всё что нужно - просто пройти по нему в глубину.
После построения дерева в памяти демон переходит в бесконечный цикл
обработки запросов клиентов (т.е. блокируется в accept, принимает
соединение, создает дочерний процесс на обработку запроса и снова
блокируется в accept).
>Т.о. имеем тривиальную process per connection архитектуру.

Если время - критичный ресурс то ты должен форгатся до первого accept(), естественно после построения дерева, после чего форк не трогать а передавать запрос принятый accept-om свободному процессу любым средством IPC. Лучше всего сиргналами хотя можеш передавать и файловый дескриптор.

==================================================================

2)

>Теперь опишу проблему:
>Дерево в памяти занимает около 100Mb, т.е. очень много.
>Известно, что при создании процесса через fork используется
оптимизация copy-on-write.
>Казалось бы, при read-only доступе к дереву в дочерних процессах
никакого копирования быть не должно и все дочерние процессы должны
разделять дерево.
>Однако, во-первых, COW опцимизация в Linux постраничная (per page).
Во-вторых, демон написан на Perl'е, который может творить "за кулисами"
>всё что угодно.

Использование perl-a для работы с большими обьёмами данных с критичным параметром время - ССЗБ

===================================================================

Уточни следующие моменты:

1) время поиска 10мс это много или мало

2) сколько это "большое количество подключений"?? 10000? 100000?

=======================================================================

>И последнее, я не уверен на 100%, но скорее всего это так - при
своппинге разделяемых страниц памяти на диск они перестают разделяться
между процессами и система уходит в глубочайший своп

напрасно. Это так и есть. Тоесть перл в дочерних процессах действительно делает write fault

кстати ты не забываеш делалать exit после закрытия соединения обслуживаемого дочерним процессом?

cvv ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.