Apa itu "ekspansi tumpukan otomatis"?

13

getrlimit (2) memiliki definisi berikut di halaman manual:

RLIMIT_AS Ukuran maksimum memori virtual proses (ruang alamat) dalam byte. Batas ini memengaruhi panggilan ke brk (2), mmap (2) dan mremap (2), yang gagal dengan kesalahan ENOMEM setelah melampaui batas ini. Juga ekspansi tumpukan otomatis akan gagal (dan menghasilkan SIGSEGV yang membunuh proses jika tidak ada tumpukan alternatif yang tersedia melalui sigaltstack (2)). Karena nilainya panjang, pada mesin dengan panjang 32-bit baik batas ini paling banyak 2 GiB, atau sumber daya ini tidak terbatas.

Apa yang dimaksud dengan "ekspansi tumpukan otomatis" di sini? Apakah tumpukan di lingkungan Linux / UNIX tumbuh sesuai kebutuhan? Jika ya, bagaimana mekanisme tepatnya?

keras dan jelas
sumber

Jawaban:

1

Ya tumpukan tumbuh secara dinamis. Tumpukan ada di bagian atas memori yang tumbuh ke bawah menuju tumpukan.

--------------
| Stack      |
--------------
| Free memory|
--------------
| Heap       |
--------------
     .
     .

Tumpukan tumbuh ke atas (setiap kali Anda melakukan malloc) dan tumpukan tumbuh ke bawah saat dan ketika fungsi baru dipanggil. Tumpukan hadir tepat di atas bagian BSS dari program. Yang berarti ukuran program Anda dan cara ia membuat memori dalam tumpukan juga memengaruhi ukuran tumpukan maksimum untuk proses itu. Biasanya ukuran stack tidak terbatas (sampai area tumpukan dan tumpukan bertemu dan / atau ditimpa yang akan memberikan stack overflow dan SIGSEGV :-)

Ini hanya untuk proses pengguna, Tumpukan kernel selalu diperbaiki (biasanya 8KB)

Santosh
sumber
"Biasanya ukuran stack tidak terbatas", tidak, biasanya dibatasi oleh 8Mb ( ulimit -s).
Eddy_Em
ya Anda benar di sebagian besar sistem. Anda memeriksa perintah ulimit shell, jika demikian ada batas keras pada ukuran stack, yang tidak terbatas (ulimit -Hs). Pokoknya, titik itu adalah untuk menekankan bahwa tumpukan dan tumpukan tumbuh ke arah yang berlawanan.
Santosh
Lalu bagaimana "ekspansi otomatis" berbeda dari "mendorong elemen ke stack"? Dari penjelasan Anda, saya merasa bahwa mereka sama. Juga, saya merasa seperti titik awal stack dan heap jauh lebih dari 8MB, sehingga stack dapat tumbuh sebanyak yang dibutuhkan (atau menyentuh heap). Benarkah itu? Jika ya, bagaimana sistem operasi memutuskan di mana menempatkan tumpukan dan tumpukan?
loudandclear
Mereka sama, tetapi tumpukan tidak memiliki ukuran yang tetap kecuali Anda membatasi ukuran menggunakan rlimit. Stack ditempatkan di ujung area memori virtual, dan tumpukan segera setelah segmen data yang dapat dieksekusi.
Santosh
Saya mengerti, terima kasih. Namun, saya tidak berpikir saya mendapatkan bagian "tumpukan tidak memiliki ukuran tetap". Jika itu masalahnya, untuk apa batas lunak 8Mb?
loudandclear
8

Mekanisme yang tepat diberikan di sini, di Linux: dalam menangani kesalahan halaman pada pemetaan anonim, Anda memeriksa untuk melihat apakah itu "alokasi yang tumbuh" yang harus Anda kembangkan seperti tumpukan. Jika catatan area VM mengatakan Anda harus, maka Anda menyesuaikan alamat mulai untuk memperluas tumpukan.

Ketika kesalahan halaman terjadi, tergantung pada alamat, itu dapat diperbaiki (dan kesalahan diatasi) melalui ekspansi stack. Perilaku "tumbuh ke bawah karena suatu kesalahan" untuk memori virtual ini dapat diminta oleh program pengguna yang sewenang-wenang dengan MAP_GROWSDOWNbendera diteruskan ke mmapsyscall.

Anda dapat dipusingkan dengan mekanisme ini di program pengguna juga:

#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>

int main() {
        long page_size = sysconf(_SC_PAGE_SIZE);
        void *mem = mmap(NULL, page_size, PROT_READ|PROT_WRITE, MAP_GROWSDOWN|MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
        if (MAP_FAILED == mem) {
                perror("failed to create growsdown mapping");
                return EXIT_FAILURE;
        }

        volatile char *tos = (char *) mem + page_size;

        int i;
        for (i = 1; i < 10 * page_size; ++i)
                tos[-i] = 42;

        fprintf(stderr, "inspect mappping for originally page-sized %p in /proc... press any key to continue...\n", mem);
        (void) getchar();

        if (munmap(mem, page_size))
                perror("failed munmap");

        return EXIT_SUCCESS;
}

Ketika diminta, Anda menemukan pid dari program (melalui ps) dan melihat /proc/$THAT_PID/mapsuntuk melihat bagaimana daerah asli telah tumbuh.

cdleary
sumber
Apakah boleh menelepon munmap untuk mem dan page_size asli meskipun wilayah memori telah berkembang melalui MAP_GROWSDOWN? Saya kira ya, karena kalau tidak itu akan menjadi API yang sangat kompleks untuk digunakan, tetapi dokumentasi tidak mengatakan apa-apa secara eksplisit tentang masalah ini
i.petruk
2
MAP_GROWSDOWN tidak boleh digunakan, dan telah dihapus dari glibc (lihat lwn.net/Articles/294001 untuk alasannya).
Collin