เพื่อให้ตัวอย่างที่เป็นรูปธรรมเกี่ยวกับวิธีที่คอมไพเลอร์จัดการกับสแต็กและวิธีการเข้าถึงค่าในสแต็กเราสามารถดูการแสดงภาพรวมถึงรหัสที่สร้างขึ้นGCC
ในสภาพแวดล้อม Linux ด้วย i386 เป็นสถาปัตยกรรมเป้าหมาย
1. เฟรมสแต็ก
อย่างที่คุณทราบสแต็กเป็นที่ตั้งในพื้นที่ที่อยู่ของกระบวนการทำงานที่ใช้โดยฟังก์ชั่นหรือขั้นตอนในแง่ที่ว่ามีการจัดสรรพื้นที่บนสแต็กสำหรับตัวแปรที่ประกาศในเครื่องรวมถึงอาร์กิวเมนต์ที่ส่งผ่านไปยังฟังก์ชัน พื้นที่สำหรับตัวแปรที่ประกาศภายนอกฟังก์ชั่นใด ๆ (เช่นตัวแปรทั่วโลก) ได้รับการจัดสรรในภูมิภาคอื่นในหน่วยความจำเสมือน) การจัดสรรพื้นที่สำหรับข้อมูลทั้งหมดของฟังก์ชั่นที่มีการอ้างถึงกองกรอบ นี่คือภาพที่แสดงให้เห็นภาพของกรอบสแต็กหลายภาพ (จากระบบคอมพิวเตอร์: มุมมองของโปรแกรมเมอร์ ):
2. การจัดการเฟรมสแต็กและตำแหน่งตัวแปร
เพื่อให้ค่าที่เขียนไปยังสแต็คภายในเฟรมสแต็คที่เฉพาะเจาะจงที่จะจัดการโดยคอมไพเลอร์และอ่านโดยโปรแกรมจะต้องมีวิธีการคำนวณตำแหน่งของค่าเหล่านี้และดึงที่อยู่หน่วยความจำของพวกเขา รีจิสเตอร์ใน CPU ที่อ้างถึงเป็นตัวชี้สแต็กและตัวชี้พื้นฐานช่วยในการนี้
ตัวชี้ฐานebp
โดยการประชุมประกอบด้วยที่อยู่หน่วยความจำของด้านล่างหรือฐานของสแต็ก ตำแหน่งของค่าทั้งหมดภายในกรอบสแต็กสามารถคำนวณได้โดยใช้ที่อยู่ในตัวชี้ฐานเป็นการอ้างอิง นี่เป็นภาพในภาพด้านบน: %ebp + 4
เป็นที่อยู่หน่วยความจำที่เก็บไว้ในตัวชี้ฐานบวก 4 เป็นต้น
3. รหัสที่คอมไพเลอร์สร้างขึ้น
แต่สิ่งที่ฉันไม่ได้รับก็คือแอปพลิเคชันอ่านแล้วว่าตัวแปรในสแต็กถ้าฉันประกาศและกำหนด x เป็นจำนวนเต็ม, พูดว่า x = 3, และหน่วยเก็บข้อมูลถูกสงวนไว้ในสแต็ก ตรงนั้นจากนั้นในฟังก์ชั่นเดียวกันฉันก็ประกาศและกำหนด y เป็น, พูด 4, แล้วต่อจากนั้นฉันก็ใช้ x ในนิพจน์อื่น, (พูด z = 5 + x) โปรแกรมจะอ่านค่า x อย่างไรเพื่อประเมิน z เมื่อ มันต่ำกว่า y ในกองหรือไม่
ให้เราใช้โปรแกรมตัวอย่างง่ายๆที่เขียนใน C เพื่อดูว่ามันทำงานอย่างไร:
int main(void)
{
int x = 3;
int y = 4;
int z = 5 + x;
return 0;
}
ให้เราตรวจสอบข้อความประกอบที่ผลิตโดย GCC สำหรับข้อความต้นฉบับ C นี้ (ฉันทำความสะอาดขึ้นเล็กน้อยเพื่อความชัดเจน):
main:
pushl %ebp # save previous frame's base address on stack
movl %esp, %ebp # use current address of stack pointer as new frame base address
subl $16, %esp # allocate 16 bytes of space on stack for function data
movl $3, -12(%ebp) # variable x at address %ebp - 12
movl $4, -8(%ebp) # variable y at address %ebp - 8
movl -12(%ebp), %eax # write x to register %eax
addl $5, %eax # x + 5 = 9
movl %eax, -4(%ebp) # write 9 to address %ebp - 4 - this is z
movl $0, %eax
leave
สิ่งที่เราสังเกตก็คือว่าตัวแปร X, Y และ Z จะอยู่ที่อยู่%ebp - 12
, %ebp -8
และ%ebp - 4
ตามลำดับ ในคำอื่น ๆสถานที่ของตัวแปรภายในกองกรอบสำหรับการคำนวณโดยใช้ที่อยู่หน่วยความจำที่บันทึกไว้ในการลงทะเบียนของmain()
CPU%ebp
4. ข้อมูลในหน่วยความจำเกินกว่าตัวชี้สแต็กอยู่นอกขอบเขต
ฉันขาดอะไรบางอย่างชัดเจน มันเป็นที่ตั้งบนสแต็คเป็นเพียงเกี่ยวกับอายุการใช้งาน / ขอบเขตของตัวแปรและว่าสแต็คทั้งหมดสามารถเข้าถึงโปรแกรมได้ตลอดเวลา? ถ้าเป็นเช่นนั้นนั่นหมายความว่ามีบางดัชนีอื่น ๆ ที่เก็บที่อยู่ของตัวแปรในสแต็กเพื่อให้สามารถดึงค่าได้หรือไม่? แต่ฉันคิดว่าจุดทั้งหมดของสแต็คคือค่านั้นถูกเก็บไว้ในที่เดียวกับที่อยู่ตัวแปรหรือไม่
สแต็คเป็นพื้นที่ในหน่วยความจำเสมือนซึ่งการใช้งานนั้นได้รับการจัดการโดยคอมไพเลอร์ คอมไพเลอร์สร้างรหัสในลักษณะที่ว่าค่าเกินกว่าตัวชี้สแต็ก (ค่าเกินกว่าด้านบนของสแต็ค) จะไม่อ้างอิง เมื่อเรียกใช้ฟังก์ชันตำแหน่งของตัวชี้สแต็กจะเปลี่ยนเพื่อสร้างพื้นที่บนสแต็กที่ถือว่าไม่ใช่ "ไม่อยู่นอกขอบเขต" ดังนั้นต้องพูด
เนื่องจากฟังก์ชันถูกเรียกและส่งคืนตัวชี้สแต็กจะลดลงและเพิ่มขึ้น ข้อมูลที่เขียนไปยังกองไม่ได้หายไปหลังจากที่มันอยู่นอกขอบเขต แต่คอมไพเลอร์ไม่ได้สร้างคำแนะนำการอ้างอิงข้อมูลนี้เพราะมีวิธีการที่คอมไพเลอร์ในการคำนวณที่อยู่ของข้อมูลเหล่านี้ไม่ใช้หรือ%ebp
%esp
5. สรุป
โค้ดที่สามารถเรียกใช้งานได้โดยตรงโดย CPU นั้นสร้างขึ้นโดยคอมไพเลอร์ คอมไพเลอร์จัดการสแต็กเฟรมสแต็คสำหรับฟังก์ชั่นและการลงทะเบียน CPU กลยุทธ์หนึ่งที่ GCC ใช้ในการติดตามตำแหน่งของตัวแปรในสแต็กเฟรมในรหัสที่มีวัตถุประสงค์เพื่อดำเนินการกับสถาปัตยกรรม i386 คือการใช้ที่อยู่หน่วยความจำในตัวชี้ฐานสแต็กเฟรม%ebp
เพื่ออ้างอิงและเขียนค่าของตัวแปรไปยังตำแหน่งต่างๆ %ebp
ที่ชดเชยไปยังที่อยู่ใน