2015-05-30 12 views
9

c:malloc() - Liệu nó sử dụng brk() hoặc mmap() code

// program break mechanism 
// TLPI exercise 7-1 

#include <stdio.h> 
#include <stdlib.h> 

void program_break_test() { 
    printf("%10p\n", sbrk(0)); 

    char *bl = malloc(1024 * 1024); 
    printf("%x\n", sbrk(0)); 

    free(bl); 
    printf("%x\n", sbrk(0)); 

} 

int main(int argc, char **argv) { 
    program_break_test(); 
    return 0; 
} 

Khi biên dịch đoạn mã sau:

printf("%10p\n", sbrk(0)); 

tôi nhận được cảnh báo tip:

format ‘%p’ expects argument of type ‘void *’, but argument 2 has type ‘int’

Câu hỏi 1: Tại sao lại như vậy?


Và sau khi tôi malloc(1024 * 1024), có vẻ như ngắt chương trình không thay đổi.

Đây là kết quả:

9b12000 
9b12000 
9b12000 

Câu hỏi 2: Liệu quá trình cấp phát bộ nhớ trên heap khi khởi động để sử dụng trong tương lai? Hoặc trình biên dịch thay đổi thời điểm để phân bổ? Nếu không, tại sao?


[cập nhật] Tóm tắt: brk() hoặc mmap()

Sau khi xem xét TLPI và kiểm tra trang người đàn ông (với sự giúp đỡ từ tác giả của TLPI), bây giờ tôi hiểu làm thế nào malloc() quyết định sử dụng brk() hoặc mmap(), như sau:

mallopt() có thể thiết lập các thông số để kiểm soát hành vi của malloc(), và có một tham số có tên M_MMAP_THRESHOLD, nói chung:

  • Nếu bộ nhớ được yêu cầu nhỏ hơn, brk() sẽ được sử dụng;
  • Nếu bộ nhớ được yêu cầu lớn hơn hoặc bằng, mmap() sẽ được sử dụng;

Giá trị mặc định của tham số là 128kb (trên hệ thống của tôi), nhưng trong chương trình thử nghiệm của tôi, tôi sử dụng 1Mb, vì vậy mmap() được chọn, khi tôi đã thay đổi bộ nhớ yêu cầu 32kb, tôi thấy brk() sẽ được sử dụng.

Sách được đề cập trong trang TLPI 147 và 1035, nhưng tôi không đọc kỹ phần đó.

Thông tin chi tiết về tham số có thể được tìm thấy trong trang hướng dẫn cho mallopt().

+3

'#include '? – JS1

+0

@ JS1 Có, giải quyết được vấn đề, bạn có thể đưa ra một giải thích, tôi mới để lập trình linux ... –

+2

Bạn cần mẫu thử nghiệm cho 'sbrk()' trong 'unistd.h'. Nếu không có một nguyên mẫu, trình biên dịch giả định rằng các hàm chưa biết trả về 'int'. – JS1

Trả lời

11

Nếu chúng ta thay đổi chương trình để xem nơi malloc 'd bộ nhớ là:

#include <unistd.h> 
#include <stdio.h> 
#include <stdlib.h> 

void program_break_test() { 
    printf("%10p\n", sbrk(0)); 

    char *bl = malloc(1024 * 1024); 
    printf("%10p\n", sbrk(0)); 
    printf("malloc'd at: %10p\n", bl); 

    free(bl); 
    printf("%10p\n", sbrk(0)); 

} 

int main(int argc, char **argv) { 
    program_break_test(); 
    return 0; 
} 

Đó là lẽ rõ ràng hơn một chút rằng sbrk sẽ không thay đổi. Bộ nhớ được cung cấp cho chúng tôi bởi malloc đang được ánh xạ vào một vị trí cực kỳ khác.

Bạn cũng có thể sử dụng strace trên Linux để xem những cuộc gọi hệ thống nào được thực hiện và tìm ra rằng malloc đang sử dụng mmap để thực hiện phân bổ.

+0

Tôi thấy rằng có một 'THRESHOLD' để kiểm soát việc sử dụng' brk() 'hay' mmap() ', tôi đã cập nhật điều đó trong câu hỏi. –

2

malloc không giới hạn sử dụng sbrk để cấp phát bộ nhớ. Ví dụ: có thể sử dụng mmap để ánh xạ khối bộ nhớ MAP_ANONYMOUS lớn; thông thường mmap sẽ chỉ định một địa chỉ ảo cách xa phân đoạn dữ liệu.

Có những khả năng khác nữa. Cụ thể, mmap, là một phần cốt lõi của thư viện chuẩn, không bị giới hạn bởi các chức năng thư viện chuẩn; nó có thể sử dụng các giao diện cụ thể cho hệ điều hành.

1

dạng '% p' ​​đối số của loại 'void *', nhưng lập luận 2 có kiểu 'int'

trả lời cho câu hỏi 1: Trình biên dịch là nói cho bạn đối số phải là một void * nhưng thay vào đó, bạn đang cung cấp một số int. Điều đó phải rõ ràng nếu bạn dành năm giây để đọc và hiểu lỗi. Có phần nào mà bạn không hiểu không? Nếu vậy, hãy hỏi một câu hỏi chi tiết hơn về điều đó mà lẫn lộn bạn, chứ không phải là "Tại sao vậy?" ...

Một cảnh báo tương tự sẽ xảy ra cho printf("%x\n", sbrk(0));, như theo the manual, %x dự kiến ​​sẽ tương ứng với một unsigned tham số. Ngoài ra, theo hướng dẫn:

Nếu bất kỳ đối số nào không phải là loại chính xác cho đặc điểm chuyển đổi tương ứng, hành vi không xác định.

Thông thường, chúng ta nên cố gắng viết các chương trình hoạt động theo cách tương tự trên bất kỳ hệ thống nào. Để làm được điều đó, chúng ta cần phải có một bộ quy tắc được thiết lập. Do đó, một cảnh báo được cung cấp để cho bạn biết rằng bạn đang vi phạm các quy tắc và gọi hành vi không xác định. Mặc dù hành vi không xác định của bạn, mã của bạn có thể hoạt động như bạn mong đợi ở trên hệ thống của mình, tại thời điểm này ... Tuy nhiên, điều này không nên dựa vào vì tại một thời điểm nào đó trong tương lai máy tính của bạn có thể tìm nạp bản cập nhật khiến mã của bạn phá vỡ theo các cách tinh tế nhưng tàn phá hoặc có thể không hoạt động trên các máy tính khác ... hoặc có thể chỉ chọn có thời điểm đó trong tháng.


Câu trả lời cho câu hỏi 2, 3 và 4:

Liệu quá trình cấp phát bộ nhớ trên heap khi khởi động để sử dụng trong tương lai?

Không có yêu cầu 'heap' tồn tại trong các quy tắc của tiêu chuẩn C, vì vậy đó là "không" ... Ít nhất, không cho đến khi bạn cho chúng tôi biết bạn đang sử dụng thư viện chuẩn/biên dịch nào.

Hoặc trình biên dịch thay đổi điểm thời gian để phân bổ?

Có thể. Trình biên dịch được phép thực hiện tối ưu hóa thậm chí có thể xóa phân bổ của bạn, miễn là chúng có thể suy ra rằng an toàn để làm như vậy (ví dụ: hành vi quan sát được không thay đổi).

Nếu không, tại sao?

Câu hỏi hay.

Tại sao chúng tôi có quy tắc được tự do cân nhắc điều gì đó hành vi không xác định và cho phép ứng dụng chạy, ngay cả khi gặp nguy hiểm của lập trình viên? Tối ưu hóa.

Tại sao bộ nhớ bạn phân bổ lại nằm trên heap hoặc khác? Tại sao bạn cần quan tâm? Vì vậy, miễn là bộ nhớ của bạn được phân bổ, phải không? Miễn là bạn có thể sử dụng nó, và nó khá nhanh. Tối ưu hóa.

Tại sao trình biên dịch thực hiện tối ưu hóa? Tôi sẽ để lại cái đó cho bạn để trả lời;)

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