Linux sẽ bắt đầu giết các tiến trình của tôi mà không hỏi tôi liệu bộ nhớ có bị thiếu không?


66

Tôi đang chạy một kịch bản shell với các lệnh để chạy một số chương trình sử dụng nhiều bộ nhớ (2-5 GB). Khi tôi quay lại để kiểm tra tiến trình của kịch bản của mình, tôi đã rất ngạc nhiên khi phát hiện ra rằng một số quy trình của tôi là Killednhư thiết bị đầu cuối của tôi đã báo cáo cho tôi. Một số chương trình đã hoàn thành liên tiếp trước khi các chương trình được Killedkhởi động sau đó , nhưng tất cả các chương trình sau đó đều bị lỗi phân đoạn (có thể hoặc không phải do lỗi trong mã của tôi, hãy tiếp tục đọc).

Tôi đã xem lịch sử sử dụng của cụm cụ thể mà tôi đang sử dụng và thấy rằng ai đó đã bắt đầu chạy một số quy trình sử dụng nhiều bộ nhớ cùng một lúc và làm như vậy làm cạn kiệt bộ nhớ thực (và thậm chí là không gian trao đổi) có sẵn cho cụm. Theo cách tốt nhất mà tôi có thể hình dung, các quy trình sử dụng nhiều bộ nhớ này bắt đầu chạy cùng lúc tôi bắt đầu gặp vấn đề với các chương trình của mình.

Có thể Linux đã giết các chương trình của tôi khi nó bắt đầu hết bộ nhớ? Và có thể lỗi phân đoạn mà tôi gặp phải sau này là do thiếu bộ nhớ để chạy các chương trình của tôi (thay vì lỗi trong mã của tôi)?


2
Khi bạn phân bổ bộ nhớ, bạn có một câu lệnh để kiểm tra xem bộ nhớ đã được phân bổ thành công chưa? Điều đó sẽ cung cấp một manh mối cho dù có lỗi trong mã của bạn hay đó là do thiếu bộ nhớ trong hệ thống.
unxnut

Câu trả lời:


72

Nó có thể.

Có hai điều kiện bộ nhớ khác nhau mà bạn có thể gặp trong Linux. Mà bạn gặp phải phụ thuộc vào giá trị của sysctl vm.overcommit_memory( /proc/sys/vm/overcommit_memory)

Giới thiệu:
Hạt nhân có thể thực hiện cái được gọi là 'overcommit'. Đây là khi kernel phân bổ các chương trình nhiều bộ nhớ hơn thực sự có trong hệ thống. Điều này được thực hiện với hy vọng rằng các chương trình sẽ không thực sự sử dụng tất cả bộ nhớ mà chúng phân bổ, vì đây là một sự cố khá phổ biến.

overcommit_memory = 2

Khi overcommit_memoryđược đặt thành 2, kernel hoàn toàn không thực hiện bất kỳ sự thừa nào. Thay vào đó khi một chương trình được cấp phát bộ nhớ, nó được đảm bảo quyền truy cập để có bộ nhớ đó. Nếu hệ thống không có đủ bộ nhớ trống để đáp ứng yêu cầu cấp phát, kernel sẽ trả về lỗi cho yêu cầu. Đó là tùy thuộc vào chương trình để xử lý tình huống duyên dáng. Nếu nó không kiểm tra việc phân bổ thành công khi nó thực sự thất bại, ứng dụng sẽ thường gặp phải một segfault.

Trong trường hợp của segfault, bạn nên tìm một dòng như thế này trong đầu ra của dmesg:

[1962.987529] myapp[3303]: segfault at 0 ip 00400559 sp 5bc7b1b0 error 6 in myapp[400000+1000]

Điều at 0đó có nghĩa là ứng dụng đã cố gắng truy cập một con trỏ chưa được khởi tạo, đây có thể là kết quả của một cuộc gọi cấp phát bộ nhớ không thành công (nhưng đó không phải là cách duy nhất).

overcommit_memory = 0 và 1

Khi overcommit_memoryđược đặt thành 0hoặc 1, overcommit được bật và các chương trình được phép phân bổ nhiều bộ nhớ hơn mức thực sự có sẵn.

Tuy nhiên, khi một chương trình muốn sử dụng bộ nhớ đã được cấp phát, nhưng kernel thấy rằng nó không thực sự có đủ bộ nhớ để đáp ứng, nó cần lấy lại bộ nhớ. Đầu tiên, nó cố gắng thực hiện các tác vụ dọn dẹp bộ nhớ khác nhau, chẳng hạn như xóa bộ nhớ cache, nhưng nếu điều này là không đủ thì nó sẽ chấm dứt một quá trình. Việc chấm dứt này được thực hiện bởi OOM-Killer. OOM-Killer nhìn vào hệ thống để xem các chương trình đang sử dụng bộ nhớ nào, chúng đã chạy được bao lâu, ai đang chạy chúng và một số yếu tố khác để xác định chương trình nào sẽ bị giết.

Sau khi quá trình bị hủy, bộ nhớ mà nó đang sử dụng sẽ được giải phóng và chương trình vừa gây ra tình trạng hết bộ nhớ giờ có bộ nhớ cần thiết.

Tuy nhiên, ngay cả trong chế độ này, các chương trình vẫn có thể bị từ chối yêu cầu phân bổ. Khi overcommit_memory0, hạt nhân cố gắng để có một đoán tốt nhất tại khi nó nên bắt đầu phủ nhận yêu cầu phân bổ. Khi được đặt thành 1, tôi không chắc nó sử dụng quyết định nào để xác định khi nào nên từ chối yêu cầu nhưng nó có thể từ chối các yêu cầu rất lớn.

Bạn có thể xem liệu OOM-Killer có liên quan hay không bằng cách xem kết quả đầu ra dmesgvà tìm một thông báo như:

[11686.043641] Out of memory: Kill process 2603 (flasherav) score 761 or sacrifice child
[11686.043647] Killed process 2603 (flasherav) total-vm:1498536kB, anon-rss:721784kB, file-rss:4228kB

Vì vậy, dường như cả hai tình huống đã xảy ra với tôi.
NeutronStar

@Joshua Mình mới cập nhật câu trả lời. Tôi quên đề cập đến việc bạn vẫn có thể bị lỗi phân bổ khi overcommit_memoryđược đặt thành 0 hoặc 2.
Patrick

Tôi nghĩ rằng việc chỉnh sửa một liên kết để thuần hóa kẻ giết người OOM vào bài viết có thể đáng giá.
0xC0000022L

@ 0xC0000022L Cảm ơn, đó là một bài viết hay (mặc dù hơi lỗi thời). Tôi không muốn đưa ra bất cứ điều gì về việc kiểm soát kẻ giết người OOM vì đó không phải là một phần của câu hỏi (và nó không phải là một chủ đề ngắn), và chúng tôi có rất nhiều câu hỏi khác ở đây về điều đó.
Patrick

1
@mikeerv Tôi không nói rằng hành vi của kẻ giết người OOM không liên quan gì đến việc kiểm soát nó. Câu hỏi là liệu linux có giết chết các chương trình của anh ấy không. Làm thế nào để ngăn chặn linux làm như vậy trước tiên đòi hỏi phải thiết lập rằng nó thực sự là linux làm điều đó. Và nếu overcommit_memory=2, kẻ giết người OOM thậm chí không được kích hoạt, vì vậy việc kiểm soát nó là không liên quan. Tuy nhiên, một khi chúng tôi xác định rằng đó là kẻ giết người OOM, nó sẽ trở thành một chủ đề khác trong đó được bao phủ bởi nhiều câu hỏi và câu trả lời khác ở đây.
Patrick

16

Sự thật là bất kể bạn nhìn nó theo cách nào - cho dù quá trình của bạn bị nghẹt thở do trình quản lý bộ nhớ của hệ thống hay do một điều gì khác - đó vẫn là một lỗi. Điều gì đã xảy ra với tất cả dữ liệu mà bạn vừa xử lý trong bộ nhớ? Nó đã được lưu.

Mặc dù overcommit_memory=là cách tổng quát nhất để định cấu hình quản lý OOM của Linux, nhưng nó cũng có thể điều chỉnh theo từng quy trình như:

echo [-+][n] >/proc/$pid/oom_adj

Sử dụng -17ở trên sẽ loại trừ một quá trình từ quản lý hết bộ nhớ. Có lẽ không phải là một ý tưởng tuyệt vời nói chung, nhưng nếu bạn đang tìm kiếm lỗi như vậy có thể đáng giá - đặc biệt là nếu bạn muốn biết liệu đó là OOM hay mã của bạn. Tăng số tích cực sẽ làm cho quá trình có nhiều khả năng bị giết trong một sự kiện OOM, điều này có thể cho phép bạn tăng cường khả năng phục hồi mã của mình trong các tình huống bộ nhớ thấp và đảm bảo bạn thoát ra một cách duyên dáng khi cần thiết.

Bạn có thể kiểm tra cài đặt hiện tại của trình xử lý OOM cho mỗi quy trình như:

cat /proc/$pid/oom_score 

Khác bạn có thể tự tử:

sysctl vm.panic_on_oom=1
sysctl kernel.panic=X

Điều đó sẽ đặt máy tính khởi động lại trong trường hợp hết bộ nhớ. Bạn đặt Xở trên thành số giây bạn muốn máy tính tạm dừng sau khi hoảng loạn kernel trước khi khởi động lại. Đi hoang dã.

Và nếu, vì một số lý do, bạn quyết định bạn thích nó, làm cho nó bền bỉ:

echo "vm.panic_on_oom=1" >> /etc/sysctl.conf
echo "kernel.panic=X" >> /etc/sysctl.conf

Đó là cụm chia sẻ tôi đang sử dụng, tôi chắc chắn rằng những người dùng khác sẽ không đánh giá cao việc khởi động lại mà không có sự đồng ý của họ.
NeutronStar

3
@Joshua - Tôi nghi ngờ rất nghiêm túc rằng bất kỳ ai cũng thích nó - nó thậm chí còn bất chấp luật robot của Asimov. Mặt khác, như tôi đã đề cập, bạn cũng có thể định cấu hình OOM cho mỗi quy trình theo cách khác. Điều đó có nghĩa là bạn có thể xử lý cá nhân dựa trên các quy tắc được xác định của riêng bạn cho mỗi quy trình. Điều đó nghe có vẻ đặc biệt hữu ích trong kịch bản cụm chia sẻ.
mikeerv
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.