Neden hala yığını geriye doğru büyütüyoruz?


46

C kodunu derlerken ve montaja bakarken, hepsinde yığının şu şekilde geriye doğru büyümesi gerekir:

_main:
    pushq   %rbp
    movl    $5, -4(%rbp)
     popq    %rbp
    ret

-4(%rbp)- bu temel göstergenin veya yığın göstergesinin aslında yukarı gitmek yerine bellek adreslerini aşağıya taşıdığı anlamına mı geliyor? Neden?

Değiştim $5, -4(%rbp)için $5, +4(%rbp)derlenmiş ve kod koştu ve hiç hata yoktur. Öyleyse neden hala bellek yığınında geriye gitmek zorundayız?


2
Not -4(%rbp)hiç baz işaretçisi hareket etmez ve bu +4(%rbp)muhtemelen iş var olamazdı.
Margaret Bloom

14
neden hala geriye gitmek zorundayız? ” - ileriye gitmenin avantajının ne olacağını düşünüyorsun? Sonuçta, önemli değil, sadece birini seçmeniz gerekiyor.
Bergi

31
"Neden yığını geriye doğru büyütüyoruz?" - çünkü eğer bir başkası yapmazsak, neden mallocyığınını geriye doğru
büyüttüğünü sorarsak

2
@MargaretBloom: Görünüşe göre OP platformunda, CRT başlangıç ​​kodu mainRBP'sini tıkalıyorsa önemsemiyor. Bu kesinlikle mümkün. (Ve evet, yazma 4(%rbp)kaydedilmiş RBP değerine basardı). Err aslında, bu ana asla yapmaz mov %rsp, %rbp, bu nedenle bellek erişimi, arayanın RBP'sine görecelidir , eğer OP gerçekten test edilmişse !!! Bu aslında derleyici çıktısından kopyalandıysa, bazı talimatlar kalmadı!
Peter Cordes

1
Bana öyle geliyor ki "geriye" ya da "ileriye" (ya da "aşağı" ve "yukarı") bakış açınıza bağlı. Belleği, üstünde düşük adresleri olan bir sütun olarak çizdiyseniz, bir yığın işaretçisini azaltarak yığını büyütmek fiziksel bir yığına benzer olacaktır.
jamesdlin

Yanıtlar:


86

Bu, temel işaretçinin veya yığın işaretçisinin gerçekte yukarı gitmek yerine bellek adreslerini aşağıya taşıdığı anlamına mı geliyor? Neden?

Evet, pushtalimatlar yığın işaretçisini azaltır ve yığına yazar pop, tersini yaparken , yığıntan oku ve yığın işaretçisini arttır.

Bu, sınırlı belleğe sahip makineler için yığının yüksek ve aşağı doğru büyürken, yığının düşük ve yukarı doğru büyütüldüğü için biraz tarihseldir. Yığın ve yığın arasında sadece bir "boş hafıza" boşluğu vardır ve bu boşluk paylaşılır, ya ayrı ayrı ihtiyaç duyulduğunda boşluğa büyüyebilir. Bu nedenle, program yalnızca yığın ve öbek çakıştığında boş bellek kalmayacaksa bellek yetersiz kalır. 

Yığın ve yığının her ikisi de aynı yönde büyürse, iki boşluk vardır ve yığın yığının aralığına gerileyemez (bunun tersi de sorunludur).

Başlangıçta, işlemciler için özel yığın işleme talimatları yoktu. Bununla birlikte, donanıma yığın desteği eklendikçe, bu aşağıya doğru büyüyen bu kalıbı üstlendi ve işlemciler bugün hala bu kalıbı takip ediyor.

64 bitlik bir makinede çoklu boşluklara izin vermek için yeterli adres alanı olduğu iddia edilebilir - ve kanıt olarak, bir işlemin birden fazla dişe sahip olması durumunda çoklu boşlukların mutlaka olması gerektiği söylenebilir. Bu, her şeyi değiştirmek için yeterli motivasyon olmasa da, çoklu aralıklı sistemlerde, büyüme yönü tartışmalı olarak keyfidir, bu nedenle gelenek / uyumluluk ölçeği ipuçları verir.


Sen yığını yönünü değiştirmek, ya da başka adanmış iterek & talimatları haşhaş (örn kullanımı vazgeçmek için CPU yığın kullanım talimatlarını değiştirmek zorunda kalacak push, pop, call, ret, diğerleri).

MIPS komut seti mimarisi adamış etmediğini Not push& pophala tek iplik süreci için tek boşluk bellek düzeni isteyebilirsiniz, ama yukarı yığını büyüyebilir ve yığın - iki yönde yığını büyümeye pratiktir yüzden aşağıya. Ancak, bunu yaptıysanız, bazı C varargs kodlarının kaynakta veya başlık altı parametre geçişinde ayarlanması gerekebilir.

(Aslında, MIPS üzerinde özel bir yığın işleme işlemi olmadığından, yığının dışına çıkarmak için tam tersini kullandığımız sürece yığının üzerine itmek için artım öncesi veya sonrası veya öncesi veya sonrası azalmayı kullanabiliriz. işletim sistemi seçilen yığın kullanım modeline saygı gösterir. Aslında, bazı gömülü sistemler ve bazı eğitim sistemlerinde, MIPS yığını yukarı doğru büyür.)


32
Bu sadece değil pushve popçoğu mimarileri üzerinde değil, aynı zamanda çok daha önemli kesme işleme, call, ret, ve başka ne pişmiş-in gelmiştir yığını ile etkileşim.
Deduplicator

3
ARM, dört yığın tadına da sahip olabilir.
Margaret Bloom

14
Ne olursa olsun, seçimin eşit derecede iyi olması anlamında "büyüme yönü isteğe bağlı" olduğunu düşünmüyorum. Büyüme özelliği, kaydedilen dönüş adresleri dahil olmak üzere önceki yığın çerçevelerin önünü kesen arabellek ucunu taşan bir özelliğe sahiptir. Büyüme özelliği, bir tamponun sonundan taşan özelliğe sahiptir, aynı veya daha sonra sadece depolamayı engeller (tampon en geç değilse, daha sonra olanlar olabilir) çağrı çerçevesi ve muhtemelen sadece kullanılmayan bir alan (tümü bir koruyucu varsayarak) yığından sonraki sayfa). Bu yüzden, güvenlik açısından bakıldığında, büyümek oldukça tercih edilir görünüyor
R.

6
@R ..: büyümek, savunmasız fonksiyonlar genellikle yaprak fonksiyonları olmadığından tampon taşması istismarlarını ortadan kaldırmaz: başka fonksiyonlar çağırırlar, tamponun üzerine bir geri dönüş adresi yerleştirirler. Arayan kişiden bir işaretçi alan yaprak işlevleri, kendi dönüş adreslerinin üzerine yazma konusunda savunmasız hale gelebilir. Örneğin, bir işlev yığına bir arabellek ayırır ve onu satır içine almazsa gets()veya strcpy()satır içine alınmazsa, bu kitaplık işlevlerindeki dönüş üzerine yazılan dönüş adresini kullanır. Şu anda aşağı doğru büyüyen yığınlarla, arayanların döndüğü zaman.
Peter Cordes

5
@PeterCordes: Gerçekten de yorumum, taşan tampondan aynı seviyede veya daha yeni yığın çerçevelerinin hala potansiyel olarak engellenebilir olduğunu belirtti, ancak bu çok daha az. Gizlenme işlevinin doğrudan tamponu olan işlev (örneğin strcpy) tarafından çağrılan bir yaprak işlevi olması durumunda, geri dönüş adresinin dökülmesi gerekmedikçe bir kayıt defterinde tutulduğu bir kemer üzerinde geri dönüşün kapatılması için bir erişim yoktur. adres.
R.

8

Sisteminizde, yığın yüksek bellek adresinden başlar ve düşük bellek adreslerine doğru aşağıya doğru büyür. (alçaktan yükseğe doğru simetrik durum da var)

-4 ve +4 arasında değiştiğinden ve kaçtığından beri bu doğru olduğu anlamına gelmez. Çalışan bir programın bellek düzeni daha karmaşıktır ve bu son derece basit programa anında çarpmamış olmanıza katkıda bulunabilecek birçok faktöre bağlıdır.


1

Yığın işaretçisi, ayrılan ve ayrılmamış yığın belleği arasındaki sınırı işaret eder. Aşağıya doğru büyümesi , tahsis edilmiş istif alanındaki ilk yapının başlangıcına , daha büyük adreslerde takip edilen diğer tahsis edilmiş kalemlere işaret ettiği anlamına gelir . İşaretçilerin, tahsis edilmiş yapıların başlangıcına işaret etmesi, diğer taraftan çok daha yaygındır.

Günümüzde pek çok sistemde , yerel değişken depo serpiştirilmiş çağrı zincirini bulmak için bir şekilde güvenilir bir şekilde çözülebilen yığın çerçeveler için ayrı bir kayıt vardır . Şekilde bu yığın çerçevesi yazmacı bu işaret biter bir mimari vasıtası ile ayarlanır arkasında yığın işaretçisi aksine lokal değişken depolama önce o. Bu yüzden bu yığın çerçeve yazmacını kullanmak negatif indeksleme gerektirir.

Yığın çerçevelerinin ve endekslemelerinin, derlenmiş bilgisayar dillerinin bir yan yönü olduğuna dikkat edin, bu yüzden derleyicinin kod oluşturucu olduğunu, kötü bir derleme dili programcısı yerine "doğallık" ile başa çıkmak zorunda olduğunu unutmayın.

Bu nedenle, aşağı doğru büyümek için yığınları seçmenin iyi tarihsel nedenleri olsa da (ve eğer montaj dilinde programlıyorsanız ve uygun bir yığın çerçevesi oluşturmayı zahmet etmiyorsanız bazıları korunur), daha az görünür hale geldiler.


2
"Bugünlerde birçok sistemde, yığın çerçeveleri için ayrı bir kayıt var" zamanın gerisindesiniz. Daha zengin hata ayıklama bilgi formatları, günümüzde çerçeve işaretçilerine olan ihtiyacı büyük ölçüde ortadan kaldırdı.
Peter Green,
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.