Làm thế nào tôi có thể giới hạn số lượng bộ nhớ có sẵn cho một quá trình?


11

Tôi đang phát triển một chương trình đi; đôi khi kết thúc việc phân bổ bộ nhớ rất lớn (>> 10G trên máy có 8G bộ nhớ vật lý), khiến hệ thống trở nên không phản hồi. Tôi muốn giới hạn số lượng bộ nhớ mà quá trình có thể phân bổ. Cách thông thường tôi sẽ làm điều này là:

ulimit -m 4000000 ; ./myprogram

... sẽ giết chương trình của tôi nếu nó cố sử dụng hơn 4GB bộ nhớ.

Trên OS X El Capitan, điều này dường như không có hiệu lực; thậm chí ulimit -m 1(giới hạn tất cả các chương trình chỉ còn 1kB bộ nhớ!) là không hiệu quả.

Làm cách nào tôi có thể đặt giới hạn trên của bộ nhớ khả dụng cho một quy trình cụ thể?


Khi bạn nói Cách thông thường tôi sẽ làm điều này , ý bạn là gì? Cụ thể hơn, khi nào bạn thường làm điều này? Và khi bạn làm điều đó, bạn có nghĩa là trong Mac OS X El Capitan hoặc trong một môi trường khác? Bạn có thể đưa ra một ví dụ về khi bạn đã sử dụng nó thành công? Về cơ bản, tôi chỉ đang cố gắng làm rõ liệu quy trình này có hoạt động bình thường với bạn không, nhưng chỉ là không hoạt động cho chương trình cụ thể này? Hoặc nó không làm việc cho bạn chút nào nhưng bạn nghĩ nó nên?
Monomeeth

1
Theo "cách thông thường tôi sẽ làm điều này", ý tôi là cách tôi sẽ làm điều này trên các hương vị khác của unix. Tôi lưu ý rằng tôi vừa phát hiện ra rằng ulimit -mkhông còn hoạt động trên Linux (> 2.4.30), mặc dù ulimit -vvẫn hoạt động như mong đợi. (Giống như ulimit -m, ulimit -vdường như cũng không có tác dụng gì với OS X.)
cpcallen

Nghe có vẻ như bạn đã bị rò rỉ bộ nhớ trong chương trình bạn đang viết và cần thực hiện bộ sưu tập rác tốt hơn. Bạn đã đọc lên quản lý bộ nhớ trong đi?
Todd Dabney

1
Không, không phải là rò rỉ bộ nhớ mà chỉ là một tìm kiếm đồ thị của một không gian trạng thái rất lớn. Tôi có thể thêm mã để hủy bỏ tìm kiếm nếu các cấu trúc bên trong khác nhau quá lớn, nhưng tôi hy vọng có thể làm điều tương tự (ngăn máy không bị vướng bởi các đầu vào lớn) với lớp vỏ một lớp.
cpcallen

Câu trả lời:


1

Có hai cách tiếp cận để hạn chế việc sử dụng bộ nhớ của bạn: Ex post facto và prejectionive. Điều đó có nghĩa là, bạn có thể cố gắng giết chương trình của mình sau khi nó quá lớn hoặc bạn có thể lập trình để nó không quá lớn ngay từ đầu.

Nếu bạn nhấn mạnh vào cách tiếp cận bài thực tế, bạn có thể sử dụng tập lệnh Bash sau. Tập lệnh này trước tiên tìm thấy dung lượng bộ nhớ (như được xác định bởi "kích thước tập hợp lưu trú") mà quá trình với pid processid đang sử dụng, lọc ra tất cả dữ liệu không phải là số bằng grep và lưu số lượng dưới dạng biến n. Tập lệnh sau đó kiểm tra nếu n lớn hơn x được chỉ định của bạn. Nếu có, quá trình với pid processid bị giết.

Xin lưu ý:

  1. Bạn phải thay thế <pid>bằng id quá trình của chương trình của bạn.
  2. Bạn phải thay thế <x>bằng rss = "kích thước cài đặt thường trú" (nghĩa là kích thước bộ nhớ thực) mà bạn không muốn chương trình vượt quá.

n=$(ps -<pid> -o rss | grep '[0-9]') if [ $n -gt <x> ]; then kill -9 <pid>; fi

Nếu bạn muốn điều này chạy mỗi y giây, chỉ cần đưa nó vào một vòng lặp, và bảo nó đợi y giây sau mỗi lần lặp. Bạn cũng có thể viết một lệnh tương tự bằng cách sử dụng top. Điểm khởi đầu của bạn sẽ là top -l 1|grep "<pid>"|awk '{print $10}'.

Câu trả lời của @ kenorb đã giúp tôi với kịch bản của mình


Mặc dù tôi tin rằng câu trả lời cho câu hỏi, về lâu dài tôi tin rằng đó là thiết kế lập trình tốt hơn để thực hiện một phương pháp phòng ngừa bằng cách sử dụng cấp phát bộ nhớ thủ công.

Đầu tiên, bạn có chắc việc sử dụng bộ nhớ thực sự là một vấn đề? Tài liệu về Go nêu rõ:

Bộ cấp phát bộ nhớ Go dành một vùng bộ nhớ ảo lớn làm đấu trường để phân bổ. Bộ nhớ ảo này là cục bộ của quá trình Go cụ thể; việc bảo lưu không làm mất các quá trình khác của bộ nhớ.

Nếu bạn vẫn nghĩ rằng bạn có vấn đề, thì tôi khuyến khích bạn tự quản lý bộ nhớ của mình như được thực hiện bằng ngôn ngữ lập trình C. Vì go được viết bằng C, tôi nghi ngờ sẽ có cách để vào quản lý / phân bổ bộ nhớ C, và thực sự là có. Xem kho lưu trữ github này,

cho phép bạn thực hiện quản lý bộ nhớ thủ công thông qua bộ cấp phát C tiêu chuẩn cho hệ thống của bạn. Nó là một lớp bọc mỏng trên đầu malloc, calloc và miễn phí. Xem man malloc để biết chi tiết về các chức năng này cho hệ thống của bạn. Thư viện này sử dụng cgo.

Trường hợp sử dụng được đưa ra là:

Tại sao bạn muốn điều này?

Khi một chương trình gây ra áp lực bộ nhớ hoặc hệ thống sắp hết bộ nhớ, có thể hữu ích để kiểm soát thủ công phân bổ bộ nhớ và giải quyết. Go có thể giúp bạn kiểm soát phân bổ nhưng không thể giải quyết rõ ràng dữ liệu không cần thiết.

Đây có vẻ như là một giải pháp lâu dài tốt hơn.

Nếu bạn muốn tìm hiểu thêm về C (bao gồm quản lý bộ nhớ), Ngôn ngữ lập trình C là tài liệu tham khảo tiêu chuẩn.


Tôi chắc chắn rằng việc sử dụng bộ nhớ thực sự là một vấn đề. Khi chương trình tăng đột ngột lên gấp đôi kích thước bộ nhớ vật lý, máy gần như không phản hồi hoàn toàn do lỗi trang. Mất khoảng một giờ giữa khi tôi nhấn ^ C và khi macOS tiếp tục phản hồi với các nhấp chuột; trong thời gian đó, bằng chứng duy nhất cho thấy nó không bị đóng băng là tiếng ồn yên tĩnh của ổ cứng nhanh chóng phát ra.
cpcallen

Đây có lẽ là một giải pháp hợp lý trong thực tế, nhưng không giống như ulimit trên các hệ điều hành UNIX (không phải Darwin), nó phụ thuộc vào việc có thể phân bổ đủ bộ nhớ một cách kịp thời để thực hiện thành công lệnh kill. Tôi thực sự muốn có giới hạn kích thước quá trình được thực thi bởi kernel.
cpcallen

@cpcallen thực hiện một số tìm kiếm có vẻ như "tham số -m đến ulimit không có tác dụng đối với các hệ thống Linux với các phiên bản kernel gần đây hơn 2.4.30." Có vẻ như OSX cũng đã chọn thay đổi này. Hãy thử tùy chọn -v để giới hạn không gian địa chỉ
Evan Rosica

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.