เหตุใด call stack จึงมีขนาดสูงสุดคงที่


46

เมื่อทำงานกับภาษาการเขียนโปรแกรมสองสามภาษาฉันสงสัยอยู่เสมอว่าทำไมเธรดสแต็กมีขนาดสูงสุดที่กำหนดไว้ล่วงหน้าแทนที่จะขยายโดยอัตโนมัติตามต้องการ 

ในการเปรียบเทียบโครงสร้างระดับสูงทั่วไปบางอย่าง (รายการแผนที่ ฯลฯ ) ซึ่งพบในภาษาการเขียนโปรแกรมส่วนใหญ่ได้รับการออกแบบให้เติบโตตามที่ต้องการในขณะที่เพิ่มองค์ประกอบใหม่ถูก จำกัด ขนาดโดยหน่วยความจำที่มีอยู่หรือตามข้อ จำกัด ด้านการคำนวณ เช่นที่อยู่ 32 บิต)

ฉันไม่ทราบว่าภาษาการเขียนโปรแกรมหรือสภาพแวดล้อมรันไทม์ที่ขนาดสแต็คสูงสุดไม่ได้ถูก จำกัด ไว้ล่วงหน้าโดยตัวเลือกเริ่มต้นหรือตัวเลือกคอมไพเลอร์ นี่คือสาเหตุที่การเรียกซ้ำมากเกินไปจะส่งผลให้เกิดข้อผิดพลาด / ข้อยกเว้นการโอเวอร์โฟลว์สแต็กอย่างรวดเร็วมากแม้ว่าจะมีการใช้หน่วยความจำเพียงเล็กน้อยในหน่วยความจำสำหรับกระบวนการ

เหตุใดจึงเป็นกรณีที่สภาพแวดล้อมรันไทม์ส่วนใหญ่ (ถ้าไม่ใช่ทั้งหมด) ตั้งค่าขีด จำกัด สูงสุดสำหรับขนาดที่สแต็กสามารถโตขึ้นที่รันไทม์?


13
สแต็กชนิดนี้เป็นพื้นที่ที่อยู่อย่างต่อเนื่องซึ่งไม่สามารถเคลื่อนไหวอย่างเงียบ ๆ เบื้องหลัง พื้นที่ที่อยู่มีค่าในระบบ 32 บิต
CodesInChaos

7
เพื่อลดการเกิดขึ้นของความคิดหอคอยงาช้างเช่นการเรียกซ้ำที่รั่วไหลออกมาจากสถาบันการศึกษาและก่อให้เกิดปัญหาในโลกแห่งความจริงเช่นการอ่านรหัสลดลงและเพิ่มค่าใช้จ่ายโดยรวมของการเป็นเจ้าของ;)
Brad Thomas

6
@BradThomas นั่นคือสิ่งที่การเพิ่มประสิทธิภาพการโทรหางเป็น
JAB

3
@ JohnWu: สิ่งเดียวกันที่ทำตอนนี้เพียงไม่ช้า: หน่วยความจำไม่เพียงพอ
Jörg W Mittag

1
ในกรณีที่ไม่ชัดเจนเหตุผลหนึ่งที่หน่วยความจำไม่เพียงพอจะแย่กว่าการใช้สแต็กก็คือ (หากมีหน้ากับดัก) การที่สแต็กหมดจะทำให้กระบวนการของคุณล้มเหลวเท่านั้น วิ่งออกมาจากหน่วยความจำที่อาจก่อให้เกิดอะไรที่จะล้มเหลวใครก็ตามที่พยายามต่อไปเพื่อให้การจัดสรรหน่วยความจำ จากนั้นอีกครั้งในระบบที่ไม่มีหน้ากับดักหรือวิธีการอื่นในการตรวจสอบว่าไม่อยู่ในสแต็กการใช้งานสแต็กอาจเป็นหายนะทำให้คุณเข้าสู่พฤติกรรมที่ไม่ได้กำหนด ในระบบดังกล่าวคุณต้องการที่จะหมดหน่วยความจำฟรีสโตร์และคุณไม่สามารถเขียนโค้ดด้วยการเรียกซ้ำแบบไม่ จำกัด ได้
Steve Jessop

คำตอบ:


13

เป็นไปได้ที่จะเขียนระบบปฏิบัติการที่ไม่ต้องการให้กองซ้อนติดกันในพื้นที่ที่อยู่ โดยทั่วไปคุณต้องมีบางสิ่งเพิ่มเติมเกี่ยวกับระเบียบในการโทรเพื่อให้แน่ใจว่า:

  1. หากมีพื้นที่ไม่เพียงพอในขอบเขตสแต็กปัจจุบันสำหรับฟังก์ชั่นที่คุณกำลังเรียกใช้คุณสร้างสแต็กขอบเขตใหม่และย้ายตัวชี้สแต็กไปยังจุดเริ่มต้นของส่วนที่เป็นส่วนหนึ่งของการโทร

  2. เมื่อคุณกลับมาจากการโทรนั้นคุณจะโอนกลับไปยังขอบเขตสแต็กดั้งเดิม เป็นไปได้มากว่าคุณจะเก็บสิ่งที่สร้างไว้ที่ (1) สำหรับการใช้ในอนาคตโดยเธรดเดียวกัน โดยหลักการแล้วคุณสามารถปล่อยมันได้ แต่วิธีนี้จะอยู่ในกรณีที่ไม่มีประสิทธิภาพซึ่งคุณจะต้องข้ามไปมาระหว่างขอบเขตในการวนซ้ำและการโทรทุกครั้งต้องมีการจัดสรรหน่วยความจำ

  3. setjmpและlongjmpหรือสิ่งที่เทียบเท่ากับระบบปฏิบัติการของคุณสำหรับการถ่ายโอนการควบคุมที่ไม่ใช่ในท้องถิ่นอยู่ในการกระทำและสามารถย้ายกลับไปยังขอบเขตสแต็กเก่าได้อย่างถูกต้องเมื่อจำเป็น

ฉันพูดว่า "การเรียกการประชุม" - จะเจาะจงฉันคิดว่ามันอาจจะทำได้ดีที่สุดในฟังก์ชั่นอารัมภบทมากกว่าโดยผู้โทร แต่ความทรงจำของฉันเกี่ยวกับเรื่องนี้มืด

เหตุผลที่มีไม่กี่ภาษาที่ระบุขนาดสแต็กคงที่สำหรับเธรดคือพวกเขาต้องการทำงานโดยใช้ native stack ใน OS ที่ไม่ทำเช่นนี้ ตามที่คำตอบของทุกคนพูดภายใต้สมมติฐานว่าแต่ละสแต็กจะต้องต่อเนื่องกันในพื้นที่ที่อยู่และไม่สามารถย้ายได้คุณจะต้องจองช่วงที่อยู่เฉพาะเพื่อใช้งานโดยแต่ละเธรด นั่นหมายถึงการเลือกขนาดด้านหน้า แม้ว่าพื้นที่ที่อยู่ของคุณจะมีขนาดใหญ่และขนาดที่คุณเลือกมีขนาดใหญ่มากคุณยังต้องเลือกทันทีที่คุณมีสองเธรด

"Aha" คุณพูดว่า "ระบบปฏิบัติการเหล่านี้ควรใช้สแต็คที่ไม่ต่อเนื่องกันอย่างไรฉันคิดว่ามันเป็นระบบการศึกษาที่ไม่ชัดเจนสำหรับฉัน!" ดีที่เป็นคำถามอีกว่าโชคดีที่ถามแล้วตอบ


36

โครงสร้างข้อมูลเหล่านั้นมักจะมีคุณสมบัติที่สแต็ค OS ไม่ได้:

  • รายการที่เชื่อมโยงไม่จำเป็นต้องใช้พื้นที่ที่อยู่ติดกัน ดังนั้นพวกเขาสามารถเพิ่มหน่วยความจำได้จากทุกที่ที่พวกเขาต้องการเมื่อพวกเขาเติบโต

  • แม้แต่คอลเลกชันที่ต้องการที่เก็บข้อมูลที่ต่อเนื่องกันเช่นเวกเตอร์ของ C ++ ก็มีข้อได้เปรียบเหนือกอง OS: พวกเขาสามารถประกาศพอยน์เตอร์ / ตัววนซ้ำทั้งหมดไม่ถูกต้องทุกครั้งที่มันเติบโต ในทางกลับกันสแต็คระบบปฏิบัติการจะต้องเก็บตัวชี้ไปยังสแต็คที่ถูกต้องจนกว่าฟังก์ชั่นที่กรอบเป้าหมายเป็นของผลตอบแทน

ภาษาโปรแกรมหรือรันไทม์สามารถเลือกที่จะใช้สแต็คของตนเองซึ่งไม่ต่อเนื่องกันหรือเคลื่อนย้ายได้เพื่อหลีกเลี่ยงข้อ จำกัด ของ OS Golang ใช้สแต็กที่กำหนดเองดังกล่าวเพื่อรองรับจำนวนที่สูงมากของ co-routines ซึ่ง แต่เดิมถูกใช้เป็นหน่วยความจำที่ไม่ต่อเนื่องและตอนนี้ผ่านสแต็กที่เคลื่อนย้ายได้ขอบคุณการติดตามตัวชี้ หลามแบบเรียงซ้อน Lua และ Erlang อาจใช้สแต็คแบบกำหนดเอง แต่ฉันไม่ได้ยืนยัน

บนระบบ 64 บิตคุณสามารถกำหนดค่าสแต็คที่ค่อนข้างใหญ่ด้วยต้นทุนที่ค่อนข้างต่ำเนื่องจากพื้นที่ที่อยู่มีจำนวนมากและหน่วยความจำกายภาพจะได้รับการจัดสรรเมื่อคุณใช้งานจริงเท่านั้น


1
นี่เป็นคำตอบที่ดีและฉันทำตามความหมายของคุณ แต่ไม่ใช่คำว่าบล็อกหน่วยความจำ "ต่อเนื่อง" ซึ่งตรงข้ามกับ "ต่อเนื่อง" เนื่องจากหน่วยความจำแต่ละหน่วยมีที่อยู่ที่ไม่ซ้ำกัน
DanK

2
+1 สำหรับ "สแต็คการโทรไม่จำเป็นต้อง จำกัด " มันมักจะถูกนำไปใช้เพื่อความเรียบง่ายและประสิทธิภาพ แต่ก็ไม่จำเป็นต้องเป็น
พอลเดรเปอร์

คุณพูดถูกแล้ว ที่จริงแล้วความเข้าใจของฉันคือรุ่นเก่ามีกองซ้อนไม่ต่อเนื่องและรุ่นใหม่มีกองเคลื่อนย้ายได้ ไม่ว่าจะด้วยวิธีใดมันเป็นสิ่งจำเป็นที่จะอนุญาตให้ goroutines จำนวนมาก การจัดสรรล่วงหน้าสองสามเมกะไบต์ต่อโกโรไทน์สำหรับสแต็กจะทำให้พวกเขามีราคาแพงเกินไปที่จะให้บริการตามวัตถุประสงค์อย่างถูกต้อง
ฮอบส์

@ ฮอบส์: ใช่เริ่มต้นด้วยกองที่เติบโตได้ แต่มันยากที่จะทำให้มันเร็ว เมื่อ Go ได้รับ Garbage Collector อย่างแม่นยำมันจะสำรองข้อมูลลูกหมูเพื่อสร้างสแต็กที่เคลื่อนย้ายได้: เมื่อสแต็คย้ายระบบจะใช้แผนที่ชนิดที่แม่นยำเพื่ออัพเดตพอยน์เตอร์ให้เป็นสแต็กก่อนหน้า
Matthieu M.

26

ในทางปฏิบัติมันเป็นเรื่องยาก (และบางครั้งเป็นไปไม่ได้) ที่จะเติบโตสแต็ค เพื่อให้เข้าใจว่าทำไมต้องมีความเข้าใจหน่วยความจำเสมือน

ใน Ye Olde Days ของแอปพลิเคชันแบบเธรดเดี่ยวและหน่วยความจำที่ต่อเนื่องกันสามรายการมีสามองค์ประกอบของพื้นที่ที่อยู่กระบวนการ: โค้ดฮีปและสแต็ก วิธีการจัดวางทั้งสามนั้นขึ้นอยู่กับระบบปฏิบัติการ แต่โดยทั่วไปแล้วรหัสจะมาก่อนเริ่มที่ด้านล่างของหน่วยความจำฮีปมาถัดไปและโตขึ้นและสแต็กก็เริ่มที่ด้านบนของหน่วยความจำและขยายตัวลง นอกจากนี้ยังมีหน่วยความจำบางส่วนที่สงวนไว้สำหรับระบบปฏิบัติการ แต่เราสามารถเพิกเฉยได้ โปรแกรมในสมัยนั้นค่อนข้างล้นสแต็กอย่างมาก: สแต็กจะชนเข้ากับฮีปและขึ้นอยู่กับว่าได้รับการอัปเดตก่อนคุณจะทำงานกับข้อมูลที่ไม่ดีหรือส่งคืนจากรูทีนย่อยไปยังหน่วยความจำบางส่วน

การจัดการหน่วยความจำเปลี่ยนโมเดลนี้ค่อนข้าง: จากมุมมองของโปรแกรมคุณยังมีองค์ประกอบสามส่วนของแผนที่หน่วยความจำกระบวนการและโดยทั่วไปแล้วพวกเขาก็จัดระเบียบแบบเดียวกัน แต่ตอนนี้องค์ประกอบแต่ละส่วนได้รับการจัดการในฐานะส่วนอิสระและ MMU จะส่งสัญญาณ OS หากโปรแกรมพยายามเข้าถึงหน่วยความจำนอกเซกเมนต์ เมื่อคุณมีหน่วยความจำเสมือนไม่จำเป็นต้องมีหรือไม่ต้องการให้โปรแกรมเข้าถึงพื้นที่ที่อยู่ทั้งหมด ดังนั้นส่วนที่ได้รับมอบหมายขอบเขตคงที่

เหตุใดจึงไม่เป็นที่พึงปรารถนาที่จะให้โปรแกรมเข้าถึงพื้นที่ที่อยู่เต็มของโปรแกรม เพราะหน่วยความจำนั้นถือว่าเป็น "กระทำการคิด" ต่อการแลกเปลี่ยน; เมื่อใดก็ตามที่หน่วยความจำใด ๆ หรือทั้งหมดสำหรับโปรแกรมหนึ่งอาจต้องเขียนเพื่อสลับเพื่อให้มีที่ว่างสำหรับหน่วยความจำของโปรแกรมอื่น หากทุกโปรแกรมอาจใช้การแลกเปลี่ยน 2GB คุณต้องเตรียมการสับเปลี่ยนให้เพียงพอสำหรับโปรแกรมทั้งหมดของคุณหรือใช้โอกาสที่โปรแกรมสองโปรแกรมต้องการมากกว่าที่จะได้รับ

ณ จุดนี้สมมติว่ามีพื้นที่ที่อยู่เสมือนเพียงพอคุณสามารถขยายเซ็กเมนต์เหล่านี้ได้ถ้าต้องการและเซ็กเมนต์ข้อมูล (ฮีป) มีอยู่จริงเมื่อเวลาผ่านไป: คุณเริ่มจากเซ็กเมนต์ข้อมูลขนาดเล็กและเมื่อตัวจัดสรรหน่วยความจำร้องขอพื้นที่เพิ่มเติมเมื่อ มันจำเป็น ณ จุดนี้ด้วยสแต็คเดียวมันจะเป็นไปได้ทางกายภาพที่จะขยายส่วนสแต็ก: ระบบปฏิบัติการสามารถดักจับความพยายามที่จะผลักบางสิ่งบางอย่างนอกส่วนและเพิ่มหน่วยความจำเพิ่มเติม แต่สิ่งนี้ก็ไม่เป็นที่ต้องการเช่นกัน

ป้อนหลายเธรด ในกรณีนี้แต่ละเธรดมีกลุ่มสแต็กอิสระขนาดคงที่อีกครั้ง แต่ตอนนี้มีการจัดวางเซกเมนต์ทีละส่วนในพื้นที่ที่อยู่เสมือนดังนั้นจึงไม่มีวิธีขยายเซ็กเมนต์หนึ่งโดยไม่ย้ายอีก - ซึ่งคุณไม่สามารถทำได้เนื่องจากโปรแกรมอาจมีพอยน์เตอร์ไปยังหน่วยความจำในกองซ้อน คุณสามารถเว้นช่องว่างระหว่างเซกเมนต์ แต่ช่องว่างนั้นจะเสียเปล่าในเกือบทุกกรณี วิธีที่ดีกว่าคือการวางภาระให้กับผู้พัฒนาแอปพลิเคชัน: หากคุณต้องการสแต็คลึกจริง ๆ คุณสามารถระบุได้ว่าเมื่อสร้างเธรด

วันนี้ด้วยพื้นที่ที่อยู่เสมือนแบบ 64 บิตเราสามารถสร้างสแต็คที่ไม่มีที่สิ้นสุดได้อย่างมีประสิทธิภาพสำหรับจำนวนเธรดที่ไม่มีที่สิ้นสุดได้อย่างมีประสิทธิภาพ แต่ก็ไม่เป็นที่พึงปรารถนาโดยเฉพาะ: ในเกือบทุกกรณีสแต็คโอเวอร์โอเวอร์จะระบุบั๊กที่มีรหัสของคุณ ให้สแต็คขนาด 1 GB เพียงแค่ป้องกันการค้นพบบั๊กนั้น


3
ซีพียู x86-64 ปัจจุบันมีที่อยู่ 48 บิตเท่านั้น
CodesInChaos

ในตอนนี้, linux จะขยายสแต็กแบบไดนามิก: เมื่อกระบวนการพยายามเข้าถึงพื้นที่ด้านล่างสแต็กที่ถูกจัดสรรในปัจจุบัน, อินเทอร์รัปต์จะถูกจัดการโดยเพียงแค่ทำการแมปหน้าเพิ่มเติมของหน่วยความจำสแต็คแทนการแยกกระบวนการ
cmaster

2
@cmaster: จริง แต่ไม่ใช่ความหมายของ kdgregory โดย "Grow the stack" ปัจจุบันมีช่วงที่อยู่ที่ถูกกำหนดให้ใช้เป็นสแต็ก คุณกำลังพูดถึงการทำแผนที่หน่วยความจำกายภาพที่ค่อยเป็นค่อยไปมากขึ้นในช่วงที่อยู่นั้นตามที่ต้องการ kdgregory กำลังบอกว่าเป็นการยากหรือเป็นไปไม่ได้ที่จะเพิ่มช่วง
Steve Jessop

x86 ไม่ใช่สถาปัตยกรรมเพียงอย่างเดียวและ 48 บิตยังคงไม่มีที่สิ้นสุดอย่างมีประสิทธิภาพ
kdgregory

1
BTW ฉันจำวันเวลาของฉันทำงานกับ x86 ได้ไม่มากนักส่วนใหญ่เป็นเพราะความจำเป็นในการจัดการกับการแบ่งส่วน ฉันชอบโครงการบนแพลตฟอร์ม MC68k มาก ;-)
kdgregory

4

สแต็กที่มีขนาดใหญ่สุดคงที่ไม่แพร่หลาย

นอกจากนี้ยังเป็นการยากที่จะได้รับสิทธิ์: ความลึกของสแต็กเป็นไปตามการกระจายของกฎหมายพลังงานซึ่งหมายความว่าไม่ว่าคุณจะทำขนาดสแต็คขนาดเล็กเพียงใดก็ยังคงมีส่วนสำคัญของฟังก์ชั่น และไม่ว่าคุณจะมีขนาดใหญ่แค่ไหนก็ยังมีฟังก์ชั่นที่มีสแต็คที่ใหญ่กว่า (ดังนั้นคุณจึงบังคับให้เกิดข้อผิดพลาดสแต็กล้นสำหรับฟังก์ชันที่ไม่มีข้อผิดพลาด) กล่าวอีกนัยหนึ่ง: ขนาดใดก็ตามที่คุณเลือกมันจะเล็กและใหญ่เกินไปในเวลาเดียวกัน

คุณสามารถแก้ไขปัญหาแรกได้โดยอนุญาตให้สแต็คเริ่มต้นเล็กและเติบโตแบบไดนามิก แต่คุณก็ยังมีปัญหาที่สอง และถ้าคุณอนุญาตให้สแต็คเติบโตแบบไดนามิกแล้วทำไมใส่ข้อ จำกัด โดยพลการมัน?

มีระบบที่สแต็คสามารถเติบโตแบบไดนามิกและไม่มีขนาดสูงสุด: Erlang, Go, Smalltalk และ Scheme มีหลายวิธีที่จะใช้บางสิ่งเช่นนั้น:

  • สแต็กที่เคลื่อนย้ายได้: เมื่อสแต็กต่อเนื่องไม่สามารถเติบโตได้อีกต่อไปเนื่องจากมีสิ่งอื่นอยู่ขวางทางให้ย้ายไปยังตำแหน่งอื่นในหน่วยความจำพร้อมพื้นที่ว่างมากขึ้น
  • สแต็กไม่ต่อเนื่อง: แทนที่จะจัดสรรสแต็กทั้งหมดในพื้นที่หน่วยความจำต่อเนื่องเดี่ยวจัดสรรในพื้นที่หน่วยความจำหลายแห่ง
  • กองที่จัดสรรฮีป: แทนที่จะมีพื้นที่หน่วยความจำแยกต่างหากสำหรับสแต็กและฮีปเพียงจัดสรรสแต็คบนฮีป ตามที่คุณสังเกตเห็นว่าโครงสร้างข้อมูลที่จัดสรรฮีปมักจะไม่มีปัญหาในการเติบโตและลดขนาดตามที่ต้องการ
  • อย่าใช้สแต็คเลย: นั่นก็เป็นตัวเลือกเช่นแทนที่จะคอยติดตามสถานะของฟังก์ชั่นในสแต็กให้ฟังก์ชันส่งค่าความต่อเนื่องไปยัง callee

ทันทีที่คุณมีโครงสร้างการควบคุมการไหลของข้อมูลที่ไม่ใช่เฉพาะในพื้นที่แนวคิดของสแต็กเดี่ยวที่ต่อเนื่องกันจะออกไปนอกหน้าต่างต่อไป: ตัวอย่างเช่นข้อยกเว้นและการดำเนินการต่อที่ต่อเนื่องตัวอย่างเช่นจะ "แยก" สแต็กดังนั้นคุณจึงจบลงด้วยเครือข่าย ของสแต็ก (เช่นนำไปใช้กับสปาเก็ตตี้กอง) นอกจากนี้ระบบที่มีสแต็คชั้นหนึ่งที่สามารถปรับเปลี่ยนได้เช่น Smalltalk ก็ต้องใช้สปาเก็ตตี้หรือสิ่งที่คล้ายกัน


1

ระบบปฏิบัติการจะต้องให้บล็อกที่ต่อเนื่องกันเมื่อมีการร้องขอสแต็ก วิธีเดียวที่ทำได้คือหากระบุขนาดสูงสุด

ตัวอย่างเช่นสมมติว่าหน่วยความจำมีลักษณะเช่นนี้ในระหว่างการร้องขอ (Xs ใช้แทน, ไม่ได้ใช้ระบบปฏิบัติการ Os):

XOOOXOOXOOOOOX

หากคำขอขนาดสแต็คเป็น 6 คำตอบของระบบปฏิบัติการจะตอบว่าไม่แม้ว่าจะมีมากกว่า 6 รายการก็ตาม หากคำร้องขอสแต็กขนาด 3 คำตอบของระบบปฏิบัติการจะเป็นหนึ่งในพื้นที่ของ 3 ช่องว่าง (Os) ในแถว

นอกจากนี้เราสามารถเห็นความยากลำบากในการอนุญาตให้มีการเติบโตเมื่อมีการครอบครองสล็อตที่ต่อเนื่องกันถัดไป

วัตถุอื่น ๆ ที่กล่าวถึง (รายการ ฯลฯ ) ไม่ได้อยู่ในสแต็กพวกเขาจบลงที่กองในพื้นที่ที่ไม่ต่อเนื่องหรือแยกส่วนดังนั้นเมื่อพวกเขาเติบโตพวกเขาเพิ่งคว้าพื้นที่พวกเขาไม่จำเป็นต้องต่อเนื่องเหมือนพวกเขา จัดการแตกต่างกัน

ระบบส่วนใหญ่ตั้งค่าที่เหมาะสมสำหรับขนาดสแต็กคุณสามารถแทนที่ได้เมื่อเธรดถูกสร้างขึ้นหากต้องการขนาดที่ใหญ่กว่า


1

บน linux นี่เป็นข้อ จำกัด ของทรัพยากรอย่างแท้จริงที่มีอยู่เพื่อฆ่ากระบวนการที่ควบคุมไม่ได้ก่อนที่จะใช้ทรัพยากรในปริมาณที่เป็นอันตราย ในระบบเดเบียนของฉันรหัสต่อไปนี้

#include <sys/resource.h>
#include <stdio.h>

int main() {
    struct rlimit limits;
    getrlimit(RLIMIT_STACK, &limits);
    printf("   soft limit = 0x%016lx\n", limits.rlim_cur);
    printf("   hard limit = 0x%016lx\n", limits.rlim_max);
    printf("RLIM_INFINITY = 0x%016lx\n", RLIM_INFINITY);
}

ผลิตผลลัพธ์

   soft limit = 0x0000000000800000
   hard limit = 0xffffffffffffffff
RLIM_INFINITY = 0xffffffffffffffff

โปรดทราบว่ามีการตั้งค่าขีด จำกัด ฮาร์ดเป็นRLIM_INFINITY: กระบวนการได้รับอนุญาตให้เพิ่มขีด จำกัด ซอฟต์เป็นจำนวนเท่าใดก็ได้ อย่างไรก็ตามตราบใดที่โปรแกรมเมอร์ไม่มีเหตุผลที่จะเชื่อว่าโปรแกรมต้องการหน่วยความจำสแต็คในปริมาณที่ผิดปกติจริงๆกระบวนการจะถูกฆ่าเมื่อมีขนาดเกินสแต็กแปด mebibytes

เนื่องจากข้อ จำกัด นี้กระบวนการที่ควบคุมไม่ได้ (การเรียกซ้ำแบบไม่สิ้นสุดที่ไม่ได้ตั้งใจ) จะถูกฆ่าเป็นเวลานานก่อนที่จะเริ่มใช้หน่วยความจำจำนวนมากจนระบบถูกบังคับให้เริ่มทำการแลกเปลี่ยน สิ่งนี้สามารถสร้างความแตกต่างระหว่างกระบวนการที่ล้มเหลวและเซิร์ฟเวอร์ที่ล้มเหลว อย่างไรก็ตามมันไม่ได้ จำกัด โปรแกรมที่มีความต้องการที่ถูกต้องสำหรับสแต็กขนาดใหญ่พวกเขาเพียงแค่ต้องตั้งค่าขีด จำกัด ซอฟต์เป็นค่าที่เหมาะสม


ในทางเทคนิคสแต็คจะเติบโตแบบไดนามิก: เมื่อ soft-limit ถูกตั้งค่าเป็นแปด mebibyte นั่นไม่ได้หมายความว่าหน่วยความจำจำนวนนี้ถูกแมปจริงแล้ว สิ่งนี้จะค่อนข้างสิ้นเปลืองเนื่องจากโปรแกรมส่วนใหญ่ไม่เคยไปถึงจุดอ่อนใด ๆ แต่เคอร์เนลจะตรวจจับการเข้าถึงด้านล่างสแต็กและเพียงแมปในหน้าหน่วยความจำเท่าที่จำเป็น ดังนั้นข้อ จำกัด ที่แท้จริงของขนาดสแต็กเท่านั้นคือหน่วยความจำที่มีอยู่บนระบบ 64 บิต (การแตกแฟรกเมนต์พื้นที่แอดเดรสค่อนข้างเชิงทฤษฎีด้วยขนาดพื้นที่แอดเดรส zebibyte 16)


2
นั่นคือสแต็กสำหรับเธรดแรกเท่านั้น เธรดใหม่ต้องจัดสรรสแต็กใหม่และถูก จำกัด เนื่องจากจะทำงานในวัตถุอื่น
Zan Lynx

0

สูงสุดขนาดสแต็คเป็นแบบคงที่เพราะนั่นคือความหมายของ "สูงสุด" การเรียงลำดับสูงสุดใด ๆ ในสิ่งใดก็ตามเป็นตัวเลข จำกัด ที่ตกลงกัน หากมันทำตัวเป็นเป้าหมายที่เคลื่อนไหวตามธรรมชาติมันจะไม่สูงสุด

สแต็คบนระบบปฏิบัติการเสมือนหน่วยความจำในความเป็นจริงไม่เติบโตแบบไดนามิกขึ้นไปสูงสุด

เมื่อพูดถึงสิ่งนี้มันไม่จำเป็นต้องนิ่ง แต่สามารถกำหนดค่าได้ในแบบต่อกระบวนการหรือต่อเธรด

ถ้าคำถามคือ "ทำไมคือมีขนาดสแต็คสูงสุด" (เป็นกำหนดเทียมหนึ่งมักจะมากน้อยกว่าหน่วยความจำที่สามารถใช้ได้)?

เหตุผลหนึ่งคืออัลกอริธึมส่วนใหญ่ไม่ต้องการพื้นที่สแต็กจำนวนมหาศาล สแต็คที่มีขนาดใหญ่เป็นข้อบ่งชี้ที่เป็นไปได้เรียกซ้ำหนี เป็นเรื่องที่ดีที่จะหยุดการสอบถามซ้ำที่มีการหลบหนีก่อนที่จะจัดสรรหน่วยความจำที่มีอยู่ทั้งหมด ปัญหาที่ดูเหมือนว่าการหลบหนีแบบวนซ้ำคือการใช้สแต็กน้อยลงซึ่งอาจเกิดจากกรณีทดสอบที่ไม่คาดคิด ตัวอย่างเช่นสมมติว่าตัวแยกวิเคราะห์สำหรับตัวดำเนินการไบนารี, มัดทำงานโดยการเรียกตัวถูกดำเนินการที่ถูกต้อง: ตัวถูกดำเนินการครั้งแรกแยกตัวดำเนินการสแกนประกอบการแยกส่วนที่เหลือของการแสดงออก a op b op c op d ...ซึ่งหมายความว่าลึกสแต็คเป็นสัดส่วนกับความยาวของการแสดงออก: กรณีทดสอบขนาดใหญ่ของแบบฟอร์มนี้จะต้องใช้สแต็กขนาดใหญ่ การยกเลิกโปรแกรมเมื่อถึงขีด จำกัด สแต็กที่เหมาะสมจะตรวจจับสิ่งนี้

เหตุผลอีกประการสำหรับขนาดสแต็กสูงสุดคงที่คือพื้นที่เสมือนสำหรับสแต็กนั้นสามารถจองผ่านการทำแผนที่ชนิดพิเศษและรับประกันได้ รับประกันหมายความว่าพื้นที่จะไม่ได้รับการจัดสรรอื่นซึ่งสแต็กจะชนกับมันก่อนที่จะถึงขีด จำกัด ต้องการพารามิเตอร์ขนาดสแต็กสูงสุดเพื่อขอการแมปนี้

เธรดต้องการขนาดสแต็กสูงสุดด้วยเหตุผลที่คล้ายกัน สแต็คของพวกเขาถูกสร้างขึ้นแบบไดนามิกและไม่สามารถเคลื่อนย้ายได้หากพวกเขาชนกับบางสิ่ง; ต้องสำรองพื้นที่เสมือนล่วงหน้าและต้องใช้ขนาดสำหรับการจัดสรรนั้น


@Lynn ไม่ได้ถามว่าทำไมขนาดสูงสุดคือคงที่ (s) เขาถามว่าทำไมมันถึงกำหนดไว้ล่วงหน้า
Will Calderwood
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.