Linux có thể hết RAM RAM không?


20

Tôi thấy một số bài đăng trên web của mọi người dường như phàn nàn về một VPS được lưu trữ bất ngờ giết chết các quy trình vì họ sử dụng quá nhiều RAM.

Sao có thể như thế được? Tôi nghĩ rằng tất cả các hệ điều hành hiện đại đều cung cấp "RAM vô hạn" bằng cách chỉ sử dụng trao đổi đĩa cho bất cứ thứ gì vượt qua RAM vật lý. Điều này có đúng không?

Điều gì có thể xảy ra nếu một quá trình "bị giết do RAM thấp"?


12
Không có hệ điều hành nào có RAM vô hạn . Ngoài các chip RAM vật lý trong máy, các hệ điều hành có thể - thông thường, tùy ý - sử dụng cái gọi là 'tệp hoán đổi' trên đĩa. Khi một máy tính cần nhiều bộ nhớ hơn so với RAM, nó sẽ hoán đổi một số thứ với tệp hoán đổi. Nhưng khi tệp hoán đổi đạt đến dung lượng của nó - vì bạn đặt kích thước tối đa (thông thường) hoặc đĩa đầy - bạn hết bộ nhớ ảo.
John Dibling

@ JohnDibling; Vì vậy, có lý do nào người ta muốn giới hạn kích thước trao đổi ngoài việc tiết kiệm không gian đĩa cho hệ thống tập tin? Nói cách khác, nếu tôi có đĩa 20 GB và chỉ 1 GB tệp, có lý do gì để không đặt kích thước trao đổi của tôi thành 19 GB không?
themirror

1
Để đơn giản hóa mọi thứ, tôi sẽ nói hai lý do để hạn chế kích thước trao đổi là 1) để giảm mức tiêu thụ đĩa và 2) để tăng hiệu suất. Cái sau có thể đúng hơn trong Windows so với / * NIX, nhưng một lần nữa, nếu bạn đang sử dụng không gian trao đổi trên đĩa, hiệu suất của bạn sẽ bị giảm. Truy cập đĩa hoặc là chậm hơn so với RAM hoặc nhiều chậm hơn so với RAM, tùy thuộc vào hệ thống.
John Dibling

9
Hoán đổi không phải là RAM . vi.wikipedia.org/wiki/Random-access_memory Lượng RAM trong hệ thống của bạn là dung lượng RAM trong hệ thống của bạn. Nó không phải là một khối lượng mơ hồ hoặc năng động. Nó hoàn toàn cố định. "Bộ nhớ" là một khái niệm mơ hồ hơn, nhưng sự khác biệt giữa RAM và các hình thức lưu trữ khác là, như terdon (+1) chỉ ra, khá đáng kể. Trao đổi đĩa không thể thay thế hiệu năng của RAM bằng nhiều đơn đặt hàng lớn . Một hệ thống phụ thuộc quá mức vào trao đổi là tốt nhất tạm thời và nói chung: rác.
goldilocks

1
Vì vậy, không gian đĩa là vô hạn?
Kaz

Câu trả lời:


41

Điều gì có thể xảy ra nếu một quá trình "bị giết do RAM thấp"?

Đôi khi người ta nói rằng linux theo mặc định không bao giờ từ chối các yêu cầu để có thêm bộ nhớ từ mã ứng dụng - ví dụ malloc(). 1 Điều này thực tế không đúng; mặc định sử dụng một heuristic theo đó

Rõ ràng là quá mức của không gian địa chỉ bị từ chối. Được sử dụng cho một hệ thống điển hình. Nó đảm bảo phân bổ hoang dã nghiêm trọng thất bại trong khi cho phép overcommit giảm sử dụng trao đổi.

Từ [linux_src]/Documentation/vm/overcommit-accounting(tất cả các trích dẫn là từ cây 3.11). Chính xác những gì được coi là "phân bổ hoang dã nghiêm túc" không được làm rõ ràng, vì vậy chúng tôi sẽ phải thông qua nguồn để xác định chi tiết. Chúng tôi cũng có thể sử dụng phương pháp thử nghiệm trong chú thích 2 (bên dưới) để thử và nhận được một số phản ánh của heuristic - dựa trên đó, quan sát thực nghiệm ban đầu của tôi là trong các trường hợp lý tưởng (== hệ thống không hoạt động), nếu bạn không ' Không có bất kỳ trao đổi nào, bạn sẽ được phép phân bổ khoảng một nửa RAM của mình và nếu bạn có trao đổi, bạn sẽ nhận được khoảng một nửa RAM cộng với tất cả các trao đổi của bạn. Đó là nhiều hơn hoặc ít hơn cho mỗi quá trình (nhưng lưu ý giới hạn này là động và có thể thay đổi vì trạng thái, xem một số quan sát trong chú thích 5).

Một nửa RAM của bạn cộng với trao đổi rõ ràng là mặc định cho trường "CommitLimit" trong /proc/meminfo. Đây là ý nghĩa của nó - và lưu ý rằng nó thực sự không liên quan gì đến giới hạn vừa thảo luận (từ [src]/Documentation/filesystems/proc.txt):

CommitLimit: Dựa trên tỷ lệ overcommit ('vm.overcommit_ratio'), đây là tổng số lượng bộ nhớ hiện có sẵn để được phân bổ trên hệ thống. Giới hạn này chỉ được tuân thủ nếu kế toán vượt mức nghiêm ngặt được bật (chế độ 2 trong 'vm.overcommit_memory'). CommitLimit được tính theo công thức sau: CommitLimit = ('vm.overcommit_ratio' * RAM vật lý) + Hoán đổi Cam kết là 7.3G.

Tài liệu kế toán overcommit được trích dẫn trước đó nói rằng mặc định vm.overcommit_ratiolà 50. Vì vậy, nếu bạn sysctl vm.overcommit_memory=2, bạn có thể điều chỉnh vm.covercommit_ratio (với sysctl) và xem hậu quả. 3 Chế độ mặc định, khi CommitLimitkhông được thi hành và chỉ "từ chối không gian địa chỉ rõ ràng bị từ chối", là khi nào vm.overcommit_memory=0.

Mặc dù chiến lược mặc định có giới hạn theo quy trình heuristic ngăn chặn "phân bổ hoang dã nghiêm trọng", nhưng nó lại khiến hệ thống hoàn toàn tự do để có được sự phân bổ nghiêm túc, khôn ngoan. 4 Điều này có nghĩa là tại một thời điểm nào đó, nó có thể hết bộ nhớ và phải tuyên bố phá sản đối với một số quy trình thông qua kẻ giết người OOM .

Kẻ giết người OOM giết cái gì? Không nhất thiết là quá trình yêu cầu bộ nhớ khi không có, vì đó không nhất thiết là quá trình thực sự có tội, và quan trọng hơn, không nhất thiết là quá trình sẽ nhanh chóng đưa hệ thống ra khỏi vấn đề.

Điều này được trích dẫn từ đây có thể trích dẫn nguồn 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)
 */

Mà có vẻ như một lý do hợp lý. Tuy nhiên, không nhận được pháp y, # 5 (dự phòng số 1) có vẻ như là một triển khai bán hàng khó khăn và # 3 là dự phòng số 2. Vì vậy, có thể có ý nghĩa khi xem xét điều này giảm xuống # 2/3 và # 4.

Tôi đã xem qua một nguồn gần đây (3.11) và nhận thấy rằng nhận xét này đã thay đổi trong thời gian tạm thời:

/**
 * 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.
 */

Đây là một chút rõ ràng hơn về # 2: "Mục tiêu là [giết] nhiệm vụ tiêu tốn nhiều bộ nhớ nhất để tránh những thất bại tiếp theo," và bằng hàm ý số 4 ( "chúng tôi muốn tiêu diệt số lượng quá trình tối thiểu ( một ) ) .

Nếu bạn muốn thấy kẻ giết người OOM hoạt động, xem chú thích 5.


1 Gilles ảo tưởng rất may đã loại bỏ tôi, xem bình luận.


2 Đây là một bit C đơn giản yêu cầu khối bộ nhớ ngày càng lớn để xác định khi nào yêu cầu thêm sẽ thất bại:

#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;
}            

Nếu bạn không biết C, bạn có thể biên dịch cái này gcc virtlimitcheck.c -o virtlimitcheck, rồi chạy ./virtlimitcheck. Nó hoàn toàn vô hại, vì quá trình không sử dụng bất kỳ không gian nào mà nó yêu cầu - tức là, nó không bao giờ thực sự sử dụng bất kỳ RAM nào.

Trên hệ thống 3.11 x86_64 với hệ thống 4 GB và 6 GB trao đổi, tôi đã thất bại ở mức ~ 7400000 kB; số lượng dao động, vì vậy có lẽ nhà nước là một yếu tố. Điều này là trùng hợp ngẫu nhiên với CommitLimittrong /proc/meminfo, nhưng sửa đổi điều này thông qua vm.overcommit_ratiokhông làm cho bất kỳ sự khác biệt. Trên hệ thống ARM 3.6 MB 32 bit 32.11 với 64 MB trao đổi, tuy nhiên, tôi thất bại ở mức ~ 230 MB. Điều này rất thú vị vì trong trường hợp đầu tiên, số lượng gần gấp đôi dung lượng RAM, trong khi trong lần thứ hai, nó chỉ bằng 1/4 - ngụ ý mạnh mẽ số lượng trao đổi là một yếu tố. Điều này đã được xác nhận bằng cách tắt trao đổi trên hệ thống đầu tiên, khi ngưỡng thất bại giảm xuống ~ 1,95 GB, một tỷ lệ rất giống với hộp ARM nhỏ.

Nhưng đây có thực sự là một quá trình? Nó xuất hiện để được. Chương trình ngắn dưới đây yêu cầu một đoạn bộ nhớ do người dùng xác định và nếu thành công, hãy đợi bạn quay trở lại - bằng cách này bạn có thể thử nhiều phiên bản đồng thời:

#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;
}

Tuy nhiên, hãy coi chừng rằng nó không nghiêm ngặt về dung lượng RAM và trao đổi bất kể sử dụng - xem chú thích 5 để biết các quan sát về tác động của trạng thái hệ thống.


3 CommitLimit đề cập đến lượng không gian địa chỉ được phép cho hệ thống khi vm.overcommit_memory = 2. Có lẽ sau đó, số tiền bạn có thể phân bổ sẽ trừ đi những gì đã cam kết, rõ ràng là Committed_AStrường.

Một thử nghiệm thú vị tiềm năng chứng minh điều này là thêm #include <unistd.h>vào đầu của virtlimitcheck.c (xem chú thích 2) và một fork()quyền trước while()vòng lặp. Điều đó không được đảm bảo để hoạt động như được mô tả ở đây mà không có sự đồng bộ hóa tẻ nhạt, nhưng có một cơ hội tốt, 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.

Điều này có ý nghĩa - nhìn vào tmp.txt một cách chi tiết, bạn có thể thấy các quy trình thay thế phân bổ lớn hơn và lớn hơn của chúng (điều này sẽ dễ dàng hơn nếu bạn ném pid vào đầu ra) cho đến khi, một điều hiển nhiên là đã tuyên bố đủ rằng cái kia thất bại. Người chiến thắng sau đó được tự do lấy mọi thứ lên đến CommitLimitâm Committed_AS.


4 Điều đáng nói, tại thời điểm này, nếu bạn chưa hiểu địa chỉ ảo và phân trang theo yêu cầu, thì điều đầu tiên có thể làm cho sự cam kết có thể xảy ra là hạt nhân phân bổ cho các quy trình của người dùng hoàn toàn không phải là bộ nhớ vật lý - đó là không gian địa chỉ ảo . Ví dụ: nếu một quá trình dự trữ 10 MB cho một cái gì đó, thì đó là một chuỗi các địa chỉ (ảo), nhưng các địa chỉ đó chưa tương ứng với bộ nhớ vật lý. Khi một địa chỉ như vậy được truy cập, điều này dẫn đến lỗi trangvà sau đó kernel cố gắng ánh xạ nó vào bộ nhớ thực để nó có thể lưu trữ một giá trị thực. Các tiến trình thường dự trữ nhiều không gian ảo hơn so với thực tế chúng truy cập, điều này cho phép kernel sử dụng RAM hiệu quả nhất. Tuy nhiên, bộ nhớ vật lý vẫn là một tài nguyên hữu hạn và khi tất cả chúng đã được ánh xạ vào không gian địa chỉ ảo, một số không gian địa chỉ ảo phải được loại bỏ để giải phóng một số RAM.


5 Cảnh báo đầu tiên : Nếu bạn thử điều này với vm.overcommit_memory=0, hãy đảm bảo bạn lưu công việc của mình trước và đóng mọi ứng dụng quan trọng, vì hệ thống sẽ bị đóng băng trong ~ 90 giây và một số quy trình sẽ chết!

Ý tưởng là chạy một quả bom ngã ba sau 90 giây, với các nhánh phân bổ không gian và một số trong số chúng ghi một lượng lớn dữ liệu vào RAM, tất cả trong khi báo cáo cho 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);
        }
    }
}                        

Biên dịch này gcc forkbomb.c -o forkbomb. Trước tiên, hãy thử với sysctl vm.overcommit_memory=2- bạn có thể sẽ nhận được một cái gì đó như:

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.

Trong môi trường này, loại bom ngã ba này không đi được xa lắm. Lưu ý rằng số trong "for N forks" không phải là tổng số quy trình, nó là số lượng quy trình trong chuỗi / nhánh dẫn đến quy trình đó.

Bây giờ hãy thử nó với vm.overcommit_memory=0. Nếu bạn chuyển hướng stderr đến một tệp, bạn có thể thực hiện một số phân tích thô sau đó, ví dụ:

> 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!

Chỉ có 15 quá trình thất bại trong việc phân bổ 1 GB - chứng minh rằng các heuristic cho overcommit_memory = 0 bị ảnh hưởng bởi nhà nước. Có bao nhiêu quá trình ở đó? Nhìn vào phần cuối của tmp.txt, có thể> 100.000. Bây giờ làm thế nào thực sự có thể sử dụng 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

Tám - một lần nữa có ý nghĩa, vì tại thời điểm đó tôi có ~ 3 GB RAM miễn phí và 6 GB trao đổi.

Có một cái nhìn vào nhật ký hệ thống của bạn sau khi bạn làm điều này. Bạn sẽ thấy điểm báo cáo sát thủ OOM (trong số những thứ khác); có lẽ điều này liên quan đến oom_badness.


hoán đổi không phải là một giải pháp (hoặc thậm chí liên quan) đến bộ nhớ theo cam kết. Cấp phát bộ nhớ (ví dụ: malloc) là về việc yêu cầu bộ nhớ ảo được bảo lưu, không phải bộ nhớ vật lý.
jlliagre

1
@jillagre: "hoán đổi không phải là một giải pháp (hoặc thậm chí liên quan) đến bộ nhớ theo cam kết" -> Vâng, thực tế là như vậy. Các trang được sử dụng không thường xuyên bị tráo đổi RAM, để lại nhiều RAM hơn để xử lý các lỗi trang do phân trang / phân bổ theo yêu cầu (đây là cơ chế có thể vượt quá cam kết). Các trang bị tráo đổi cũng có thể phải được yêu cầu phân trang lại vào RAM tại một số điểm.
goldilocks

"Cấp phát bộ nhớ (ví dụ: malloc) là về việc yêu cầu bộ nhớ ảo được bảo lưu, không phải bộ nhớ vật lý." -> Phải, nhưng hạt nhân có thể (và tùy chọn, sẽ) nói không khi không còn ánh xạ vật lý . Chắc chắn điều đó sẽ không xảy ra vì một quá trình đã hết không gian địa chỉ ảo (hoặc ít nhất là không thường xuyên, vì điều đó cũng có thể xảy ra, ít nhất là trên các hệ thống 32 bit).
goldilocks

Phân trang nhu cầu không phải là những gì làm cho bộ nhớ trên cam kết có thể. Linux chắc chắn qua bộ nhớ cam kết trên các hệ thống không có vùng trao đổi nào cả. Bạn có thể nhầm lẫn bộ nhớ trên cam kết và yêu cầu phân trang. Nếu Linux nói "không" với malloc với quy trình 64 bit, nghĩa là nếu nó không được cấu hình để luôn quá mức, thì đó có thể là do hỏng bộ nhớ hoặc do tổng của tất cả các đặt trước bộ nhớ (cho dù được ánh xạ hay không vào RAM hoặc đĩa) vượt quá ngưỡng ngưỡng tùy thuộc vào cấu hình. Điều này không liên quan gì đến việc sử dụng RAM vì nó có thể xảy ra ngay cả khi vẫn còn RAM miễn phí.
jlliagre

"Phân trang theo yêu cầu không phải là những gì làm cho bộ nhớ vượt quá cam kết có thể." -> Có lẽ sẽ tốt hơn nếu nói rằng đó là địa chỉ ảo khiến cả hai yêu cầu phân trang và vượt quá cam kết có thể. "Linux chắc chắn vượt quá bộ nhớ cam kết trên các hệ thống không có vùng trao đổi nào cả." -> Rõ ràng, vì phân trang nhu cầu không yêu cầu trao đổi; phân trang nhu cầu từ hoán đổi chỉ là một ví dụ đặc biệt của phân trang nhu cầu. Một lần nữa, hoán đổi một giải pháp cho sự cam kết quá mức, không phải theo nghĩa là nó giải quyết vấn đề, mà theo nghĩa sẽ giúp ngăn chặn các sự kiện OOM tiềm năng có thể xảy ra do quá cam kết.
goldilocks

16

Điều này sẽ không xảy ra với bạn nếu bạn chỉ tải 1G dữ liệu vào bộ nhớ. Nếu bạn tải nhiều hơn nữa thì sao? Ví dụ, tôi thường làm việc với các tệp lớn chứa hàng triệu xác suất cần được tải vào R. Điều này cần khoảng 16 GB RAM.

Chạy quá trình trên máy tính xách tay của tôi sẽ khiến nó bắt đầu hoán đổi như điên ngay khi 8GB RAM của tôi đã được lấp đầy. Điều đó, đến lượt nó, sẽ làm mọi thứ chậm lại vì đọc từ đĩa chậm hơn nhiều so với đọc từ RAM. Nếu tôi có một máy tính xách tay có RAM 2 GB và chỉ còn 10 GB thì sao? Khi quá trình đã chiếm hết RAM, nó cũng sẽ lấp đầy đĩa vì nó đang ghi để trao đổi và tôi không còn RAM và không còn chỗ để trao đổi nữa (mọi người có xu hướng giới hạn trao đổi vào một phân vùng chuyên dụng thay vì hoán đổi cho chính xác lý do đó). Đó là nơi mà kẻ giết người OOM đến và bắt đầu các quá trình giết chóc.

Vì vậy, hệ thống thực sự có thể hết bộ nhớ. Hơn nữa, các hệ thống tráo đổi mạnh có thể trở nên không sử dụng được lâu trước khi điều này xảy ra đơn giản vì các hoạt động I / O chậm do hoán đổi. Một người thường muốn tránh trao đổi càng nhiều càng tốt. Ngay cả trên các máy chủ cao cấp có ổ SSD nhanh cũng có hiệu suất giảm rõ rệt. Trên máy tính xách tay của tôi, có ổ 7200RPM cổ điển, bất kỳ sự hoán đổi đáng kể nào về cơ bản đều khiến hệ thống không thể sử dụng được. Nó càng hoán đổi, nó càng chậm. Nếu tôi không giết quá trình vi phạm một cách nhanh chóng thì mọi thứ sẽ bị treo cho đến khi kẻ giết người OOM bước vào.


5

Các quy trình không bị giết khi không còn RAM, chúng bị giết khi chúng bị lừa theo cách này:

  • Nhân Linux thường cho phép các tiến trình phân bổ (tức là dự trữ) một lượng bộ nhớ ảo lớn hơn dung lượng thực sự có sẵn (một phần của RAM + tất cả các vùng trao đổi)
  • miễn là các quy trình chỉ truy cập vào một tập hợp con của các trang mà chúng đã dành riêng, mọi thứ đều chạy tốt.
  • nếu sau một thời gian, một quá trình cố gắng truy cập một trang mà nó sở hữu nhưng không có thêm trang nào miễn phí, tình trạng hết bộ nhớ xảy ra
  • Kẻ giết người OOM chọn một trong các quy trình, không nhất thiết là quy trình yêu cầu một trang mới và chỉ cần giết nó để khôi phục bộ nhớ ảo.

Điều này có thể xảy ra ngay cả khi hệ thống không chủ động hoán đổi, ví dụ nếu khu vực trao đổi chứa đầy các trang bộ nhớ daemon ngủ.

Điều này không bao giờ xảy ra trên các hệ điều hành không quá bộ nhớ. Với chúng, không có quá trình ngẫu nhiên nào bị giết nhưng quá trình đầu tiên yêu cầu bộ nhớ ảo trong khi nó cạn kiệt có malloc (hoặc tương tự) bị lỗi. Do đó, nó được trao một cơ hội để xử lý tình huống. Tuy nhiên, trên các HĐH này, hệ thống cũng có thể hết bộ nhớ ảo trong khi vẫn còn RAM miễn phí, điều này khá khó hiểu và thường bị hiểu lầm.


3

Khi hết RAM có sẵn, kernel bắt đầu hoán đổi các bit xử lý vào đĩa. Trên thực tế, kernel bắt đầu hoán đổi khi RAM gần cạn kiệt: nó bắt đầu hoán đổi một cách chủ động khi nó có thời gian rảnh, để phản ứng nhanh hơn nếu ứng dụng đột nhiên cần nhiều bộ nhớ hơn.

Lưu ý rằng RAM không chỉ được sử dụng để lưu trữ bộ nhớ của các tiến trình. Trên một hệ thống khỏe mạnh điển hình, chỉ có khoảng một nửa RAM được sử dụng bởi các quy trình và nửa còn lại được sử dụng cho bộ đệm và bộ đệm đĩa. Điều này cung cấp một sự cân bằng tốt giữa các quy trình đang chạy và đầu vào / đầu ra tệp.

Không gian hoán đổi không phải là vô hạn. Tại một số điểm, nếu các quy trình tiếp tục phân bổ bộ nhớ ngày càng nhiều, dữ liệu lan tỏa từ RAM sẽ lấp đầy trao đổi. Khi điều đó xảy ra, các quá trình cố gắng yêu cầu thêm bộ nhớ sẽ thấy yêu cầu của chúng bị từ chối.

Theo mặc định, Linux vượt quá bộ nhớ. Điều này có nghĩa là đôi khi nó sẽ cho phép một tiến trình chạy với bộ nhớ mà nó đã dành riêng, nhưng không được sử dụng. Lý do chính có overcommitment là cách forking công trình. Khi một tiến trình khởi chạy một tiến trình con, tiến trình con hoạt động theo khái niệm trong một bản sao của bộ nhớ của cha mẹ - hai tiến trình ban đầu có bộ nhớ có cùng một nội dung, nhưng nội dung đó sẽ phân kỳ khi các tiến trình thay đổi từng bước trong không gian riêng của chúng. Để thực hiện điều này đầy đủ, kernel sẽ phải sao chép tất cả bộ nhớ của cha mẹ. Điều này sẽ làm cho việc chuyển đổi chậm, vì vậy kernel thực hành sao chép trên ghi: ban đầu, đứa trẻ chia sẻ tất cả bộ nhớ của nó với cha mẹ; Bất cứ khi nào quá trình ghi vào một trang được chia sẻ, kernel sẽ tạo một bản sao của trang đó để phá vỡ việc chia sẻ.

Thường thì một đứa trẻ sẽ để lại nhiều trang không bị ảnh hưởng. Nếu hạt nhân được phân bổ đủ bộ nhớ để sao chép không gian bộ nhớ của cha mẹ trên mỗi ngã ba, rất nhiều bộ nhớ sẽ bị lãng phí trong các đặt chỗ mà các tiến trình con không bao giờ sử dụng. Do đó quá mức: hạt nhân chỉ dự trữ một phần của bộ nhớ đó, dựa trên ước tính số lượng trang mà đứa trẻ sẽ cần.

Nếu một quá trình cố gắng phân bổ một số bộ nhớ và không còn đủ bộ nhớ, quá trình sẽ nhận được phản hồi lỗi và xử lý khi nó thấy phù hợp. Nếu một quá trình gián tiếp yêu cầu bộ nhớ bằng cách viết vào một trang chia sẻ phải không được chia sẻ, thì đó là một câu chuyện khác. Không có cách nào để báo cáo tình huống này cho ứng dụng: nó tin rằng nó có dữ liệu có thể ghi được ở đó, và thậm chí có thể đọc nó - chỉ là cách viết liên quan đến một số thao tác phức tạp hơn một chút dưới mui xe. Nếu hạt nhân không thể cung cấp một trang bộ nhớ mới, tất cả những gì nó có thể làm là giết quá trình yêu cầu hoặc giết một số quy trình khác để lấp đầy bộ nhớ.

Bạn có thể nghĩ rằng tại thời điểm này, giết quá trình yêu cầu là giải pháp rõ ràng. Nhưng trong thực tế, điều này là không tốt. Quá trình có thể là một quá trình quan trọng xảy ra khi chỉ cần truy cập vào một trong các trang của nó, trong khi có thể có các quy trình khác ít quan trọng hơn đang chạy. Vì vậy, hạt nhân bao gồm các heuristic phức tạp để chọn quy trình nào để giết - kẻ giết người OOM nổi tiếng (trong) .


2

Chỉ cần thêm vào một quan điểm khác từ các câu trả lời khác, nhiều máy chủ lưu trữ của VPS có nhiều máy ảo trên bất kỳ máy chủ nào. Bất kỳ VM đơn lẻ nào cũng sẽ có một lượng RAM được chỉ định để sử dụng riêng. Nhiều nhà cung cấp cung cấp "RAM nổ", trong đó họ có thể sử dụng RAM vượt quá số lượng được chỉ định. Điều này có nghĩa là chỉ dành cho sử dụng ngắn hạn và những người vượt quá thời gian kéo dài này có thể bị phạt bởi máy chủ giết chết các quy trình để giảm lượng RAM sử dụng để những người khác không phải chịu đựng Máy chủ bị quá tải.


-1

Một số thời gian linux mất không gian ảo bên ngoài. Đó là phân vùng trao đổi. Khi Ram được lấp đầy, linux sẽ lấy vùng trao đổi này để chạy tiến trình ưu tiên thấp.


1
Không có quá trình được chạy từ trao đổi. Bộ nhớ ảo được chia thành các đơn vị riêng biệt có kích thước bằng nhau được gọi là các trang. Khi bộ nhớ vật lý được giải phóng, các trang ưu tiên thấp sẽ bị xóa khỏi RAM. Trong khi các trang trong bộ đệm tệp có sao lưu hệ thống tệp, các trang ẩn danh phải được lưu trữ trong trao đổi. Mức độ ưu tiên của một trang không liên quan trực tiếp đến mức độ ưu tiên của quy trình mà nó thuộc về, nhưng mức độ thường xuyên được sử dụng. Nếu một quy trình đang chạy cố gắng truy cập một trang không có trong bộ nhớ vật lý, một lỗi trang được tạo ra và quy trình được ưu tiên cho một quy trình khác trong khi (các) trang cần thiết được tìm nạp từ đĩa.
Thomas Nyman
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.