ฉันไม่ใช่นักพัฒนาเคอร์เนล แต่ฉันใช้เวลาหลายปีในการแก้ไขปัญหานี้เพราะฉันพบเจอปัญหานี้หลายครั้ง จริง ๆ แล้วฉันได้คำอุปมาสำหรับสถานการณ์ทั้งหมดดังนั้นให้ฉันบอกคุณว่า ฉันจะสมมติในเรื่องของฉันว่าไม่มีอะไรอย่าง "สลับ" การสลับไม่เข้าท่ากับ RAM ขนาด 32 GB ในปัจจุบัน
ลองนึกภาพพื้นที่ใกล้เคียงของคุณที่มีน้ำเชื่อมต่อกับแต่ละอาคารผ่านท่อและเมืองจำเป็นต้องจัดการความจุ สมมติว่าคุณมีการผลิตน้ำเพียง 100 หน่วยต่อวินาที (และความจุที่ไม่ได้ใช้ทั้งหมดจะสูญเปล่าเพราะคุณไม่มีถังเก็บน้ำ) แต่ละบ้าน (home = แอปเล็ก ๆ น้อย ๆ เทอร์มินัลวิดเจ็ตนาฬิกาและอื่น ๆ ) ต้องใช้น้ำ 1 หน่วยต่อวินาที ทั้งหมดนี้ดีและดีเพราะประชากรของคุณเป็น 90 ดังนั้นทุกคนจึงได้รับน้ำเพียงพอ
ตอนนี้นายกเทศมนตรี (= คุณ) ตัดสินใจว่าคุณต้องการเปิดร้านอาหารขนาดใหญ่ (= เบราว์เซอร์) ร้านอาหารนี้จะเป็นที่ตั้งของพ่อครัวหลายคน (= แท็บเบราว์เซอร์) ผู้ปรุงแต่ละคนต้องการน้ำ 1 หน่วยต่อวินาที คุณเริ่มต้นด้วยพ่อครัว 10 คนดังนั้นปริมาณการใช้น้ำโดยรวมสำหรับพื้นที่ใกล้เคียงทั้งหมดคือ 100 หน่วยน้ำซึ่งยังคงดีอยู่
ตอนนี้สิ่งที่สนุกเริ่มต้นขึ้น: คุณจ้างคนทำอาหารเข้ามาในร้านอาหารของคุณซึ่งทำให้ความต้องการน้ำทั้งหมด 101 ซึ่งคุณไม่มี คุณต้องทำอะไรสักอย่าง
การจัดการน้ำ (= เคอร์เนล) มี 3 ตัวเลือก
1.ตัวเลือกแรกคือยกเลิกการเชื่อมต่อบริการสำหรับบ้านที่ไม่ได้ใช้น้ำเมื่อเร็ว ๆ นี้ นี่เป็นเรื่องปกติ แต่ถ้าบ้านที่ถูกตัดการเชื่อมต่อต้องการใช้น้ำอีกครั้งพวกเขาจะต้องผ่านกระบวนการลงทะเบียนที่มีความยาวอีกครั้ง การจัดการสามารถตัดการเชื่อมต่อบ้านหลายหลังเพื่อเพิ่มทรัพยากรน้ำให้มากขึ้น ที่จริงแล้วพวกเขาจะตัดการเชื่อมต่อทุกบ้านที่ไม่ได้ใช้น้ำเมื่อเร็ว ๆ นี้ดังนั้นจึงมีน้ำฟรีให้ใช้อยู่เสมอ
แม้ว่าเมืองของคุณจะยังคงใช้งานได้ข้อเสียคือความคืบหน้าจะหยุดชะงัก เวลาส่วนใหญ่ของคุณใช้เวลาในการรอการจัดการน้ำเพื่อคืนสถานะบริการของคุณ
นี่คือสิ่งที่เคอร์เนลทำกับเพจที่มีไฟล์สำรอง หากคุณเรียกใช้ไฟล์ปฏิบัติการขนาดใหญ่ (เช่น chrome) ไฟล์จะถูกคัดลอกหน่วยความจำ เมื่อหน่วยความจำเหลือน้อยหรือมีบางส่วนที่ไม่ได้เข้าถึงเมื่อเร็ว ๆ นี้เคอร์เนลจะสามารถดรอปส่วนเหล่านั้นได้เนื่องจากสามารถโหลดซ้ำจากดิสก์ได้ หากทำสิ่งนี้มากเกินไปสิ่งนี้จะทำให้เดสก์ท็อปของคุณหยุดทำงานเนื่องจากทุกอย่างจะรอดิสก์ IO โปรดทราบว่าเคอร์เนลจะลดลงอย่างมากเมื่อเร็ว ๆ นี้หน้าที่ใช้เมื่อคุณเริ่มทำ IO จำนวนมาก นี่คือเหตุผลที่ใช้เวลานานในการเปลี่ยนเป็นแอปพื้นหลังหลังจากที่คุณคัดลอกไฟล์ขนาดใหญ่หลาย ๆ ไฟล์เช่นภาพ DVD
นี่เป็นพฤติกรรมที่น่ารำคาญที่สุดสำหรับฉันเพราะฉันเกลียด hickups และคุณไม่สามารถควบคุมมันได้ มันเป็นการดีที่จะสามารถปิดได้ ฉันกำลังคิดถึงบางสิ่งตามแนวของ
sed -i 's/may_unmap = 1/may_unmap = (vm_swappiness >= 0)/' mm/vmscan.c
จากนั้นคุณสามารถตั้งค่า vm_swappiness เป็น -1 เพื่อปิดการใช้งานนี้ สิ่งนี้ทำงานได้ค่อนข้างดีในการทดสอบเล็ก ๆ น้อย ๆ ของฉัน แต่ฉันไม่ได้เป็นนักพัฒนาเคอร์เนลดังนั้นฉันจึงไม่ได้ส่งให้ใคร
2ผู้บริหารสามารถปฏิเสธคำขอของแม่ครัวคนใหม่สำหรับน้ำได้ ตอนแรกฟังดูเหมือนความคิดที่ดี อย่างไรก็ตามมีข้อเสียอยู่สองประการ อย่างแรกคือมี บริษัท หลายแห่งที่ร้องขอการบอกรับสมาชิกจำนวนมากถึงแม้ว่าพวกเขาจะไม่ใช้ก็ตาม เหตุผลหนึ่งที่เป็นไปได้ในการทำเช่นนี้คือหลีกเลี่ยงค่าใช้จ่ายทั้งหมดในการพูดคุยกับการจัดการน้ำเมื่อใดก็ตามที่พวกเขาต้องการน้ำเพิ่ม ปริมาณการใช้น้ำของพวกเขาขึ้นและลงขึ้นอยู่กับเวลา เช่นในกรณีของร้านอาหาร บริษัท ต้องการน้ำมากขึ้นในช่วงเที่ยงเมื่อเทียบกับเที่ยงคืน ดังนั้นพวกเขาจึงร้องขอน้ำที่เป็นไปได้ทั้งหมดที่พวกเขาอาจใช้ แต่นั่นเป็นการสิ้นเปลืองน้ำในช่วงเที่ยงคืน ปัญหาคือทุก บริษัท ไม่สามารถคาดการณ์การใช้งานสูงสุดของพวกเขาได้อย่างถูกต้องดังนั้นพวกเขาจึงขอมากขึ้นโดยหวังว่าพวกเขาจะไม่ต้องกังวลเกี่ยวกับการร้องขอมากขึ้น
นี่คือสิ่งที่เครื่องเสมือนของ Java ทำ: มันจัดสรรหน่วยความจำจำนวนมากเมื่อเริ่มต้นและทำงานจากนั้น โดยค่าเริ่มต้นเคอร์เนลจะจัดสรรหน่วยความจำเฉพาะเมื่อแอป Java ของคุณเริ่มใช้งานจริง อย่างไรก็ตามหากคุณปิดใช้งาน overcommit เคอร์เนลจะทำการจองอย่างจริงจัง มันจะช่วยให้การจัดสรรจะประสบความสำเร็จถ้ามันมีทรัพยากรสำหรับมัน
อย่างไรก็ตามยังมีอีกปัญหาหนึ่งที่ร้ายแรงกว่าด้วยวิธีนี้ สมมติว่า บริษัท หนึ่งเริ่มขอน้ำหนึ่งหน่วยทุกวัน (มากกว่าในขั้นตอนที่ 10) ในที่สุดคุณจะไปถึงสถานะที่คุณมี 0 หน่วยฟรี ตอนนี้ บริษัท นี้จะไม่สามารถจัดสรรเพิ่มเติมได้ ไม่เป็นไรใครสนใจ บริษัท ใหญ่ ๆ แต่อย่างใด แต่ปัญหาคือบ้านเล็ก ๆ จะไม่สามารถขอน้ำเพิ่มได้อีก! คุณจะไม่สามารถสร้างห้องน้ำสาธารณะขนาดเล็กเพื่อรับมือกับการไหลเข้าของนักท่องเที่ยวอย่างฉับพลัน คุณจะไม่สามารถให้น้ำฉุกเฉินสำหรับไฟในป่าใกล้เคียง
ในแง่ของคอมพิวเตอร์: ในสถานการณ์ที่หน่วยความจำเหลือน้อยโดยไม่มีคำสั่งมากเกินไปคุณจะไม่สามารถเปิด xterm ใหม่คุณจะไม่สามารถ ssh เข้าไปในเครื่องของคุณคุณจะไม่สามารถเปิดแท็บใหม่เพื่อค้นหาสิ่งที่เป็นไปได้ แก้ไข กล่าวอีกนัยหนึ่งคือการปิดใช้งานการโอเวอร์เดตทำให้เดสก์ทอปของคุณไร้ประโยชน์เมื่อหน่วยความจำเหลือน้อย
3. ต่อไปนี้เป็นวิธีที่น่าสนใจในการจัดการปัญหาเมื่อ บริษัท เริ่มใช้น้ำมากเกินไป การจัดการน้ำพัดมันขึ้นมา! แท้จริง: มันไปที่เว็บไซต์ของร้านอาหารโยนระเบิดลงไปและรอจนกว่ามันจะระเบิด สิ่งนี้จะลดความต้องการน้ำของเมืองในทันทีโดยมากเพื่อให้ผู้คนใหม่ ๆ สามารถเข้ามาคุณสามารถสร้างห้องน้ำสาธารณะ ฯลฯ คุณในฐานะนายกเทศมนตรีสามารถสร้างร้านอาหารใหม่ได้ด้วยความหวังว่าคราวนี้จะต้องใช้น้ำน้อยลง ตัวอย่างเช่นคุณจะบอกให้คนอื่นไม่เข้าไปในร้านอาหารหากมีคนอยู่ภายในจำนวนมากเกินไป (เช่นคุณจะเปิดแท็บเบราว์เซอร์น้อยลง)
นี่คือสิ่งที่เคอร์เนลทำเมื่อไม่มีตัวเลือกทั้งหมดและต้องการหน่วยความจำ: มันเรียก OOM killer มันเลือกแอปพลิเคชั่นขนาดใหญ่ (ขึ้นอยู่กับฮิวริสติกจำนวนมาก) และฆ่ามันปล่อยหน่วยความจำจำนวนมาก แต่ยังคงเดสก์ท็อปที่ตอบสนองได้ อันที่จริงแล้วเคอร์เนล Android ทำสิ่งนี้อย่างจริงจังยิ่งขึ้น: มันฆ่าแอปที่ใช้งานน้อยที่สุดเมื่อหน่วยความจำเหลือน้อย (เมื่อเทียบกับเคอร์เนลหุ้นซึ่งทำหน้าที่เป็นทางเลือกสุดท้ายเท่านั้น) สิ่งนี้เรียกว่า Viking Killer ใน Android
ฉันคิดว่านี่เป็นหนึ่งในวิธีแก้ปัญหาที่ง่ายที่สุด: ไม่ใช่ว่าคุณมีตัวเลือกมากกว่านี้ดังนั้นทำไมไม่รีบไปเร็วกว่านี้ใช่ไหม? ปัญหาคือเคอร์เนลบางครั้งทำงานค่อนข้างมากเพื่อหลีกเลี่ยงการเรียก OOM killer นั่นเป็นสาเหตุที่คุณเห็นว่าเดสก์ท็อปของคุณช้ามากและเคอร์เนลไม่ได้ทำอะไรเกี่ยวกับเรื่องนี้ แต่โชคดีที่มีตัวเลือกในการเรียก OOM killer ด้วยตัวคุณเอง! ก่อนอื่นตรวจสอบให้แน่ใจว่าได้เปิดใช้งานคีย์ Magic sysrq (เช่นecho 1 | sudo tee
/proc/sys/kernel/sysrq
) ทุกครั้งที่คุณรู้สึกว่าเคอร์เนลมีหน่วยความจำเหลือน้อยเพียงกด Alt + SysRQ, Alt + f
ตกลงดังนั้นทั้งหมดที่ดี แต่คุณต้องการที่จะลอง? สถานการณ์หน่วยความจำต่ำนั้นง่ายมากในการทำซ้ำ ฉันมีแอพที่ง่ายมากสำหรับสิ่งนั้น คุณจะต้องเรียกใช้สองครั้ง การรันครั้งแรกจะกำหนดว่าคุณมี RAM ว่างเท่าใดการรันครั้งที่สองจะสร้างสถานการณ์หน่วยความจำเหลือน้อย โปรดทราบว่าวิธีนี้ถือว่าคุณปิดใช้งานการสลับ (เช่นทำsudo swapoff -a
) รหัสและการใช้งานมีดังนี้:
// gcc -std=c99 -Wall -Wextra -Werror -g -o eatmem eatmem.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main(int argc, char** argv)
{
int limit = 123456789;
if (argc >= 2) {
limit = atoi(argv[1]);
}
setbuf(stdout, NULL);
for (int i = 1; i <= limit; i++) {
memset(malloc(1 << 20), 1, 1 << 20);
printf("\rAllocated %5d MiB.", i);
}
sleep(10000);
return 0;
}
และนี่คือวิธีที่คุณใช้:
$ gcc -std=c99 -Wall -Wextra -Werror -g -o eatmem eatmem.c
$ ./eatmem
Allocated 31118 MiB.Killed
$ ./eatmem 31110
Allocated 31110 MiB.Killed
การเรียกครั้งแรกตรวจพบว่าเรามี RAM 31,118 MiB ฟรี ดังนั้นฉันจึงบอกให้แอปพลิเคชั่นจัดสรร 31,110 MiB RAM เพื่อที่เคอร์เนลจะไม่ฆ่า แต่กินหน่วยความจำเกือบทั้งหมด ระบบของฉันค้าง: แม้แต่ตัวชี้เมาส์ก็ไม่ขยับเขยื่อน ฉันกด Alt + SysRQ, Alt + f และมันฆ่ากระบวนการ eatmem ของฉันและระบบได้รับการกู้คืน
แม้ว่าเราจะครอบคลุมตัวเลือกของเราสิ่งที่ทำในสถานการณ์ความจำต่ำวิธีที่ดีที่สุด (เช่นเดียวกับสถานการณ์อันตรายอื่น ๆ ) คือการหลีกเลี่ยงในครั้งแรก มีหลายวิธีในการทำเช่นนี้ วิธีการทั่วไปที่ฉันเห็นคือการนำแอปพลิเคชั่นที่ทำงานผิดปกติ (เช่นเบราว์เซอร์) ลงในคอนเทนเนอร์ที่แตกต่างจากส่วนที่เหลือของระบบ ในกรณีนี้เบราว์เซอร์จะไม่สามารถส่งผลกระทบต่อเดสก์ท็อปของคุณ แต่การป้องกันตัวเองอยู่นอกขอบเขตของคำถามดังนั้นฉันจะไม่เขียนเกี่ยวกับมัน
TL; DR:แม้ว่าในขณะนี้ไม่มีวิธีที่จะหลีกเลี่ยงการเพจได้อย่างสมบูรณ์ แต่คุณสามารถลดการหยุดทำงานของระบบได้อย่างสมบูรณ์โดยการปิดใช้งานคำสั่งเกินคำสั่ง แต่ระบบของคุณจะยังไม่สามารถใช้งานได้ในช่วงที่มีหน่วยความจำเหลือน้อย แต่จะแตกต่างกัน ในสถานการณ์ที่มีหน่วยความจำต่ำให้กด Alt + SysRQ, Alt + f เพื่อฆ่ากระบวนการขนาดใหญ่ของการเลือกเคอร์เนล ระบบของคุณควรกู้คืนการตอบสนองภายในไม่กี่วินาที นี่ถือว่าคุณเปิดใช้งานคีย์ sysrq (โดยค่าเริ่มต้น)