Nó thực sự phụ thuộc vào hệ thống, nhưng các hệ điều hành hiện đại có bộ nhớ ảo có xu hướng tải hình ảnh quá trình của chúng và phân bổ bộ nhớ giống như thế này:
+---------+
| stack | function-local variables, return addresses, return values, etc.
| | often grows downward, commonly accessed via "push" and "pop" (but can be
| | accessed randomly, as well; disassemble a program to see)
+---------+
| shared | mapped shared libraries (C libraries, math libs, etc.)
| libs |
+---------+
| hole | unused memory allocated between the heap and stack "chunks", spans the
| | difference between your max and min memory, minus the other totals
+---------+
| heap | dynamic, random-access storage, allocated with 'malloc' and the like.
+---------+
| bss | Uninitialized global variables; must be in read-write memory area
+---------+
| data | data segment, for globals and static variables that are initialized
| | (can further be split up into read-only and read-write areas, with
| | read-only areas being stored elsewhere in ROM on some systems)
+---------+
| text | program code, this is the actual executable code that is running.
+---------+
Đây là không gian địa chỉ quy trình chung trên nhiều hệ thống bộ nhớ ảo phổ biến. "Lỗ" là kích thước của tổng bộ nhớ của bạn, trừ đi không gian được chiếm bởi tất cả các khu vực khác; điều này mang lại một lượng lớn không gian cho đống phát triển thành. Đây cũng là "ảo", nghĩa là nó ánh xạ tới bộ nhớ thực của bạn thông qua bảng dịch và có thể được lưu trữ thực sự tại bất kỳ vị trí nào trong bộ nhớ thực. Nó được thực hiện theo cách này để bảo vệ một tiến trình truy cập vào bộ nhớ của tiến trình khác và để làm cho mỗi tiến trình nghĩ rằng nó đang chạy trên một hệ thống hoàn chỉnh.
Lưu ý rằng các vị trí của, ví dụ, ngăn xếp và đống có thể theo thứ tự khác nhau trên một số hệ thống (xem câu trả lời của Billy O'Neal bên dưới để biết thêm chi tiết về Win32).
Các hệ thống khác có thể rất khác nhau. Ví dụ, DOS chạy ở chế độ thực và cấp phát bộ nhớ của nó khi chạy các chương trình trông khác nhau nhiều:
+-----------+ top of memory
| extended | above the high memory area, and up to your total memory; needed drivers to
| | be able to access it.
+-----------+ 0x110000
| high | just over 1MB->1MB+64KB, used by 286s and above.
+-----------+ 0x100000
| upper | upper memory area, from 640kb->1MB, had mapped memory for video devices, the
| | DOS "transient" area, etc. some was often free, and could be used for drivers
+-----------+ 0xA0000
| USER PROC | user process address space, from the end of DOS up to 640KB
+-----------+
|command.com| DOS command interpreter
+-----------+
| DOS | DOS permanent area, kept as small as possible, provided routines for display,
| kernel | *basic* hardware access, etc.
+-----------+ 0x600
| BIOS data | BIOS data area, contained simple hardware descriptions, etc.
+-----------+ 0x400
| interrupt | the interrupt vector table, starting from 0 and going to 1k, contained
| vector | the addresses of routines called when interrupts occurred. e.g.
| table | interrupt 0x21 checked the address at 0x21*4 and far-jumped to that
| | location to service the interrupt.
+-----------+ 0x0
Bạn có thể thấy rằng DOS cho phép truy cập trực tiếp vào bộ nhớ hệ điều hành, không có sự bảo vệ nào, điều đó có nghĩa là các chương trình không gian người dùng thường có thể truy cập trực tiếp hoặc ghi đè lên bất cứ thứ gì họ thích.
Tuy nhiên, trong không gian địa chỉ quy trình, các chương trình có xu hướng trông giống nhau, chỉ có chúng được mô tả là phân đoạn mã, phân đoạn dữ liệu, heap, phân đoạn ngăn xếp, v.v., và nó được ánh xạ hơi khác một chút. Nhưng hầu hết các khu vực chung vẫn còn đó.
Khi tải chương trình và các lib được chia sẻ cần thiết vào bộ nhớ và phân phối các phần của chương trình vào đúng khu vực, HĐH bắt đầu thực hiện quy trình của bạn bất cứ nơi nào có phương thức chính và chương trình của bạn tiếp tục từ đó, thực hiện các cuộc gọi hệ thống khi cần thiết nó cần chúng
Các hệ thống khác nhau (được nhúng, bất cứ thứ gì) có thể có các kiến trúc rất khác nhau, chẳng hạn như hệ thống không chồng, hệ thống kiến trúc Harvard (với mã và dữ liệu được giữ trong bộ nhớ vật lý riêng biệt), các hệ thống thực sự giữ BSS trong bộ nhớ chỉ đọc (ban đầu được đặt bởi lập trình viên), v.v ... Nhưng đây là ý chính chung.
Bạn đã nói:
Tôi cũng biết rằng một chương trình máy tính sử dụng hai loại bộ nhớ: stack và heap, cũng là một phần của bộ nhớ chính của máy tính.
"Ngăn xếp" và "đống" chỉ là các khái niệm trừu tượng, chứ không phải (nhất thiết) các "loại" bộ nhớ riêng biệt.
Một ngăn xếp chỉ là một cấu trúc dữ liệu vào trước, ra trước. Trong kiến trúc x86, nó thực sự có thể được xử lý ngẫu nhiên bằng cách sử dụng phần bù từ cuối, nhưng các chức năng phổ biến nhất là PUSH và POP để thêm và xóa các mục khỏi nó, tương ứng. Nó thường được sử dụng cho các biến chức năng-cục bộ (còn gọi là "lưu trữ tự động"), đối số hàm, địa chỉ trả về, v.v. (nhiều hơn bên dưới)
Một "đống" chỉ là một biệt hiệu cho một đoạn bộ nhớ có thể được phân bổ theo yêu cầu, và được giải quyết một cách ngẫu nhiên (có nghĩa là, bạn có thể truy cập vào bất kỳ vị trí trong đó trực tiếp). Nó thường được sử dụng cho các cấu trúc dữ liệu mà bạn phân bổ trong thời gian chạy (trong C ++, sử dụng new
và delete
và malloc
và bạn bè trong C, vv).
Ngăn xếp và đống, trên kiến trúc x86, cả hai đều nằm trong bộ nhớ hệ thống (RAM) của bạn và được ánh xạ thông qua cấp phát bộ nhớ ảo vào không gian địa chỉ quy trình như được mô tả ở trên.
Các thanh ghi (vẫn trên x86), nằm bên trong bộ xử lý (trái ngược với RAM) và được bộ xử lý tải, từ khu vực TEXT (và cũng có thể được tải từ nơi khác trong bộ nhớ hoặc các vị trí khác tùy theo hướng dẫn của CPU được thực hiện). Chúng thực chất chỉ là những vị trí bộ nhớ trên chip rất nhỏ, rất nhanh được sử dụng cho một số mục đích khác nhau.
Bố cục đăng ký phụ thuộc nhiều vào kiến trúc (trên thực tế, các thanh ghi, tập lệnh và bố cục / thiết kế bộ nhớ, chính xác là "kiến trúc" nghĩa là gì, và vì vậy tôi sẽ không mở rộng theo nó, nhưng khuyên bạn nên mở rộng khóa học ngôn ngữ để hiểu chúng tốt hơn.
Câu hỏi của bạn:
Tại điểm nào là ngăn xếp được sử dụng để thực hiện các hướng dẫn? Hướng dẫn đi từ RAM, đến ngăn xếp, đến thanh ghi?
Ngăn xếp (trong các hệ thống / ngôn ngữ có và sử dụng chúng) thường được sử dụng như thế này:
int mul( int x, int y ) {
return x * y; // this stores the result of MULtiplying the two variables
// from the stack into the return value address previously
// allocated, then issues a RET, which resets the stack frame
// based on the arg list, and returns to the address set by
// the CALLer.
}
int main() {
int x = 2, y = 3; // these variables are stored on the stack
mul( x, y ); // this pushes y onto the stack, then x, then a return address,
// allocates space on the stack for a return value,
// then issues an assembly CALL instruction.
}
Viết một chương trình đơn giản như thế này, sau đó biên dịch nó thành tập hợp ( gcc -S foo.c
nếu bạn có quyền truy cập vào GCC), và hãy xem. Việc lắp ráp là khá dễ dàng để làm theo. Bạn có thể thấy rằng ngăn xếp được sử dụng cho các biến cục bộ của hàm và để gọi các hàm, lưu trữ các đối số của chúng và trả về các giá trị. Đây cũng là lý do tại sao khi bạn làm một cái gì đó như:
f( g( h( i ) ) );
Tất cả những thứ này được gọi lần lượt. Đó thực sự là xây dựng một chồng các lệnh gọi hàm và đối số của chúng, thực thi chúng và sau đó bật chúng ra khi nó quay ngược trở lại (hoặc lên;). Tuy nhiên, như đã đề cập ở trên, ngăn xếp (trên x86) thực sự nằm trong không gian bộ nhớ xử lý của bạn (trong bộ nhớ ảo) và do đó, nó có thể được thao tác trực tiếp; đó không phải là một bước riêng trong quá trình thực thi (hoặc ít nhất là trực giao với quy trình).
FYI, ở trên là quy ước gọi C , cũng được sử dụng bởi C ++. Các ngôn ngữ / hệ thống khác có thể đẩy các đối số lên ngăn xếp theo một thứ tự khác và một số ngôn ngữ / nền tảng thậm chí không sử dụng ngăn xếp và thực hiện theo các cách khác nhau.
Cũng lưu ý, đây không phải là dòng thực thi mã C. Trình biên dịch đã chuyển đổi chúng thành các hướng dẫn ngôn ngữ máy trong tệp thực thi của bạn. Sau đó, chúng (thường) được sao chép từ vùng văn bản vào đường ống CPU, sau đó vào các thanh ghi CPU và được thực hiện từ đó. [Điều này không chính xác. Xem phần chỉnh sửa của Ben Voigt bên dưới.]