Saya tahu teori umum tetapi saya tidak bisa memasukkan rinciannya.
Saya tahu bahwa sebuah program berada di memori sekunder komputer. Setelah program mulai dieksekusi sepenuhnya disalin ke RAM. Kemudian prosesor mengeluarkan beberapa instruksi (tergantung pada ukuran bus) sekaligus, menempatkannya dalam register dan menjalankannya.
Saya juga tahu bahwa program komputer menggunakan dua jenis memori: tumpukan dan tumpukan, yang juga merupakan bagian dari memori utama komputer. Tumpukan digunakan untuk memori non-dinamis, dan tumpukan untuk memori dinamis (misalnya, semua yang terkait dengan new
operator di C ++)
Yang tidak saya mengerti adalah bagaimana kedua hal itu terhubung. Pada titik apa tumpukan digunakan untuk pelaksanaan instruksi? Instruksi pergi dari RAM, ke tumpukan, ke register?
sumber
Jawaban:
Itu benar-benar tergantung pada sistem, tetapi OS modern dengan memori virtual cenderung memuat gambar proses mereka dan mengalokasikan memori seperti ini:
Ini adalah ruang alamat proses umum pada banyak sistem memori virtual umum. "Lubang" adalah ukuran total memori Anda, dikurangi ruang yang digunakan oleh semua area lainnya; ini memberi banyak ruang bagi tumpukan untuk tumbuh. Ini juga "virtual", artinya memetakan ke memori Anda yang sebenarnya melalui tabel terjemahan, dan dapat disimpan di lokasi mana pun di memori yang sebenarnya. Hal ini dilakukan dengan cara ini untuk melindungi satu proses dari mengakses memori proses lain, dan membuat setiap proses berpikir itu berjalan pada sistem yang lengkap.
Perhatikan bahwa posisi, misalnya, tumpukan dan tumpukan mungkin berada dalam urutan berbeda pada beberapa sistem (lihat jawaban Billy O'Neal di bawah ini untuk detail lebih lanjut tentang Win32).
Sistem lain bisa sangat berbeda. DOS, misalnya, berjalan dalam mode nyata , dan alokasi memorinya ketika menjalankan program tampak jauh berbeda:
Anda dapat melihat bahwa DOS memungkinkan akses langsung ke memori sistem operasi, tanpa perlindungan, yang berarti bahwa program ruang pengguna secara umum dapat langsung mengakses atau menimpa apa pun yang mereka sukai.
Namun, dalam ruang alamat proses, program cenderung terlihat serupa, hanya saja mereka digambarkan sebagai segmen kode, segmen data, heap, segmen stack, dll., Dan dipetakan sedikit berbeda. Tetapi sebagian besar area umum masih ada.
Setelah memuat program dan lib bersama yang diperlukan ke dalam memori, dan mendistribusikan bagian-bagian program ke area yang tepat, OS mulai menjalankan proses Anda di mana pun metode utamanya berada, dan program Anda mengambil alih dari sana, membuat panggilan sistem seperlunya saat itu membutuhkan mereka.
Sistem yang berbeda (tertanam, apa pun) mungkin memiliki arsitektur yang sangat berbeda, seperti sistem tanpa tumpukan, sistem arsitektur Harvard (dengan kode dan data disimpan dalam memori fisik terpisah), sistem yang benar-benar menyimpan BSS dalam memori hanya baca (awalnya ditetapkan oleh programmer), dll. Tapi inilah intinya.
Kamu berkata:
"Stack" dan "heap" hanyalah konsep abstrak, daripada "jenis" memori yang berbeda secara fisik.
Sebuah tumpukan hanyalah struktur data pertama-out terakhir di,. Dalam arsitektur x86, itu sebenarnya dapat diatasi secara acak dengan menggunakan offset dari ujung, tetapi fungsi yang paling umum adalah PUSH dan POP untuk menambah dan menghapus item dari masing-masing. Ini biasanya digunakan untuk variabel fungsi-lokal (disebut "penyimpanan otomatis"), argumen fungsi, alamat pengirim, dll. (Selengkapnya di bawah)
Sebuah "tumpukan" hanya nama panggilan untuk sepotong memori yang dapat dialokasikan pada permintaan, dan ditujukan secara acak (yang berarti, Anda dapat mengakses setiap lokasi di langsung). Ini biasanya digunakan untuk struktur data yang Anda alokasikan saat runtime (dalam C ++, menggunakan
new
dandelete
, danmalloc
dan teman-teman di C, dll).Tumpukan dan tumpukan, pada arsitektur x86, keduanya secara fisik berada di memori sistem Anda (RAM), dan dipetakan melalui alokasi memori virtual ke dalam ruang alamat proses seperti yang dijelaskan di atas.
The register (masih pada x86), secara fisik berada di dalam prosesor (sebagai lawan RAM), dan dimuat oleh prosesor, dari daerah TEXT (dan juga dapat diambil dari tempat lain dalam memori atau tempat lain tergantung pada instruksi CPU yang sebenarnya dieksekusi). Mereka pada dasarnya hanya sangat kecil, lokasi memori on-chip yang sangat cepat yang digunakan untuk sejumlah tujuan berbeda.
Tata letak register sangat tergantung pada arsitektur (pada kenyataannya, register, set instruksi, dan tata letak / desain memori, persis apa yang dimaksud dengan "arsitektur"), jadi saya tidak akan memperluasnya, tetapi menyarankan Anda untuk mengambil kursus bahasa assembly untuk memahami mereka dengan lebih baik.
Pertanyaanmu:
Tumpukan (dalam sistem / bahasa yang memiliki dan menggunakannya) paling sering digunakan seperti ini:
Tulis program sederhana seperti ini, dan kemudian kompilasi untuk perakitan (
gcc -S foo.c
jika Anda memiliki akses ke GCC), dan lihatlah. Perakitan ini cukup mudah diikuti. Anda dapat melihat bahwa stack digunakan untuk variabel fungsi lokal, dan untuk memanggil fungsi, menyimpan argumen mereka dan mengembalikan nilai. Ini juga mengapa ketika Anda melakukan sesuatu seperti:Semua ini dipanggil pada gilirannya. Ini benar-benar membangun setumpuk pemanggilan fungsi dan argumen mereka, mengeksekusi mereka, dan kemudian memunculkannya saat angin turun kembali (atau naik;). Namun, seperti yang disebutkan di atas, tumpukan (pada x86) sebenarnya berada di ruang memori proses Anda (dalam memori virtual), dan sehingga dapat dimanipulasi secara langsung; itu bukan langkah terpisah selama eksekusi (atau setidaknya orthogonal ke proses).
FYI, di atas adalah konvensi pemanggilan C , juga digunakan oleh C ++. Bahasa / sistem lain dapat mendorong argumen ke tumpukan dalam urutan yang berbeda, dan beberapa bahasa / platform bahkan tidak menggunakan tumpukan, dan melakukannya dengan cara yang berbeda.
Perhatikan juga, ini bukan baris aktual dari eksekusi kode C. Kompiler telah mengubahnya menjadi instruksi bahasa mesin di executable Anda.
Mereka kemudian (umumnya) disalin dari area TEXT ke dalam pipeline CPU, kemudian ke register CPU, dan dieksekusi dari sana.[Ini tidak benar. Lihat koreksi Ben Voigt di bawah ini.]sumber
Sdaz mendapatkan banyak sekali upvotes dalam waktu yang sangat singkat, tetapi sayangnya terus melanggengkan kesalahpahaman tentang bagaimana instruksi bergerak melalui CPU.
Pertanyaan yang diajukan:
Sdaz berkata:
Tapi ini salah. Kecuali untuk kasus khusus kode modifikasi diri, instruksi tidak pernah memasukkan datapath. Dan mereka tidak, tidak bisa, dieksekusi dari datapath.
The register x86 CPU adalah:
Daftar umum EAX EBX ECX EDX
Register segmen CS DS ES FS GS SS
Indeks dan petunjuk ESI EDI EBP EIP ESP
Indikator EFLAGS
Ada juga beberapa floating-point dan register SIMD, tetapi untuk keperluan diskusi ini kita akan mengklasifikasikan mereka sebagai bagian dari coprocessor dan bukan CPU. Unit manajemen memori di dalam CPU juga memiliki beberapa register sendiri, kami akan memperlakukannya sebagai unit pemrosesan yang terpisah.
Tidak satu pun dari register ini digunakan untuk kode yang dapat dieksekusi.
EIP
berisi alamat instruksi pelaksanaan, bukan instruksi itu sendiri.Instruksi melalui jalur yang sama sekali berbeda dalam CPU dari data (arsitektur Harvard). Semua mesin saat ini adalah arsitektur Harvard di dalam CPU. Sebagian besar hari ini juga arsitektur Harvard di cache. x86 (mesin desktop umum Anda) adalah arsitektur Von Neumann di memori utama, yang berarti data dan kode saling terkait dalam RAM. Itu intinya, karena kita sedang berbicara tentang apa yang terjadi di dalam CPU.
Urutan klasik yang diajarkan dalam arsitektur komputer adalah fetch-decode-execute. Pengontrol memori mencari instruksi yang tersimpan di alamat
EIP
. Bit-bit instruksi melewati beberapa logika kombinasional untuk membuat semua sinyal kontrol untuk multiplexer berbeda dalam prosesor. Dan setelah beberapa siklus, unit logika aritmatika tiba pada hasil, yang di-clock ke tujuan. Kemudian instruksi selanjutnya diambil.Pada prosesor modern, semuanya bekerja sedikit berbeda. Setiap instruksi yang masuk diterjemahkan ke dalam serangkaian instruksi mikrokode. Ini memungkinkan pipelining, karena sumber daya yang digunakan oleh microinstruction pertama tidak diperlukan nanti, sehingga mereka dapat mulai bekerja pada microinstruction pertama dari instruksi selanjutnya.
Di atas itu, terminologi sedikit bingung karena register adalah istilah teknik elektrik untuk koleksi D-flipflops. Dan instruksi (atau terutama microinstructions) dapat disimpan sementara dalam koleksi D-flipflops. Tapi ini bukan apa yang dimaksud ketika seorang ilmuwan komputer atau insinyur perangkat lunak atau pengembang run-of-the-mill menggunakan istilah register . Mereka berarti register datapath seperti yang tercantum di atas, dan ini tidak digunakan untuk mengangkut kode.
Nama dan jumlah register data bervariasi untuk arsitektur CPU lainnya, seperti ARM, MIPS, Alpha, PowerPC, tetapi semuanya menjalankan instruksi tanpa melewati mereka melalui ALU.
sumber
Tata letak memori yang tepat saat proses dieksekusi sepenuhnya tergantung pada platform yang Anda gunakan. Pertimbangkan program uji berikut:
Pada Windows NT (dan ini anak-anak), program ini biasanya akan menghasilkan:
Pada kotak POSIX, ia akan mengatakan:
Model memori UNIX dijelaskan dengan sangat baik di sini oleh @Sdaz MacSkibbons, jadi saya tidak akan mengulanginya di sini. Tapi itu bukan satu-satunya model memori. Alasan POSIX memerlukan model ini adalah panggilan sistem sbrk . Pada dasarnya, pada kotak POSIX, untuk mendapatkan lebih banyak memori, sebuah proses hanya memberitahu Kernel untuk memindahkan pembagi antara "lubang" dan "tumpukan" lebih jauh ke wilayah "lubang". Tidak ada cara untuk mengembalikan memori ke sistem operasi, dan sistem operasi itu sendiri tidak mengelola tumpukan Anda. Pustaka runtime C Anda harus menyediakannya (via malloc).
Ini juga memiliki implikasi untuk jenis kode yang sebenarnya digunakan dalam binari POSIX. Kotak POSIX (hampir secara universal) menggunakan format file ELF. Dalam format ini, sistem operasi bertanggung jawab untuk komunikasi antar perpustakaan dalam file ELF yang berbeda. Oleh karena itu, semua perpustakaan menggunakan kode posisi-independen (Yaitu, kode itu sendiri dapat dimuat ke alamat memori yang berbeda dan masih beroperasi), dan semua panggilan antara perpustakaan dilewatkan melalui tabel pencarian untuk mencari tahu di mana kontrol perlu melompat untuk menyeberang panggilan fungsi perpustakaan. Ini menambahkan beberapa overhead dan dapat dieksploitasi jika salah satu pustaka mengubah tabel pencarian.
Model memori Windows berbeda karena jenis kode yang digunakan berbeda. Windows menggunakan format file PE, yang meninggalkan kode dalam format tergantung posisi. Artinya, kode tergantung di mana tepatnya dalam memori virtual kode tersebut dimuat. Ada bendera dalam spesifikasi PE yang memberitahu OS di mana tepatnya di memori perpustakaan atau executable ingin dipetakan ketika program Anda berjalan. Jika suatu program atau pustaka tidak dapat dimuat pada alamat yang diinginkan, pemuat Windows harus melakukan rebaselibrary / executable - pada dasarnya, ini memindahkan kode yang tergantung pada posisi untuk menunjuk pada posisi baru - yang tidak memerlukan tabel pencarian dan tidak dapat dieksploitasi karena tidak ada tabel pencarian untuk ditimpa. Sayangnya, ini membutuhkan implementasi yang sangat rumit di Windows loader, dan memang memiliki overhead waktu startup yang cukup jika sebuah gambar perlu direbase. Paket perangkat lunak komersial besar sering memodifikasi perpustakaan mereka untuk memulai dengan sengaja di alamat yang berbeda untuk menghindari rebasing; windows sendiri melakukan ini dengan pustaka sendiri (mis. ntdll.dll, kernel32.dll, psapi.dll, dll. - semuanya memiliki alamat awal yang berbeda secara default)
Pada Windows, memori virtual diperoleh dari sistem melalui panggilan ke VirtualAlloc , dan itu dikembalikan ke sistem melalui VirtualFree (Oke, secara teknis VirtualAlloc membuka ke NtAllocateVirtualMemory, tapi itu detail implementasi) (Bandingkan ini dengan POSIX, di mana memori tidak bisa direklamasi). Proses ini lambat (dan IIRC, mengharuskan Anda mengalokasikan dalam potongan berukuran halaman fisik; biasanya 4kb atau lebih). Windows juga menyediakan fungsi tumpukan itu sendiri (HeapAlloc, HeapFree, dll.) Sebagai bagian dari perpustakaan yang dikenal sebagai RtlHeap, yang dimasukkan sebagai bagian dari Windows itu sendiri, di mana runtime C (yaitu,
malloc
dan teman-teman) biasanya diimplementasikan.Windows juga memiliki beberapa API alokasi memori lama dari masa ketika harus berurusan dengan 80386s lama, dan fungsi-fungsi ini sekarang dibangun di atas RtlHeap. Untuk informasi lebih lanjut tentang berbagai API yang mengontrol manajemen memori di Windows, lihat artikel MSDN ini: http://msdn.microsoft.com/en-us/library/ms810627 .
Perhatikan juga bahwa ini berarti pada Windows sebuah proses tunggal dan biasanya memiliki lebih dari satu tumpukan. (Biasanya, setiap perpustakaan bersama menciptakan tumpukan itu sendiri.)
(Sebagian besar informasi ini berasal dari "Secure Coding in C and C ++" oleh Robert Seacord)
sumber
Tumpukan
Dalam arsitektur X86, CPU menjalankan operasi dengan register. Tumpukan hanya digunakan untuk alasan kenyamanan. Anda dapat menyimpan konten register Anda untuk ditumpuk sebelum memanggil subrutin atau fungsi sistem dan kemudian memuatnya kembali untuk melanjutkan operasi di mana Anda pergi. (Anda bisa melakukannya secara manual tanpa tumpukan, tetapi ini adalah fungsi yang sering digunakan sehingga memiliki dukungan CPU). Tetapi Anda dapat melakukan hampir semua hal tanpa tumpukan di PC.
Misalnya perkalian bilangan bulat:
Mengalikan register AX dengan register BX. (Hasilnya akan di DX dan AX, DX berisi bit yang lebih tinggi).
Mesin berbasis stack (seperti JAVA VM) menggunakan stack untuk operasi dasar mereka. Perkalian di atas:
Ini memunculkan dua nilai dari atas tumpukan dan mengalikan tem, kemudian mendorong hasilnya kembali ke tumpukan. Stack sangat penting untuk jenis mesin ini.
Beberapa bahasa pemrograman tingkat yang lebih tinggi (seperti C dan Pascal) menggunakan metode ini nanti untuk meneruskan parameter ke fungsi: parameter didorong ke tumpukan di urutan kiri ke kanan dan muncul oleh badan fungsi dan nilai kembali didorong kembali. (Ini adalah pilihan yang dibuat oleh pembuat kompiler dan jenis penyalahgunaan cara X86 menggunakan stack).
Tumpukan itu
Heap adalah konsep lain yang hanya ada di ranah kompiler. Dibutuhkan rasa sakit menangani memori di belakang variabel Anda, tetapi itu bukan fungsi dari CPU atau OS, itu hanya pilihan housekeeping blok memori yang diberikan oleh OS. Anda bisa melakukan ini banyak-banyak jika mau.
Mengakses sumber daya sistem
Sistem operasi memiliki antarmuka publik bagaimana Anda dapat mengakses fungsinya. Dalam parameter DOS dilewatkan dalam register CPU. Windows menggunakan tumpukan untuk melewati parameter untuk fungsi OS (API Windows).
sumber