การจัดสรรหน่วยความจำแบบใช้สแต็ก

ในสถาปัตยกรรมคอมพิวเตอร์ สแต็กคือส่วนของหน่วยความจำที่ข้อมูลจะถูกเพิ่มหรือลบออกในลักษณะเข้าหลังออกก่อน (LIFO)
ในระบบคอมพิวเตอร์สมัยใหม่ส่วนใหญ่ แต่ละเธรดจะมีพื้นที่หน่วยความจำที่สงวนไว้เรียกว่า สแต็ก (stack) เมื่อฟังก์ชันทำงาน ฟังก์ชันอาจเพิ่มข้อมูลสถานะภายในบางส่วนลงในส่วนบนสุดของสแต็ก และเมื่อฟังก์ชันสิ้นสุดการทำงาน ฟังก์ชันจะต้องรับผิดชอบในการลบข้อมูลเหล่านั้นออกจากสแต็ก อย่างน้อยที่สุด สแต็กของเธรดจะใช้สำหรับจัดเก็บตำแหน่งของที่อยู่ส่งคืนที่ผู้เรียกกำหนด เพื่อให้คำสั่ง return สามารถส่งกลับไปยังตำแหน่งที่ถูกต้องได้
โดยทั่วไปแล้ว สแต็กมักใช้สำหรับจัดเก็บตัวแปรที่มีความยาวคงที่ ซึ่งเป็นตัวแปรเฉพาะที่อยู่ภายในฟังก์ชันที่กำลังทำงานอยู่ โปรแกรมเมอร์อาจเลือกที่จะใช้สแต็กเพื่อจัดเก็บข้อมูลเฉพาะที่มีความยาวแปรผันได้ หากพื้นที่หน่วยความจำอยู่บนสแต็กของเธรด หน่วยความจำนั้นจะถูกเรียกว่าได้รับการจัดสรรบนสแต็ก หรือที่เรียกว่าการจัดสรรหน่วยความจำแบบสแต็ก (SBMA) ซึ่งแตกต่างจากการจัดสรรหน่วยความจำแบบฮีป (HBMA) SBMA มักเชื่อมโยงอย่างใกล้ชิดกับ สแต็ กการเรียกฟังก์ชัน
ข้อดีและข้อเสีย
เนื่องจากข้อมูลถูกเพิ่มและลบในลักษณะเข้าหลังออกก่อน (Last-in-first-out) การจัดสรรหน่วยความจำแบบใช้สแต็กจึงง่ายมากและโดยทั่วไปเร็วกว่าการจัดสรรหน่วยความจำแบบใช้ฮีป (หรือที่เรียกว่าการจัดสรรหน่วยความจำแบบไดนามิก ) เช่น ในภาษาCmalloc
คุณสมบัติอีกประการหนึ่งคือหน่วยความจำบนสแต็กจะถูกเรียกคืนโดยอัตโนมัติและมีประสิทธิภาพมากเมื่อฟังก์ชันสิ้นสุด ซึ่งอาจเป็นประโยชน์สำหรับโปรแกรมเมอร์หากไม่ต้องการข้อมูลอีกต่อไป[ 1 ] (เช่นเดียวกันกับlongjmpหากย้ายไปยังจุดก่อนที่จะallocaมีการเรียกใช้) อย่างไรก็ตาม หากจำเป็นต้องเก็บข้อมูลไว้ในรูปแบบใดรูปแบบหนึ่ง จะต้องคัดลอกข้อมูลจากสแต็กไปยังฮีปก่อนที่ฟังก์ชันจะสิ้นสุด ดังนั้น การจัดสรรตามสแต็กจึงเหมาะสมสำหรับข้อมูลชั่วคราวหรือข้อมูลที่ไม่ต้องการอีกต่อไปหลังจากฟังก์ชันปัจจุบันสิ้นสุด
ขนาดสแต็กที่กำหนดให้กับเธรดอาจมีขนาดเล็กเพียงไม่กี่ไบต์บนซีพียูขนาดเล็กบางตัว การจัดสรรหน่วยความจำบนสแต็กมากกว่าที่มีอยู่อาจส่งผลให้เกิดการขัดข้องเนื่องจากสแต็กโอเวอร์โฟลว์นี่คือเหตุผลที่ฟังก์ชันที่ใช้allocaมักจะถูกป้องกันไม่ให้ถูกอินไลน์: [ 2 ]หากฟังก์ชันดังกล่าวถูกอินไลน์เข้าไปในลูป ผู้เรียกจะประสบปัญหาการใช้งานสแต็กที่เพิ่มขึ้นโดยไม่คาดคิด ทำให้มีโอกาสเกิดโอเวอร์โฟลว์มากขึ้น
การจัดสรรแบบใช้สแต็กอาจทำให้เกิดปัญหาประสิทธิภาพเล็กน้อยได้เช่นกัน: มันทำให้เฟรมสแต็กมีขนาดแปรผัน ดังนั้นทั้งตัวชี้สแต็กและเฟรมจึงต้องได้รับการจัดการ (ด้วยเฟรมสแต็กขนาดคงที่ ตัวชี้สแต็กจะซ้ำซ้อนเนื่องจากการคูณตัวชี้เฟรมสแต็กด้วยขนาดของแต่ละเฟรม) โดยทั่วไปแล้ววิธีนี้จะมีค่าใช้จ่ายน้อยกว่าการเรียกใช้mallocและfreeอยู่แล้ว โดยเฉพาะอย่างยิ่ง หากฟังก์ชันปัจจุบันมีทั้งการเรียกใช้allocaและบล็อกที่มีข้อมูลโลคอลที่มีความยาวแปรผัน จะเกิดความขัดแย้งระหว่างความพยายามของ alloca ในการเพิ่มเฟรมสแต็กปัจจุบันจนกว่าฟังก์ชันปัจจุบันจะสิ้นสุดลง กับความต้องการของคอมไพเลอร์ในการวางตัวแปรโลคอลที่มีความยาวแปรผันในตำแหน่งเดียวกันในเฟรมสแต็ก ความขัดแย้งนี้มักจะได้รับการแก้ไขโดยการสร้างห่วงโซ่พื้นที่จัดเก็บฮีปแยกต่างหากสำหรับการเรียกใช้แต่ละครั้งalloca[ 3 ] ห่วง โซ่นี้จะบันทึกความลึกของสแต็กที่การจัดสรรแต่ละครั้งเกิดขึ้น การเรียกใช้allocaในฟังก์ชันใดๆ ในภายหลังจะตัดห่วงโซ่นี้ลงให้เหลือความลึกของสแต็กปัจจุบันเพื่อปลดปล่อยพื้นที่จัดเก็บใดๆ บนห่วงโซ่นี้ในที่สุด (แต่ไม่ใช่ทันที) การเรียกใช้allocaด้วยอาร์กิวเมนต์เป็นศูนย์ยังสามารถใช้เพื่อกระตุ้นการปลดปล่อยหน่วยความจำโดยไม่ต้องจัดสรรหน่วยความจำดังกล่าวเพิ่มเติม ผลจากความขัดแย้งระหว่างallocaการจัดเก็บตัวแปรแบบโลคอลและแบบโลคอล การใช้allocaอาจไม่มีประสิทธิภาพมากกว่าการmallocใช้
อินเทอร์เฟซระบบ
ระบบ ที่คล้าย Unixหลายระบบ รวมถึงMicrosoft Windowsใช้ฟังก์ชันที่เรียกallocaว่าการจัดสรรหน่วยความจำสแต็กแบบไดนามิกในลักษณะที่คล้ายกับแบบฮีป โดยmallocทั่วไปคอมไพเลอร์จะแปลงเป็นคำสั่งอินไลน์ที่จัดการตัวชี้สแต็ก คล้ายกับวิธีการจัดการอาร์เรย์ที่มีความยาวแปรผัน[ 4 ]แม้ว่าจะไม่จำเป็นต้องปลดปล่อยหน่วยความจำอย่างชัดเจน แต่ก็มีความเสี่ยงที่จะเกิดพฤติกรรมที่ไม่แน่นอนเนื่องจากสแต็กโอเวอร์โฟลว์[ 5 ]ฟังก์ชันนี้มีอยู่ในระบบ Unix มาตั้งแต่เวอร์ชัน32/V (1978) แต่ไม่ได้เป็นส่วนหนึ่งของมาตรฐาน CหรือมาตรฐานPOSIX ใดๆ
มีเวอร์ชันที่ปลอดภัยกว่าallocaที่เรียกว่า_mallocaซึ่งจะจัดสรรบนฮีปหากขนาดการจัดสรรใหญ่เกินไป และรายงานข้อผิดพลาดสแต็กโอเวอร์โฟลว์ บน Microsoft Windows ต้องใช้_freea[ 6 ] gnulibมีอินเทอร์เฟซที่เทียบเท่ากัน แม้ว่าแทนที่จะโยนข้อยกเว้น SEH เมื่อเกิดโอเวอร์โฟลว์ มันจะส่งต่อไปยังmallocเมื่อตรวจพบขนาดที่ใหญ่เกินไป[ 7 ]คุณสมบัติที่คล้ายกันสามารถจำลองได้โดยใช้การบัญชีและการตรวจสอบขนาดด้วยตนเอง เช่น ในการใช้งานalloca_accountใน glibc [ 8 ]
โปรเซสเซอร์บางตระกูล เช่นx86มีคำสั่งพิเศษสำหรับการจัดการสแต็กของเธรดที่กำลังทำงานอยู่ ในขณะที่โปรเซสเซอร์ตระกูลอื่น ๆ เช่นRISC-V , PowerPCและMIPSไม่มีระบบรองรับสแต็กโดยตรง แต่จะใช้ธรรมเนียมปฏิบัติและมอบหมายการจัดการสแต็กให้กับอินเทอร์เฟซไบนารีแอปพลิเคชัน (ABI) ของระบบปฏิบัติการแทน
ออโต้ วีแอลเอ
นอกจากนี้ ตั้งแต่เวอร์ชัน C99 (เป็นตัวเลือกตั้งแต่ C11) สามารถสร้างอาร์เรย์บนสแต็กภายในฟังก์ชันได้โดยอัตโนมัติ ซึ่งเรียกว่าauto VLA ( variable-length array ) [ 9 ]อย่างไรก็ตาม ฟังก์ชัน นี้ไม่ได้รับการสนับสนุนในC++ [ 10 ]
void f ( int len ) { // auto VLA - ความยาวของอาร์เรย์นี้จะถูกกำหนด ณเวลาที่เรียกใช้ฟังก์ชัน / สร้างสแต็กint b [ len ]; // auto VLA - ความยาวของอาร์เรย์นี้จะถูกกำหนด ณ เวลาที่ เรียกใช้ฟังก์ชัน for ( int i = 0 ; i < len ; i ++ ) { b [ i ] = 1 ; } // เมื่อสิ้นสุดฟังก์ชันนี้ b จะอยู่ภายในเฟรมสแต็ก และจะ// หายไปเมื่อฟังก์ชันสิ้นสุดการทำงาน ดังนั้นจึงไม่จำเป็นต้องเรียกใช้ free() อย่างชัดเจน}