2013-01-17 34 views
8

Câu hỏi cơ bản của tôi là tại sao VSIZE cho quá trình 64 bit lớn hơn nhiều so với chương trình tương tự được biên dịch cho 32 bit?Tại sao VSIZE lại lớn hơn rất nhiều đối với quá trình 64 bit Linux?

Sau đây là đầu ra của tệp/proc/<pid>/bản đồ cho quy trình 32 bit.

00148000-00149000 r-xp 00000000 00:00 0    [vdso] 
00149000-002d2000 r-xp 00000000 fd:02 8914142   /lib/libc-2.12.so 
002d2000-002d3000 ---p 00189000 fd:02 8914142   /lib/libc-2.12.so 
002d3000-002d5000 r--p 00189000 fd:02 8914142   /lib/libc-2.12.so 
002d5000-002d6000 rw-p 0018b000 fd:02 8914142   /lib/libc-2.12.so 
002d6000-002d9000 rw-p 00000000 00:00 0 
005c9000-005da000 r-xp 00000000 fd:02 17059392  /tmp/vsizetest/lib/libtesting.so 
005da000-005db000 rw-p 00010000 fd:02 17059392  /tmp/vsizetest/lib/libtesting.so 
005db000-0061b000 rw-p 00000000 00:00 0 
00661000-00689000 r-xp 00000000 fd:02 8917713   /lib/libm-2.12.so 
00689000-0068a000 r--p 00027000 fd:02 8917713   /lib/libm-2.12.so 
0068a000-0068b000 rw-p 00028000 fd:02 8917713   /lib/libm-2.12.so 
00694000-006ab000 r-xp 00000000 fd:02 8917680   /lib/libpthread-2.12.so 
006ab000-006ac000 r--p 00016000 fd:02 8917680   /lib/libpthread-2.12.so 
006ac000-006ad000 rw-p 00017000 fd:02 8917680   /lib/libpthread-2.12.so 
006ad000-006af000 rw-p 00000000 00:00 0 
006e5000-00703000 r-xp 00000000 fd:00 3150403   /lib/ld-2.12.so 
00703000-00704000 r--p 0001d000 fd:00 3150403   /lib/ld-2.12.so 
00704000-00705000 rw-p 0001e000 fd:00 3150403   /lib/ld-2.12.so 
00983000-009a0000 r-xp 00000000 fd:02 8914997   /lib/libgcc_s-4.4.5-20110214.so.1 
009a0000-009a1000 rw-p 0001d000 fd:02 8914997   /lib/libgcc_s-4.4.5-20110214.so.1 
00ca5000-00d86000 r-xp 00000000 fd:02 6300601   /usr/lib/libstdc++.so.6.0.13 
00d86000-00d8a000 r--p 000e0000 fd:02 6300601   /usr/lib/libstdc++.so.6.0.13 
00d8a000-00d8c000 rw-p 000e4000 fd:02 6300601   /usr/lib/libstdc++.so.6.0.13 
00d8c000-00d92000 rw-p 00000000 00:00 0 
08048000-08049000 r-xp 00000000 fd:02 21134666  /tmp/vsizetest/bin/testvsz 
08049000-0804a000 rw-p 00000000 fd:02 21134666  /tmp/vsizetest/bin/testvsz 
09b8d000-09bae000 rw-p 00000000 00:00 0    [heap] 
f7796000-f779c000 rw-p 00000000 00:00 0 
ff998000-ff9ae000 rw-p 00000000 00:00 0    [stack] 

Những kết quả trong một tổng VSIZE của 3656.

Sau đây là sản phẩm của/proc/pid < tập tin >/bản đồ cho quá trình 64 bit.

00400000-00401000 r-xp 00000000 fd:02 21134667    /tmp/vsizetest/bin64/testvsz 
00600000-00601000 rw-p 00000000 fd:02 21134667    /tmp/vsizetest/bin64/testvsz 
02301000-02322000 rw-p 00000000 00:00 0      [heap] 
3b7c800000-3b7c820000 r-xp 00000000 fd:00 661349   /lib64/ld-2.12.so 
3b7ca1f000-3b7ca20000 r--p 0001f000 fd:00 661349   /lib64/ld-2.12.so 
3b7ca20000-3b7ca21000 rw-p 00020000 fd:00 661349   /lib64/ld-2.12.so 
3b7ca21000-3b7ca22000 rw-p 00000000 00:00 0 
3b7cc00000-3b7cd86000 r-xp 00000000 fd:00 661350   /lib64/libc-2.12.so 
3b7cd86000-3b7cf86000 ---p 00186000 fd:00 661350   /lib64/libc-2.12.so 
3b7cf86000-3b7cf8a000 r--p 00186000 fd:00 661350   /lib64/libc-2.12.so 
3b7cf8a000-3b7cf8b000 rw-p 0018a000 fd:00 661350   /lib64/libc-2.12.so 
3b7cf8b000-3b7cf90000 rw-p 00000000 00:00 0 
3b7d000000-3b7d083000 r-xp 00000000 fd:00 661365   /lib64/libm-2.12.so 
3b7d083000-3b7d282000 ---p 00083000 fd:00 661365   /lib64/libm-2.12.so 
3b7d282000-3b7d283000 r--p 00082000 fd:00 661365   /lib64/libm-2.12.so 
3b7d283000-3b7d284000 rw-p 00083000 fd:00 661365   /lib64/libm-2.12.so 
3b7d800000-3b7d817000 r-xp 00000000 fd:00 661352   /lib64/libpthread-2.12.so 
3b7d817000-3b7da16000 ---p 00017000 fd:00 661352   /lib64/libpthread-2.12.so 
3b7da16000-3b7da17000 r--p 00016000 fd:00 661352   /lib64/libpthread-2.12.so 
3b7da17000-3b7da18000 rw-p 00017000 fd:00 661352   /lib64/libpthread-2.12.so 
3b7da18000-3b7da1c000 rw-p 00000000 00:00 0 
3b7e000000-3b7e007000 r-xp 00000000 fd:00 661361   /lib64/librt-2.12.so 
3b7e007000-3b7e206000 ---p 00007000 fd:00 661361   /lib64/librt-2.12.so 
3b7e206000-3b7e207000 r--p 00006000 fd:00 661361   /lib64/librt-2.12.so 
3b7e207000-3b7e208000 rw-p 00007000 fd:00 661361   /lib64/librt-2.12.so 
3b87000000-3b87016000 r-xp 00000000 fd:00 664219   /lib64/libgcc_s-4.4.6-20110824.so.1 
3b87016000-3b87215000 ---p 00016000 fd:00 664219   /lib64/libgcc_s-4.4.6-20110824.so.1 
3b87215000-3b87216000 rw-p 00015000 fd:00 664219   /lib64/libgcc_s-4.4.6-20110824.so.1 
3d44c00000-3d44ce8000 r-xp 00000000 fd:00 3019214   /usr/lib64/libstdc++.so.6.0.13 
3d44ce8000-3d44ee8000 ---p 000e8000 fd:00 3019214   /usr/lib64/libstdc++.so.6.0.13 
3d44ee8000-3d44eef000 r--p 000e8000 fd:00 3019214   /usr/lib64/libstdc++.so.6.0.13 
3d44eef000-3d44ef1000 rw-p 000ef000 fd:00 3019214   /usr/lib64/libstdc++.so.6.0.13 
3d44ef1000-3d44f06000 rw-p 00000000 00:00 0 
7f30ab397000-7f30ab39c000 rw-p 00000000 00:00 0 
7f30ab39c000-7f30ab3ad000 r-xp 00000000 fd:02 21127804  /tmp/vsizetest/lib64/libtesting.so 
7f30ab3ad000-7f30ab5ac000 ---p 00011000 fd:02 21127804  /tmp/vsizetest/lib64/libtesting.so 
7f30ab5ac000-7f30ab5ad000 rw-p 00010000 fd:02 21127804  /tmp/vsizetest/lib64/libtesting.so 
7f30ab5ad000-7f30ab5ee000 rw-p 00000000 00:00 0 
7f30ab606000-7f30ab609000 rw-p 00000000 00:00 0 
7fff69512000-7fff69528000 rw-p 00000000 00:00 0    [stack] 
7fff695ff000-7fff69600000 r-xp 00000000 00:00 0    [vdso] 
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0  [vsyscall] 

Những kết quả trong một VSIZE của 18480.

Sự khác biệt chính giữa 2 bản đồ là các mục sau đây từ các dữ liệu 64 bit:

3b7cd86000-3b7cf86000 ---p 00186000 fd:00 661350    /lib64/libc-2.12.so 
3b7d083000-3b7d282000 ---p 00083000 fd:00 661365    /lib64/libm-2.12.so 
3b7d817000-3b7da16000 ---p 00017000 fd:00 661352    /lib64/libpthread-2.12.so 
3b7e007000-3b7e206000 ---p 00007000 fd:00 661361    /lib64/librt-2.12.so 
3b87016000-3b87215000 ---p 00016000 fd:00 664219    /lib64/libgcc_s-4.4.6-20110824.so.1 
3d44ce8000-3d44ee8000 ---p 000e8000 fd:00 3019214   /usr/lib64/libstdc++.so.6.0.13 
7f30ab3ad000-7f30ab5ac000 ---p 00011000 fd:02 21127804  /tmp/vsizetest/lib64/libtesting.so 

nào chiếm 14.316 của 18.480 VSIZE.

Thử nghiệm khác với các chương trình khác dường như cho thấy rằng trong 64 bit, bạn dường như nhận được một bộ nhớ riêng tư, không thể đọc, không thể ghi, không thực thi được cho mỗi thư viện được sử dụng bởi quy trình, trong khi ở 32 bit, hầu như không có bất kỳ khối nào trong số này.

Có ai biết bộ nhớ này là gì không?

Lưu ý: Dựa trên một số câu trả lời cho một câu hỏi tương tự, What these memory regions for, from a Linux process?, đây không phải là quy trình đa luồng và đã được biên dịch -fPIC.

Trả lời

1

[Không thực sự là một câu trả lời ... nói qua kiến ​​thức của tôi]

Nếu các phân đoạn bộ nhớ đang thực sự "riêng tư, không thể đọc được, không có khả năng ghi, không thực thi", sau đó họ nên không bao giờ được đề cập đến, và mặc dù chúng tồn tại trong không gian bộ nhớ VIRTUAL, chúng sẽ không bao giờ chiếm bất kỳ bộ nhớ thực nào, và do đó không phải lo lắng nhiều. (?)

Nó phải là một số loại vấn đề giữ sách hoặc phân mảnh. Vì đây là một phần của các thư viện được chia sẻ (* .so), nó chỉ là cách các thư viện được xây dựng. Nó thực sự không liên quan gì tới chương trình của bạn, ngoài việc nó được liên kết với các thư viện đó. Trừ khi bạn muốn xây dựng lại các thư viện đó, hoặc không sử dụng chúng, không có nhiều việc phải làm (và cũng không có nhiều thứ để đạt được vì chúng không nên sử dụng bộ nhớ thực).

Có thể có liên quan? Trong What these memory regions for, from a Linux process?

@caf cho biết một số phân đoạn bộ nhớ là "--- p" là "trang bảo vệ".

Điều đó cho thấy chúng tồn tại chỉ để bắt con trỏ lạc hoặc ngăn xếp phát triển đến lỗi xa ... sắp xếp dấu phân tách cứng trong bộ nhớ để hệ thống có thể gặp lỗi thường gặp và ngừng xử lý thay vì để lỗi phổ biến đó là một lỗi nghiêm trọng để ám chỉ đến họ, và họ thực sự sẽ KHÔNG BAO GIỜ sử dụng bất kỳ bộ nhớ thực nào).

1

Trả lời lý do tại sao và những gì cấu thành thư viện được chia sẻ 64 bit có một bộ nhớ bổ sung, bằng cách lấy ví dụ về tải libc.so và tìm cách này từ bộ nạp tải thư viện động.Dưới đây là các kết quả đầu ra strace cho cả thực thi 32 bit và 64 bit cho chúng tôi biết có các cuộc gọi tới mmap & mprotect.

[email protected]:~/32_64bit$ strace ./crash-x86-64 
... 
open("/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3 
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\200\30\2\0\0\0\0\0"..., 
832) = 832 
fstat(3, {st_mode=S_IFREG|0755, st_size=1811128, ...}) = 0 
mmap(NULL, 3925208, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) =  
0x7fa354f8a000 
mprotect(0x7fa35513f000, 2093056, PROT_NONE) = 0 
mmap(0x7fa35533e000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 
3, 0x1b4000) = 0x7fa35533e000 
mmap(0x7fa355344000, 17624, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, 
-1, 0) = 0x7fa355344000 
close(3)        = 0 
... 
[email protected]:~/32_64bit$ strace ./crash 
... 
open("/lib/i386-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3 
read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0000\226\1\0004\0\0\0"..., 
512) = 512 
fstat64(3, {st_mode=S_IFREG|0755, st_size=1730024, ...}) = 0 
mmap2(NULL, 1743580, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 
0xfffffffff7546000 
mprotect(0xf76e9000, 4096, PROT_NONE) = 0 
mmap2(0xf76ea000, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 
3, 0x1a3) = 0xfffffffff76ea000 
mmap2(0xf76ed000, 10972, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, 
-1, 0) = 0xfffffffff76ed000 
close(3)        = 0 
... 

chặt chẽ quan sát cả hai điều của strace đang cần phải được điều tra,

1. Mỗi trong số họ ánh xạ bộ nhớ 3 lần và 1 cuộc gọi để mprotect chính xác sau khi đã mmap .

2. So sánh mprotect kêu gọi 64bit & 32bit có 2093056B & 4096B của khu vực được bảo vệ tương ứng.

Trong dl-load.c, chương trình con _dl_map_object_from_fd() bản đồ phân đoạn bộ nhớ thư viện động vào không gian ảo bằng cách thiết lập quyền yêu cầu và không đầy .bss phần của thư viện và cập nhật các cấu trúc bản đồ liên kết. Cho phép nhận được ở đây một số phần của mã cho phân tích hơn,

struct link_map * 
_dl_map_object_from_fd () 
{ 
... 
    /* Scan the program header table, collecting its load commands. */ 
    struct loadcmd 
    { 
    ElfW(Addr) mapstart, mapend, dataend, allocend; 
    off_t mapoff; 
    int prot; 
    } loadcmds[l->l_phnum], *c; // l is link_map struct described for each object 
            of dynamic linker 
    size_t nloadcmds = 0; 
    bool has_holes = false; 
    ... 
    for (ph = phdr; ph < &phdr[l->l_phnum]; ++ph) 
    switch (ph->p_type) 
    { 
    ... 
    case PT_LOAD: 
    ... 
    c = &loadcmds[nloadcmds++]; 
    c->mapstart = ph->p_vaddr & ~(GLRO(dl_pagesize) - 1); 
    c->mapend = ((ph->p_vaddr + ph->p_filesz + GLRO(dl_pagesize) - 1) 
        & ~(GLRO(dl_pagesize) - 1)); 
    ... 
    if (nloadcmds > 1 && c[-1].mapend != c->mapstart) 
     has_holes = true; 
    ... 
    } 
    ... 
    if (has_holes) 
     __mprotect ((caddr_t) (l->l_addr + c->mapend), 
      loadcmds[nloadcmds - 1].mapstart - c->mapend, PROT_NONE); 
    ... 
} 

Trong đoạn mã trên l_phnum sử dụng trong for tuyên bố giữ số lượng các mục trong phần đầu chương trình ELF. Lý tưởng nhất cho mỗi lần lặp lại, mỗi phân đoạn mục nhập được ánh xạ. Khi PT_LOAD trường hợp phân đoạn lần truy cập lần đầu tiên, về cơ bản là phần .text hoặc .rodata được lấy mmapped (1 mmap trong strace) và đoạn thứ hai PT_LOAD đại diện cho .data phần được ánh xạ (số 02 mmap trong strace). Trước khi phân đoạn thứ hai PT_LOAD được ánh xạ, mapstartmapend được giữ nguyên, hãy tham khảo phần bắt đầu và kết thúc của phần văn bản. Trong lần lặp tiếp theo PT_LOAD nếu đoạn trước mapend không bằng với đoạn (.data) hiện tại mapstart thì chúng là lỗ giữa hai đoạn PT_LOAD (có nghĩa là khoảng cách giữa các đoạn .text.data). Vì vậy, nếu chúng là một lỗ hổng giữa các vùng bộ nhớ với các điều khoản null, trình nạp sẽ bảo vệ (gọi là mprotect trong strace) hoặc làm cho nó không thể truy cập được. Vùng được bảo vệ cho quy trình 64 bit và 32 bit là Chỉ Vs trang tương ứng thêm vào bộ nhớ lớn cho thư viện 64 bit.

Proof cho khu vực không thể tiếp cận 64bit: objdump cho libc.so dưới đây cung cấp cho chúng tôi một số địa chỉ ảo (VA) thống kê được roundoff một cách thích hợp như sau,

    PT_LOAD(1)    PT_LOAD(2)      
mapstart VA 0x0000000000000000  0x00000000003b4000 
mapend VA 0x00000000001b5000  0x00000000003A0000 

đây PT_LOAD(1) mapend (0x00000000001b5000) không bằng PT_LOAD(2) mapstart (0x00000000003b4000) dẫn đến lỗ bộ nhớ 0x00000000001FF000 (Trong số thập phân 2093056B).

[email protected]:~/32_64bit$objdump -x -s -d -D /lib/x86_64-linux-gnu/libc.so.6 
Program Header: 
... 
    LOAD off 0x0000000000000000 vaddr 0x0000000000000000 paddr 0x0000000000000000 align 2**21 
     filesz 0x00000000001b411c memsz 0x00000000001b411c flags r-x 
    LOAD off 0x00000000001b4700 vaddr 0x00000000003b4700 paddr 0x00000000003b4700 align 2**21 
     filesz 0x0000000000005160 memsz 0x0000000000009dd8 flags rw- 
... 

Mở văn bản 64bit đầu mất một đại diện cao giảng dạy byte so với 32bit. Tương tự kích thước con trỏ trên 64bit là 8B thêm 4 byte khác.Ngoài ra cấu trúc dữ liệu liên kết là một 8B liên kết trong 64bit làm cho khu vực bản đồ lớn hơn.

Simple size lệnh trên mã nhị phân có thể thấy sự khác biệt giữa 32/64 bit vùng bộ nhớ chương trình như dưới đây,

[email protected]:~/32_64bit$ ls -lrt 
total 10368 
-rwxrwxrwx 1 esunboj ei 5758776 Oct 10 11:35 crash-x86-64 
-rwxrwxrwx 1 esunboj ei 4855676 Oct 10 11:36 crash 
[email protected]:~/32_64bit$ size crash 
    text data  bss  dec  hex filename 
4771286 82468 308704 5162458 4ec5da crash 
[email protected]:~/32_64bit$ size crash-x86-64 
    text data  bss  dec  hex filename 
5634861 121164 1623728 7379753 709b29 crash-x86-64 
8

Sự khác biệt VSIZE lớn xuất phát từ cách ánh xạ PROT_NONE (chế độ "--- p") của các thư viện được chia sẻ được thực hiện trong trường hợp của các phiên bản 32-bit và 64-bit.

Đây chính xác là các ánh xạ mà bạn phát hiện khi tạo ra sự khác biệt.

Nói chung cho mỗi thư viện chia sẻ nạp chúng ta sẽ có bốn ánh xạ:

3b7cc00000-3b7cd86000 r-xp 00000000 fd:00 661350   /lib64/libc-2.12.so 
3b7cd86000-3b7cf86000 ---p 00186000 fd:00 661350   /lib64/libc-2.12.so 
3b7cf86000-3b7cf8a000 r--p 00186000 fd:00 661350   /lib64/libc-2.12.so 
3b7cf8a000-3b7cf8b000 rw-p 0018a000 fd:00 661350   /lib64/libc-2.12.so 

Đầu tiên là đoạn mã với các điều khoản thực thi, thứ hai các PROT_NONE (chế độ ---) lập bản đồ (trang có thể không được truy cập) và hai phần cuối cùng của phân đoạn dữ liệu (chỉ đọc một phần và đọc ghi).

PROT_NONE có kích thước MAXPAGESIZE và do đó nó được tạo khác nhau trong các phiên bản 32 bit và 64 bit. Trong trường hợp của phiên bản 32-bit, nó có kích thước 4KB (MAXPAGESIZE cho i386) và trong trường hợp của phiên bản 64 bit 2MB (tiêu chuẩn MAXPAGESIZE cho các hệ thống x86_64).

Cần lưu ý rằng bộ nhớ này không được thực tế tiêu thụ (nó chỉ tiêu thụ địa chỉ của không gian địa chỉ) như đã nêu ở đây:

http://www.greenend.org.uk/rjk/tech/dataseg.html

"thêm này không chi phí bạn bất cứ RAM hoặc hoán đổi không gian, chỉ cần địa chỉ không gian trong mỗi quá trình, đó là nguồn cung dồi dào trên nền tảng 64-bit. Lý do cơ bản là làm với việc giữ các thư viện có thể chia sẻ một cách hiệu quả, nhưng việc triển khai thực hiện hơi lạ. "

Chỉ cần một thủ thuật cuối cùng, tôi tìm thấy dễ dàng hơn để kiểm tra ánh xạ bộ nhớ bằng cách sử dụng tiện ích pmap hơn phân tích các tập tin bản đồ và tạo ra một đầu ra đơn giản hơn để đọc:

Đối với thông tin cơ bản:

pmap <PID> 

Đối với mở rộng thông tin:

pmap -x <PID> 
+0

câu trả lời hay, bộ nhớ nhọn được tải theo trang. Chỉ cần thêm rằng pmap được thực hiện bằng cách đọc "/ proc//maps" ... :-) – tristan

+0

Chỉ cần sửa đổi câu để làm rõ cách thức hoạt động của pmap. Điểm tốt nhất về pmap là nó phân tích cú pháp tệp bản đồ và tính toán kích thước của các vùng cho bạn. – alcachi

Các vấn đề liên quan