Jika, saat program dimuat, penyimpanan di alamat tersebut tidak tersedia, pemuat harus memindahkan program yang dimuat agar sesuai dengan alamat pemuatan sebenarnya.
Tidak semua format file mendukung ini:
GCC untuk Windows 32-bit akan menambahkan informasi yang diperlukan untuk pemuat dalam hal perpustakaan dinamis (.dll
). Namun, informasi tersebut tidak ditambahkan ke file yang dapat dieksekusi (.exe
), sehingga file yang dapat dieksekusi tersebut harus dimuat ke alamat tetap.
Di Linux, ini sedikit lebih rumit; namun, juga tidak mungkin untuk memuat banyak file yang dapat dieksekusi (biasanya 32-bit yang lebih lama) ke alamat yang berbeda sementara perpustakaan dinamis (.so
) dapat dimuat ke alamat yang berbeda.
Misalkan saya memiliki executable lain yang sedang berjalan di mesin saya dan sudah menggunakan ruang memori antara 400540
dan 601040
...
Komputer modern (semua komputer x86 32-bit) memiliki MMU paging yang digunakan oleh sebagian besar sistem operasi modern. Ini adalah beberapa sirkuit (biasanya di CPU) yang menerjemahkan alamat yang dilihat oleh perangkat lunak ke alamat yang dilihat oleh RAM. Dalam contoh Anda, 400540
dapat diterjemahkan ke 1234000
, jadi mengakses alamat 400540
sebenarnya akan mengakses alamat 1234000
di RAM.
Intinya adalah: OS modern menggunakan konfigurasi MMU yang berbeda untuk tugas yang berbeda. Jadi jika Anda memulai program Anda lagi, konfigurasi MMU berbeda digunakan yang menerjemahkan alamat 400540
yang dilihat oleh perangkat lunak ke alamat 2345000
di RAM. Kedua program yang menggunakan alamat 400540
dapat dijalankan secara bersamaan karena satu program akan benar-benar mengakses alamat 1234000
dan program lainnya akan mengakses alamat 2345000
di RAM ketika program mengakses alamat 400540
.
Ini berarti bahwa beberapa alamat (misalnya 400540
) tidak akan pernah digunakan ketika file yang dapat dieksekusi dimuat.
Alamat tersebut mungkin sudah digunakan ketika perpustakaan dinamis (.so
/.dll
) dimuat karena perpustakaan ini berbagi memori dengan file yang dapat dieksekusi.
... bagaimana memutuskan di mana memulai linux baru saya yang dapat dieksekusi?
Di Linux, file yang dapat dieksekusi akan dimuat ke alamat tetap jika ditautkan sedemikian rupa sehingga tidak dapat dipindahkan ke alamat lain. (Seperti yang telah dikatakan: Ini tipikal untuk file 32-bit lama.) Dalam contoh Anda, string Hello world akan ditempatkan di alamat 0x601040
jika kompiler dan linker Anda membuat file yang dapat dieksekusi dengan cara itu.
Namun, sebagian besar file executable 64-bit dapat dimuat ke alamat yang berbeda. Linux akan memuatnya ke alamat acak tertentu karena alasan keamanan sehingga lebih sulit bagi virus atau malware lain untuk menyerang program.
... sehingga tumpukan dapat bertambah di bawah segmen teks ...
Saya belum pernah melihat tata letak memori ini di sistem operasi mana pun:
Baik di Linux maupun di Solaris, tumpukan terletak di ujung ruang alamat (sekitar 0xBFFFFF00
), sedangkan segmen teks dimuat cukup dekat dengan awal memori (mungkin alamat 0x401000
).
...dan heap dapat bertambah dari akhir data, ...
misalkan tumpukan aplikasi sebelumnya merayap naik..
Banyak implementasi sejak akhir tahun 1990an tidak menggunakan heap lagi. Sebaliknya, mereka menggunakan mmap()
untuk memesan memori baru.
Menurut halaman manual brk()
, heap dinyatakan sebagai fitur lawas pada tahun 2001, sehingga tidak boleh digunakan lagi oleh program baru.
(Namun, menurut Peter Cordes malloc()
sepertinya masih menggunakan heap dalam beberapa kasus.)
Tidak seperti sistem operasi sederhana seperti MS-DOS, Linux tidak mengizinkan Anda menggunakan heap begitu saja, tetapi Anda harus memanggil fungsi brk()
untuk memberi tahu Linux berapa banyak heap yang ingin Anda gunakan.
Jika suatu program menggunakan heap dan menggunakan lebih banyak heap daripada yang tersedia, fungsi brk()
mengembalikan beberapa kode kesalahan dan fungsi malloc()
hanya mengembalikan NULL
.
Namun, situasi ini biasanya terjadi karena tidak ada lagi RAM yang tersedia dan bukan karena heap tersebut tumpang tindih dengan area memori lainnya.
...sementara tumpukan linux yang baru diluncurkan telah berkembang ke bawah menjadi ...
Segera, akan terjadi bentrokan/tumpang tindih alamat memori. Apa yang terjadi jika bentrokan akhirnya terjadi?
Memang ukuran tumpukannya terbatas.
Jika Anda menggunakan terlalu banyak tumpukan, Anda akan mengalami tumpukan yang meluap.
Program ini sengaja menggunakan terlalu banyak tumpukan - hanya untuk melihat apa yang terjadi:
.globl _start
_start:
sub $0x100000, %rsp
push %rax
push %rax
jmp _start
Dalam kasus sistem operasi dengan MMU (seperti Linux), program Anda akan crash dengan pesan kesalahan:
~$ ./example_program
Segmentation fault (core dumped)
~$
EDIT/TAMBAHAN
Apakah tumpukan untuk semua program yang sedang berjalan terletak di bagian akhir?
Pada versi Linux yang lebih lama, tumpukan terletak di dekat (tetapi tidak persis pada) ujung memori virtual yang dapat diakses oleh program: Program dapat mengakses rentang alamat dari 0
hingga 0xBFFFFFFF
pada versi Linux tersebut. Penunjuk tumpukan awal terletak di sekitar 0xBFFFFE00
. (Argumen baris perintah dan variabel lingkungan muncul setelah tumpukan.)
Dan apakah ini akhir dari memori fisik yang sebenarnya? Bukankah tumpukan program yang sedang berjalan akan tercampur? Saya mendapat kesan bahwa semua tumpukan dan memori suatu program tetap berdekatan dalam memori fisik sebenarnya, ...
Pada komputer yang menggunakan MMU, program tidak pernah melihat memori fisik:
Saat program dimuat, OS akan mencari area bebas pada RAM - mungkin menemukan beberapa di alamat fisik 0xABC000
. Kemudian mengkonfigurasi MMU sedemikian rupa sehingga alamat virtual 0xBFFFF000-0xBFFFFFFF
diterjemahkan ke alamat fisik 0xABC000-0xABCFFF
.
Artinya: Setiap kali program mengakses alamat 0xBFFFFE20
(misalnya menggunakan operasi push
), alamat fisik 0xABCE20
di RAM sebenarnya diakses.
Tidak ada kemungkinan sama sekali bagi suatu program untuk mengakses alamat fisik tertentu.
Jika Anda menjalankan program lain, MMU dikonfigurasi sedemikian rupa sehingga alamat 0xBFFFF000-0xBFFFFFFF
diterjemahkan ke alamat 0x345000-0x345FFF
saat program lain sedang berjalan.
Jadi jika salah satu dari dua program akan melakukan operasi push
dan penunjuk tumpukan adalah 0xBFFFFE20
, alamat 0xABCE20
di RAM akan diakses; jika program lain melakukan operasi push
(dengan nilai penunjuk tumpukan yang sama), alamat 0x345E20
akan diakses.
Oleh karena itu, tumpukannya tidak akan tercampur.
OS yang tidak menggunakan MMU namun mendukung multitasking (contohnya adalah Amiga 500 atau Apple Macintosh versi awal) tentu saja tidak akan berfungsi dengan cara ini. OS tersebut menggunakan format file khusus (dan bukan ELF) yang dioptimalkan untuk menjalankan banyak program tanpa MMU. Mengkompilasi program untuk OS semacam itu jauh lebih rumit daripada mengkompilasi program untuk Linux atau Windows. Dan bahkan ada batasan bagi pengembang perangkat lunak (contoh: fungsi dan array tidak boleh terlalu panjang).
Selain itu, apakah setiap program memiliki penunjuk tumpukan, penunjuk dasar, register, dll.? Atau apakah OS hanya memiliki satu set register ini untuk digunakan bersama oleh semua program?
(Dengan asumsi CPU single-core), CPU memiliki satu set register; dan hanya satu program yang dapat dijalankan pada waktu yang bersamaan.
Saat Anda memulai beberapa program, OS akan beralih antar program. Artinya program A berjalan (misalnya) 1/50 detik, kemudian program B berjalan 1/50 detik, kemudian program A berjalan 1/50 detik dan seterusnya. Tampaknya bagi Anda seolah-olah program tersebut berjalan pada waktu yang sama.
Ketika OS beralih dari program A ke program B, ia harus terlebih dahulu menyimpan nilai register (program A). Maka harus mengubah konfigurasi MMU. Akhirnya ia harus mengembalikan nilai register program B.
person
Martin Rosenau
schedule
06.08.2020