Malloc () ve free () nasıl çalışır?


276

Ben nasıl bilmek istiyorum mallocve freeçalışması.

int main() {
    unsigned char *p = (unsigned char*)malloc(4*sizeof(unsigned char));
    memset(p,0,4);
    strcpy((char*)p,"abcdabcd"); // **deliberately storing 8bytes**
    cout << p;
    free(p); // Obvious Crash, but I need how it works and why crash.
    cout << p;
    return 0;
}

Cevabın hafıza düzeyinde derinlemesine olması mümkünse gerçekten minnettar olurum.


5
Aslında derleyiciye ve kullanılan çalışma zamanı kitaplığına bağlı olmamalı mı?
Vilx-

9
CRT uygulamasına bağlı olacaktır. Yani genelleme yapamazsınız.
Naveen

58
strcpy 8 değil 9 bayt yazar. NULL sonlandırıcıyı ;-) unutmayın.
Evan Teran


2
@ LưuVĩnhPhúc bu C ++. Notcout <<
Braden Best

Yanıtlar:


385

Tamam malloc hakkında bazı cevaplar zaten gönderildi.

Daha ilginç olan kısım, nasıl çalıştığıdır (ve bu yönde, malloc da daha iyi anlaşılabilir).

Birçok malloc / free uygulamasında, free normalde belleği işletim sistemine geri döndürmez (veya en azından nadir durumlarda). Bunun nedeni, yığınınızda boşluklar görmeniz ve böylece gerçekleşebilir, 2 veya 4 GB'lık sanal belleğinizi boşluklarla bitirmeniz yeterlidir. Bundan kaçınılmalıdır, çünkü sanal bellek biter bitmez, gerçekten büyük belada olacaksınız. Diğer neden, işletim sisteminin yalnızca belirli bir boyut ve hizalamadaki bellek yığınlarını işleyebilmesidir. Özel olarak belirtmek gerekirse: İşletim sistemi normalde yalnızca sanal bellek yöneticisinin işleyebileceği blokları işleyebilir (çoğunlukla 4KB gibi 512 baytın katları).

Bu yüzden işletim sistemine 40 bayt döndürmek işe yaramaz. Peki özgür ne yapar?

Free, bellek bloğunu kendi serbest blok listesine koyacaktır. Normalde adres alanındaki bitişik blokları birleştirmeye çalışır. Serbest blok listesi, başlangıçta bazı idari verilere sahip olan bellek parçalarının sadece dairesel bir listesidir. Bu ayrıca standart malloc / free ile çok küçük bellek öğelerinin yönetilmesinin etkili olmamasının nedenidir. Her bellek yığının ek verilere ihtiyacı vardır ve daha küçük boyutlarda daha fazla parçalanma olur.

Serbest liste, malloc'un yeni bir bellek yığını gerektiğinde baktığı ilk yerdir. İşletim sisteminden yeni bellek çağırmadan önce taranır. Gerekli hafızadan daha büyük bir yığın bulunduğunda, iki bölüme ayrılır. Biri arayan kişiye geri döner, diğeri tekrar ücretsiz listeye alınır.

Bu standart davranışta birçok farklı optimizasyon vardır (örneğin, küçük bellek parçaları için). Ancak malloc ve free'in bu kadar evrensel olması gerektiğinden, alternatifler kullanılamadığında standart davranış her zaman geri dönüştür. Serbest listenin işlenmesinde optimizasyonlar da vardır - örneğin, parçaları boyutlara göre sıralanmış listelerde saklamak. Ancak tüm optimizasyonların da kendi sınırlamaları vardır.

Kodunuz neden çöküyor:

Bunun nedeni, 4 karakter büyüklüğünde bir alana 9 karakter (sondaki boş bayt unutmayın) yazarak, veri kümenizin "arkasında" bulunan başka bir bellek yığını için depolanan yönetimsel verilerin üzerine yazmanızdır ( çünkü bu veriler çoğunlukla bellek parçalarının "önünde" saklanır). Daha sonra ücretsiz, yığınınızı ücretsiz listeye koymaya çalıştığında, bu yönetimsel verilere dokunabilir ve bu nedenle üzerine yazılmış bir işaretçi üzerinde yanabilir. Bu sistem çökecektir.

Bu oldukça zarif bir davranış. Ben de bir yerde kaçak işaretçi belleksiz listede verilerin üzerine yazdığı ve sistem hemen çökmedi ama bazı alt rutinleri daha sonra durumları gördüm. Orta karmaşıklıkta bir sistemde bile bu tür problemleri ayıklamak gerçekten zor olabilir! Katıldığım bir vakada, çökmenin nedenini bulmamız birkaç gün sürdü (daha büyük bir geliştirici grubu) - çünkü bellek dökümünde belirtilenlerden tamamen farklı bir yerdeydi. Saatli bomba gibi. Biliyorsun, bir sonraki "özgür" veya "malloc" çökecek, ama nedenini bilmiyorsun!

Bunlar en kötü C / C ++ sorunlarından bazıları ve işaretçilerin bu kadar sorunlu olmasının bir nedeni.


63
Pek çok kişi, free () 'in işletim sistemine bellek döndürmeyebileceğini, sinir bozucu olduğunu fark etmiyor. Aydınlatmaya yardım ettiğiniz için teşekkürler.
Artelius

Artelius: Tam tersine, yeni irade her zaman yapar mı?
Guillaume07

3
@ Guillaume07 Silmek istediğini varsayıyorum, yeni değil. Hayır, zorunlu değildir. sil ve serbest (neredeyse) aynı şeyi. MSVC2013'te her birinin aradığı kod: goo.gl/3O2Kyu
Yay295

1
delete her zaman yıkıcıyı çağırır, ancak belleğin kendisi daha sonra tahsis etmek için bir serbest listeye gidebilir. Uygulamaya bağlı olarak, malloc'un kullandığı aynı serbest liste bile olabilir.
David C.

1
@Juergen Ancak, free () malloc'dan ne kadar bellek ayrıldığı bilgisini içeren ekstra baytı okuduğunda, 4 elde eder. O zaman çökme nasıl gerçekleşti veya yönetim verilerine ne kadar özgür () dokunun?
Tanımsız Davranış

56

Aluser'un bu forumda söylediği gibi :

İşleminizin, x adresinden y adresine, yığın adı verilen bir bellek bölgesi vardır. Tüm hatalı verileriniz bu bölgede yaşıyor. malloc (), öbekteki tüm boş alan parçalarının bir veri yapısını, diyelim bir listesini tutar. Malloc'u aradığınızda, sizin için yeterince büyük bir yığın için listeye bakar, ona bir işaretçi döndürür ve artık ne kadar büyük olduğu kadar özgür olmadığı gerçeğini kaydeder. Aynı işaretçi ile free () öğesini çağırdığınızda, free (), yığının ne kadar büyük olduğunu arar ve tekrar serbest parçalar () listesine ekler. Malloc () öğesini çağırırsanız ve öbekte yeterince büyük bir yığın bulamazsanız, öbeği büyütmek için brk () sistem çağrısını kullanır, yani y adresini arttırır ve eski y ile yeni y arasındaki tüm adreslerin geçerli bellek. brk () bir sistem çağrısı olmalıdır;

malloc () sisteme / derleyiciye bağımlı olduğundan belirli bir cevap vermek zordur. Bununla birlikte, temel olarak, hangi belleğin tahsis edildiğini ve nasıl yaptığına bağlı olarak, ücretsiz aramalarınızın başarısız olabileceğini veya başarılı olabileceğini izler.

malloc() and free() don't work the same way on every O/S.


1
Bu yüzden tanımsız davranış denir. Bir uygulama, geçersiz bir yazma işleminden sonra ücretsiz olarak aradığınızda şeytanların burnunuzdan uçmasına neden olabilir. Asla bilemezsin.
Braden Best

36

Malloc / free uygulamasının bir uygulaması aşağıdakileri yapar:

  1. Sbrk () (Unix çağrısı) aracılığıyla işletim sisteminden bir bellek bloğu alın.
  2. Bu bellek bloğunun etrafında, boyut, izinler, bir sonraki ve bir önceki blokun bulunduğu bilgiler gibi bir başlık ile bir üstbilgi ve bir altbilgi oluşturun.
  3. Malloc çağrısı geldiğinde, uygun büyüklükteki blokları gösteren bir listeye başvurulur.
  4. Bu blok daha sonra döndürülür ve üstbilgiler ve altbilgiler buna göre güncellenir.

25

Bellek koruması sayfa ayrıntı düzeyine sahiptir ve çekirdek etkileşimi gerektirir

Örnek kodunuz esas olarak örnek programın neden tuzağa düşmediğini soruyor ve yanıt, bellek korumasının bir çekirdek özelliği olduğu ve yalnızca tüm sayfalar için geçerli olduğu halde, bellek ayırıcı bir kitaplık özelliğidir ve uygulama olmadan yönetir .. keyfi genellikle sayfalardan çok daha küçük boyutlu bloklar.

Bellek, programınızdan yalnızca sayfalar halinde çıkarılabilir ve bu muhtemelen gözlemlenmesi olası değildir.

calloc (3) ve malloc (3) gerekirse bellek almak için çekirdek ile etkileşime girer. Ancak çoğu free (3) uygulaması belleği 1 çekirdeğine döndürmez , sadece serbest bırakılan blokları yeniden kullanmak için calloc () ve malloc () 'un daha sonra danışacağı ücretsiz bir listeye ekler.

Bir free () belleği sisteme geri döndürmek istese bile, çekirdeğin bölgeyi gerçekten korumasını sağlamak için en az bir bitişik bellek sayfasına ihtiyaç duyar, bu nedenle küçük bir blok bırakmak yalnızca bir koruma değişikliğine yol açar son bir sayfada küçük bloğu.

Yani bloğunuz orada, ücretsiz listede oturuyor. Neredeyse her zaman ona ve yakındaki belleğe hala ayrılmış gibi erişebilirsiniz. C doğrudan makine kodunu derler ve özel hata ayıklama düzenlemeleri olmadan yükler ve depolar üzerinde sağlık kontrolü yoktur. Şimdi, ücretsiz bir bloğa erişmeye çalışırsanız, kütüphane uygulayıcılarından makul olmayan taleplerde bulunmamak için davranış standart tarafından tanımlanmamıştır. Ayrılan bir bloğun dışında serbest bırakılmış belleğe veya meory'ye erişmeye çalışırsanız, yanlış gidebilecek çeşitli şeyler vardır:

  • Bazen ayırıcılar ayrı bellek blokları tutarlar, bazen bloğunuzdan hemen önce veya sonra tahsis ettikleri bir üstbilgi kullanırlar (bir "altbilgi", sanırım), ancak ücretsiz listeyi tutmak için bloğun içindeki belleği kullanmak isteyebilirler birbirine bağlı. Öyleyse, bloğu okumanız tamamdır, ancak içeriği değişebilir ve bloğa yazmanın, ayırıcıya yanlış davranmasına veya çökmesine neden olması muhtemeldir.
  • Doğal olarak, bloğunuz gelecekte tahsis edilebilir ve daha sonra kodunuz veya bir kütüphane rutini tarafından veya calloc () ile sıfırlarla yazılması muhtemeldir.
  • Blok yeniden tahsis edilirse, boyutu da değişmiş olabilir, bu durumda çeşitli yerlerde daha fazla bağlantı veya başlatma yazılacaktır.
  • Açıkçası, programınızın çekirdeği bilinen segmentlerinden birinin sınırını aştığınız aralık dışında başvurabilirsiniz ve bu durumda tuzağa düşersiniz.

Operasyon teorisi

Böylece, örneğinizden genel teoriye doğru geriye doğru hareket eden malloc (3), gerektiğinde ve tipik olarak sayfa birimlerinde çekirdekten bellek alır. Bu sayfalar, programın gerektirdiği şekilde bölünür veya birleştirilir. Malloc ve bir dizini korumak için serbest işbirliği. Büyük bloklar sağlayabilmek için mümkün olduğunda bitişik serbest blokları birleştirirler. Dizin, bağlantılı bir liste oluşturmak için belleğin serbest bırakılmış bloklarda kullanılmasını içerebilir veya içermeyebilir. (Alternatif biraz daha paylaşılan bellek ve disk belleği dostu ve özellikle dizin için bellek ayırmayı içerir.) Malloc ve free, özel ve isteğe bağlı hata ayıklama kodu derlendiğinde bile bireysel bloklara erişimi zorlama yeteneğine sahip değilse çok az program.


1. Çok az sayıda free () uygulamasının belleği sisteme geri döndürmeye çalışmasının, uygulayıcıların yavaşlaması nedeniyle olması gerekmez. Çekirdekle etkileşim kurmak, kütüphane kodunu yürütmekten çok daha yavaştır ve yarar küçük olur. Çoğu programın sabit bir durumu veya artan bir bellek alanı vardır, bu nedenle geri döndürülebilir bellek arayan öbeği analiz etmek için harcanan zaman tamamen boşa gider. Diğer nedenler, iç parçalanmanın sayfaya göre hizalanmış blokların var olmasını pek mümkün kılmaması ve bir bloğun döndürülmesinin her iki tarafa da blokları parçalamasıdır. Son olarak, büyük miktarda bellek döndüren az sayıdaki programın malloc () işlevini atlaması ve yine de sayfaları ayırması ve serbest bırakması olasıdır.


İyi cevap. Makaleyi tavsiye ederim: Dinamik Depolama Ayırma: Wilson ve ark. Tarafından, ayırıcılar tarafından kullanılan başlık alanları ve serbest listeler gibi iç mekanizmalar hakkında ayrıntılı bir inceleme için yapılan bir anket ve Eleştirel inceleme.
Goaler444

23

Teorik olarak, malloc bu uygulama için işletim sisteminden bellek alır. Ancak, yalnızca 4 bayt isteyebileceğiniz ve işletim sisteminin sayfalarda (genellikle 4k) çalışması gerektiğinden, malloc bundan biraz daha fazlasını yapar. Bir sayfa alır ve kendi bilgilerini koyar, böylece o sayfadan ne ayırdığınızı ve serbest bıraktığınızı takip edebilirsiniz.

Örneğin, 4 bayt ayırdığınızda malloc size 4 bayta bir işaretçi verir. Fark etmeyebileceğiniz şey, hafızanın 8-12 bayt önce 4 malloc tarafından ayırdığınız tüm hafızanın zincirini yapmak için kullanılmasıdır. Ücretsiz aradığınızda, işaretçinizi alır, verinin bulunduğu yere yedekler ve bu konuda çalışır.

Belleği boşalttığınızda, malloc o bellek bloğunu zincirden çıkarır ... ve o belleği işletim sistemine geri gönderebilir veya getirmeyebilir. Varsa, işletim sistemi bu konuma erişmek için izinlerinizi alacağından, bu belleğe erişmek daha büyük olasılıkla başarısız olacaktır. Malloc hafızayı tutarsa ​​(o sayfada veya başka bir optimizasyon için ayrılmış başka şeyler olduğu için) erişim işe yarayacaktır. Hala yanlış, ama işe yarayabilir.

YASAL UYARI: Tarif ettiğim şey, malloc'un ortak bir uygulamasıdır, ancak hiçbir şekilde mümkün değildir.


12

Strcpy satırınız, NUL sonlandırıcısı nedeniyle 8 değil 9 bayt depolamaya çalışır. Tanımlanmamış davranışı başlatır.

Ücretsiz çağrı, çökebilir veya çökmeyebilir. 4 bayt ayırmanın "sonra" belleği, C veya C ++ uygulamanız tarafından başka bir şey için kullanılabilir. Başka bir şey için kullanılırsa, o zaman her şeyi karalamak "başka bir şeyin" yanlış gitmesine neden olur, ancak başka bir şey için kullanılmazsa, ondan kaçabilirsiniz. "Bundan kurtulmak" kulağa hoş gelebilir, ancak aslında kötüdür, çünkü kodunuz iyi çalışıyor gibi görünecektir, ancak gelecekteki bir çalıştırmada bundan kaçamayabilirsiniz.

Hata ayıklama tarzı bir bellek ayırıcısı ile, orada özel bir koruma değerinin yazıldığını ve bu değeri ve bulamazsa panikleri ücretsiz olarak kontrol ettiğini görebilirsiniz.

Aksi takdirde, sonraki 5 baytın henüz tahsis edilmemiş başka bir bellek bloğuna ait bir bağlantı düğümünün bir kısmını içerdiğini görebilirsiniz. Bloğunuzu boşaltmanız, kullanılabilir bloklar listesine eklenmesini de içerebilir ve liste düğümüne karaladığınız için, bu işlem geçersiz bir değere sahip bir işaretçiyi geçersiz kılarak çökmeye neden olabilir.

Her şey bellek ayırıcıya bağlıdır - farklı uygulamalar farklı mekanizmalar kullanır.


12

Malloc () ve free () nasıl çalışır, kullanılan çalışma zamanı kitaplığına bağlıdır. Genellikle malloc (), işletim sisteminden bir yığın (bellek bloğu) tahsis eder. Malloc () öğesine yapılan her istek daha sonra bu belleğin küçük bir parçasını arayana bir işaretçi döndürüyor. Bellek ayırma yordamları, kullanılan ve boş belleği yığın üzerinde takip edebilmek için ayrılan bellek bloğu hakkında bazı ek bilgileri saklamak zorunda kalacaktır. Bu bilgi genellikle malloc () tarafından döndürülen işaretçiden hemen önce birkaç baytta depolanır ve bellek bloklarının bağlantılı bir listesi olabilir.

Malloc () tarafından ayrılan bellek bloğunu yazarak, büyük olasılıkla bir sonraki bloğun kitap tutma bilgilerinin kalan kullanılmayan bellek bloğu olabilecek bazılarını yok edersiniz.

Programladığınız yerlerden biri de ara belleğe çok fazla karakter kopyalarken. Ek karakterler yığının dışında bulunuyorsa, var olmayan belleğe yazmaya çalıştığınızda erişim ihlali alabilirsiniz.


6

Bunun özellikle malloc ve free ile ilgisi yoktur. Dizeniz kopyalandıktan sonra programınız tanımlanmamış davranış gösteriyor - bu noktada veya daha sonra herhangi bir noktada çökebilir. Malloc ve free kullanmamış olsanız ve char dizisini yığına veya statik olarak ayırmış olsanız bile bu doğru olur.


5

malloc ve free uygulamaya bağlıdır. Tipik bir uygulama, kullanılabilir hafızanın "boş bir listeye" (mevcut hafıza bloklarının bağlantılı bir listesine) bölünmesini içerir. Birçok uygulama yapay olarak küçük ve büyük nesnelere böler. Ücretsiz bloklar, bellek bloğunun ne kadar büyük olduğu ve bir sonraki blokun nerede olduğu hakkında bilgi ile başlar.

Malloc yaptığınızda, serbest listeden bir blok alınır. Serbest bıraktığınızda, blok tekrar serbest listeye alınır. Muhtemelen, işaretçinizin sonunun üzerine yazdığınızda, serbest listedeki bir bloğun başlığına yazıyorsunuzdur. Belleğinizi boşalttığınızda, free () bir sonraki bloğa bakmaya çalışır ve muhtemelen bir otobüs hatasına neden olan bir işaretçiyi vurur.


4

Bu, bellek ayırıcı uygulamasına ve işletim sistemine bağlıdır.

Örneğin pencereler altında bir işlem bir sayfa veya daha fazla RAM isteyebilir. OS daha sonra bu sayfaları işleme atar. Ancak bu, uygulamanıza ayrılan bellek değildir. CRT bellek ayırıcı, belleği bitişik bir "kullanılabilir" blok olarak işaretler. CRT bellek ayırıcı daha sonra serbest bloklar listesinden geçecek ve kullanabileceği en küçük bloğu bulacaktır. Daha sonra bu bloğun ihtiyaç duyduğu kadarını alır ve onu "ayrılmış" bir listeye ekler. Gerçek bellek tahsisinin başına eklenmiş bir başlık olacaktır. Bu üstbilgi çeşitli bit bilgileri içerecektir (örneğin, bağlantılı bir liste oluşturmak için önceki ve sonraki tahsis edilmiş blokları içerebilir. Büyük olasılıkla tahsisat boyutunu içerecektir).

Free daha sonra üstbilgiyi kaldırır ve tekrar boş bellek listesine ekler. Eğer çevreleyen serbest bloklarla daha büyük bir blok oluşturuyorsa, bunlar daha büyük bir blok vermek üzere birlikte eklenecektir. Bir sayfanın tamamı ücretsizse, ayırıcı büyük olasılıkla sayfayı işletim sistemine geri döndürür.

Bu basit bir sorun değil. OS ayırıcı kısmı tamamen sizin kontrolünüz dışında. Oldukça hızlı bir ayırıcının nasıl çalışacağını anlamak için Doug Lea's Malloc (DLMalloc) gibi bir şeyi okumanızı tavsiye ederim.

Düzenleme: Çökme nedeniyle tahsis daha büyük yazarak bir sonraki bellek başlığının üzerine yazmış olmanız gerekir. Bu şekilde serbest kaldığında, tam olarak neyin özgür olduğu ve aşağıdaki blokta nasıl birleştirileceği konusunda çok karışık olur. Bu her zaman doğrudan ücretsiz bir çökmeye neden olmayabilir. Daha sonra çökmeye neden olabilir. Genel olarak bellek üzerine yazmaktan kaçının!


3

Programınız, size ait olmayan bir bellek kullandığı için çöküyor. Başka biri tarafından kullanılabilir ya da kullanılamaz - eğer şanslıysanız kaza yaparsanız, sorun uzun süre gizli kalabilir ve geri gelip daha sonra ısırır.

Malloc / ücretsiz uygulama ile ilgili olarak - tüm kitaplar konuya ayrılmıştır. Temel olarak, ayırıcı işletim sisteminden daha büyük bellek parçaları alır ve sizin için yönetir. Bir bölücü tarafından ele alınması gereken sorunlardan bazıları şunlardır:

  • Yeni bellek nasıl edinilir
  • Nasıl saklanır - (liste veya diğer yapı, farklı boyuttaki bellek parçaları için birden fazla liste vb.)
  • Kullanıcı şu anda kullanılabilir olandan daha fazla bellek isterse ne yapmalı (işletim sisteminden daha fazla bellek isteyin, mevcut blokların bazılarına katılın, tam olarak nasıl katılacağınız, ...)
  • Kullanıcı belleği boşalttığında ne yapmalı
  • Hata ayıklayıcı ayırıcılar size istediğiniz daha büyük yığın verebilir ve bazı bayt desenini doldurabilir, ayırıcı, bloğun dışında yazılıp yazılmadığını kontrol edebilecek hafızayı serbest bıraktığınızda (muhtemelen sizin durumunuzda gerçekleşir) ...

2

Söylemesi zor çünkü gerçek davranış farklı derleyiciler / çalışma zamanları arasında farklı. Hata ayıklama / bırakma yapılarının bile farklı davranışları vardır. Hata ayıklama yapıları VS2005 bellek bozulmasını algılamak için ayırmalar arasına işaretçiler ekler, bu nedenle bir çökme yerine, ücretsiz olarak () iddia edecektir.


1

Bu sadece etrafında programı mola imlecini hareket olduğunu fark etmek de önemlidir brkve sbrkaslında değil tahsis bellek, sadece adres boşluğu ayarlar. Örneğin Linux'ta, adres aralığına erişildiğinde bellek gerçek fiziksel sayfalar tarafından "desteklenir" ve bu da sayfa hatasına neden olur ve sonunda çekirdeğin bir yedek sayfa almak için sayfa ayırıcıya çağrı yapmasına neden olur.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.