Matikan pembunuh OOM Linux secara default?

37

Pembunuh OOM di Linux mendatangkan malapetaka dengan berbagai aplikasi setiap begitu sering, dan tampaknya tidak banyak yang benar-benar dilakukan pada sisi pengembangan kernel untuk meningkatkan ini. Tidakkah lebih baik, sebagai praktik terbaik ketika menyiapkan server baru , untuk membalikkan default pada overcommitting memori, yaitu, matikan ( vm.overcommit_memory=2) kecuali Anda tahu Anda menginginkannya untuk penggunaan khusus Anda? Dan apa yang menggunakan kasus-kasus di mana Anda tahu Anda ingin overcommitting?

Sebagai bonus, karena perilaku dalam hal vm.overcommit_memory=2tergantung pada vm.overcommit_ratiodan menukar ruang, apa aturan praktis yang baik untuk mengukur dua yang terakhir sehingga seluruh pengaturan ini tetap berfungsi dengan baik?

Peter Eisentraut
sumber

Jawaban:

63

Analogi yang menarik (dari http://lwn.net/Articles/104179/ ):

Sebuah perusahaan pesawat menemukan bahwa lebih murah menerbangkan pesawatnya dengan bahan bakar lebih sedikit. Pesawat akan lebih ringan dan menggunakan lebih sedikit bahan bakar dan uang dihemat. Namun pada kesempatan langka jumlah bahan bakar tidak mencukupi, dan pesawat akan jatuh. Masalah ini diselesaikan oleh para insinyur perusahaan dengan mengembangkan mekanisme OOF (kehabisan bahan bakar) khusus. Dalam kasus darurat seorang penumpang dipilih dan diusir dari pesawat. (Bila perlu, prosedur diulangi.) Sejumlah besar teori dikembangkan dan banyak publikasi dikhususkan untuk masalah pemilihan korban yang tepat untuk dikeluarkan. Haruskah korban dipilih secara acak? Atau haruskah seseorang memilih orang yang terberat? Atau yang tertua? Jika penumpang membayar agar tidak dikeluarkan, sehingga korban akan menjadi yang termiskin di atas kapal? Dan jika misalnya orang terberat dipilih, haruskah ada pengecualian khusus jika itu adalah pilot? Haruskah penumpang kelas satu dibebaskan? Sekarang mekanisme OOF ada, itu akan diaktifkan setiap sekarang dan kemudian, dan mengeluarkan penumpang bahkan ketika tidak ada kekurangan bahan bakar. Para insinyur masih mempelajari dengan tepat bagaimana kerusakan ini disebabkan.

An̲̳̳drew
sumber
11
Saya sangat menikmatinya, terima kasih telah menggali itu.
Nick Bolton
32

Pembunuh OOM hanya mendatangkan malapetaka jika Anda telah membebani sistem Anda secara berlebihan. Berikan pertukaran yang cukup, dan jangan menjalankan aplikasi yang tiba-tiba memutuskan untuk memakan sejumlah besar RAM, dan Anda tidak akan memiliki masalah.

Untuk secara khusus menjawab pertanyaan Anda:

  • Saya tidak berpikir itu ide yang baik untuk mematikan komitmen berlebihan dalam kasus umum; sangat sedikit aplikasi yang ditulis untuk menangani dengan benar brk(2) (dan pembungkus yang menggunakannya, seperti malloc(3)) mengembalikan kesalahan. Ketika saya bereksperimen dengan ini di pekerjaan saya sebelumnya, itu dianggap lebih merepotkan untuk mendapatkan semua yang mampu menangani kesalahan kehabisan memori daripada hanya untuk berurusan dengan konsekuensi dari OOM (yang, dalam kasus kami, jauh lebih buruk daripada harus me-restart layanan sesekali jika OOM terjadi - kami harus me-reboot seluruh cluster, karena GFS adalah tumpukan kotoran yang mengepul).
  • Anda ingin overcommitting untuk proses apa pun yang overcommits memory. Dua penyebab paling umum di sini adalah Apache dan JVM, tetapi banyak aplikasi melakukan ini pada tingkat yang lebih besar atau lebih kecil. Mereka pikir mereka mungkin membutuhkan banyak memori di beberapa titik di masa depan, jadi mereka langsung mengambil sebagian besar. Pada sistem yang terlalu aktif, kernel berjalan "meh, apa pun, datang mengganggu saya ketika Anda benar-benar ingin menulis ke halaman-halaman itu" dan tidak ada hal buruk terjadi. Pada sistem overcommit-off, kernel mengatakan "tidak, Anda tidak dapat memiliki banyak memori, jika Anda kebetulan menulis semuanya pada beberapa titik di masa depan saya bertulang, jadi tidak ada memori untuk Anda!" dan alokasi gagal. Karena tidak adadi luar sana berbunyi "oh, OK, bisakah saya memiliki jumlah segmen data proses yang lebih kecil ini?", lalu prosesnya (a) berhenti dengan kesalahan kehabisan memori, atau (b) tidak memeriksa kode kembali dari malloc, berpikir tidak masalah untuk pergi, dan menulis ke lokasi memori yang tidak valid, menyebabkan segfault. Untungnya, JVM melakukan semua itu prealloc pada startup (jadi JVM Anda langsung atau mati langsung mati, yang biasanya Anda perhatikan), tetapi Apache melakukan itu hal-hal yang funky dengan setiap anak baru, yang dapat memiliki efek menarik dalam produksi (tidak dapat diproduksi ulang "tidak menangani koneksi "Jenis kegembiraan).
  • Saya tidak ingin mengatur overcommit_ratio saya lebih tinggi dari default 50%. Sekali lagi, dari pengujian saya, walaupun pengaturannya sekitar 80 atau 90 mungkin terdengar seperti ide yang keren, kernel membutuhkan potongan besar memori pada waktu yang tidak nyaman, dan sistem yang terisi penuh dengan rasio overcommit tinggi cenderung memiliki memori cadangan yang tidak mencukupi ketika kernel membutuhkannya (mengarah ke ketakutan, sampar, dan oopses). Jadi bermain dengan overcommit memperkenalkan mode kegagalan yang baru, bahkan lebih menyenangkan - daripada hanya memulai kembali proses apa pun yang OOM ketika Anda kehabisan memori, sekarang mesin Anda macet, yang menyebabkan pemadaman segala sesuatu pada mesin. LUAR BIASA!
  • Ruang swap dalam sistem bebas-komit tergantung pada seberapa banyak memori yang diminta-tetapi-tidak terpakai yang dibutuhkan aplikasi Anda, ditambah margin keamanan yang sehat. Mengolah apa yang dibutuhkan dalam kasus tertentu dibiarkan sebagai latihan untuk pembaca.

Pada dasarnya, pengalaman saya adalah mematikan overcommit adalah eksperimen yang bagus yang jarang berhasil dalam praktiknya seperti yang terdengar dalam teori. Ini sesuai dengan pengalaman saya dengan merdu lain di kernel - pengembang kernel Linux hampir selalu lebih pintar dari Anda, dan defaultnya bekerja paling baik untuk sebagian besar kasus. Biarkan mereka sendiri, dan alih-alih cari proses apa yang mengalami kebocoran dan perbaiki.

womble
sumber
2
Saya tidak ingin proses pencadangan saya terbunuh karena seseorang melakukan server web saya. Pengecualian baik-baik saja tetapi standarnya adalah keamanan dan konsistensi. Optimalisasi seperti OOM harus diaktifkan secara manual IMHO. Ini seperti pengkodean, kode Anda bersih, dan kemudian dioptimalkan. Komitmen berlebihan adalah fitur yang bagus, tetapi seharusnya tidak menjadi default.
Aki
1
Jika Anda tidak ingin proses pencadangan Anda terbunuh karena seseorang melakukan server Web Anda, jangan konfigurasikan server web Anda sedemikian rupa sehingga DoS dapat menyebabkan sumber daya pada sistem menjadi kewalahan.
womble
Saya memiliki 8GB RAM dan hanya menjalankan Firefox dan mesin virtual terkadang menghasilkan pembunuh OOM yang membunuh VM. Mengkompilasi Unreal Engine 4, setiap doa dentang membutuhkan 1 ~ 1,5GB memori dan sekali lagi, pembunuh OOM membunuh satu setiap sekarang dan kemudian. Sekarang saya umumnya baik-baik saja dengan itu, tanpa pembunuh OOM mereka mungkin akan segfault pula. Hanya saja setiap kali pembunuh OOM ingin membunuh suatu proses, sistem saya membeku selama 10 menit sebelum proses yang buruk benar-benar dibunuh. Bug mungkin? Yang paling disukai. Apakah saya menginginkannya? Tentu saja tidak. Dan itulah alasan Anda mengapa seseorang mungkin ingin menonaktifkan pembunuh OOM.
Shahbaz
1
Jika Anda melakukan semua itu pada sebuah kotak, Anda membutuhkan lebih banyak RAM, dan menonaktifkan overcommit hanya akan memperburuknya.
Ben Lutgens
6

Hmm, saya tidak sepenuhnya yakin dengan argumen yang mendukung pembunuhan yang berlebihan dan OOM ... Ketika womble menulis,

"Pembunuh OOM hanya mendatangkan malapetaka jika kamu telah membebani sistemmu secara berlebihan. Berikan pertukaran yang cukup, dan jangan menjalankan aplikasi yang tiba-tiba memutuskan untuk memakan sejumlah besar RAM, dan kamu tidak akan memiliki masalah."

Dia menjelaskan tentang skenario lingkungan di mana overcommit dan pembunuh OOM tidak ditegakkan, atau tidak bertindak 'benar-benar' (jika semua aplikasi mengalokasikan memori sesuai kebutuhan, dan ada cukup memori virtual untuk dialokasikan, penulisan memori akan mengikuti alokasi memori tanpa kesalahan, jadi kami tidak dapat benar-benar berbicara tentang sistem overcommited walaupun strategi overcommit diaktifkan). Itu tentang pengakuan implisit bahwa overcommit dan pembunuh OOM bekerja paling baik ketika intervensi mereka tidak diperlukan, yang entah bagaimana dibagikan oleh sebagian besar pendukung strategi ini, sejauh yang saya tahu (dan saya akui saya tidak bisa mengatakan banyak ...). Selain itu, merujuk pada aplikasi dengan perilaku tertentu ketika mengalokasikan memori membuat saya berpikir bahwa penanganan tertentu dapat disesuaikan pada tingkat distribusi, daripada memiliki standar,

Untuk apa pun yang menyangkut JVM, yah, ini adalah mesin virtual, sampai taraf tertentu ia perlu mengalokasikan semua sumber daya yang dibutuhkan pada startup, sehingga ia dapat menciptakan lingkungan 'palsu' untuk aplikasinya, dan menjaga sumber daya yang tersedia terpisah dari host. lingkungan, sejauh mungkin. Dengan demikian, mungkin lebih baik gagal pada saat startup, daripada setelah beberapa saat sebagai akibat dari kondisi OOM 'eksternal' (yang disebabkan oleh overcommit / pembunuh OOM / apa pun), atau tetap menderita karena kondisi seperti itu mengganggu sendiri strategi penanganan OOM internal (secara umum, VM harus mendapatkan sumber daya yang diperlukan dari awal dan sistem host harus 'mengabaikan' mereka sampai akhir, dengan cara yang sama jumlah ram fisik yang dibagikan dengan kartu grafis tidak pernah - dan tidak dapat - tersentuh oleh OS).

Tentang Apache, saya ragu bahwa seluruh server kadang-kadang terbunuh dan dimulai kembali lebih baik daripada membiarkan satu anak, bersama dengan satu koneksi, gagal dari permulaannya (= anak / koneksi) (seolah-olah itu adalah contoh baru dari JVM dibuat setelah menjalankan instance lain untuk sementara waktu). Saya kira 'solusi' terbaik mungkin tergantung pada konteks tertentu. Misalnya, mempertimbangkan layanan e-commerce, mungkin jauh lebih baik untuk memiliki, kadang-kadang, beberapa koneksi ke grafik belanja gagal secara acak alih-alih kehilangan seluruh layanan, dengan risiko, misalnya, untuk mengganggu penyelesaian pesanan yang sedang berlangsung, atau (mungkin lebih buruk) proses pembayaran, dengan semua konsekuensi kasus (mungkin tidak berbahaya, tapi mungkin berbahaya - dan yang pasti, ketika masalah muncul,

Dengan cara yang sama, pada workstation proses yang menghabiskan banyak sumber daya, dan dengan demikian menjadi pilihan pertama untuk pembunuh OOM, bisa menjadi aplikasi yang intensif memori, seperti transcoder video, atau perenderan perangkat lunak, kemungkinan satu-satunya aplikasi pengguna ingin disentuh. Pertimbangan ini mengisyaratkan saya bahwa kebijakan default pembunuh OOM terlalu agresif. Ia menggunakan pendekatan "kecocokan terburuk" yang entah bagaimana mirip dengan beberapa sistem file (OOMK mencoba dan membebaskan sebanyak mungkin memori, sambil mengurangi jumlah subproses yang terbunuh, untuk mencegah intervensi lebih lanjut dalam waktu singkat, seperti dan juga fs dapat mengalokasikan lebih banyak ruang disk daripada yang sebenarnya dibutuhkan untuk file tertentu, untuk mencegah alokasi lebih lanjut jika file tumbuh dan dengan demikian mencegah fragmentasi, sampai batas tertentu).

Namun, saya berpikir bahwa kebijakan yang berlawanan, seperti pendekatan 'paling cocok', bisa lebih disukai, sehingga untuk membebaskan memori tepat yang diperlukan pada titik tertentu, dan tidak perlu repot dengan proses 'besar', yang mungkin membuang-buang memori, tetapi juga mungkin tidak, dan kernel tidak dapat mengetahui hal itu (hmm, saya dapat membayangkan bahwa menjaga jejak akses halaman dihitung dan waktu dapat mengisyaratkan jika suatu proses mengalokasikan memori itu tidak perlu lagi, jadi untuk menebak apakah suatu proses adalah pemborosan memori atau hanya menggunakan banyak, tetapi penundaan akses harus ditimbang pada siklus cpu untuk membedakan memori yang terbuang dari memori dan aplikasi intensif cpu, tetapi, sementara berpotensi tidak akurat, heuristik seperti itu dapat memiliki overhead yang berlebihan).

Selain itu, mungkin tidak benar bahwa membunuh lebih sedikit proses yang mungkin selalu merupakan pilihan yang baik. Misalnya, pada lingkungan desktop (mari kita pikirkan nettop atau netbook dengan sumber daya terbatas, sebagai contoh) pengguna mungkin menjalankan browser dengan beberapa tab (dengan demikian, memakan memori - mari kita asumsikan ini adalah pilihan pertama untuk OOMK) , ditambah beberapa aplikasi lain (pengolah kata dengan data yang tidak disimpan, klien email, pembaca pdf, pemutar media, ...), ditambah beberapa daemon (sistem), ditambah beberapa contoh manajer file. Sekarang, kesalahan OOM terjadi, dan OOMK memilih untuk membunuh browser saat pengguna melakukan sesuatu yang dianggap 'penting' di internet ... pengguna akan kecewa. Di sisi lain, menutup beberapa file manager '

Ngomong-ngomong, saya pikir pengguna harus diaktifkan untuk mengambil keputusan sendiri tentang apa yang harus dilakukan. Dalam sistem desktop (= interaktif), yang seharusnya relatif cukup mudah dilakukan, asalkan cukup sumber daya dicadangkan untuk meminta pengguna menutup aplikasi apa pun (tetapi bahkan menutup beberapa tab saja sudah cukup) dan menangani pilihannya (sebuah opsi bisa terdiri dari membuat file swap tambahan, jika ada ruang yang cukup). Untuk layanan (dan secara umum), saya juga akan mempertimbangkan dua peningkatan lebih lanjut yang mungkin: satu adalah login intervensi pembunuh OOM, serta proses memulai / forking kegagalan sedemikian rupa sehingga kegagalan dapat dengan mudah didebug (misalnya, API dapat informasikan proses mengeluarkan pembuatan proses baru atau forking - dengan demikian, server seperti Apache, dengan tambalan yang tepat, dapat memberikan logging yang lebih baik untuk kesalahan tertentu); ini dapat dilakukan secara independen dari komitmen berlebihan / OOMK dalam upaya; di tempat kedua, tetapi tidak penting, suatu mekanisme dapat dibentuk untuk menyempurnakan algoritma OOMK - Saya tahu adalah mungkin, sampai batas tertentu, untuk menentukan kebijakan spesifik pada suatu proses dengan proses, tetapi saya bertujuan mekanisme konfigurasi 'terpusat', berdasarkan pada satu atau lebih daftar nama aplikasi (atau id) untuk mengidentifikasi proses yang relevan dan memberi mereka tingkat kepentingan tertentu (sesuai atribut yang tercantum); mekanisme seperti itu harus (atau paling tidak bisa) juga berlapis, sehingga mungkin ada daftar tingkat atas yang ditetapkan pengguna, daftar yang ditentukan sistem (distribusi-), dan entri yang ditentukan aplikasi (tingkat bawah) (jadi , misalnya, pengelola file DE dapat menginstruksikan OOMK untuk secara aman membunuh instance apa pun,

Selain itu, API dapat disediakan untuk memungkinkan aplikasi untuk menaikkan atau menurunkan tingkat 'kepentingannya' pada saat run-time (sehubungan dengan tujuan manajemen memori dan tanpa memprioritaskan eksekusi), sehingga, misalnya, pengolah kata dapat memulai dengan 'kepentingan' yang rendah tetapi naikkan karena beberapa data ditahan sebelum dibilas ke suatu file, atau operasi penulisan sedang dilakukan, dan kepentingan yang lebih rendah lagi begitu operasi tersebut berakhir (secara analog, manajer file dapat mengubah level ketika dilewatkan dari hanya membiarkan file untuk berurusan dengan data dan sebaliknya, alih-alih menggunakan proses yang terpisah, dan Apache dapat memberikan tingkat kepentingan yang berbeda untuk anak yang berbeda, atau mengubah status anak berdasarkan beberapa kebijakan yang diputuskan oleh sysadmin dan diekspos melalui Apache - atau server jenis apa pun lainnya. - pengaturan). Tentu saja, API seperti itu dapat dan akan disalahgunakan / disalahgunakan, tetapi saya pikir itu adalah masalah kecil dibandingkan dengan kernel yang secara sewenang-wenang membunuh proses untuk mengosongkan memori tanpa informasi yang relevan tentang apa yang terjadi pada sistem (dan konsumsi memori / waktu pembuatan atau yang serupa 'tidak cukup relevan atau' memvalidasi 'bagi saya) - hanya pengguna, admin, dan penulis program yang benar-benar dapat menentukan apakah suatu proses' masih diperlukan 'karena beberapa alasan, apa alasannya, dan / atau jika aplikasi dalam keadaan terkemuka kehilangan data atau kerusakan / masalah lain jika terbunuh; Namun, beberapa asumsi belum dapat dibuat, misalnya mencari sumber daya dari jenis tertentu (deskriptor file, soket jaringan, dll.) yang diperoleh oleh suatu proses dan dengan operasi yang tertunda dapat mengetahui apakah suatu proses harus dalam 'keadaan' yang lebih tinggi daripada satu set,

Atau, hindari overcommitting dan biarkan kernel melakukan apa yang harus dilakukan kernel, mengalokasikan sumber daya (tetapi tidak menyelamatkan mereka secara sewenang-wenang seperti yang dilakukan pembunuh OOM), menjadwalkan proses, mencegah kelaparan dan kebuntuan (atau menyelamatkan dari mereka), memastikan preemption penuh dan pemisahan ruang memori, dan sebagainya ...

Saya juga akan menghabiskan lebih banyak kata tentang pendekatan berlebihan. Dari diskusi lain saya telah membuat gagasan bahwa salah satu masalah utama tentang overcommit (baik sebagai alasan untuk menginginkannya dan sebagai sumber masalah yang mungkin terjadi) terdiri dari penanganan garpu: jujur, saya tidak tahu bagaimana tepatnya salinannya. strategi on-write diimplementasikan, tetapi saya pikir bahwa setiap kebijakan agresif (atau optimis) dapat dikurangi dengan strategi lokalitas swap-sama. Artinya, alih-alih hanya mengkloning (dan menyesuaikan) halaman kode proses bercabang-cabang dan struktur penjadwalan, beberapa halaman data lain dapat disalin sebelum penulisan yang sebenarnya, memilih di antara halaman-halaman yang diakses oleh proses induk untuk menulis lebih sering (yaitu, menggunakan penghitung untuk operasi penulisan).

Semuanya, tentu saja, IMHO.


sumber
5
"Selain itu, API dapat disediakan untuk memungkinkan aplikasi untuk naik atau menurunkan tingkat 'kepentingan' mereka pada saat run-time" Pentingnya /proc/$PID/oom_adj.
Vi.
1
Mengenai JVM, ada gotcha yang membuat Anda ingin memory overcommit dalam beberapa kasus: jika Anda ingin membuat JVM lain dari JVM asli Anda, ia akan memanggil fork (). Panggilan garpu akan mengalokasikan memori sebanyak proses aslinya (pertama), sampai benar-benar memulai proses. Jadi katakanlah Anda memiliki JVM 4GB dan ingin membuat JVM 512KB baru darinya, kecuali jika Anda memiliki overcommit, Anda akan memerlukan memori 8GB untuk melakukan itu ...
alci
4
@ Vi. Tampaknya sekarang/proc/$PID/oom_score_adj
m3nda
1

Jika memori Anda habis digunakan oleh proses sejauh yang mungkin dapat mengancam stabilitas sistem, maka pembunuh OOM muncul dalam gambar. Ini adalah tugas Pembunuh OOM untuk membunuh proses sampai memori yang cukup dibebaskan untuk kelancaran fungsi sisa proses. Pembunuh OOM harus memilih proses “terbaik” untuk dibunuh. "Terbaik" di sini mengacu pada proses yang akan membebaskan memori maksimum setelah membunuh dan juga paling tidak penting bagi sistem. Tujuan utamanya adalah untuk membunuh jumlah proses yang meminimalkan kerusakan yang dilakukan dan pada saat yang sama memaksimalkan jumlah memori yang dibebaskan. Untuk memfasilitasi ini, kernel mempertahankan oom_score untuk setiap proses. Anda dapat melihat oom_score masing-masing proses di sistem file / proc di bawah direktori pid

# cat /proc/10292/oom_score

Semakin tinggi nilai oom_score dari setiap proses, semakin tinggi kemungkinan terbunuh oleh Pembunuh OOM dalam situasi di luar memori.

Kredit: - Kernel Linux memulai pembunuh OOM

dinkey jhanwar
sumber