Python subprocess.Popen „OSError: [Errno 12] Cannot alokować pamięć”

114

Uwaga: to pytanie zostało pierwotnie zadane tutaj, ale czas nagród minął, mimo że w rzeczywistości nie znaleziono akceptowalnej odpowiedzi. Ponownie zadaję to pytanie, uwzględniając wszystkie szczegóły podane w pierwotnym pytaniu.

Skrypt w Pythonie uruchamia zestaw funkcji klasowych co 60 sekund za pomocą modułu schedule :

# sc is a sched.scheduler instance
sc.enter(60, 1, self.doChecks, (sc, False))

Skrypt działa jako proces zdemonizowany przy użyciu kodu znajdującego się tutaj .

Szereg metod klas, które są wywoływane jako część doChecks, używa modułu podprocesu do wywoływania funkcji systemowych w celu uzyskania statystyk systemowych:

ps = subprocess.Popen(['ps', 'aux'], stdout=subprocess.PIPE).communicate()[0]

Działa to dobrze przez pewien czas, zanim cały skrypt ulegnie awarii z następującym błędem:

File "/home/admin/sd-agent/checks.py", line 436, in getProcesses
File "/usr/lib/python2.4/subprocess.py", line 533, in __init__
File "/usr/lib/python2.4/subprocess.py", line 835, in _get_handles
OSError: [Errno 12] Cannot allocate memory

Wynik działania free -m na serwerze po awarii skryptu to:

$ free -m
                  total       used       free     shared     buffers    cached
Mem:                894        345        549          0          0          0
-/+ buffers/cache:  345        549
Swap:                 0          0          0

Na serwerze działa CentOS 5.3. Nie mogę odtworzyć na moich własnych skrzynkach CentOS ani z żadnym innym użytkownikiem zgłaszającym ten sam problem.

Próbowałem wielu rzeczy, aby debugować to, jak sugerowano w pierwotnym pytaniu:

  1. Rejestrowanie wyników działania free -m przed i po wywołaniu Popen. Nie ma znaczącej zmiany w zużyciu pamięci, tj. Pamięć nie jest stopniowo wykorzystywana w trakcie wykonywania skryptu.

  2. Dodałem close_fds = True do wywołania Popen, ale to nie miało znaczenia - skrypt nadal zawieszał się z tym samym błędem. Sugerowane tutaj i tutaj .

  3. Sprawdziłem rlimity, które pokazały (-1, -1) zarówno na RLIMIT_DATA, jak i RLIMIT_AS, jak sugerowano tutaj .

  4. Artykuł sugerował mającą swap może być przyczyną, ale wymiany jest rzeczywiście dostępne na żądanie (w zależności od hostingowego) i to również było sugerowane jako fałszywe powodować tutaj .

  5. Procesy są zamykane, ponieważ jest to zachowanie przy użyciu .communicate (), zgodnie z kopią zapasową kodu źródłowego Pythona i komentarzami tutaj .

Wszystkie testy można znaleźć na GitHub tutaj, z funkcją getProcesses zdefiniowaną w linii 442. Jest ona wywoływana przez doChecks () począwszy od linii 520.

Skrypt został uruchomiony ze strace z następującymi danymi wyjściowymi przed awarią:

recv(4, "Total Accesses: 516662\nTotal kBy"..., 234, 0) = 234
gettimeofday({1250893252, 887805}, NULL) = 0
write(3, "2009-08-21 17:20:52,887 - checks"..., 91) = 91
gettimeofday({1250893252, 888362}, NULL) = 0
write(3, "2009-08-21 17:20:52,888 - checks"..., 74) = 74
gettimeofday({1250893252, 888897}, NULL) = 0
write(3, "2009-08-21 17:20:52,888 - checks"..., 67) = 67
gettimeofday({1250893252, 889184}, NULL) = 0
write(3, "2009-08-21 17:20:52,889 - checks"..., 81) = 81
close(4)                                = 0
gettimeofday({1250893252, 889591}, NULL) = 0
write(3, "2009-08-21 17:20:52,889 - checks"..., 63) = 63
pipe([4, 5])                            = 0
pipe([6, 7])                            = 0
fcntl64(7, F_GETFD)                     = 0
fcntl64(7, F_SETFD, FD_CLOEXEC)         = 0
clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0xb7f12708) = -1 ENOMEM (Cannot allocate memory)
write(2, "Traceback (most recent call last"..., 35) = 35
open("/usr/bin/sd-agent/agent.py", O_RDONLY|O_LARGEFILE) = -1 ENOMEM (Cannot allocate memory)
open("/usr/bin/sd-agent/agent.py", O_RDONLY|O_LARGEFILE) = -1 ENOMEM (Cannot allocate memory)
open("/usr/lib/python24.zip/agent.py", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
open("/usr/lib/python2.4/agent.py", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
open("/usr/lib/python2.4/plat-linux2/agent.py", O_RDONLY|O_LARGEFILE) = -1 ENOMEM (Cannot allocate memory)
open("/usr/lib/python2.4/lib-tk/agent.py", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
open("/usr/lib/python2.4/lib-dynload/agent.py", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
open("/usr/lib/python2.4/site-packages/agent.py", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
write(2, "  File \"/usr/bin/sd-agent/agent."..., 52) = 52
open("/home/admin/sd-agent/daemon.py", O_RDONLY|O_LARGEFILE) = -1 ENOMEM (Cannot allocate memory)
open("/usr/bin/sd-agent/daemon.py", O_RDONLY|O_LARGEFILE) = -1 ENOMEM (Cannot allocate memory)
open("/usr/lib/python24.zip/daemon.py", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
open("/usr/lib/python2.4/daemon.py", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
open("/usr/lib/python2.4/plat-linux2/daemon.py", O_RDONLY|O_LARGEFILE) = -1 ENOMEM (Cannot allocate memory)
open("/usr/lib/python2.4/lib-tk/daemon.py", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
open("/usr/lib/python2.4/lib-dynload/daemon.py", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
open("/usr/lib/python2.4/site-packages/daemon.py", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
write(2, "  File \"/home/admin/sd-agent/dae"..., 60) = 60
open("/usr/bin/sd-agent/agent.py", O_RDONLY|O_LARGEFILE) = -1 ENOMEM (Cannot allocate memory)
open("/usr/bin/sd-agent/agent.py", O_RDONLY|O_LARGEFILE) = -1 ENOMEM (Cannot allocate memory)
open("/usr/lib/python24.zip/agent.py", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
open("/usr/lib/python2.4/agent.py", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
open("/usr/lib/python2.4/plat-linux2/agent.py", O_RDONLY|O_LARGEFILE) = -1 ENOMEM (Cannot allocate memory)
open("/usr/lib/python2.4/lib-tk/agent.py", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
open("/usr/lib/python2.4/lib-dynload/agent.py", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
open("/usr/lib/python2.4/site-packages/agent.py", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
write(2, "  File \"/usr/bin/sd-agent/agent."..., 54) = 54
open("/usr/lib/python2.4/sched.py", O_RDONLY|O_LARGEFILE) = 8
write(2, "  File \"/usr/lib/python2.4/sched"..., 55) = 55
fstat64(8, {st_mode=S_IFREG|0644, st_size=4054, ...}) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7d28000
read(8, "\"\"\"A generally useful event sche"..., 4096) = 4054
write(2, "    ", 4)                     = 4
write(2, "void = action(*argument)\n", 25) = 25
close(8)                                = 0
munmap(0xb7d28000, 4096)                = 0
open("/usr/bin/sd-agent/checks.py", O_RDONLY|O_LARGEFILE) = -1 ENOMEM (Cannot allocate memory)
open("/usr/bin/sd-agent/checks.py", O_RDONLY|O_LARGEFILE) = -1 ENOMEM (Cannot allocate memory)
open("/usr/lib/python24.zip/checks.py", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
open("/usr/lib/python2.4/checks.py", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
open("/usr/lib/python2.4/plat-linux2/checks.py", O_RDONLY|O_LARGEFILE) = -1 ENOMEM (Cannot allocate memory)
open("/usr/lib/python2.4/lib-tk/checks.py", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
open("/usr/lib/python2.4/lib-dynload/checks.py", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
open("/usr/lib/python2.4/site-packages/checks.py", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
write(2, "  File \"/usr/bin/sd-agent/checks"..., 60) = 60
open("/usr/bin/sd-agent/checks.py", O_RDONLY|O_LARGEFILE) = -1 ENOMEM (Cannot allocate memory)
open("/usr/bin/sd-agent/checks.py", O_RDONLY|O_LARGEFILE) = -1 ENOMEM (Cannot allocate memory)
open("/usr/lib/python24.zip/checks.py", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
open("/usr/lib/python2.4/checks.py", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
open("/usr/lib/python2.4/plat-linux2/checks.py", O_RDONLY|O_LARGEFILE) = -1 ENOMEM (Cannot allocate memory)
open("/usr/lib/python2.4/lib-tk/checks.py", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
open("/usr/lib/python2.4/lib-dynload/checks.py", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
open("/usr/lib/python2.4/site-packages/checks.py", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
write(2, "  File \"/usr/bin/sd-agent/checks"..., 64) = 64
open("/usr/lib/python2.4/subprocess.py", O_RDONLY|O_LARGEFILE) = 8
write(2, "  File \"/usr/lib/python2.4/subpr"..., 65) = 65
fstat64(8, {st_mode=S_IFREG|0644, st_size=39931, ...}) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7d28000
read(8, "# subprocess - Subprocesses with"..., 4096) = 4096
read(8, "lso, the newlines attribute of t"..., 4096) = 4096
read(8, "code < 0:\n        print >>sys.st"..., 4096) = 4096
read(8, "alse does not exist on 2.2.0\ntry"..., 4096) = 4096
read(8, " p2cread\n        # c2pread    <-"..., 4096) = 4096
write(2, "    ", 4)                     = 4
write(2, "errread, errwrite)\n", 19)    = 19
close(8)                                = 0
munmap(0xb7d28000, 4096)                = 0
open("/usr/lib/python2.4/subprocess.py", O_RDONLY|O_LARGEFILE) = 8
write(2, "  File \"/usr/lib/python2.4/subpr"..., 71) = 71
fstat64(8, {st_mode=S_IFREG|0644, st_size=39931, ...}) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7d28000
read(8, "# subprocess - Subprocesses with"..., 4096) = 4096
read(8, "lso, the newlines attribute of t"..., 4096) = 4096
read(8, "code < 0:\n        print >>sys.st"..., 4096) = 4096
read(8, "alse does not exist on 2.2.0\ntry"..., 4096) = 4096
read(8, " p2cread\n        # c2pread    <-"..., 4096) = 4096
read(8, "table(self, handle):\n           "..., 4096) = 4096
read(8, "rrno using _sys_errlist (or siml"..., 4096) = 4096
read(8, " p2cwrite = None, None\n         "..., 4096) = 4096
write(2, "    ", 4)                     = 4
write(2, "self.pid = os.fork()\n", 21)  = 21
close(8)                                = 0
munmap(0xb7d28000, 4096)                = 0
write(2, "OSError", 7)                  = 7
write(2, ": ", 2)                       = 2
write(2, "[Errno 12] Cannot allocate memor"..., 33) = 33
write(2, "\n", 1)                       = 1
unlink("/var/run/sd-agent.pid")         = 0
close(3)                                = 0
munmap(0xb7e0d000, 4096)                = 0
rt_sigaction(SIGINT, {SIG_DFL, [], SA_RESTORER, 0x589978}, {0xb89a60, [], SA_RESTORER, 0x589978}, 8) = 0
brk(0xa022000)                          = 0xa022000
exit_group(1)                           = ?
davidmytton
źródło
1
zabrakło „potoków”, deskryptorów plików lub związanego z nimi zasobu jądra?
Blauohr
Sprawdź /var/log/messageslub dmesgpolecenie.
mark4o
W dzienniku nie ma nic związanego z tym.
davidmytton
Czy kiedykolwiek znalazłeś rozwiązanie tego problemu? Mam bardzo podobne objawy. Mam dużo wolnej pamięci, ale po dodaniu zamiany (jak sugerują niektóre z twoich odpowiedzi) problem znika. Zastanawiałem się, czy coś się dowiedziałeś w ciągu miesięcy od tamtego czasu do teraz. -- dzięki!
dpb
Mam ten sam problem, ale nie ma rozwiązania - jakieś pomysły?

Odpowiedzi:

88

Zgodnie z ogólną zasadą (tj wanilia jądrach), fork/ cloneporażki z ENOMEM nastąpić w szczególności z powodu bądź uczciwy do Boga out-of-pamięci stanu ( dup_mm, dup_task_struct, alloc_pid, mpol_dup, mm_inititd rechot), albo dlatego security_vm_enough_memory_mmzawiodły cię podczas egzekwowania na politykę overcommitu .

Zacznij od sprawdzenia rozmiaru maszyny wirtualnej procesu, któremu nie udało się rozwidlić w momencie próby forkowania, a następnie porównaj z ilością wolnej pamięci (fizycznej i wymiany) w odniesieniu do polityki overcommit (podłącz liczby).

W twoim konkretnym przypadku należy pamiętać, że Virtuozzo ma dodatkowe kontrole w egzekwowaniu nadmiernego zobowiązania . Ponadto, nie jestem pewien, ile kontroli naprawdę mają od wewnątrz kontenera, na zamiany i konfiguracji overcommitu (w celu wywarcia wpływu na wynik egzekucji).

Teraz, aby faktycznie iść do przodu, powiedziałbym, że masz dwie opcje :

  • przełączyć się na większą instancję lub
  • włóż trochę wysiłku w kodowanie, aby skuteczniej kontrolować zużycie pamięci skryptu

PAMIĘTAJ, że wysiłek związany z kodowaniem może pójść na marne, jeśli okaże się, że to nie ty, ale jakiś inny facet w innej instancji na tym samym serwerze, na którym działa amock.

Jeśli chodzi o pamięć, wiemy już, że subprocess.Popenużywa fork/ clone pod maską , co oznacza, że ​​za każdym razem, gdy to nazywasz, żądasz jeszcze raz tyle pamięci, ile Python już pochłania , tj. W setkach dodatkowych MB, wszystko po to, aby to zrobić execsłaby plik wykonywalny o rozmiarze 10 kB, taki jak freelub ps. W przypadku niekorzystnej polityki nadmiernego zaangażowania wkrótce zobaczysz ENOMEM.

Alternatywami dla forktych, które nie mają tabel stron nadrzędnych itp., Są problemy z kopiowaniem vforki posix_spawn. Ale jeśli nie masz ochoty na przepisywanie fragmentów subprocess.Popenw zakresie vfork/ posix_spawn, rozważ użycie suprocess.Popentylko raz, na początku swojego skryptu (gdy ślad pamięci Pythona jest minimalny), aby odrodzić skrypt powłoki, który następnie uruchamia free/ ps/ sleepi cokolwiek innego w pętla równoległa do twojego skryptu; sonduj dane wyjściowe skryptu lub czytaj je synchronicznie, być może z oddzielnego wątku, jeśli masz inne rzeczy, którymi musisz zająć się asynchronicznie - wykonaj przetwarzanie danych w Pythonie, ale pozostaw rozwidlenie procesowi podrzędnemu.

JEDNAK , w twoim konkretnym przypadku możesz pominąć wywoływanie psi freecałkowicie; te informacje są łatwo dostępne w Pythonie bezpośrednio zprocfs , niezależnie od tego, czy zdecydujesz się uzyskać do nich dostęp samodzielnie, czy za pośrednictwem istniejących bibliotek i / lub pakietów . Jeśli psi freebyłyby jedynymi narzędziami, z których korzystałeś, możesz całkowicie wyeliminowaćsubprocess.Popen .

Wreszcie, cokolwiek zrobisz, jeśli o subprocess.Popento chodzi, jeśli twój skrypt wycieknie z pamięci, w końcu i tak uderzysz w ścianę. Miej go na oku i sprawdź , czy nie ma wycieków pamięci .

vladr
źródło
7
Odkryłem, że uruchomienie gc.collect()tuż przedtem subprocess.Popenpomaga w przypadkach, gdy odśmiecacz nie działał przez jakiś czas.
letmaik
Napisałem demona do obsługi strategii skryptu pomocniczego: github.com/SeanHayes/errand-boy Używam go w produkcji z jednym z moich klientów i nasze problemy z „Nie można przydzielić pamięci” zniknęły.
Seán Hayes,
Byłbym wdzięczny za prostą diagnostykę, np. /proc/fd/mapsŚledzenie, aby ustalić, czy w rzeczywistości problemem jest nadmierna pamięć
Dima Tisnek
18

Patrząc na wynik free -m, wydaje mi się, że w rzeczywistości nie masz dostępnej pamięci wymiany. Nie jestem pewien, czy w Linuksie zamiana zawsze będzie dostępna automatycznie na żądanie, ale miałem ten sam problem i żadna z odpowiedzi tutaj naprawdę nie pomogła. Dodanie pamięci wymiany rozwiązało jednak problem w moim przypadku, więc ponieważ może to pomóc innym osobom borykającym się z tym samym problemem, publikuję odpowiedź na temat dodawania 1 GB wymiany (w systemie Ubuntu 12.04, ale powinno działać podobnie w innych dystrybucjach).

Możesz najpierw sprawdzić, czy jest włączona jakakolwiek pamięć wymiany.

$sudo swapon -s

jeśli jest pusty, oznacza to, że nie masz włączonej wymiany. Aby dodać wymianę 1 GB:

$sudo dd if=/dev/zero of=/swapfile bs=1024 count=1024k
$sudo mkswap /swapfile
$sudo swapon /swapfile

Dodaj następujący wiersz fstabdo, aby zamiana była stała.

$sudo vim /etc/fstab

     /swapfile       none    swap    sw      0       0 

Źródło i więcej informacji można znaleźć tutaj .

Nima
źródło
1
Czy to rozwiązało ten sam problem, czy inny?
Dima Tisnek
Zrobiło to dla mnie w CentOS 6.4. Napotkano błąd podczas instalowania awstats, dzięki.
Ruslan Abuzant
Chociaż pozwoliło mi to wykonać kod, tak naprawdę nie rozwiązało problemu, który prawdopodobnie leży w bibliotece, której używam.
philmaweb
1
Naprawiłeś mój problem. Dzięki! +1
sscirrus
8

swap może nie być sugerowanym wcześniej czerwonym śledziem. Jak duży jest dany proces w Pythonie tuż przed ENOMEM?

W jądrze 2.6 /proc/sys/vm/swappinesskontroluje, jak agresywnie jądro będzie obracać się w celu wymiany i overcommit*zapisuje, ile i jak precyzyjnie jądro może przydzielić pamięć mrugnięciem i skinieniem głowy. Podobnie jak status twojego związku na Facebooku, jest to skomplikowane .

... ale zamiana jest faktycznie dostępna na żądanie (zgodnie z usługodawcą hostingowym) ...

ale nie zgodnie z danymi wyjściowymi free(1)polecenia, które nie pokazują żadnej przestrzeni wymiany rozpoznawanej przez instancję serwera. Twój usługodawca hostingowy może z pewnością wiedzieć o wiele więcej na ten temat niż ja, ale wirtualne systemy RHEL / CentOS, których użyłem, zgłosiły wymianę dostępną dla systemu gościa.

Dostosowanie artykułu KB Red Hat 15252 :

System Red Hat Enterprise Linux 5 będzie działał dobrze bez miejsca na wymianę, o ile suma pamięci anonimowej i współdzielonej systemu V jest mniejsza niż około 3/4 pamięci RAM. .... Systemy z 4 GB pamięci RAM lub mniej [zaleca się mieć] co najmniej 2 GB przestrzeni wymiany.

Porównaj swoje /proc/sys/vmustawienia ze zwykłą instalacją CentOS 5.3. Dodaj plik wymiany. Opuść swappinessi zobacz, czy dłużej pożyjesz.

pilcrow
źródło
Jaki jest najlepszy sposób sprawdzenia rozmiaru procesu w Pythonie? ps?
davidmytton
coś takiego ps -o user,pid,vsz="Mem(Kb)" -o cmd $PYTHON_PIDlub top (1), powinno to zrobić.
pilcrow
7

Możesz to łatwo naprawić

echo 1 > /proc/sys/vm/overcommit_memory

jeśli jesteś pewien, że Twój system ma wystarczającą ilość pamięci. Zobacz Linux ponad heurystykę .

serv-inc
źródło
1
Dziękuję bardzo! Takie proste rozwiązanie, uratowałeś mi dzień)
igolkotek
5

Nadal podejrzewam, że twój klient / użytkownik ma załadowany jakiś moduł jądra lub sterownik, który koliduje z clone()wywołaniem systemowym (może jakieś niejasne rozszerzenie bezpieczeństwa, coś w rodzaju LIDS, ale bardziej niejasne?) Lub w jakiś sposób zapełnia niektóre struktury danych jądra, które są niezbędne do fork()/ clone()do działania (tablica procesów, tablice stron, tablice deskryptorów plików itp.).

Oto odpowiednia część strony podręcznika fork(2):

BŁĘDY
       EAGAIN fork () nie może przydzielić wystarczającej ilości pamięci, aby skopiować tabele stron nadrzędnych i przydzielić strukturę zadań dla
              dziecko.

       EAGAIN Nie można było utworzyć nowego procesu, ponieważ napotkano limit zasobów RLIMIT_NPROC wywołującego. Do
              przekroczyć ten limit, proces musi mieć zdolność CAP_SYS_ADMIN lub CAP_SYS_RESOURCE.

       ENOMEM fork () nie mógł przydzielić niezbędnych struktur jądra, ponieważ pamięć jest ograniczona.

Sugeruję, aby użytkownik spróbował tego po uruchomieniu standardowego, ogólnego jądra i po załadowaniu tylko minimalnego zestawu modułów i sterowników (minimum niezbędne do uruchomienia aplikacji / skryptu). Stamtąd, zakładając, że działa w tej konfiguracji, mogą przeprowadzić wyszukiwanie binarne między tym a konfiguracją, w której występuje problem. Jest to standardowa procedura rozwiązywania problemów 101 z administratorem systemu.

Odpowiedni wiersz w Twoim straceto:

clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0xb7f12708) = -1 ENOMEM (Cannot allocate memory)

... Wiem, że inni mówili o wymianie i dostępności pamięci (i zalecałbym skonfigurowanie przynajmniej małej partycji wymiany, o ironio, nawet jeśli jest na dysku RAM ... ścieżki kodu przez jądro Linuksa, gdy ma nawet niewielka część dostępnej wymiany była wykonywana znacznie intensywniej niż te (ścieżki obsługi wyjątków), w których jest dostępna zerowa wymiana.

Podejrzewam jednak, że to nadal czerwony śledź.

Fakt, że freezgłasza 0 (ZERO) pamięci używanej przez pamięć podręczną i bufory, jest bardzo niepokojący. Podejrzewam, że freewynik ... i prawdopodobnie problem z aplikacją tutaj, są spowodowane przez jakiś zastrzeżony moduł jądra, który w jakiś sposób koliduje z alokacją pamięci.

Zgodnie ze stronami podręcznika dla fork () / clone () wywołanie systemowe fork () powinno zwrócić EAGAIN, jeśli wywołanie spowodowałoby naruszenie limitu zasobów (RLIMIT_NPROC) ... jednak nie mówi, czy ma zostać zwrócony EAGAIN przez inne naruszenia RLIMIT *. W każdym razie, jeśli twój cel / host ma jakieś dziwne Vormetric lub inne ustawienia bezpieczeństwa (lub nawet jeśli twój proces działa pod jakąś dziwną polityką SELinux), może to powodować tę awarię -ENOMEM.

Jest mało prawdopodobne, aby był to normalny, typowy problem z Linuksem / UNIXem. Dzieje się tam coś niestandardowego.

Jim Dennis
źródło
1
Serwer działa na bazie Media Template (dv), która wykorzystuje Virtuozzo do wirtualizacji.
davidmytton
Spróbuj przeszukać fora dyskusyjne Virtuozzo i system śledzenia błędów i być może poszukaj aktualizacji samego podsystemu Virtuozzo.
Jim Dennis
2

Czy próbowałeś użyć:

(status,output) = commands.getstatusoutput("ps aux")

Myślałem, że to rozwiązało dokładnie ten sam problem dla mnie. Ale potem mój proces skończył się śmiercią zamiast niepowodzeniem odrodzenia, co jest jeszcze gorsze.

Po kilku testach stwierdziłem, że dzieje się tak tylko w starszych wersjach Pythona: dzieje się tak z 2.6.5, ale nie z 2.7.2

Moje wyszukiwanie przywiodło mnie tutaj python-close_fds-issue , ale wyłączenie funkcji closed_fds nie rozwiązało problemu. Wciąż warto go przeczytać.

Odkryłem, że Python przecieka deskryptory plików, po prostu obserwując go:

watch "ls /proc/$PYTHONPID/fd | wc -l"

Podobnie jak ty, chcę przechwytywać dane wyjściowe polecenia i chcę uniknąć błędów OOM ... ale wygląda na to, że jedynym sposobem jest użycie mniej błędnej wersji Pythona. Nieidealny...

totaam
źródło
0

munmap (0xb7d28000, 4096) = 0
write (2, "OSError", 7) = 7

Widziałem niechlujny kod, który wygląda tak:

serrno = errno;
some_Syscall(...)
if (serrno != errno)
/* sound alarm: CATROSTOPHIC ERROR !!! */

Powinieneś sprawdzić, czy tak się dzieje w kodzie Pythona. Errno jest poprawne tylko w przypadku niepowodzenia wywołania systemowego.

Edytowano, aby dodać:

Nie mówisz, jak długo ten proces trwa. Potencjalni konsumenci pamięci

  • procesy rozwidlone
  • nieużywane struktury danych
  • biblioteki współdzielone
  • pliki mapowane w pamięci
codeDr
źródło
2
Tak, ale widzimy ze strace OP, że pierwsza awaria wywołania systemowego - z clone () - to ENOMEM, jak podano. Ten błąd jest zachowywany wraz z potknięciem się o małą pamięć w Pythonie poprzez konstrukcję śledzenia wstecznego, mimo że biblioteka C errnojest resetowana wiele razy po drodze.
pilcrow