Linux สามารถ“ ใช้งาน RAM หมด” ได้หรือไม่?


20

ฉันเห็นหลายโพสต์ในเว็บของผู้คนเห็นได้ชัดว่าบ่นเกี่ยวกับ VPS โฮสต์โดยไม่คาดคิดกระบวนการฆ่าเพราะพวกเขาใช้แรมมากเกินไป

เป็นไปได้อย่างไร? ฉันคิดว่าระบบปฏิบัติการสมัยใหม่ทั้งหมดให้ "infinite RAM" โดยใช้ disk swap สำหรับทุกอย่างที่มากกว่า RAM จริง ถูกต้องหรือไม่

อาจเกิดอะไรขึ้นถ้ากระบวนการ "ถูกฆ่าเนื่องจาก RAM ต่ำ"


12
ระบบปฏิบัติการไม่มี RAM ที่ไม่มีที่สิ้นสุด นอกเหนือจากชิป RAM แบบฟิสิคัลในเครื่องแล้วระบบปฏิบัติการยังสามารถเลือกใช้ 'ไฟล์สลับ' ซึ่งอยู่บนดิสก์ได้ เมื่อคอมพิวเตอร์ต้องการหน่วยความจำมากกว่าที่มีอยู่ใน RAM sticks มันจะแลกเปลี่ยนข้อมูลบางอย่างกับไฟล์สลับ แต่เมื่อไฟล์ swap ถึงความจุไม่ว่าจะเป็นเพราะคุณตั้งค่าขนาดสูงสุด (ทั่วไป) หรือดิสก์เต็ม - คุณมีหน่วยความจำเสมือนไม่เพียงพอ
John Dibling

@JohnDibling; ดังนั้นมีเหตุผลหนึ่งที่ต้องการ จำกัด ขนาดการแลกเปลี่ยนอื่นนอกเหนือจากการประหยัดพื้นที่ดิสก์สำหรับระบบไฟล์หรือไม่ กล่าวอีกนัยหนึ่งถ้าฉันมีดิสก์ 20GB และไฟล์เพียง 1GB มีเหตุผลใดที่จะไม่ตั้งค่าขนาดการสลับเป็น 19GB
themirror

1
เพื่อให้สิ่งต่าง ๆ มีความซับซ้อนฉันจะบอกว่าเหตุผลสองข้อในการ จำกัด ขนาดการสลับคือ 1) เพื่อลดการใช้ดิสก์และ 2) เพื่อเพิ่มประสิทธิภาพ หลังอาจเป็นจริงมากขึ้นภายใต้ Windows มากกว่า / * NIX แต่แล้วอีกครั้งหากคุณใช้พื้นที่ swap บนดิสก์ของคุณประสิทธิภาพของคุณจะลดลง การเข้าถึงดิสก์เป็นทั้งช้ากว่าแรมหรือมากช้ากว่า RAM, ขึ้นอยู่กับระบบ
John Dibling

9
Swap ไม่ได้เป็นแรม en.wikipedia.org/wiki/Random-access_memoryจำนวน RAM ในระบบของคุณคือจำนวน RAM ในระบบของคุณ - ช่วงเวลา มันไม่ได้เป็นปริมาณที่ไม่ชัดเจนหรือแบบไดนามิก มันได้รับการแก้ไขอย่างแน่นอน "หน่วยความจำ" เป็นแนวคิดที่คลุมเครือมากขึ้น แต่ความแตกต่างระหว่างหน่วยความจำแรมและรูปแบบอื่น ๆ ของการจัดเก็บนั้นเป็นเพราะ terdon (+1) ชี้ให้เห็นอย่างชัดเจน การสลับดิสก์ไม่สามารถทดแทนประสิทธิภาพการทำงานของ RAM ด้วยขนาดที่หลากหลาย ระบบที่ขึ้นอยู่กับการสลับมากเกินไปเป็นการชั่วคราวที่ดีที่สุดและโดยทั่วไป: ขยะ
goldilocks

1
ดังนั้นพื้นที่ว่างในดิสก์จึงไม่มีที่สิ้นสุดในตอนนี้?
Kaz

คำตอบ:


41

อาจเกิดอะไรขึ้นถ้ากระบวนการ "ถูกฆ่าเนื่องจาก RAM ต่ำ"

บางครั้งก็บอกว่าลินุกซ์ที่เริ่มต้นไม่เคยปฏิเสธการร้องขอสำหรับหน่วยความจำเพิ่มเติมจากรหัสโปรแกรม - malloc()เช่น 1 สิ่งนี้ไม่เป็นความจริง ค่าเริ่มต้นจะใช้การวิเคราะห์พฤติกรรมด้วยเหตุนี้

ชัดเจนเกินขอบเขตของที่อยู่จะถูกปฏิเสธ ใช้สำหรับระบบทั่วไป มันช่วยให้มั่นใจได้ว่าการจัดสรรที่รุนแรงจะล้มเหลวในขณะที่ยอมให้ overcommit ลดการใช้ swap

จาก[linux_src]/Documentation/vm/overcommit-accounting(ราคาทั้งหมดมาจากต้นไม้ 3.11) สิ่งที่นับว่าเป็นการ "จัดสรรป่าอย่างจริงจัง" นั้นไม่ชัดเจนดังนั้นเราจะต้องผ่านแหล่งข้อมูลเพื่อกำหนดรายละเอียด เราสามารถใช้วิธีการทดลองในเชิงอรรถ 2 (ด้านล่าง) เพื่อลองและรับการสะท้อนของฮิวริสติก - จากนั้นการสังเกตเชิงประจักษ์เริ่มต้นของฉันคือภายใต้สถานการณ์ในอุดมคติ (== ระบบไม่ได้ทำงาน) ถ้าคุณไม่ได้ ' ไม่มีการสลับใด ๆ คุณจะได้รับอนุญาตให้จัดสรร RAM ครึ่งหนึ่งของคุณและถ้าคุณมีการสลับคุณจะได้รับ RAM ประมาณครึ่งหนึ่งบวกกับการสลับทั้งหมดของคุณ นั่นคือมากขึ้นหรือน้อยลงต่อกระบวนการ (แต่โปรดทราบว่าขีด จำกัด นี้เป็นแบบไดนามิกและอาจมีการเปลี่ยนแปลงเนื่องจากสถานะให้ดูข้อสังเกตบางอย่างในเชิงอรรถ 5)

ครึ่ง RAM บวกแลกเปลี่ยนของคุณอย่างชัดเจนเริ่มต้นสำหรับ "CommitLimit" /proc/meminfoในสนาม นี่คือความหมาย - และโปรดทราบว่าจริง ๆ แล้วไม่มีอะไรเกี่ยวข้องกับขีด จำกัด ที่เพิ่งกล่าวถึง (จาก[src]/Documentation/filesystems/proc.txt):

CommitLimit:ขึ้นอยู่กับอัตราส่วน overcommit ('vm.overcommit_ratio') นี่เป็นจำนวนหน่วยความจำทั้งหมดที่มีอยู่ในปัจจุบันซึ่งสามารถจัดสรรให้กับระบบได้ ข้อ จำกัด นี้จะปฏิบัติตามเฉพาะเมื่อเปิดใช้งานการบัญชี overcommit ที่เข้มงวด (โหมด 2 ใน 'vm.overcommit_memory') CommitLimit ถูกคำนวณด้วยสูตรต่อไปนี้: CommitLimit = ('vm.overcommit_ratio' * ฟิสิคัลแรม) + Swap ตัวอย่างเช่นบนระบบที่มี 1G ของฟิสิคัลแรมและ 7G ของการสลับกับ 'vm.overcommit_ratio' 30 CommitLimit 7.3G

เอกสาร overcommit-accounting ที่ยกมาก่อนหน้านี้ระบุว่าค่าเริ่มต้นvm.overcommit_ratioคือ 50 ดังนั้นหากคุณsysctl vm.overcommit_memory=2คุณสามารถปรับ vm.covercommit_ratio (พร้อมsysctl) และดูผลที่ตามมา 3 โหมดเริ่มต้นเมื่อCommitLimitไม่ได้บังคับใช้และมีเพียง "overcommits ชัดเจนของพื้นที่ที่อยู่จะปฏิเสธ" vm.overcommit_memory=0คือเมื่อ

ในขณะที่กลยุทธ์เริ่มต้นจะมีการ จำกัด ฮิวริสติกต่อกระบวนการเพื่อป้องกัน "การจัดสรรที่มีความรุนแรงมาก" แต่จะทำให้ระบบโดยรวมมีอิสระที่จะได้รับการจัดสรรที่จริงจัง 4 ซึ่งหมายความว่าในบางครั้งมันอาจมีหน่วยความจำไม่เพียงพอและต้องประกาศล้มละลายกระบวนการบางอย่างผ่านทางนักฆ่า OOM

นักฆ่า OOM ฆ่าอะไร ไม่จำเป็นต้องเป็นกระบวนการที่ขอหน่วยความจำเมื่อไม่มีเนื่องจากไม่จำเป็นต้องเป็นกระบวนการที่ผิดจริงและที่สำคัญไม่จำเป็นต้องเป็นกระบวนการที่จะนำระบบออกจากปัญหาอย่างรวดเร็วที่สุด

นี่คือการอ้างถึงจากที่นี่ซึ่งอาจอ้างอิงแหล่ง 2.6.x:

/*
 * oom_badness - calculate a numeric value for how bad this task has been
 *
 * The formula used is relatively simple and documented inline in the
 * function. The main rationale is that we want to select a good task
 * to kill when we run out of memory.
 *
 * Good in this context means that:
 * 1) we lose the minimum amount of work done
 * 2) we recover a large amount of memory
 * 3) we don't kill anything innocent of eating tons of memory
 * 4) we want to kill the minimum amount of processes (one)
 * 5) we try to kill the process the user expects us to kill, this
 *    algorithm has been meticulously tuned to meet the principle
 *    of least surprise ... (be careful when you change it)
 */

ซึ่งดูเหมือนจะเป็นเหตุผลที่ดี อย่างไรก็ตามหากไม่มีนิติเวช # 5 (ซึ่งซ้ำซ้อนอันดับ 1) ดูเหมือนว่าการดำเนินการขายที่ชาญฉลาดและ # 3 ซ้ำซ้อนอันดับที่ 2 ดังนั้นจึงควรพิจารณาว่าเรื่องนี้ตัดกับ # 2/3 และ # 4

ฉันพูดถึงแหล่งที่มาล่าสุด (3.11) และสังเกตว่าความคิดเห็นนี้เปลี่ยนไปในระหว่างกาล

/**
 * oom_badness - heuristic function to determine which candidate task to kill
 *
 * The heuristic for determining which task to kill is made to be as simple and
 * predictable as possible.  The goal is to return the highest value for the
 * task consuming the most memory to avoid subsequent oom failures.
 */

นี่คืออีกเล็กน้อยอย่างชัดเจนเกี่ยวกับ # 2: "เป้าหมายคือ [ฆ่า] งานที่ใช้หน่วยความจำมากที่สุดเพื่อหลีกเลี่ยงความล้มเหลวของ oom ที่ตามมา"และโดยนัย # 4 ( "เราต้องการฆ่าจำนวนขั้นต่ำของกระบวนการ ( หนึ่ง ) ) .

หากคุณต้องการเห็นการทำงานของ OOM killer ดูเชิงอรรถ 5


1ความเข้าใจผิด Gilles กำจัดฉันออกอย่างเห็นได้ชัด


2นี่คือบิต C ที่ตรงไปตรงมาซึ่งจะขอหน่วยความจำขนาดใหญ่มากขึ้นเพื่อกำหนดว่าเมื่อใดที่การร้องขอเพิ่มเติมจะล้มเหลว:

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>

#define MB 1 << 20

int main (void) {
    uint64_t bytes = MB;
    void *p = malloc(bytes);
    while (p) {
        fprintf (stderr,
            "%lu kB allocated.\n",
            bytes / 1024
        );
        free(p);
        bytes += MB;
        p = malloc(bytes);
    }
    fprintf (stderr,
        "Failed at %lu kB.\n",
        bytes / 1024
    );
    return 0;
}            

หากคุณไม่ทราบ C, คุณสามารถรวบรวมนี้แล้วเรียกใช้gcc virtlimitcheck.c -o virtlimitcheck ./virtlimitcheckมันไม่เป็นอันตรายอย่างสมบูรณ์เนื่องจากกระบวนการไม่ได้ใช้พื้นที่ที่ต้องการ - กล่าวคือมันไม่เคยใช้ RAM ใด ๆ เลย

สำหรับระบบ 3.11 x86_64 ที่มีระบบ 4 GB และการแลกเปลี่ยน 6 GB ฉันล้มเหลวที่ ~ 7400000 kB; จำนวนผันผวนดังนั้นรัฐอาจเป็นปัจจัย นี่เป็นเรื่องบังเอิญใกล้กับCommitLimitใน/proc/meminfoแต่การแก้ไขผ่านทางนี้vm.overcommit_ratioไม่ได้สร้างความแตกต่างใด ๆ สำหรับระบบ ARM 448 MB ขนาด 3.6.11 32 บิตพร้อม swap 64 MB แต่ฉันล้มเหลวที่ ~ 230 MB สิ่งนี้น่าสนใจเนื่องจากในกรณีแรกจำนวนเกือบสองเท่าของจำนวน RAM ในขณะที่ในครั้งที่สองนั้นประมาณ 1/4 ซึ่งหมายความว่าจำนวน swap อย่างมากเป็นปัจจัย สิ่งนี้ได้รับการยืนยันโดยการปิดสวิตช์ในระบบแรกเมื่อเกณฑ์ความล้มเหลวลดลงเหลือ ~ 1.95 GB อัตราส่วนที่คล้ายกันมากกับกล่อง ARM ตัวเล็ก ๆ

แต่นี่เป็นกระบวนการจริงหรือ มันดูเหมือนจะเป็น โปรแกรมสั้น ๆ ด้านล่างนี้จะถามถึงหน่วยความจำที่ผู้ใช้กำหนดและหากประสบความสำเร็จโปรดรอให้คุณกลับมา - ด้วยวิธีนี้คุณสามารถลองหลาย ๆ อินสแตนซ์พร้อมกันได้:

#include <stdio.h>
#include <stdlib.h>

#define MB 1 << 20

int main (int argc, const char *argv[]) {
    unsigned long int megabytes = strtoul(argv[1], NULL, 10);
    void *p = malloc(megabytes * MB);
    fprintf(stderr,"Allocating %lu MB...", megabytes);
    if (!p) fprintf(stderr,"fail.");
    else {
        fprintf(stderr,"success.");
        getchar();
        free(p);
    }
    return 0;
}

อย่างไรก็ตามระวังว่ามันไม่ได้เกี่ยวกับปริมาณ RAM และการสลับอย่างไม่คำนึงถึงการใช้งาน - ดูเชิงอรรถ 5 สำหรับการสังเกตเกี่ยวกับผลกระทบของสถานะระบบ


3 CommitLimitหมายถึงจำนวนพื้นที่ที่อยู่ที่อนุญาตสำหรับระบบเมื่อ vm.overcommit_memory = 2 สมมุติว่าจำนวนเงินที่คุณสามารถจัดสรรได้ควรเป็นลบด้วยสิ่งที่ได้กระทำไปแล้วซึ่งเห็นได้ชัดว่าเป็นCommitted_ASฟิลด์

การทดลองที่น่าสนใจที่แสดงให้เห็นถึงสิ่งนี้คือการเพิ่ม#include <unistd.h>ที่ด้านบนของ virtlimitcheck.c (ดูเชิงอรรถ 2) และfork()ขวาก่อนwhile()ลูป ไม่รับประกันว่าจะทำงานตามที่อธิบายไว้ที่นี่โดยไม่มีการซิงโครไนซ์ที่น่าเบื่อ แต่มีโอกาสที่ดีที่มันจะ YMMV:

> sysctl vm.overcommit_memory=2
vm.overcommit_memory = 2
> cat /proc/meminfo | grep Commit
CommitLimit:     9231660 kB
Committed_AS:    3141440 kB
> ./virtlimitcheck 2&> tmp.txt
> cat tmp.txt | grep Failed
Failed at 3051520 kB.
Failed at 6099968 kB.

สิ่งนี้สมเหตุสมผลแล้วโดยดูที่ tmp.txt โดยละเอียดคุณสามารถดูกระบวนการสลับการจัดสรรที่ใหญ่กว่าและใหญ่กว่า (ซึ่งจะง่ายกว่าถ้าคุณใส่ pid ลงในเอาต์พุต) จนกว่าจะเห็นได้ชัดว่ามีคนอ้างว่าอีกฝ่ายล้มเหลว ผู้ชนะคือแล้วฟรีกับทุกสิ่งที่คว้าได้ถึงลบCommitLimitCommitted_AS


4ในตอนนี้ถ้าคุณยังไม่เข้าใจการกำหนดที่อยู่เสมือนจริงและความต้องการการเพจว่าสิ่งที่ทำให้เกิดความมุ่งมั่นที่เป็นไปได้ในตอนแรกก็คือสิ่งที่เคอร์เนลจัดสรรให้กับกระบวนการ userland นั้นไม่ใช่หน่วยความจำทางกายภาพเลยพื้นที่ที่อยู่เสมือน ตัวอย่างเช่นหากกระบวนการสำรอง 10 MB สำหรับบางสิ่งนั่นแสดงว่าเป็นลำดับของที่อยู่ (เสมือน) แต่ที่อยู่เหล่านั้นยังไม่ตรงกับหน่วยความจำกายภาพ เมื่อมีการเข้าถึงที่อยู่ดังกล่าวจะส่งผลให้เกิดความผิดพลาดของหน้าจากนั้นเคอร์เนลพยายามแมปมันลงในหน่วยความจำจริงเพื่อให้สามารถเก็บค่าจริง โดยทั่วไปกระบวนการจะจองพื้นที่เสมือนมากกว่าที่จะเข้าถึงจริงซึ่งช่วยให้เคอร์เนลสามารถใช้ RAM ได้อย่างมีประสิทธิภาพสูงสุด อย่างไรก็ตามหน่วยความจำกายภาพยังคงเป็นทรัพยากรที่มีอยู่อย่าง จำกัด และเมื่อทั้งหมดถูกแมปไปยังพื้นที่ที่อยู่เสมือนพื้นที่ที่อยู่เสมือนบางแห่งจะต้องถูกกำจัดเพื่อให้ RAM ว่างขึ้น


5 คำเตือนครั้งแรก: หากคุณลองทำสิ่งนี้vm.overcommit_memory=0ตรวจสอบให้แน่ใจว่าคุณได้บันทึกงานของคุณก่อนและปิดแอปพลิเคชันที่สำคัญใด ๆ เนื่องจากระบบจะถูกแช่แข็งเป็นเวลา ~ 90 วินาทีและกระบวนการบางอย่างจะตาย!

ความคิดคือการรันfork bombที่หมดเวลาหลังจาก 90 วินาทีโดยมีการจัดสรรพื้นที่และบางคนก็เขียนข้อมูลจำนวนมากไปยัง RAM ทั้งหมดในขณะที่รายงานไปยัง stderr

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/time.h>
#include <errno.h>
#include <string.h>

/* 90 second "Verbose hungry fork bomb".
Verbose -> It jabbers.
Hungry -> It grabs address space, and it tries to eat memory.

BEWARE: ON A SYSTEM WITH 'vm.overcommit_memory=0', THIS WILL FREEZE EVERYTHING
FOR THE DURATION AND CAUSE THE OOM KILLER TO BE INVOKED.  CLOSE THINGS YOU CARE
ABOUT BEFORE RUNNING THIS. */

#define STEP 1 << 30 // 1 GB
#define DURATION 90

time_t now () {
    struct timeval t;
    if (gettimeofday(&t, NULL) == -1) {
        fprintf(stderr,"gettimeofday() fail: %s\n", strerror(errno));
        return 0;
    }
    return t.tv_sec;
}

int main (void) {
    int forks = 0;
    int i;
    unsigned char *p;
    pid_t pid, self;
    time_t check;
    const time_t start = now();
    if (!start) return 1;

    while (1) {
    // Get our pid and check the elapsed time.
        self = getpid();
        check = now();
        if (!check || check - start > DURATION) return 0;
        fprintf(stderr,"%d says %d forks\n", self, forks++);
    // Fork; the child should get its correct pid.
        pid = fork();
        if (!pid) self = getpid();
    // Allocate a big chunk of space.
        p = malloc(STEP);
        if (!p) {
            fprintf(stderr, "%d Allocation failed!\n", self);
            return 0;
        }
        fprintf(stderr,"%d Allocation succeeded.\n", self);
    // The child will attempt to use the allocated space.  Using only
    // the child allows the fork bomb to proceed properly.
        if (!pid) {
            for (i = 0; i < STEP; i++) p[i] = i % 256;
            fprintf(stderr,"%d WROTE 1 GB\n", self);
        }
    }
}                        

gcc forkbomb.c -o forkbombคอมไพล์นี้ ก่อนอื่นให้ลองด้วยsysctl vm.overcommit_memory=2- คุณอาจได้รับสิ่งต่อไปนี้:

6520 says 0 forks
6520 Allocation succeeded.
6520 says 1 forks
6520 Allocation succeeded.
6520 says 2 forks
6521 Allocation succeeded.
6520 Allocation succeeded.
6520 says 3 forks
6520 Allocation failed!
6522 Allocation succeeded.

ในสภาพแวดล้อมนี้ส้อมระเบิดชนิดนี้อยู่ไม่ไกลมาก โปรดทราบว่าหมายเลขใน "says N forks" ไม่ใช่จำนวนกระบวนการทั้งหมดเป็นจำนวนกระบวนการในห่วงโซ่ / สาขาที่นำไปสู่กระบวนการนั้น

vm.overcommit_memory=0ตอนนี้ลองกับ หากคุณเปลี่ยนเส้นทาง stderr ไปที่ไฟล์คุณสามารถทำการวิเคราะห์อย่างหยาบภายหลังได้เช่น:

> cat tmp.txt | grep failed
4641 Allocation failed!
4646 Allocation failed!
4642 Allocation failed!
4647 Allocation failed!
4649 Allocation failed!
4644 Allocation failed!
4643 Allocation failed!
4648 Allocation failed!
4669 Allocation failed!
4696 Allocation failed!
4695 Allocation failed!
4716 Allocation failed!
4721 Allocation failed!

เพียง 15 กระบวนการล้มเหลวในการจัดสรร 1 GB - แสดงให้เห็นว่าการแก้ปัญหาสำหรับ overcommit_memory = 0 จะได้รับผลกระทบโดยรัฐ มีกระบวนการกี่กระบวนการ? ดูตอนท้ายของ tmp.txt อาจ> 100,000 ตอนนี้อาจจะต้องใช้ 1 GB อย่างไร

> cat tmp.txt | grep WROTE
4646 WROTE 1 GB
4648 WROTE 1 GB
4671 WROTE 1 GB
4687 WROTE 1 GB
4694 WROTE 1 GB
4696 WROTE 1 GB
4716 WROTE 1 GB
4721 WROTE 1 GB

แปด - ซึ่งทำให้รู้สึกอีกครั้งตั้งแต่ตอนที่ฉันมี ~ 3 GB RAM ฟรีและ 6 GB ของการแลกเปลี่ยน

ดูบันทึกระบบของคุณหลังจากที่คุณทำเช่นนี้ คุณควรเห็นคะแนนการรายงานของนักฆ่า OOM (เหนือสิ่งอื่นใด); oom_badnessสันนิษฐานนี้เกี่ยวข้องกับ


การสลับออกไม่ใช่วิธีแก้ปัญหา (หรือเกี่ยวข้องกับ) กับหน่วยความจำมากกว่าความมุ่งมั่น การจัดสรรหน่วยความจำ (เช่น: malloc) เป็นเรื่องเกี่ยวกับการร้องขอหน่วยความจำเสมือนที่จะจองไม่ใช่ทางกายภาพ
jlliagre

1
@jillagre: "การสลับออกไม่ใช่วิธีแก้ปัญหา (หรือแม้แต่ที่เกี่ยวข้อง) กับหน่วยความจำมากกว่าความมุ่งมั่น" -> ใช่จริง ๆ แล้วมันคือ เพจที่ใช้งานไม่บ่อยครั้งจะถูกสับเปลี่ยนออกจาก RAM ทำให้มี RAM เพิ่มขึ้นเพื่อจัดการกับความผิดพลาดของหน้าเว็บที่เกิดจากความต้องการการเพจ / การจัดสรร (ซึ่งเป็นกลไกที่ทำให้เกิดภาระผูกพันมากเกินไป) เพจที่ถูกสลับเปลี่ยนอาจจำเป็นต้องได้รับการทำเพจจด์กลับเข้าสู่ RAM ในบางจุด
goldilocks

"การจัดสรรหน่วยความจำ (เช่น: malloc) เป็นเรื่องเกี่ยวกับการร้องขอหน่วยความจำเสมือนที่จะจองไม่ใช่ตัวจริง" -> ถูกต้อง แต่เคอร์เนลสามารถ (และเลือกได้) จะปฏิเสธเมื่อไม่มีการจับคู่ทางกายภาพที่เหลืออยู่ แน่นอนว่าจะไม่เป็นเพราะกระบวนการหมดพื้นที่ที่อยู่เสมือน (หรืออย่างน้อยก็ไม่ปกติเนื่องจากเป็นไปได้เช่นกันอย่างน้อยในระบบ 32 บิต)
goldilocks

การเพจจิ้มความต้องการไม่ใช่สิ่งที่ทำให้หน่วยความจำเกินความเป็นไปได้ ลินุกซ์ยอมรับหน่วยความจำบนระบบโดยไม่มีพื้นที่สว็อปเลย คุณอาจสับสนระหว่างหน่วยความจำและความต้องการการเพจ หาก Linux บอกว่า "ไม่" ถึง malloc ด้วยกระบวนการ 64 บิตนั่นคือหากไม่ได้กำหนดค่าให้มีการใช้งานบ่อยเกินไปนั่นอาจเป็นเพราะหน่วยความจำเสียหายหรือเป็นผลรวมของการจองหน่วยความจำทั้งหมด (ไม่ว่าจะแมปหรือไม่กับ RAM หรือดิสก์) สูงกว่าขีด จำกัด ที่กำหนดทั้งนี้ขึ้นอยู่กับการกำหนดค่า สิ่งนี้ไม่เกี่ยวกับการใช้ RAM เนื่องจากอาจเกิดขึ้นแม้ในขณะที่ยังมี RAM ว่างอยู่
jlliagre

"เพจจิ้งอุปสงค์ไม่ใช่สิ่งที่ทำให้หน่วยความจำเกินความเป็นไปได้" -> บางทีอาจเป็นการดีกว่าถ้าจะบอกว่าเป็นการระบุที่อยู่เสมือนซึ่งทำให้ทั้งเพจมีความต้องการและมีความมุ่งมั่นมากกว่า "ลินุกซ์ยอมรับหน่วยความจำบนระบบอย่างแน่นอนโดยไม่มีพื้นที่สว็อปเลย" -> เห็นได้ชัดว่าเนื่องจากการแบ่งหน้าตามความต้องการไม่จำเป็นต้องสลับ เพจจิ้งความต้องการจากการแลกเปลี่ยนเป็นเพียงตัวอย่างพิเศษของเพจจิ้ง อีกครั้ง swap เป็นวิธีการแก้ปัญหาที่เกินความมุ่งมั่นไม่ใช่ในแง่ที่ว่ามันแก้ปัญหาได้ แต่ในแง่ที่จะช่วยป้องกันเหตุการณ์ OOM ที่อาจเกิดขึ้นซึ่งอาจเป็นผลมาจากความมุ่งมั่นมากกว่า
goldilocks

16

สิ่งนี้จะไม่เกิดขึ้นกับคุณหากคุณโหลดข้อมูล 1G ไปยังหน่วยความจำเท่านั้น เกิดอะไรขึ้นถ้าคุณโหลดมากขึ้น? ตัวอย่างเช่นฉันมักจะทำงานกับไฟล์ขนาดใหญ่ที่มีความน่าจะเป็นนับล้านที่ต้องโหลดลงใน R สิ่งนี้ใช้เวลาประมาณ 16GB ของ RAM

การใช้กระบวนการข้างต้นบนแล็ปท็อปของฉันจะทำให้มันเริ่มการแลกเปลี่ยนอย่างบ้าคลั่งทันทีที่ RAM 8GB ของฉันเต็ม ในทางกลับกันจะทำให้ทุกอย่างช้าลงเนื่องจากการอ่านจากดิสก์ช้ากว่าการอ่านจาก RAM มาก ถ้าฉันมีแล็ปท็อปที่มี RAM 2GB และมีพื้นที่ว่างเพียง 10GB ล่ะ เมื่อกระบวนการได้ใช้ RAM ทั้งหมดแล้วมันก็จะเติมดิสก์ให้เต็มเพราะมันกำลังเขียนเพื่อสลับและฉันก็เหลือ RAM อีกไม่และไม่มีที่ว่างสำหรับการแลกเปลี่ยน (คนมักจะ จำกัด การสลับไปที่พาร์ติชันเฉพาะแทนที่จะเป็น swapfile ด้วยเหตุผลนั้น) นั่นคือสิ่งที่ OOM killer เข้ามาและเริ่มกระบวนการฆ่า

ดังนั้นระบบสามารถใช้หน่วยความจำไม่เพียงพอ นอกจากนี้ระบบการแลกเปลี่ยนอย่างมากอาจไม่สามารถใช้งานได้นานก่อนที่จะเกิดเหตุการณ์นี้เนื่องจากการดำเนินการ I / O ที่ช้าเนื่องจากการแลกเปลี่ยน หนึ่งโดยทั่วไปต้องการหลีกเลี่ยงการแลกเปลี่ยนมากที่สุด แม้บนเซิร์ฟเวอร์ระดับไฮเอนด์ที่มี SSD อย่างรวดเร็วประสิทธิภาพการทำงานลดลงอย่างชัดเจน บนแล็ปท็อปของฉันซึ่งมีไดรฟ์ 7200RPM แบบคลาสสิกการสลับสับเปลี่ยนที่สำคัญทำให้ระบบใช้ไม่ได้ ยิ่งมันแลกเปลี่ยนมากก็จะยิ่งช้าลง หากฉันไม่ฆ่ากระบวนการที่กระทำผิดอย่างรวดเร็วทุกอย่างจะหยุดทำงานจนกว่า OOM killer จะเข้ามา


5

กระบวนการจะไม่ถูกฆ่าเมื่อไม่มี RAM อีกต่อไปกระบวนการเหล่านั้นจะถูกฆ่าเมื่อพวกเขาถูกโกงด้วยวิธีนี้:

  • เคอร์เนล Linux โดยทั่วไปอนุญาตให้กระบวนการจัดสรร (เช่นสำรอง) จำนวนหน่วยความจำเสมือนซึ่งมีขนาดใหญ่กว่าที่มีอยู่จริง (ส่วนหนึ่งของ RAM + พื้นที่สลับทั้งหมด)
  • ตราบใดที่กระบวนการเข้าถึงเฉพาะเซ็ตย่อยของเพจที่พวกเขาจองไว้ทุกอย่างจะทำงานได้ดี
  • หากหลังจากเวลาผ่านไปกระบวนการพยายามเข้าถึงเพจที่เป็นเจ้าของ แต่ไม่มีเพจว่างอีกต่อไปสถานการณ์หน่วยความจำไม่เพียงพอจะเกิดขึ้น
  • OOM killer เลือกหนึ่งในกระบวนการไม่จำเป็นต้องเป็นหนึ่งที่ขอหน้าใหม่และเพียงแค่ฆ่ามันเพื่อกู้คืนหน่วยความจำเสมือน

สิ่งนี้อาจเกิดขึ้นแม้ในขณะที่ระบบไม่ได้ทำการแลกเปลี่ยนเช่นหากพื้นที่ swap เต็มไปด้วยหน้าหน่วยความจำของ sleep daemons

สิ่งนี้ไม่เคยเกิดขึ้นกับระบบปฏิบัติการที่ไม่ได้ใช้งานเกินหน่วยความจำ กับพวกเขาไม่มีกระบวนการสุ่มถูกฆ่าตาย แต่กระบวนการแรกขอหน่วยความจำเสมือนในขณะที่มันหมดมี malloc (หรือคล้ายกัน) กลับมาผิดพลาด จึงได้รับโอกาสในการจัดการกับสถานการณ์อย่างเหมาะสม อย่างไรก็ตามในระบบปฏิบัติการเหล่านี้มันอาจเกิดขึ้นได้ว่าระบบไม่มีหน่วยความจำเสมือนในขณะที่ยังมี RAM ว่างซึ่งค่อนข้างสับสนและเข้าใจผิดโดยทั่วไป


3

เมื่อ RAM ที่มีอยู่หมดลงเคอร์เนลจะเริ่มทำการแลกเปลี่ยนบิตของการประมวลผลไปยังดิสก์ ที่จริงแล้วเคอร์เนลจะเริ่มทำการสลับ a เมื่อ RAM ใกล้จะหมด: มันจะทำการสลับในเชิงรุกเมื่อมันมีช่วงเวลาที่ไม่ได้ใช้งานเพื่อที่จะตอบสนองได้ดีกว่าหากแอพพลิเคชั่นต้องการหน่วยความจำมากขึ้น

โปรดทราบว่า RAM ไม่ได้ใช้เพื่อจัดเก็บหน่วยความจำของกระบวนการเท่านั้น บนระบบปกติทั่วไปจะใช้ RAM เพียงครึ่งเดียวในการประมวลผลและอีกครึ่งหนึ่งจะใช้สำหรับดิสก์แคชและบัฟเฟอร์ สิ่งนี้ให้ความสมดุลที่ดีระหว่างกระบวนการทำงานและอินพุต / เอาต์พุตไฟล์

พื้นที่สว็อปไม่มีที่สิ้นสุด ในบางกรณีหากกระบวนการจัดสรรหน่วยความจำมากขึ้นเรื่อย ๆ ข้อมูลที่ล้นจาก RAM จะเติมเต็มการสลับ เมื่อสิ่งนั้นเกิดขึ้นกระบวนการที่พยายามขอหน่วยความจำเพิ่มเติมจะเห็นคำขอของพวกเขาถูกปฏิเสธ

โดยค่าเริ่มต้น Linux จะovercommitsหน่วยความจำ นี่หมายความว่าบางครั้งจะอนุญาตให้กระบวนการทำงานด้วยหน่วยความจำที่สงวนไว้ แต่ไม่ได้ใช้ เหตุผลหลักสำหรับการมี overcommitment เป็นวิธีการฟอร์กงาน เมื่อกระบวนการเปิดใช้งานกระบวนการย่อยกระบวนการลูกจะดำเนินการตามแนวคิดในหน่วยความจำของผู้ปกครอง - ทั้งสองกระบวนการเริ่มต้นมีหน่วยความจำที่มีเนื้อหาเดียวกัน แต่เนื้อหานั้นจะแตกต่างกันไปตามกระบวนการที่เปลี่ยนแปลงในพื้นที่ของตนเอง ในการดำเนินการนี้อย่างสมบูรณ์เคอร์เนลจะต้องคัดลอกหน่วยความจำทั้งหมดของผู้ปกครอง นี่จะทำให้การฟอร์กช้าดังนั้นเคอร์เนลจึงฝึกหัดการคัดลอกเมื่อเขียน: เริ่มแรกเด็กแชร์หน่วยความจำทั้งหมดกับผู้ปกครอง; เมื่อใดก็ตามที่กระบวนการเขียนไปยังเพจที่ใช้ร่วมกันเคอร์เนลจะสร้างสำเนาของเพจนั้นเพื่อหยุดการแบ่งใช้

บ่อยครั้งที่เด็กจะออกจากหน้ากระดาษจำนวนมากโดยไม่มีใครแตะต้อง หากเคอร์เนลจัดสรรหน่วยความจำเพียงพอที่จะทำซ้ำพื้นที่หน่วยความจำของผู้ปกครองในแต่ละส้อมหน่วยความจำจำนวนมากจะสูญเปล่าในการจองที่กระบวนการลูกจะไม่เคยใช้ ดังนั้น overcommitting: เคอร์เนลเท่านั้นขอสงวนส่วนหนึ่งของหน่วยความจำที่อยู่บนพื้นฐานของการประมาณการของวิธีการหลาย ๆ หน้าเด็กจะต้อง

หากกระบวนการพยายามจัดสรรหน่วยความจำบางส่วนและมีหน่วยความจำเหลือไม่เพียงพอกระบวนการจะได้รับการตอบสนองข้อผิดพลาดและจัดการกับมันตามที่เห็นสมควร หากกระบวนการร้องขอหน่วยความจำทางอ้อมโดยการเขียนไปยังเพจที่แชร์ซึ่งต้องยกเลิกการแชร์นั่นเป็นอีกเรื่องหนึ่ง ไม่มีวิธีรายงานสถานการณ์นี้กับแอปพลิเคชัน: เชื่อว่ามีข้อมูลที่สามารถเขียนได้ที่นั่นและสามารถอ่านได้ - เป็นเพียงการเขียนที่เกี่ยวข้องกับการดำเนินการที่ซับซ้อนกว่าเล็กน้อยภายใต้ประทุน หากเคอร์เนลไม่สามารถจัดเตรียมหน้าหน่วยความจำใหม่สิ่งที่ทำได้คือฆ่ากระบวนการร้องขอหรือฆ่ากระบวนการอื่นเพื่อเติมหน่วยความจำ

คุณอาจคิดว่า ณ จุดนี้การฆ่ากระบวนการที่ร้องขอเป็นทางออกที่ชัดเจน แต่ในทางปฏิบัติมันไม่ดี กระบวนการนี้อาจเป็นสิ่งสำคัญที่เกิดขึ้นกับเพียงต้องการเข้าถึงหน้าใดหน้าหนึ่งของมันในขณะนี้ในขณะที่อาจมีกระบวนการอื่น ๆ ที่มีความสำคัญน้อยกว่ากำลังทำงานอยู่ ดังนั้นเคอร์เนลรวมถึงการวิเคราะห์พฤติกรรมที่ซับซ้อนในการเลือกกระบวนการที่จะฆ่า - (ใน) ที่มีชื่อเสียงนักฆ่า OOM


2

เพียงแค่เพิ่มมุมมองอื่นจากคำตอบอื่น ๆ โฮสต์ของ VPS หลายเครื่องเสมือนหลายเครื่องบนเซิร์ฟเวอร์ใดก็ตาม VM เดียวใด ๆ จะมีจำนวน RAM ที่ระบุสำหรับการใช้งานของตนเอง ผู้ให้บริการหลายแห่งเสนอ "burst RAM" ซึ่งพวกเขาสามารถใช้ RAM ได้มากกว่าจำนวนที่กำหนดไว้ นี่เป็นเพียงการใช้งานระยะสั้นเท่านั้นและผู้ที่ใช้เวลาเกินกว่าระยะเวลานี้อาจถูกลงโทษโดยโฮสต์ที่ฆ่ากระบวนการเพื่อลดปริมาณ RAM ที่ใช้งานเพื่อไม่ให้ผู้อื่นใช้งาน เครื่องโฮสต์ถูกโหลดมากเกินไป


-1

บางครั้ง linux ต้องใช้พื้นที่เสมือนภายนอก นั่นคือพาร์ทิชัน swap เมื่อ Ram ถูกเติมเต็ม linux จะใช้พื้นที่การสลับนี้เพื่อเรียกใช้กระบวนการที่มีลำดับความสำคัญต่ำ


1
ไม่มีกระบวนการใดถูกเรียกใช้จากการสลับ หน่วยความจำเสมือนถูกแบ่งออกเป็นหน่วยต่าง ๆ ขนาดเท่า ๆ กันที่เรียกว่าหน้า เมื่อหน่วยความจำกายภาพเป็นอิสระหน้าความสำคัญต่ำจะถูกขับออกจาก RAM ในขณะที่หน้าในไฟล์แคชมีการสำรองระบบไฟล์หน้านิรนามจะต้องเก็บไว้ในการแลกเปลี่ยน ลำดับความสำคัญของหน้าไม่เกี่ยวข้องโดยตรงกับลำดับความสำคัญของกระบวนการ แต่เป็นความถี่ที่จะใช้ หากกระบวนการที่ทำงานอยู่พยายามเข้าถึงหน้าเว็บที่ไม่ได้อยู่ในหน่วยความจำกายภาพระบบจะสร้างข้อผิดพลาดของหน้าขึ้นมาและกระบวนการนั้นจะถูกจองไว้ล่วงหน้าในกระบวนการอื่นขณะที่หน้าที่ต้องการถูกดึงมาจากดิสก์
Thomas Nyman
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.