2011-10-30 10 views
17

Przeczytałem, że na systemach uniksowych, malloc można zwrócić wskaźnik nie-NULL, nawet jeśli pamięć nie jest faktycznie dostępna, a próba użycia pamięci później spowoduje błąd. Ponieważ nie mogę złapać takiego błędu przez sprawdzenie NULL, zastanawiam się, jak przydatne jest sprawdzenie w ogóle wartości NULL?Czy mogę polegać na zwracaniu wartości NULL malloc?

Na podobnej zasadzie, Herb Sutter mówi, że obsługa błędów pamięci C++ jest bezużyteczna, ponieważ system przechodzi w skoki stronicowania na długo przed wystąpieniem wyjątku. Czy dotyczy to również malloc?

+3

Myślę, że nie powinieneś używać malloc w C++: http://stackoverflow.com/questions/184537/in-what-cases-do-i-use-malloc-vs-new – lc2817

+1

@ lc2817 powinieneś używać tylko malloc, jeśli piszesz kod z interfejsem C (tj. funkcje, które mają być używane z C, ale napisane w C++) ** i ** kod C jest odpowiedzialny za uwolnienie tej pamięci. –

+0

@WTP dzięki za tę precyzję. Chociaż nie wiem, czy tak jest w tym przypadku. – lc2817

Odpowiedz

32

Cytowanie Linux manuals:

Domyślnie Linux następująco optymistyczną strategię alokacji pamięci. Oznacza to, że gdy malloc() zwraca wartość inną niż NULL, nie ma gwarancji, że pamięć jest rzeczywiście dostępna. To jest naprawdę zły błąd. Jeśli okaże się, że w systemie brakuje pamięci, jeden lub więcej procesów zostanie zabitych przez niesławnego zabójcę OOM. W przypadku Linuksa jest zatrudniony w warunkach, gdzie byłoby mniej pożądane nagle stracić niektóre losowo odebrał procesów, a ponadto wersja jądra jest wystarczająco niedawno, można wyłączyć tę overcommitting zachowanie pomocą polecenia jak:

# echo 2 > /proc/sys/vm/overcommit_memory 

Powinieneś sprawdzić pod kątem powrotu do NULL, szczególnie w systemach 32-bitowych, ponieważ przestrzeń adresowa procesu mogła zostać wyczerpana dużo wcześniej niż RAM: w 32-bitowym systemie Linux proces użytkownika może mieć na przykład dostępną przestrzeń adresową 2G - 3G w przeciwieństwie do 4G całkowitej pamięci RAM. W systemach 64-bitowych sprawdzanie kodu powrotnego może być bezużyteczne, ale i tak może być uważane za dobrą praktykę, a to czyni twój program bardziej przenośnym. I pamiętaj, że dereferencja z zerowym wskaźnikiem zabija twój proces na pewno; niektóre zamiany mogą nie zaszkodzić dużo w porównaniu do tego.

Jeśli malloc stanie wrócić NULL gdy ktoś próbuje przeznaczyć tylko niewielką ilość pamięci, a następnie trzeba być ostrożnym, gdy próbuje odzyskać od stanu błędu, jak każdy kolejny malloc może zabraknąć też aż tyle dostępnej pamięci.

Domyślny operator C++ new jest często opakowaniem o tych samych mechanizmach alokacji używanych przez malloc().

+8

+1 za cytowanie dobrego słowa o tym, jak domyślnie Linux jest ** zepsuty **. Dobry program powinien zawsze sprawdzać zwracaną wartość 'malloc'. Jeśli użytkownik źle skonfigurował swój system (lub zostawił go w uszkodzonej domyślnej konfiguracji), to oczywiście może to nie pomóc, ale nic nie możesz zrobić, a awaria leży poza twoją odpowiedzialnością. Ale jeśli nie uda ci się sprawdzić wartości zwracanej przez 'malloc', twój program się zepsuje, nawet jeśli działa na systemach, w których użytkownik/administrator ** faktycznie dba o poprawność ** i wyłączył nadmiarowość. Użytkownik prawdopodobnie wtedy rozważy twoje bzdury z programu. :-) –

+2

Cóż, prawda jest nieco bardziej skomplikowana. W przestrzeni adresowej procesu znajdują się dziury; na przykład program może nigdy nie dotknąć wszystkich stron w systemie BSS lub zmienić stronę odwzorowaną w segmencie danych. Niedopasowanie jest zwykle większym problemem w systemie komputerowym niż w przypadku nadmiernej prowizji. Partycja wymiany, jeśli jest włączona, zapewnia również dodatkową poduszkę, zanim sytuacja stanie się naprawdę zła. –

+0

Nie zgadzam się. Zasada niepełnego zlecenia nie stanowi problemu, ponieważ zawsze można po prostu wymusić na nim więcej swapów. W każdym razie, jeśli masz nietknięte strony bss/danych, to znaczy, że masz globalne zmienne (nie tylko GOT/PLT tam), co jest większym problemem. :-) Być może kilka jest koniecznych, ale więcej niż jedna strona lub dwie wartości prawie na pewno świadczy o problemach z projektowaniem ... –

5

W systemie Linux, nie można rzeczywiście polegać na malloc powracającego NULL jeśli wystarczająca ilość pamięci nie jest dostępna z powodu strategii zawyżone jądra, ale należy jeszcze sprawdzić, bo w pewnych okolicznościach mallocbędzie powrót NULL, np gdy poprosisz o więcej pamięci niż jest dostępna w urządzeniu w sumie. Strona Linux malloc(3) nazywa generowanie "naprawdę złym błędem" i zawiera porady, jak go wyłączyć.

Nigdy nie słyszałem o tym zachowaniu występującym również w innych wersjach Uniksa.

Co do "skurczu stronicowania", zależy to od konfiguracji urządzenia. Np. Zwykle nie instaluję partycji wymiany na instalacjach Linux na komputer przenośny, ponieważ dokładne zachowanie, które się obawia, może zabić dysk twardy. Wciąż chciałbym programów C/C++, które uruchamiam, aby sprawdzić wartości zwracane, dać odpowiednie komunikaty o błędach i jeśli to możliwe, oczyścić po sobie.

+1

Overcommit nie jest ani cechą, ani błędem, ściśle mówiąc. To było tylko historyczne lenistwo: nadmierne zaangażowanie jest dużo łatwiejsze do wdrożenia niż rozliczanie zarzutem popełnienia błędu. Prawdopodobnie niektórzy ludzie przyzwyczaili się do tego i lubili to (z jakichkolwiek perwersyjnych powodów), a niektórzy nawet zaczęli pisać programy, które "malloc" 1 gb jako rzadki zestaw i jeszcze bardziej perwersyjne rzeczy, więc teraz utknęliśmy w tym, że jesteśmy na bieżąco. domyślnie ... –

1

tego poglądu z alternatywnego punktu widzenia:

malloc może zwrócić niezerowe wskaźnik nawet jeśli pamięć nie jest faktycznie dostępny” nie oznacza, że ​​zawsze zwraca niepuste. Mogą być (i będą) przypadki, w których zwracana jest wartość NULL (jak już powiedzieli inni), więc ta kontrola jest jednak niezbędna.

2

Sprawdzenie zwrotu malloc nie pomaga samemu, aby twoje alokacje były bezpieczniejsze lub mniej podatne na błędy. Może to być nawet pułapka, jeśli jest to jedyny test, który zastosujesz.

Po wywołaniu z argumentem 0 standard zezwala na to, aby malloc zwrócił unikalny adres, który nie jest wskaźnikiem zerowym, a do którego użytkownik nie ma prawa dostępu. Jeśli więc po prostu przetestujesz, czy zwrot jest 0, ale nie przetestuj argumentów na malloc, calloc lub realloc, możesz natrafić na błąd segfault znacznie później.

Ten błąd (pamięć wyczerpana) występuje dość rzadko w środowiskach "hostowanych". Zwykle masz kłopoty na długo przed tym, zanim natkniesz się na ten rodzaj błędu. (Ale jeśli piszesz biblioteki uruchomieniowe, jesteś hakerem jądra lub konstruktorem rakiet, to jest inaczej i tam test ma sens).

Ludzie mają tendencję do ozdabiania swojego kodu skomplikowanymi chwytami tego błędu, który obejmuje kilka linie, robienie perror i podobne rzeczy, które mogą mieć wpływ na czytelność kodu.

Myślę, że to "sprawdź powrót malloc" jest znacznie zawyżone, czasami nawet bronione dość dogmatycznie. Inne rzeczy są znacznie ważniejsze:

  • zawsze inicjalizuj zmienne, zawsze. dla zmiennych wskaźnika jest to kluczowe, niech program się psuje, zanim wszystko pójdzie źle. niezainicjowani członkowie wskaźnika w numerach struct są ważną przyczyną błędów, które są trudne do znalezienia.
  • zawsze sprawdzić argument malloc and Co., czy jest to stała czas kompilacji jak sizof toto nie może być problemem, ale zawsze upewnić się, że przydział wektor obsługuje sprawę właściwie zerowe.

Łatwo sprawdzić pod kątem powrotu malloc jest opakować coś podobnego do memset(malloc(n), 0, 1). To po prostu zapisuje 0 w pierwszym bajcie i ładnie się zawiesza, jeśli malloc miał błąd lub na początku był.

+0

Powiedzmy, że o wiele przyjemniej jest powiedzieć użytkownikowi "Out of heap at line foo" niż "wyjątek wskaźnika zerowego na pasku"; dla niego wystarczyłoby zwykłe opakowanie (makro?) dla malloc. To na wypadek, gdyby ktoś użył niedorzecznej ilości pamięci i może oczekiwać więcej niż 2G na systemach 32-bitowych. –

Powiązane problemy