LINUX.ORG.RU

Что еще можно применить вместо fork()?

 , , memfree,


6

3

Работаю в сфере встраиваемых решений. Есть ситуация, ошибочная, когда с помощью fork() невозможно запустить другую программу из приложения. Ситуация ошибочна потому, что приложение «съело» столько озу, что на fork() места не остается.

Что еще можно применить вместо fork()? Есть ли еще какие-нибудь идеи, мысли, методы запуска программ из программ?



Последнее исправление: beastie (всего исправлений: 1)

Есть ли еще какие-нибудь идеи, мысли, методы запуска программ из программ?

Заранее ограничить доступную приложению память, чтобы на fork+exec осталось место.

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

Балласт сбросить. Балласт естественно создается заранее.

А что если память успеют «доесть» после сброса балласта, но до fork+exec?

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

vfork(2), posix_spawn
Можно запускать программу заранее.

vfork() это мысль, попробую.

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

Заранее ограничить доступную приложению память, чтобы на fork+exec осталось место.

Тоже интересно, направьте меня плиз в нужный мануал или урл.

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

с размером стека еще имеет смысл поиграться

ananas ★★★★★
()

Работаю в сфере встраиваемых решений.

приложение «съело» столько озу, что на fork() места не остается.

Так не должно быть!!! Серьезно пересмотрите использование памяти в вашем проекте.

Deleted
()

Конечно:

for(temp=0;temp<=200;temp++){
    system("someProgramWithArguments -t ~/test.txt -c setup &")
}

// & forks!
Но это то же самое вроде как

minakov ★★★★★
()
30 июля 2014 г.

Результат

Не смотря на то, что теме почти год, подведем итог.

Найти другой, штатный в linux, способ запуска, не удалось.

И fork() и clone() [sys_clone], сначала делают дубликат родительского процесса в ОЗУ (со всем содержимым), затем его замещают новым.

Поэтому, создана программа прослойка, launcher, минимального размера, которая сидит в ОЗУ и запускает процессы, по мере надобности остального комплекса программ. Остальные же программы, выдают этому launcher-у команды на запуск приложений и параметры запуска.

PS. Жалко, что подобную возможность не добавили в прародитель всех процессов пользовательского уровня, init, который имеет pid=1, или в состав функций самого ядра (ведь процесс init запускается без родителя ;) ).

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

Тему до конца 2014 пока не помечаю решенной, у кого есть желание можно подискутировать на счет решения.

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

Заранее ограничить доступную приложению память, чтобы на fork+exec осталось место.

Тоже интересно, направьте меня плиз в нужный мануал или урл.

mironov_ivan, я так и не нашел где и как это делается...

Vic
() автор топика
Ответ на: Результат от Vic

И fork() и clone() [sys_clone], сначала делают дубликат родительского процесса в ОЗУ (со всем содержимым), затем его замещают новым.

Что, простите? В линуксах же clone по-человечески сделан, никакого дублирования не осуществляется, только copy-on-write.

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

И fork() и clone() [sys_clone], сначала делают дубликат родительского процесса в ОЗУ (со всем содержимым), затем его замещают новым.

Что, простите? В линуксах же clone по-человечески сделан, никакого дублирования не осуществляется, только copy-on-write.

Могу предположить, что уже после создания копии родительского процесса И загрузки нового, объем ОЗУ корректируется. У меня же получается ENOMEM в процессе-родителе.

На десктопе Вы такого не увидите (надеюсь не надо объяснять почему?).

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

В линуксах же clone по-человечески сделан, никакого дублирования не осуществляется, только copy-on-write.

При отключенном overcommit даже fork резервирует память. Правда, vfork работает - ХЗ, что там у ТС не получилось.

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

На десктопе Вы такого не увидите (надеюсь не надо объяснять почему?).

Объясни, будь ласков.

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

На десктопе Вы такого не увидите (надеюсь не надо объяснять почему?).

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

Iron_Bug ★★★★★
()

Можно использовать псевдо-многозадачность. Пусть есть функция с множеством case (switch-case конструкция), каждый case пусть делает какую-то малую часть работы. Соответственно используя state-переменную, можно добиться, чтобы на каждую итерацию этой функции выполнялся какой-то кусок работы.

hibou ★★★★★
()

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

О системе:

ucdk:~# uname -a
Linux ucdk 2.6.27 #1 Thu Jul 11 15:33:34 MSK 2013 armv5tejl GNU/Linux

ucdk:~# getconf GNU_LIBC_VERSION
glibc 2.9

ucdk:~# date
Wed Jul 30 14:59:46 UTC 2014
Типичные условия в которых работает комплекс программ:
ucdk:~# free -t
             total       used       free     shared    buffers     cached
Mem:        126740     121240       4500          0        668       5728
-/+ buffers/cache:       5284      10896
Swap:            0          0          0
Total:      126740     119240       4500
Тоже через top, плюс иллюстрация:
ucdk:~# top -bn1
top - 14:34:12 up 13 days, 23:37,  1 user,  load average: 0.29, 0.51, 0.62
Tasks:  63 total,   1 running,  62 sleeping,   0 stopped,   0 zombie
Cpu(s):  5.5%us,  4.1%sy,  0.0%ni, 90.2%id,  0.1%wa,  0.0%hi,  0.1%si,  0.0%st
Mem:    126740k total,   121352k used,     4388k free,     668k buffers
Swap:        0k total,        0k used,        0k free,    5740k cached

  PID USER      PR  NI  VIRT   RES  SHR S %CPU %MEM    TIME+  COMMAND
 8623 root      20   0  2524  1052  852 R  5.5  0.8   0:00.26 top
    1 root      20   0  2012   704  616 S  0.0  0.6   0:08.54 init
    2 root      15  -5     0     0    0 S  0.0  0.0   0:00.00 kthreadd
...
  771 root      20   0 59236 15412  996 S  0.0 12.4   4:18.99 SrvController
...
 6101 root      20   0  3024  1576 1216 S  0.0  1.2   0:00.44 bash
...
SrvController используется для запуска приложений. Как видно, в системе свободно ОЗУ = 4388k free, 4Мб, что меньше, чем, использует SrvController = RES = 15Мб.

И в такой вот момент, и fork() и vfork(), выдавали -1, ENOMEM, когда SrvController пытался запустить любое приложение, даже тот же top или ps. Хотя, как видите, из консоли я смотреть еще что-то могу.

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

tailgunner, на персоналке получить это практически нельзя, т.к. по сути памяти всегда достаточно в пределах разрядности операционки, из-за наличия файла подкачки. Подозреваю, что нечто подобно можно получить методом, который написал Iron_Bug, искусственно вводя ограничения, но я сам такое не проделывал, т.к. у меня экспериментальный стенд под рукой :)

hibou, именно по такому принципу и построен комплекс программ, а рассматриваемая ситуация, является банальной ошибкой внутри этого комплекса.

Противность ошибки в том, что для ее выявления пришлось физически ехать к устройству и уже консолью все просматривать, т.к. удаленный мониторинг, который был задуман через SrvController, отказался работать.

Отказ в работе удаленного мониторинга стал очень не приятным «сюрпризом», который и породил дальнейшие изыскания, чем я с Вами и решил поделиться.

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

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

А как связан лимит с реальным «съел всю память»?

(софт не должен падать)

Упадёт.

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

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

SwapTotal:             0 kB
SwapFree:              0 kB

Кулл стори.

Подозреваю, что нечто подобно можно получить методом, который написал Iron_Bug, искусственно вводя ограничения, но я сам такое не проделывал, т.к. у меня экспериментальный стенд под рукой :)

Нельзя - этот детсад.

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

когда система больше не может выделять пулы под новые таски - это и есть «съел всю память». в общем-то, это нештатная ситуация, но проверять всё равно нужно. и таки софт не падает, ибо везде стоят проверки на этот случай. у нас сервера, на которых висят миллионы юзерских сессий. и падать им запрещено по определению.
просто без ограничений даже на моей рабочей машине 32 гига памяти, ещё и своп (а на сервере в своп ваще нельзя уходить). а на серверах и вовсе 64-128. очень сложно так нагрузить, чтобы сожрать всё. для тестов приходится ограничивать искусственно, отрубать своп и делать вид, что памяти больше нема.

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

огда система больше не может выделять пулы под новые таски - это и есть «съел всю память».

Не ясно.

и таки софт не падает, ибо везде стоят проверки на этот случай.

Что ему помешает? В линуксе ленивое связывание страниц. Причем префаулт в юзерспейсе не сделаешь, поэтому дело не в проверках.

у нас сервера, на которых висят миллионы юзерских сессий. и падать им запрещено по определению.

Они переполняются? Либо они заполняются до барьера и просто не переполняются?

просто без ограничений даже на моей рабочей машине 32 гига памяти, ещё и своп (а на сервере в своп ваще нельзя уходить). а на серверах и вовсе 64-128. очень сложно так нагрузить, чтобы сожрать всё.

В линуксе разве есть лимиты, которые ограничивают реальную память? - я таких не знаю, но возможно они есть. Либо юзать что-то типа:

#include <stdio.h>
#include <error.h>
#include <stdint.h>
#include <errno.h>
#include <stdlib.h>
#include <sys/mman.h>

int main(int argc, char * argv[]) {
  if(argc < 2)
    error(1, 0, "usage: %s mem count in GB\n", *argv);
  uint64_t alloc_size = atoi(*(argv + 1));
  if(mmap(NULL, alloc_size * (1024 * 1024 * 1024), PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_POPULATE, 0, 0) == MAP_FAILED)
    error(2, errno, "alloc %luGB", alloc_size);
  fprintf(stdout, "%s\n", "press any key/sigkill to free");
  getchar();//маздайский гетчар, либо какой другой лок.
}
Carb_blog5
()
Ответ на: комментарий от Vic

на персоналке получить это практически нельзя, т.к. по сути памяти всегда достаточно в пределах разрядности операционки

Отключаешь оверкоммит и наслаждаешься.

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

Уважаемые форумчане - спокойствие, и еще раз спокойствие!

Carb_blog5

ucdk:~# sysctl vm.overcommit_memory
vm.overcommit_memory = 0

ucdk:~# df -h
Filesystem            Size  Used Avail Use% Mounted on
rootfs                123M   52M   72M  42% /
/dev/root             123M   52M   72M  42% /
tmpfs                  62M     0   62M   0% /lib/init/rw
tmpfs                  62M   12K   62M   1% /dev/shm
/dev/mtdblock7        2.0M  424K  1.6M  21% /var/local/log
tmpfs                  62M   40M   23M  65% /tmp
tmpfs                  62M   40M   23M  65% /dev
tmpfs                  62M   12K   62M   1% /dev/shm
/dev/mmcblk0p1        3.7G   16M  3.7G   1% /media/flash

А новей ничего нету ?

joy4eg, неа. Прикладные программы приходится проектировать так, что бы они работали не только на «новом», но и на «старом».

Это особенность массового производства и встраиваемых систем, надо поддерживать то что расставлено массово, где нет никакого выхода в интернет.

И где, самое главное, - нет человека. Это же не смартфон и не сервер в дата центре ;)

Для централизованного сопровождения каналы связи не доступны, т.к. технологические процессы и коммерческую информацию во вне ни кто не выводит (выводят только личные кабинеты на «айпады», но это уже с серверов в датацентрах). А если технологическое оборудование надо вывести во вне, то делают это только на время, зачастую GSM/CSD 1200 бод (если кто помнит обычные модемы под конец массовой карьеры работали на 56000 бод).

Поэтому, системная часть не меняется очень долго, а прикладные программы проектируются так, что бы были совместимы с ранее выпущенными устройствами, N-летней разумной давности.

Обращаю внимание. У меня случилась ситуация, которая выглядит как «не достаточно памяти» на вполне конкретных системных вызовах fork() и vfork(). И надежно ее воспроизводить получилось при условиях, которые я проиллюстрировал. Дело в том, что подобная ситуация вполне может проявляться и при других условиях, но их поиском я же не занимался.

Помните вывод free? У меня в устройстве всего 128 Мб ОЗУ. Так вот, если я сделаю malloc на 500 Мб, то malloc завершится безошибочно. Причем в части этого гигантского массива работать приложение будет без проблемно. А снято системой будет, если например, сделать memset всей выделенной области.

Такова стратегия работы виртуальной памяти и она вправе быть такой.

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

Iron_Bug и Carb_blog5, респект, а за почти рабочий код вообще уважуха, хотя и с варнингами и ошибкой ;)

// Варнинги
||=== testsizet, Release ===|
c:\UniversalPlatform\branches\Tryes\testsizet\main.c||In function 'main':|
c:\UniversalPlatform\branches\Tryes\testsizet\main.c|13|warning: format '%lu' expects argument of type 'long unsigned int', but argument 4 has type 'uint64_t' [-Wformat]|
c:\UniversalPlatform\branches\Tryes\testsizet\main.c|16|warning: control reaches end of non-void function [-Wreturn-type]|
||=== Build finished: 0 errors, 2 warnings (0 minutes, 13 seconds) ===|

// Результат

ucdk:/tmp# ./testsizet.exe 1
./testsizet.exe: alloc 1073884304GB: Cannot allocate memory
ucdk:/tmp# ./testsizet.exe 2
./testsizet.exe: alloc 1073884304GB: Cannot allocate memory
ucdk:/tmp# ./testsizet.exe 3
./testsizet.exe: alloc 1073884304GB: Cannot allocate memory

// Результат после устранения варнингов и ошибки чтения агрмента

./testsizet.exe: alloc 1GB: Cannot allocate memory
ucdk:/tmp# ./testsizet.exe 3
./testsizet.exe: alloc 3GB: Cannot allocate memory
ucdk:/tmp# ./testsizet.exe 4
./testsizet.exe: alloc 4GB: Invalid argument

// Результат в Мб

ucdk:/tmp# ./testsizet.exe 1
press any key/sigkill to free

ucdk:/tmp# ./testsizet.exe 10
press any key/sigkill to free

ucdk:/tmp# ./testsizet.exe 100
./testsizet.exe: alloc 100 MB: Cannot allocate memory

// А вот тут система просто повисла и перезапустилась по WDT!
// Так что мотайте на ус!

ucdk:/tmp# ./testsizet.exe 50
Killed

Broadcast message from root@ucdk (Thu Jul 31 10:04:33 2014):

The system is going down for reboot NOW!


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

«не хватило диска»

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

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

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

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

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

бывает вообще написание ПО с доказательствами: там, где особая надёжность и системы безопасности. но я в такие дебри не лезу: там не так страшно писать софт, как потом упарываться с доказательствами. так что нафиг нада. но с их точки зрения мы такие же индусы с непроверенным софтом :) так что всё относительно.

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

естетственно что в cgroup коробку ограничевающую память и память+свом нужно класть только процесс, который исследуется, тога остальные процессы юзера никак не будут затронуты.

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

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

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

vm.overcommit_memory = 0

Это у вас на целевой системе включен оверкоммит? Мде...

Беда без оверкоммита.

Программы, специально разработанные для систем с ограниченной памятью, типа utelnetd и dropbear отлично работают некоторое время.

Все «бытовые программы» (апач, фтп и пр.) падают сразу - либо с ошибкой, либо тихо, либо в нерабочем состоянии.

Но в итоге, примерно через часик, все устройство перестает адекватно реагировать, даже в системной консоли.

В консоли попадаются вот такие сообщения:

error while loading shared libraries: libpthread.so.0: failed to map segment from shared object: Cannot allocate memory

Iron_Bug, про относительность индусов Вы абсолютно правы!!! +1 :)

А еще, вот такое сообщение от фтп, мне напомнило мою исходную ситуацию, когда свободной памяти достаточно для приложения, а запустить дочерний процесс нельзя. Тут скорее-всего причина другая, но проявление сходное.

220 Welcome to UCDK FTP service.
500 OOPS: mmap
500 OOPS: child died
ucdk:~# free
             total       used       free     shared    buffers     cached
Mem:        126740      38220      88520          0         88      18332
-/+ buffers/cache:      19800     106940
Swap:            0          0          0

Вообще же менеджеры виртуальной памяти законно очень умные программы, т.к. им приходится учитывать разнообразие 100% потребностей всех написанных программ и даже не написанных еще программ. И в них всегда используется тонкая грань компромисса, между скоростью/надежностью/оптимальностью. Потому, если я вижу free = 106 Мб, я прекрасно отдаю себе отчет, что эта цифра может не соответствовать реально доступному ОЗУ для приложения (например, если память сильно фрагментирована и ее перераспределение еще не выполнено, то и 1 Мб может не найтись).

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

PS. Т.к. tailgunner не сказал конкретно про отключение оверкоммита, то поясняю, что оверкоммит отключается в файле /etc/sysctl.conf, изменением, или добавлением в случае отсутствия, двух строк:

vm.overcommit_memory=2
vm.overcommit_ratio=100

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

qnikst продемонстрируйте пожалуйста (конфиги, команды консоли), как ограничить конкретно, апач для root-а (у меня один root в системе и никого больше).

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

команды могут отличаться в зависимости от настроек

я предполагаю, что иерархия cgroup уже примонтирована в /sys/fs/cgroups (у меня это делает openrc, при systemd все может и отличаться и возможно там нужно использовать systemd-cgroupctl, то же самое для libcgroup)

если же нет, то:

  • монтируем корень цгруппы:
    mount -t tmpfs cgroup_root /sys/fs/cgroup
    
  • монтируем иерархию управление памятью:
    mkdir /sys/fs/cgroup/memory
    mount -t cgroup -omemory memory /sys/fs/cgroup/memory
    
  • создаем место для апача
     
    mkdir /sys/fs/cgroup/memory/apache
    echo -n 6M >  /sys/fs/cgroup/memory/memory.limit_in_bytes 
    echo -n 6M >  /sys/fs/cgroup/memory/memory.memsw.limit_in_bytes 
    
  • перемещаем pid апача в его группу
    echo `pidof httpd` > /sys/fs/cgroup/memory/apache/tasks
    

последнее лучше сделать как

echo 0 > /sys/fs/cgroup/memory/apache/tasks
/etc/init.d/httpd start
ну или интегрировать в init скрипт.

если что полная документация в /usr/src/linux/Documentation/cgroups/*

я мог по памяти слегка наврать.

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

qnikst, спасибо! эх, на моем маленьком девайсе анрил...

ucdk:/# apt-get install cgroup-bin libcgroup1
Reading package lists... Error!
E: Couldn't make mmap of 25165824 bytes - mmap (22 Invalid argument)
W: Unable to munmap
E: The package lists or status file could not be parsed or opened.

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

Все «бытовые программы» (апач, фтп и пр.) падают сразу - либо с ошибкой, либо тихо, либо в нерабочем состоянии.

Как всё запущено...

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

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

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

дык занялся установкой потому что вот...

ucdk:/# mount -t cgroup none /cgroup -o subsystem
mount: unknown filesystem type 'cgroup'

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

тогда о цгруппах можно забыть, они в 3.8 появились.. если только на десктопе тестить. В старых самое близкое это запуск из своего терминала с ulimit, но я не помню, чтобы там swap можно было контролировать.

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

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

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