2008-12-15 79 views
6

Tôi đang làm việc trên một bộ xử lý nhúng (400 MHz Intel PXA255 XScale), và tôi nghĩ rằng tôi thấy một trường hợp không đủ bộ nhớ để đáp ứng hoạt động 'mới'. Chương trình đã không sụp đổ, vì vậy tôi giả định các chủ đề khác đã giải phóng bộ nhớ của họ và nó chỉ là một điều thoáng qua. Đây là một số mã khá quan trọng, vì vậy thoát ra không phải là một lựa chọn, và một số loại lỗi cần phải được trả lại cho người dùng từ xa.Khôi phục lỗi phân bổ bộ nhớ động

Sửa chữa nhỏ sau đây có đủ để giải quyết vấn đề không, hoặc có cách nào tốt hơn không? Trước khi thay thế mọi 'mới' bằng đoạn mã sau, tôi nghĩ tôi sẽ hỏi.

char someArr[]; 
do{ 
    someArr = new char[10]; 
    Sleep(100); // no justification for choosing 100 ms 
} while (someArr == NULL); 

Trợ giúp về chế độ ngủ có giúp được không? Tôi có nên đặt một số lần thử lại tối đa không? Có thể sử dụng khởi tạo tĩnh ở mọi nơi không?

CẬP NHẬT CUỐI CÙNG: Cảm ơn bạn rất nhiều vì những phản hồi hữu ích, nhưng hóa ra là đã xảy ra lỗi khi kiểm tra mã để phân bổ bộ nhớ không thành công. Tôi sẽ giữ tất cả các câu trả lời trong tâm trí, và thay thế nhiều malloc và mới như tôi có thể, mặc dù (đặc biệt là trong mã xử lý lỗi).

Trả lời

1

Có một vài cách khác nhau để tấn công điều này - lưu ý rằng hướng dẫn công cụ sẽ thay đổi một chút, dựa trên phiên bản Windows CE/Windows Mobile bạn đang sử dụng.

Một số câu hỏi để trả lời:

1. Được ứng dụng của bạn bị rò rỉ bộ nhớ, dẫn đến tình trạng bộ nhớ thấp này?

2. Ứng dụng của bạn có sử dụng quá nhiều bộ nhớ ở một số giai đoạn nhất định, dẫn đến tình trạng bộ nhớ thấp này không?

1 và 2 có thể được điều tra bằng công cụ Windows CE AppVerifier, công cụ này có thể cung cấp các công cụ ghi nhật ký bộ nhớ chi tiết cho sản phẩm của bạn. Các công cụ gói heap khác cũng có thể cung cấp thông tin tương tự (và có thể có hiệu năng cao hơn), tùy thuộc vào thiết kế sản phẩm của bạn.

http://msdn.microsoft.com/en-us/library/aa446904.aspx

3. Bạn đang phân bổ và giải phóng bộ nhớ rất thường xuyên trong quá trình này?

Windows CE, trước HĐH 6.0 (không gây nhầm lẫn với Windows Mobile 6.x) có giới hạn bộ nhớ ảo 32MB/quá trình, có xu hướng gây ra nhiều vấn đề phân mảnh thú vị. Trong trường hợp này, ngay cả khi bạn có đủ bộ nhớ vật lý, bạn có thể đang hết bộ nhớ ảo. Sử dụng phân bổ khối tùy chỉnh thường là một giảm nhẹ cho vấn đề này.

4. Bạn có phân bổ bộ nhớ rất lớn không? (> 2MB)

Có liên quan đến 3, bạn chỉ có thể làm cạn kiệt không gian bộ nhớ ảo của quá trình. Có những thủ thuật, phần nào phụ thuộc vào phiên bản hệ điều hành, để cấp phát bộ nhớ trong một không gian VM được chia sẻ, bên ngoài không gian quy trình. Nếu bạn đang chạy ra khỏi máy ảo, nhưng không phải là RAM vật lý, điều này có thể giúp đỡ.

5. Bạn có đang sử dụng số lượng lớn tệp DLL không?

Cũng liên quan đến 3, Tùy thuộc vào phiên bản hệ điều hành, DLL cũng có thể giảm tổng số VM hiện có rất nhanh.

Hơn nữa nhảy ra khỏi điểm:

Tổng quan về các công cụ nhớ CE

http://blogs.msdn.com/ce_base/archive/2006/01/11/511883.aspx

cửa sổ điều khiển Target 'mi' công cụ

http://msdn.microsoft.com/en-us/library/aa450013.aspx

0

Chắc chắn nó sẽ phụ thuộc vào việc bạn có một kỳ vọng hợp lý của bộ nhớ trở nên có sẵn trong giấc ngủ 100 (mili giây?)? Chắc chắn, bạn nên hạn chế số lần nó cố gắng.

Với tôi thứ gì đó không có mùi ở đây. Hmmm ...

Hệ thống nhúng thường cần phải cực kỳ xác định - có lẽ bạn nên xem xét toàn bộ hệ thống và người đứng đầu tiềm năng để điều này không thành công; và sau đó chỉ thất bại, nó thực sự xảy ra trong thực tế.

1

Dựa trên câu hỏi của bạn, tôi giả định rằng heap của bạn được chia sẻ giữa nhiều luồng.

Nếu không thì mã ở trên sẽ không hoạt động, bởi vì không có gì sẽ được giải thoát khỏi vùng heap trong khi vòng lặp đang chạy.

Nếu heap được chia sẻ, thì ở trên có thể sẽ hoạt động. Tuy nhiên, nếu bạn có một đống chia sẻ, sau đó gọi "mới" có thể sẽ dẫn đến khóa xoay (vòng lặp tương tự với vòng bạn có, nhưng sử dụng lệnh CAS), hoặc nó sẽ chặn dựa trên một số tài nguyên hạt nhân.

Trong cả hai trường hợp, vòng lặp bạn có sẽ làm giảm thông lượng của hệ thống. Điều này là do bạn sẽ phải chịu thêm nhiều công tắc ngữ cảnh sau đó bạn cần, hoặc sẽ mất nhiều thời gian hơn để phản hồi sự kiện "bộ nhớ hiện có sẵn".

Tôi sẽ xem xét ghi đè toán tử "mới" và "xóa". Khi mới thất bại, bạn có thể chặn (hoặc xoay khóa trên một biến số đếm) chờ đợi một luồng khác để giải phóng bộ nhớ, và sau đó xóa có thể báo hiệu luồng "mới" bị chặn hoặc tăng biến truy cập bằng CAS.

Điều đó sẽ cung cấp cho bạn thông tốt hơn và có thêm một chút efficent

1

Một vài điểm:

  • chương trình nhúng thường phân bổ tất cả các bộ nhớ lúc khởi động hoặc chỉ sử dụng bộ nhớ tĩnh để tránh những tình huống như thế này.
  • Trừ khi có thứ gì khác chạy trên thiết bị giải phóng bộ nhớ một cách thường xuyên, giải pháp của bạn không có khả năng có hiệu quả.
  • Viper Tôi có RAM 64MB, tôi không nghĩ rằng chúng có ít hơn 32MB, ứng dụng của bạn sử dụng bao nhiêu bộ nhớ?
15

Bạn đang cố gắng giải quyết vấn đề toàn cầu thông qua lý luận địa phương. Vấn đề toàn cầu là toàn bộ thiết bị có một số lượng RAM giới hạn (và có thể là cửa hàng sao lưu) cho hệ điều hành và tất cả các ứng dụng. Để đảm bảo số lượng RAM này không vượt quá, bạn có một vài tùy chọn:

  • Mỗi quá trình hoạt động với một lượng RAM cố định được xác định cho mỗi quá trình tại thời điểm khởi động; lập trình viên có lý do để đảm bảo mọi thứ phù hợp. Vì vậy, có, có thể phân bổ mọi thứ tĩnh. Nó chỉ là rất nhiều công việc, và mỗi khi bạn thay đổi cấu hình hệ thống của bạn, bạn phải xem xét lại các phân bổ.

  • Quy trình nhận thức được nhu cầu sử dụng bộ nhớ của riêng mình và nhu cầu và liên tục tư vấn cho nhau về lượng bộ nhớ họ cần. Chúng hợp tác để chúng không hết bộ nhớ. Điều này giả định rằng ít nhất một số quy trình trong hệ thống có thể điều chỉnh các yêu cầu bộ nhớ riêng của chúng (ví dụ: bằng cách thay đổi kích thước của bộ nhớ cache nội bộ). Alonso và Appel đã viết một paper about this approach.

  • Mỗi quá trình nhận thức được rằng bộ nhớ có thể bị cạn kiệt và có thể không thành công ở trạng thái mà nó tiêu thụ một lượng bộ nhớ tối thiểu. Thường thì chiến lược này được thực hiện bằng cách có ngoại lệ ngoài bộ nhớ. Ngoại lệ được xử lý trong hoặc gần main() và sự kiện ngoài bộ nhớ về cơ bản khởi động lại chương trình từ đầu. Chế độ chuyển đổi dự phòng này có thể hoạt động nếu bộ nhớ phát triển theo yêu cầu của người dùng; nếu các yêu cầu bộ nhớ của chương trình phát triển độc lập với những gì người dùng làm, nó có thể dẫn đến việc đập.

Đề xuất của bạn ở trên không khớp với trường hợp nào. Thay vào đó, bạn đang hy vọng một số quy trình khác sẽ giải quyết vấn đề và bộ nhớ bạn cần cuối cùng sẽ xuất hiện. Bạn có thể may mắn. Bạn có thể không.

Nếu bạn muốn hệ thống của mình hoạt động đáng tin cậy, bạn sẽ làm tốt để xem xét lại thiết kế của mọi quá trình chạy trên hệ thống khi cần chia sẻ bộ nhớ bị giới hạn. Nó có thể là một công việc lớn hơn bạn mong đợi, nhưng nếu bạn hiểu vấn đề, bạn có thể làm điều này. Chúc may mắn!

+0

Vấn đề đằng sau tất cả điều này là khoảng 99% của tất cả các mã không có khái niệm làm thế nào để xử lý phân bổ bộ nhớ không thành công một cách duyên dáng. Ứng dụng không theo những cách bí ẩn. Không có bộ nhớ miễn phí mang lại cho hầu như tất cả các hệ điều hành để dừng lại screeching. Đáng buồn nhưng là sự thật. – Thorsten79

2

Có rất nhiều điều tốt trong các câu trả lời khác, nhưng tôi nghĩ rằng nó đáng giá thêm rằng nếu tất cả các chủ đề nhận được trong một vòng lặp tương tự, sau đó chương trình sẽ bị bế tắc.

Câu trả lời "đúng" cho tình huống này có thể có giới hạn nghiêm ngặt đối với các phần khác nhau của chương trình để đảm bảo rằng chúng không tiêu thụ quá nhiều bộ nhớ. Điều đó có lẽ sẽ yêu cầu viết lại các phần chính trên tất cả các phần của chương trình.

Giải pháp tốt nhất tiếp theo là có một số cuộc gọi lại khi nỗ lực phân bổ không thành công có thể cho phần còn lại của chương trình cần nhiều bộ nhớ hơn. Có lẽ các phần khác của chương trình có thể phát hành một số bộ đệm mạnh hơn bình thường, hoặc giải phóng bộ nhớ được sử dụng để lưu trữ kết quả tìm kiếm hoặc một thứ gì đó. Điều này sẽ yêu cầu mã mới cho các phần khác của chương trình. Tuy nhiên, điều này có thể được thực hiện từng bước, thay vì yêu cầu viết lại toàn bộ chương trình.

Một giải pháp khác là để chương trình bảo vệ các yêu cầu bộ nhớ lớn (tạm thời) bằng một mutex. Có vẻ như bạn tự tin rằng bộ nhớ sẽ sớm được phát hành nếu bạn chỉ có thể thử lại sau. Tôi đề nghị bạn sử dụng một mutex cho các hoạt động có thể tiêu tốn rất nhiều bộ nhớ, điều này sẽ cho phép thread được đánh thức ngay lập tức khi một luồng khác đã giải phóng bộ nhớ cần thiết. Nếu không, chủ đề của bạn sẽ ngủ trong một phần mười giây ngay cả khi bộ nhớ giải phóng ngay lập tức.

Bạn cũng có thể thử chế độ ngủ (0), thao tác này sẽ đơn giản là kiểm soát mọi chủ đề khác đã sẵn sàng để chạy. Điều này sẽ cho phép thread của bạn lấy lại quyền kiểm soát ngay lập tức nếu tất cả các chủ đề khác đi ngủ, thay vì phải đợi câu 100 mili giây của nó. Nhưng nếu ít nhất một luồng vẫn muốn chạy, bạn sẽ vẫn phải chờ cho đến khi nó từ bỏ quyền kiểm soát. Điều này thường là 10 mili giây trên các máy Linux, cuối cùng tôi đã kiểm tra. Tôi không biết về các nền tảng khác. Chủ đề của bạn cũng có thể có mức ưu tiên thấp hơn trong bộ lập lịch nếu nó đã tự nguyện chuyển sang chế độ ngủ.

1

Tôi thứ hai là điều hợp lý nhất cần làm là sử dụng phân bổ bộ nhớ tĩnh, vì vậy bạn có một số ý tưởng về những gì đang diễn ra. Phân bổ bộ nhớ động là thói quen xấu từ lập trình máy tính để bàn không phù hợp với các máy tài nguyên hạn chế (trừ khi bạn dành một chút thời gian và công sức để tạo ra một hệ thống quản lý bộ nhớ được quản lý và kiểm soát tốt).

Ngoài ra, hãy kiểm tra tính năng của hệ điều hành trong thiết bị của bạn (giả sử nó có một thiết bị ARM cao cấp như thế này có xu hướng chạy hệ điều hành) để xử lý bộ nhớ.

1

Bạn sử dụng C++. Vì vậy, bạn có thể sử dụng một số tiện ích C++ để làm cho cuộc sống của bạn dễ dàng hơn. Ví dụ, tại sao không sử dụng new_handler?

void my_new_handler() { 
    // make room for memory, then return, or throw bad_alloc if 
    // nothing can be freed. 
} 

int main() { 
    std::set_new_handler(&my_new_handler); 

    // every allocation done will ask my_new_handler if there is 
    // no memory for use anymore. This answer tells you what the 
    // standard allocator function does: 
    // https://stackoverflow.com/questions/377178 
} 

Trong new_handler, bạn có thể gửi tất cả các ứng dụng một tín hiệu để họ biết rằng bộ nhớ là cần thiết đối với một số ứng dụng, và sau đó chờ một chút để cung cấp cho các ứng dụng khác thời gian để thực hiện các yêu cầu cho bộ nhớ. Quan trọng là bạn làm điều gì đó và không phải âm thầm hy vọng cho bộ nhớ khả dụng. Nhà điều hành mới sẽ gọi lại cho người xử lý của bạn nếu vẫn không đủ bộ nhớ, vì vậy bạn không phải lo lắng về việc liệu tất cả các ứng dụng có được giải phóng bộ nhớ cần thiết hay không. Bạn cũng có thể toán tử quá tải mới nếu bạn cần biết kích thước bộ nhớ cần thiết trong new_handler. Xem other answer của tôi về cách thực hiện điều đó. Bằng cách này, bạn có một vị trí trung tâm để xử lý các vấn đề về bộ nhớ, thay vì nhiều nơi liên quan đến vấn đề đó.

1

Như những người khác đã đề cập, lý tưởng, bạn sẽ tránh được vấn đề này bằng cách lên phía trước thiết kế và kiến ​​trúc phần mềm, nhưng tôi giả định vào thời điểm này thực sự không phải là một lựa chọn.

Như một bài đăng khác đề cập đến, bạn nên kết hợp logic trong một số chức năng tiện ích để bạn không phải viết mã hết bộ nhớ.

Để khắc phục sự cố thực, bạn cố gắng sử dụng tài nguyên dùng chung, bộ nhớ, nhưng không thể vì tài nguyên được chia sẻ đó đang được sử dụng bởi một chuỗi khác trong hệ thống. Lý tưởng nhất những gì bạn muốn làm là chờ đợi một trong các chủ đề khác trong hệ thống để giải phóng tài nguyên bạn cần, và sau đó có được tài nguyên đó. Nếu bạn có cách chặn tất cả phân bổ và cuộc gọi miễn phí, bạn có thể thiết lập thứ gì đó để chuỗi phân bổ bị chặn cho đến khi bộ nhớ khả dụng và giải phóng tín hiệu cho chuỗi phân bổ khi bộ nhớ khả dụng. Nhưng tôi sẽ cho rằng đó chỉ đơn giản là quá nhiều công việc.

Với những ràng buộc không thể hoàn toàn kiến ​​trúc lại hệ thống, hoặc viết lại bộ cấp phát bộ nhớ, tôi nghĩ giải pháp của bạn là thực tế nhất, miễn là bạn (và những người khác trong nhóm của bạn), hiểu những hạn chế, và các vấn đề nó sẽ gây ra theo dõi.

Bây giờ để cải thiện cách tiếp cận cụ thể của bạn, bạn có thể muốn đo khối lượng công việc để xem tần suất bộ nhớ được phân bổ và giải phóng. Điều này sẽ cung cấp cho bạn một ay tốt hơn tính toán những gì khoảng thời gian thử lại nên được.

Thứ hai, bạn muốn thử tăng thời gian chờ cho mỗi lần lặp để giảm tải của chuỗi đó trên hệ thống.

Cuối cùng, bạn chắc chắn nên có một số thời gian của trường hợp lỗi/hoảng loạn nếu luồng không thể thực hiện tiến trình sau một số lần lặp lại. Điều này sẽ cho phép bạn ít nhất là nhìn thấy trường hợp khóa sống tiềm ẩn mà bạn có thể gặp phải nếu tất cả các chuỗi đang chờ một chuỗi khác trong hệ thống giải phóng bộ nhớ. Bạn có thể chỉ cần chọn một số lần lặp dựa trên những gì được chứng minh theo kinh nghiệm để làm việc, hoặc bạn có thể thông minh hơn về nó và theo dõi xem có bao nhiêu luồng bị mắc kẹt chờ đợi bộ nhớ, và nếu kết thúc là tất cả các luồng hoảng sợ.

Lưu ý: Đây rõ ràng không phải là giải pháp hoàn hảo và vì các áp phích khác đã đề cập một cách toàn diện hơn về ứng dụng nói chung là cần thiết để giải quyết vấn đề một cách chính xác, nhưng ở trên là một kỹ thuật thực tế Trong thời gian ngắn.

Các vấn đề liên quan