Tổ chức không gian địa chỉ lôgic Linux Kernel


8

Theo "Viết mã lớn" trong hầu hết tất cả bộ nhớ thời gian chạy hệ điều hành được tổ chức thành các vùng sau:

HĐH | Chồng | Đống | Văn bản | Tĩnh | Lưu trữ / BSS

[Trong thời trang địa chỉ ngày càng tăng]

Quá trình không gian người dùng sử dụng vùng bộ nhớ cao hơn cho các loại đối tượng dữ liệu khác nhau.

Quá trình không gian hạt nhân cũng có các loại đối tượng dữ liệu khác nhau. Các đối tượng này có chia sẻ các vùng bộ nhớ không gian người dùng (stack, heap, v.v.) hay chúng có các phần phụ riêng (heap, stack, v.v.) nằm trong vùng OS. Và, nếu vậy, thứ tự chúng được sắp xếp là gì . Cảm ơn,

Câu trả lời:


5

Đó là sai về thứ tự. HĐH được đặt ở phía trên cùng của bộ nhớ, thường cao hơn mốc 3 GB (0xC0000000) trong hạt nhân 32 bit và trong nhân 64 bit, nó là nửa điểm của 0x8000000000000000 IIRC.

Vị trí của ngăn xếp và đống được ngẫu nhiên. Không có quy tắc thực sự nào về thứ tự của các phân đoạn văn bản / dữ liệu / bss trong chương trình chính và mỗi thư viện động đều có tập hợp riêng của chúng, vì vậy có rất nhiều trong số chúng nằm rải rác trên bộ nhớ.

Quay lại khi khủng long cai trị trái đất (hơn 20 năm trước), không gian địa chỉ chương trình là tuyến tính (không có lỗ hổng) và thứ tự là văn bản, dữ liệu, bss, stack, heap. Lúc đó cũng không có thư viện động hay xâu chuỗi. Tất cả đã thay đổi với bộ nhớ ảo.

Các tiến trình kernel hoàn toàn được chứa trong phần kernel của không gian địa chỉ; phần người dùng bị bỏ qua. Điều này cho phép kernel tăng tốc độ chuyển đổi ngữ cảnh giữa các luồng kernel, vì nó không phải cập nhật các bảng trang vì tất cả các quy trình chia sẻ cùng một phần kernel của các bảng trang.


4

Điều này không đúng với hầu hết các OS OS. Các loại vùng nhớ được biểu thị khá điển hình, nhưng không có lý do tại sao chúng phải theo thứ tự cụ thể và có thể có nhiều hơn một loại nhất định.

Dưới Linux, bạn có thể nhìn vào không gian địa chỉ của một quá trình với cat /proc/$pid/mapsnơi $pidlà quá trình ID, ví dụ như cat /proc/$$/mapsnhìn vào vỏ bạn đang chạy cattừ, hoặc cat /proc/self/mapsđể nhìn vào catánh xạ riêng của quá trình. Lệnh pmaptạo đầu ra đẹp hơn một chút.

08048000-08054000 r-xp 00000000 08:01 828061     /bin/cat
08054000-08055000 r--p 0000b000 08:01 828061     /bin/cat
08055000-08056000 rw-p 0000c000 08:01 828061     /bin/cat
08c7f000-08ca0000 rw-p 00000000 00:00 0          [heap]
b755a000-b7599000 r--p 00000000 08:01 273200     /usr/lib/locale/en_US.utf8/LC_CTYPE
b7599000-b759a000 rw-p 00000000 00:00 0 
b759a000-b76ed000 r-xp 00000000 08:01 269273     /lib/tls/i686/cmov/libc-2.11.1.so
b76ed000-b76ee000 ---p 00153000 08:01 269273     /lib/tls/i686/cmov/libc-2.11.1.so
b76ee000-b76f0000 r--p 00153000 08:01 269273     /lib/tls/i686/cmov/libc-2.11.1.so
b76f0000-b76f1000 rw-p 00155000 08:01 269273     /lib/tls/i686/cmov/libc-2.11.1.so
b76f1000-b76f4000 rw-p 00000000 00:00 0 
b770b000-b7712000 r--s 00000000 08:01 271618     /usr/lib/gconv/gconv-modules.cache
b7712000-b7714000 rw-p 00000000 00:00 0 
b7714000-b7715000 r-xp 00000000 00:00 0          [vdso]
b7715000-b7730000 r-xp 00000000 08:01 263049     /lib/ld-2.11.1.so
b7730000-b7731000 r--p 0001a000 08:01 263049     /lib/ld-2.11.1.so
b7731000-b7732000 rw-p 0001b000 08:01 263049     /lib/ld-2.11.1.so
bfbec000-bfc01000 rw-p 00000000 00:00 0          [stack]

Bạn có thể xem mã và dữ liệu đọc (văn bản và BSS) từ tệp thực thi, sau đó là heap, sau đó là tệp ánh xạ bộ nhớ, sau đó thêm một chút dữ liệu đọc, sau đó mã, dữ liệu chỉ đọc và đọc ghi dữ liệu từ thư viện dùng chung (lại văn bản và BSS), dữ liệu đọc-ghi nhiều hơn, thư viện chia sẻ khác (chính xác hơn là trình liên kết động) và cuối cùng là ngăn xếp của luồng duy nhất.

Mã hạt nhân sử dụng phạm vi địa chỉ riêng của nó. Trên nhiều nền tảng, Linux sử dụng phần trên của không gian địa chỉ cho kernel, thường là phần trên 1GB. Lý tưởng nhất, không gian này sẽ đủ để ánh xạ mã kernel, dữ liệu kernel và bộ nhớ hệ thống (RAM) và mọi thiết bị ánh xạ bộ nhớ. Trên các PC 32 bit thông thường hiện nay, điều này là không thể, điều này đòi hỏi các mâu thuẫn chỉ dành cho các tin tặc kernel.

Trong khi mã hạt nhân đang xử lý một cuộc gọi hệ thống, lý tưởng nhất là khi không có các mâu thuẫn nói trên), bộ nhớ của quy trình được ánh xạ tại cùng một địa chỉ. Điều này cho phép các quá trình truyền dữ liệu đến kernel và kernel có thể đọc trực tiếp từ con trỏ. Tuy nhiên, đó không phải là một lợi ích lớn vì các con trỏ cần phải được xác nhận hợp lệ (để quá trình không thể lừa kernel đọc từ bộ nhớ mà quá trình không được phép truy cập).

Các vùng bộ nhớ trong không gian nhân Linux khá phức tạp. Có một số nhóm bộ nhớ khác nhau và sự khác biệt chính không phải là bộ nhớ đến từ đâu mà là nó được chia sẻ với ai. Nếu bạn tò mò về chúng, hãy bắt đầu với LDD3 .


1

Không phải là một câu trả lời, mà là một FYI cần nhiều không gian hơn.

Tôi không nghĩ rằng quan niệm của bạn về bố cục địa chỉ hợp lý là hoàn toàn chính xác.

Bạn có thể biên dịch và chạy chương trình này để xem quy trình người dùng có địa chỉ gì:

#include <stdio.h>
long global_initialized = 119234;
long global_uninitialized;
extern int _end, _edata, _etext;
int
main(int ac, char **av)
{
        long local;

        printf("main at 0x%lx\n", main);
        printf("ac at   0x%lx\n", &ac);
        printf("av at   0x%lx\n", &av);
        printf("av has  0x%lx\n", av);
        printf("initialized global at 0x%lx\n", &global_initialized);
        printf("global at             0x%lx\n", &global_uninitialized);
        printf("local at              0x%lx\n", &local);
        printf("_end at               0x%lx\n", &_end);
        printf("_edata at             0x%lx\n", &_edata);
        printf("_etext at             0x%lx\n", &_etext);
        return 0;
}

Máy chủ doanh nghiệp Red Hat mà tôi đang chạy, có readelf, có thể được sử dụng để nói nơi kernel sẽ (một cách hợp lý) tải một tệp thực thi:

readelf -S where

Hiển thị cho tôi rất nhiều thông tin địa chỉ tương tự mà đầu ra của wherecung cấp.

Tôi không nghĩ readelfsẽ dễ dàng làm việc trên nhân Linux (/ boot / vmlinuz hoặc một số thứ khác) và tôi nghĩ rằng mặc định hạt nhân bắt đầu từ 0x80000000 trong không gian địa chỉ của chính nó: mặc dù nó không được ánh xạ trong quy trình người dùng, mặc dù đã sử dụng địa chỉ trên đầu ngăn xếp người dùng ở 0x7fffffff (x86, địa chỉ 32 bit).


Cảm ơn ví dụ! Chỉ cần lưu ý của tôi về phần Linux - Tôi vừa thử ví dụ này như where.ctrên Ubuntu 11.04 bằng cách sử dụng gcc where.c -o where; báo cáo "chính tại 0x80483c4". Đã thử readelf -S where, và nó báo cáo, nói "[13] .text PROGBITS 08048 310 ..." trông như thế nào phải không? Mặc dù tôi cũng nhận được "ac ở 0xbfb035a0" và "cục bộ tại 0xbfb0353c", và phạm vi địa chỉ đó (0xbf ...) dường như không được báo cáo bởi readelf -S.
sdaau

@sdaau - Các đối số acavbiến tự động localcó thể sẽ có các địa chỉ khác nhau trên mỗi lệnh gọi. Hầu hết các nhân Linux hiện đại đều có "Ngẫu nhiên bố trí không gian địa chỉ" để làm cho việc khai thác bộ đệm tràn ra khó khăn hơn.
Bruce Ediger
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.