Catatan: Pertanyaan ini awalnya ditanyakan di sini tetapi waktu hadiah telah habis meskipun jawaban yang dapat diterima sebenarnya tidak ditemukan. Saya menanyakan kembali pertanyaan ini termasuk semua detail yang diberikan dalam pertanyaan asli.
Skrip python menjalankan serangkaian fungsi kelas setiap 60 detik menggunakan modul sched :
# sc is a sched.scheduler instance
sc.enter(60, 1, self.doChecks, (sc, False))
Skrip berjalan sebagai proses daemonisasi menggunakan kode di sini .
Sejumlah metode kelas yang disebut sebagai bagian dari doChecks menggunakan modul subprocess untuk memanggil fungsi sistem guna mendapatkan statistik sistem:
ps = subprocess.Popen(['ps', 'aux'], stdout=subprocess.PIPE).communicate()[0]
Ini berjalan dengan baik untuk jangka waktu tertentu sebelum seluruh skrip mengalami crash dengan kesalahan berikut:
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
Output free -m di server setelah skrip rusak adalah:
$ free -m
total used free shared buffers cached
Mem: 894 345 549 0 0 0
-/+ buffers/cache: 345 549
Swap: 0 0 0
Server menjalankan CentOS 5.3. Saya tidak dapat mereproduksi di kotak CentOS saya sendiri atau dengan pengguna lain yang melaporkan masalah yang sama.
Saya telah mencoba sejumlah hal untuk men-debug ini seperti yang disarankan dalam pertanyaan asli:
Mencatat keluaran free -m sebelum dan sesudah panggilan Popen. Tidak ada perubahan signifikan dalam penggunaan memori yaitu memori tidak digunakan secara bertahap saat skrip dijalankan.
Saya menambahkan close_fds = Sesuai dengan panggilan Popen tetapi ini tidak membuat perbedaan - skrip masih macet dengan kesalahan yang sama. Disarankan di sini dan di sini .
Saya memeriksa batas yang menunjukkan (-1, -1) pada RLIMIT_DATA dan RLIMIT_AS seperti yang disarankan di sini .
Sebuah artikel menyarankan tidak adanya ruang swap mungkin menjadi penyebabnya, tetapi swap sebenarnya tersedia sesuai permintaan (menurut web host) dan ini juga disarankan sebagai penyebab palsu di sini .
Proses ditutup karena itu adalah perilaku menggunakan .communicate () seperti yang dicadangkan oleh kode sumber Python dan komentar di sini .
Seluruh pemeriksaan dapat ditemukan di GitHub di sini dengan fungsi getProcesses yang ditentukan dari baris 442. Ini dipanggil oleh doChecks () mulai dari baris 520.
Skrip dijalankan dengan strace dengan output berikut sebelum crash:
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) = ?
/var/log/messages
, ataudmesg
perintahkan.Jawaban:
Sebagai aturan umum (yaitu di vanili kernel),
fork
/clone
kegagalan denganENOMEM
terjadi secara khusus karena baik jujur kepada Allah out-of-memori kondisi (dup_mm
,dup_task_struct
,alloc_pid
,mpol_dup
,mm_init
dll parau), atau karenasecurity_vm_enough_memory_mm
gagal Anda sementara menegakkan yang kebijakan overcommit .Mulailah dengan memeriksa vmsize dari proses yang gagal untuk melakukan fork, pada saat percobaan fork, dan kemudian bandingkan dengan jumlah memori bebas (fisik dan swap) yang berkaitan dengan kebijakan overcommit (colokkan angkanya.)
Dalam kasus khusus Anda, perhatikan bahwa Virtuozzo memiliki pemeriksaan tambahan dalam penegakan komitmen berlebihan . Selain itu, saya tidak yakin seberapa besar kendali yang benar-benar Anda miliki, dari dalam penampung Anda, atas konfigurasi swap dan overcommit (untuk memengaruhi hasil penegakan.)
Sekarang, untuk benar-benar bergerak maju, saya akan mengatakan Anda memiliki dua opsi :
CATATAN bahwa upaya pengkodean mungkin sia-sia jika ternyata itu bukan Anda, tetapi beberapa orang lain bertumpuk dalam contoh yang berbeda di server yang sama saat Anda menjalankan amock.
Dari segi memori, kita sudah tahu bahwa
subprocess.Popen
usefork
/clone
under the hood , yang berarti bahwa setiap kali Anda memanggilnya, Anda meminta sekali lagi memori sebanyak Python sudah menghabiskan , yaitu dalam ratusan MB tambahan, semuanya untuk kemudianexec
10kB kecil yang dapat dieksekusi sepertifree
ataups
. Dalam kasus kebijakan overcommit yang tidak menguntungkan, Anda akan segera melihatnyaENOMEM
.Alternatif untuk
fork
yang tidak memiliki tabel halaman induk ini dll. Masalah salin adalahvfork
danposix_spawn
. Tetapi jika Anda tidak ingin menulis ulang potongansubprocess.Popen
dalam istilahvfork
/posix_spawn
, pertimbangkan untuk menggunakansuprocess.Popen
hanya sekali, di awal skrip Anda (ketika jejak memori Python minimal), untuk menelurkan skrip shell yang kemudian menjalankanfree
/ps
/sleep
dan apa pun di a loop sejajar dengan skrip Anda; polling keluaran skrip atau baca secara sinkron, mungkin dari utas terpisah jika Anda memiliki hal lain yang harus ditangani secara asinkron - lakukan pemrosesan data Anda dengan Python tetapi serahkan percabangan ke proses bawahan.NAMUN , dalam kasus khusus Anda, Anda dapat melewati permintaan
ps
danfree
sama sekali; bahwa informasi sudah tersedia untuk Anda di Python langsung dariprocfs
, apakah Anda memilih untuk mengakses sendiri atau melalui perpustakaan dan / atau paket yang ada . Jikaps
danfree
merupakan satu-satunya utilitas yang Anda jalankan, maka Anda dapat melakukannyasubprocess.Popen
sepenuhnya .Akhirnya, apa pun yang Anda lakukan sejauh
subprocess.Popen
yang bersangkutan, jika skrip Anda membocorkan memori, Anda akan tetap terbentur dinding pada akhirnya. Awasi, dan periksa kebocoran memori .sumber
gc.collect()
sebelumsubprocess.Popen
membantu dalam kasus ketika pengumpul sampah tidak berjalan untuk sementara waktu./proc/fd/maps
untuk menentukan apakah memori yang terlaluMelihat outputnya
free -m
menurut saya Anda sebenarnya tidak memiliki memori swap yang tersedia. Saya tidak yakin apakah di Linux swap akan selalu tersedia secara otomatis sesuai permintaan, tetapi saya mengalami masalah yang sama dan tidak ada jawaban di sini yang benar-benar membantu saya. Namun, menambahkan beberapa memori swap, memperbaiki masalah dalam kasus saya jadi karena ini dapat membantu orang lain menghadapi masalah yang sama, saya memposting jawaban saya tentang cara menambahkan swap 1GB (di Ubuntu 12.04 tetapi seharusnya berfungsi sama untuk distribusi lain.)Anda dapat terlebih dahulu memeriksa apakah ada memori swap yang diaktifkan.
jika kosong, itu berarti Anda tidak mengaktifkan swap. Untuk menambahkan swap 1GB:
Tambahkan baris berikut ke
fstab
untuk membuat swap permanen.Sumber dan informasi lebih lanjut dapat ditemukan di sini .
sumber
swap mungkin bukan ikan haring merah yang disarankan sebelumnya. Seberapa besar proses python yang dimaksud sebelum
ENOMEM
?Di bawah kernel 2.6,
/proc/sys/vm/swappiness
mengontrol seberapa agresif kernel akan beralih ke swap, danovercommit*
mencatat seberapa banyak dan seberapa tepatnya kernel dapat membagi memori dengan kedipan dan anggukan. Seperti status hubungan facebook Anda, itu rumit .tetapi tidak sesuai dengan output dari
free(1)
perintah Anda , yang menunjukkan tidak ada ruang swap yang dikenali oleh instance server Anda. Sekarang, host web Anda mungkin tahu lebih banyak daripada saya tentang topik ini, tetapi sistem RHEL / CentOS virtual yang saya gunakan telah melaporkan swap tersedia untuk OS tamu.Mengadaptasi Red Hat KB Article 15252 :
Bandingkan
/proc/sys/vm
pengaturan Anda dengan instalasi CentOS 5.3 biasa. Tambahkan file swap. Ratchet turunswappiness
dan lihat apakah Anda hidup lebih lama.sumber
ps -o user,pid,vsz="Mem(Kb)" -o cmd $PYTHON_PID
, atau top (1), harus melakukannya.Untuk perbaikan yang mudah, Anda bisa
jika Anda yakin bahwa sistem Anda memiliki cukup memori. Lihat Linux over commit heuristic .
sumber
Saya terus mencurigai bahwa pelanggan / pengguna Anda memiliki beberapa modul kernel atau driver yang dimuat yang mengganggu
clone()
panggilan sistem (mungkin beberapa peningkatan keamanan yang tidak jelas, sesuatu seperti LIDS tetapi lebih tidak jelas?) Atau entah bagaimana mengisi beberapa struktur data kernel yang diperlukan untukfork()
/clone()
untuk mengoperasikan (tabel proses, tabel halaman, tabel deskriptor file, dll).Inilah bagian yang relevan dari
fork(2)
halaman manual:Saya sarankan agar pengguna mencoba ini setelah boot ke dalam sebuah stok, kernel generik dan hanya dengan set minimal modul dan driver yang dimuat (minimal diperlukan untuk menjalankan aplikasi / skrip Anda). Dari sana, dengan asumsi itu berfungsi dalam konfigurasi itu, mereka dapat melakukan pencarian biner antara itu dan konfigurasi yang menunjukkan masalah tersebut. Ini adalah pemecahan masalah sysadmin standar 101.
Baris yang relevan di Anda
strace
adalah:... Saya tahu orang lain telah membicarakan tentang swap dan ketersediaan memori (dan saya akan merekomendasikan agar Anda mengatur setidaknya partisi swap kecil, ironisnya bahkan jika itu ada di disk RAM ... jalur kode melalui kernel Linux ketika ada bahkan sedikit swap yang tersedia telah dilakukan jauh lebih luas daripada yang (jalur penanganan pengecualian) di mana tidak ada swap yang tersedia.
Namun saya curiga ini masih ikan haring.
Fakta bahwa
free
melaporkan 0 (ZERO) memory yang digunakan oleh cache dan buffer sangat mengganggu. Saya menduga bahwafree
output ... dan mungkin masalah aplikasi Anda di sini, disebabkan oleh beberapa modul kernel berpemilik yang mengganggu alokasi memori dalam beberapa cara.Menurut halaman manual untuk fork () / clone () panggilan sistem fork () harus mengembalikan EAGAIN jika panggilan Anda menyebabkan pelanggaran batas sumber daya (RLIMIT_NPROC) ... namun, tidak disebutkan apakah EAGAIN akan dikembalikan oleh pelanggaran RLIMIT * lainnya. Bagaimanapun jika target / host Anda memiliki semacam Vormetric aneh atau pengaturan keamanan lainnya (atau bahkan jika proses Anda berjalan di bawah beberapa kebijakan SELinux yang aneh) maka itu mungkin menyebabkan kegagalan -ENOMEM ini.
Ini sangat tidak mungkin menjadi masalah Linux / UNIX run-of-the-mill normal. Ada sesuatu yang tidak standar di sana.
sumber
Sudahkah Anda mencoba menggunakan:
Saya pikir ini telah memperbaiki masalah yang sama persis untuk saya. Tapi kemudian proses saya berakhir terbunuh bukannya gagal bertelur, yang bahkan lebih buruk ..
Setelah beberapa pengujian saya menemukan bahwa ini hanya terjadi pada versi python yang lebih lama: itu terjadi dengan 2.6.5 tetapi tidak dengan 2.7.2
Pencarian saya telah membawa saya ke sini python-close_fds-issue , tetapi membatalkan pengaturan closed_fds tidak menyelesaikan masalah. Ini masih layak dibaca.
Saya menemukan bahwa python membocorkan deskriptor file hanya dengan mengawasinya:
Seperti Anda, saya ingin menangkap keluaran perintah, dan saya ingin menghindari kesalahan OOM ... tetapi sepertinya satu-satunya cara bagi orang-orang untuk menggunakan versi Python yang tidak terlalu bermasalah. Tidak ideal ...
sumber
Saya telah melihat kode ceroboh yang terlihat seperti ini:
Anda harus memeriksa untuk melihat apakah ini yang terjadi pada kode python. Errno hanya valid jika panggilan sistem yang dilanjutkan gagal.
Diedit untuk menambahkan:
Anda tidak mengatakan berapa lama proses ini berlangsung. Kemungkinan konsumen memori
sumber
errno
disetel ulang berkali-kali di sepanjang jalan.Mungkin Anda bisa
Ini bekerja untuk kasus saya.
Referensi: https://github.com/openai/gym/issues/110#issuecomment-220672405
sumber