Tại sao các ngăn xếp thường phát triển xuống dưới?


95

Tôi biết rằng trong các kiến ​​trúc mà cá nhân tôi quen thuộc (x86, 6502, v.v.), ngăn xếp thường phát triển xuống dưới (tức là mọi mục được đẩy lên ngăn xếp dẫn đến SP giảm dần chứ không phải tăng dần).

Tôi đang băn khoăn về lý do lịch sử cho việc này. Tôi biết rằng trong một không gian địa chỉ thống nhất, thật thuận tiện để bắt đầu ngăn xếp ở đầu đối diện của phân đoạn dữ liệu (giả sử), vì vậy chỉ có vấn đề nếu hai bên va chạm ở giữa. Nhưng tại sao theo cách truyền thống, ngăn xếp có phần trên cùng? Đặc biệt là làm thế nào đây là đối lập với mô hình "khái niệm"?

(Và lưu ý rằng trong kiến ​​trúc 6502, ngăn xếp cũng phát triển xuống dưới, mặc dù nó được giới hạn trong một trang 256 byte duy nhất và lựa chọn hướng này có vẻ tùy ý.)

Câu trả lời:


52

Về lý do lịch sử, tôi không thể nói chắc chắn (vì tôi không thiết kế chúng). Suy nghĩ của tôi về vấn đề này là các CPU đời đầu có bộ đếm chương trình ban đầu của chúng được đặt thành 0 và mong muốn tự nhiên là bắt đầu ngăn xếp ở đầu kia và phát triển xuống dưới, vì mã của chúng tự nhiên tăng lên.

Ngoài ra, hãy lưu ý rằng cài đặt này của bộ đếm chương trình về 0 khi đặt lại không phải là trường hợp cho tất cả các CPU đời đầu. Ví dụ: Motorola 6809 sẽ tìm nạp bộ đếm chương trình từ các địa chỉ 0xfffe/fđể bạn có thể bắt đầu chạy ở một vị trí tùy ý, tùy thuộc vào những gì được cung cấp tại địa chỉ đó (thông thường, nhưng không giới hạn ở, ROM).

Một trong những điều đầu tiên mà một số hệ thống lịch sử làm sẽ là quét bộ nhớ từ trên xuống cho đến khi nó tìm thấy một vị trí có thể đọc lại cùng một giá trị đã ghi, để nó biết được RAM thực được cài đặt (ví dụ: z80 với không gian địa chỉ 64K không nhất thiết phải có 64K hoặc RAM, trên thực tế, 64K sẽ rất lớn trong những ngày đầu của tôi). Khi nó tìm thấy địa chỉ thực tế hàng đầu, nó sẽ đặt con trỏ ngăn xếp một cách thích hợp và sau đó có thể bắt đầu gọi các chương trình con. Quá trình quét này thường được thực hiện bởi mã CPU đang chạy trong ROM như một phần của quá trình khởi động.

Liên quan đến sự tăng trưởng ngăn xếp, không phải tất cả chúng đều tăng trưởng xuống dưới, hãy xem câu trả lời này để biết chi tiết.


1
Tôi thích câu chuyện chiến lược phát hiện RAM của Z80. Có nghĩa là các phân đoạn văn bản được sắp xếp theo chiều hướng lớn dần lên - các lập trình viên của bạn đã có phần tiếp xúc trực tiếp hơn với việc giải quyết các tác động của nó hơn là ngăn xếp. Cảm ơn paxdiablo. Con trỏ đến tập hợp các dạng triển khai ngăn xếp thay thế cũng rất thú vị.
Ben Zotto

Không phải bộ nhớ ban ngày có cách nào để thông báo kích thước của nó và chúng ta phải tính toán nó theo cách thủ công?
phuclv

1
@ LưuVĩnhPhúc, tôi phải cho rằng bạn đi sau tôi một (hai) thế hệ. Tôi vẫn nhớ phương pháp TRS-80 model 3 để lấy ngày và giờ để hỏi người dùng tại thời điểm khởi động. Có một máy quét bộ nhớ để thiết lập giới hạn trên của bộ nhớ được coi là hiện đại ngày xưa :-) Bạn có thể tưởng tượng điều gì sẽ xảy ra nếu Windows hỏi thời gian, hoặc bạn có bao nhiêu bộ nhớ mỗi khi khởi động không?
paxdiablo

1
Thật vậy, tài liệu về Zilog Z80 cho biết phần này khởi động bằng cách đặt thanh ghi PC thành 0000h và thực thi. Nó đặt chế độ ngắt thành 0, vô hiệu hóa ngắt và đặt cả thanh ghi I và R thành 0. Sau đó, nó bắt đầu thực thi. Lúc 0000h, nó bắt đầu chạy mã. Mã RẰNG phải khởi tạo con trỏ ngăn xếp trước khi nó có thể gọi một chương trình con hoặc cho phép ngắt. Nhà cung cấp nào bán một chiếc Z80 hoạt động theo cách bạn mô tả?
MikeB

1
Mike, xin lỗi, tôi nên nói rõ hơn. Khi tôi nói CPU quét bộ nhớ, tôi không có ý đó là một tính năng của chính CPU. Nó thực sự được điều khiển từ một chương trình trong ROM. Tôi sẽ làm rõ.
paxdiablo

21

Một lời giải thích hay mà tôi đã nghe là một số máy trong quá khứ chỉ có thể có hiệu số không có dấu, vì vậy bạn muốn ngăn xếp tăng dần xuống để bạn có thể đánh vào cục bộ của mình mà không phải mất thêm hướng dẫn để giả mạo bù trừ.


7

Stanley Mazor (kiến trúc sư 4004 và 8080) giải thích cách chọn hướng tăng trưởng ngăn xếp cho 8080 (và cuối cùng là 8086) trong "Bộ vi xử lý Intel: 8008 đến 8086" :

Con trỏ ngăn xếp được chọn để chạy "xuống dốc" (với ngăn xếp tiến về phía bộ nhớ thấp hơn) để đơn giản hóa việc lập chỉ mục vào ngăn xếp từ chương trình của người dùng (lập chỉ mục tích cực) và để đơn giản hóa việc hiển thị nội dung của ngăn xếp từ bảng điều khiển phía trước.


6

Một lý do có thể là nó đơn giản hóa việc căn chỉnh. Nếu bạn đặt một biến cục bộ trên ngăn xếp phải được đặt trên ranh giới 4 byte, bạn có thể chỉ cần trừ kích thước của đối tượng khỏi con trỏ ngăn xếp, sau đó trừ đi hai bit dưới để có địa chỉ được căn chỉnh đúng. Nếu ngăn xếp tăng dần lên, việc đảm bảo căn chỉnh trở nên phức tạp hơn một chút.


1
Máy tính không trừ; họ thêm vào lời khen của 2. Bất cứ điều gì được thực hiện bằng cách trừ thực sự được thực hiện bằng cách cộng. Hãy xem xét, máy tính có bộ cộng, không có bộ trừ.
jww

1
@jww - đó là sự khác biệt mà không có sự khác biệt. Tôi cũng có thể khẳng định rằng máy tính không cộng, chúng chỉ trừ! Đối với mục đích của câu trả lời này, nó không thực sự quan trọng - nhưng hầu hết các ALU sẽ sử dụng một mạch hỗ trợ cả phép cộng và phép trừ với cùng một hiệu suất. Có nghĩa là, trong khi A - Bvề mặt khái niệm có thể được triển khai dưới dạng A + (-B)(tức là một bước phủ định riêng biệt cho B), thì nó không có trong thực tế.
BeeOnRope

@jww Nitpick của bạn là sai đối với các máy tính đời đầu - phải mất một thời gian để phần bổ sung của hai người giành chiến thắng và cho đến khi nó xảy ra, đã có những máy tính sử dụng phần bổ sung và ký hiệu và độ lớn của một người và có thể là những thứ khác thay thế. Với những cách triển khai đó, có thể có lợi thế hơn khi cộng với trừ. Vì vậy, trong trường hợp không có thông tin bổ sung, sẽ sai nếu loại trừ đây là một yếu tố có thể ảnh hưởng đến các lựa chọn sơ đồ giải quyết như hướng ngăn xếp.
mtraceur

4

IIRC ngăn xếp tăng dần xuống dưới vì đống tăng lên trên. Nó có thể là một cách khác.


5
Một đống tăng lên cho phép phân bổ lại hiệu quả trong một số trường hợp, nhưng một đống tăng dần xuống thì không bao giờ làm được.
Peter Cordes

@PeterCordes tại sao?
Yashas

3
@Yashas: vì realloc(3)cần thêm không gian sau một đối tượng để chỉ mở rộng ánh xạ mà không cần sao chép. Có thể tái phân bổ lặp lại cùng một đối tượng khi nó được theo sau bởi một lượng không gian không sử dụng tùy ý.
Peter Cordes

2

Tôi tin rằng đó hoàn toàn là một quyết định thiết kế. Không phải tất cả chúng đều phát triển theo chiều hướng đi xuống - hãy xem luồng SO này để biết một số thảo luận tốt về hướng phát triển ngăn xếp trên các kiến ​​trúc khác nhau.


1

Tôi tin rằng quy ước bắt đầu với IBM 704 và "sổ đăng ký sắc lệnh" khét tiếng của nó. Bài phát biểu hiện đại sẽ gọi nó là một trường offset của lệnh, nhưng điểm mấu chốt là họ đã đi xuống , không lên .


1

Chỉ 2c nữa:

Ngoài tất cả những lý do lịch sử đã đề cập, tôi khá chắc chắn rằng không có lý do nào hợp lệ trong các bộ vi xử lý hiện đại. Tất cả các bộ xử lý đều có thể thực hiện các phần bù đã ký và việc tối đa hóa khoảng cách đống / ngăn xếp là điều khá tranh cãi kể từ khi chúng tôi bắt đầu xử lý nhiều luồng.

Cá nhân tôi coi đây là một lỗ hổng thiết kế bảo mật. Giả sử, nếu các nhà thiết kế của kiến ​​trúc x64 đã đảo ngược hướng phát triển ngăn xếp, thì hầu hết các lỗi tràn bộ đệm ngăn xếp sẽ bị loại bỏ - đó là một vấn đề lớn. (kể từ khi chuỗi phát triển trở lên).


0

Tôi không chắc lắm nhưng tôi đã lập trình một số cho VAX / VMS trước đây. Tôi dường như nhớ một phần của bộ nhớ (đống ??) tăng lên và ngăn xếp đi xuống. Khi hai người gặp nhau, thì bạn đã hết nhớ.


1
Điều này đúng, nhưng sau đó tại sao đống tăng lên phía trên mà không phải ngược lại?
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

0

Một lợi thế của việc tăng trưởng ngăn xếp giảm dần trong một hệ thống nhúng tối thiểu là một đoạn RAM có thể được ánh xạ dự phòng vào cả trang O và trang 1, cho phép chỉ định các biến số trang bắt đầu từ 0x000 và ngăn xếp tăng dần xuống từ 0x1FF, tối đa hóa số lượng nó sẽ phải tăng lên trước khi ghi đè các biến.

Một trong những mục tiêu thiết kế ban đầu của 6502 là nó có thể được kết hợp với, chẳng hạn như 6530, tạo ra một hệ thống vi điều khiển hai chip với 1 KB ROM chương trình, bộ đếm thời gian, I / O và 64 byte RAM được chia sẻ giữa các biến số không ngăn xếp và trang. Để so sánh, hệ thống nhúng tối thiểu vào thời điểm đó dựa trên 8080 hoặc 6800 sẽ là bốn hoặc năm chip.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.