Ví dụ runnable tối thiểu
Hệ thống brk () gọi để làm gì?
Yêu cầu kernel cho phép bạn đọc và ghi vào một đoạn bộ nhớ liền kề được gọi là heap.
Nếu bạn không hỏi, nó có thể khiến bạn bối rối.
Không có brk
:
#define _GNU_SOURCE
#include <unistd.h>
int main(void) {
/* Get the first address beyond the end of the heap. */
void *b = sbrk(0);
int *p = (int *)b;
/* May segfault because it is outside of the heap. */
*p = 1;
return 0;
}
Với brk
:
#define _GNU_SOURCE
#include <assert.h>
#include <unistd.h>
int main(void) {
void *b = sbrk(0);
int *p = (int *)b;
/* Move it 2 ints forward */
brk(p + 2);
/* Use the ints. */
*p = 1;
*(p + 1) = 2;
assert(*p == 1);
assert(*(p + 1) == 2);
/* Deallocate back. */
brk(b);
return 0;
}
GitHub ngược dòng .
Ở trên có thể không có trang mới và không segfault ngay cả khi không có brk
, vì vậy đây là phiên bản tích cực hơn phân bổ 16MiB và rất có khả năng segfault mà không có brk
:
#define _GNU_SOURCE
#include <assert.h>
#include <unistd.h>
int main(void) {
void *b;
char *p, *end;
b = sbrk(0);
p = (char *)b;
end = p + 0x1000000;
brk(end);
while (p < end) {
*(p++) = 1;
}
brk(b);
return 0;
}
Đã thử nghiệm trên Ubuntu 18.04.
Trực quan hóa không gian địa chỉ ảo
Trước brk
:
+------+ <-- Heap Start == Heap End
Sau brk(p + 2)
:
+------+ <-- Heap Start + 2 * sizof(int) == Heap End
| |
| You can now write your ints
| in this memory area.
| |
+------+ <-- Heap Start
Sau brk(b)
:
+------+ <-- Heap Start == Heap End
Để hiểu rõ hơn về không gian địa chỉ, bạn nên làm quen với phân trang: Làm thế nào để phân trang x86 hoạt động? .
Tại sao chúng ta cần cả hai brk
và sbrk
?
brk
tất nhiên có thể được thực hiện với sbrk
+ tính toán bù, cả hai tồn tại chỉ để thuận tiện.
Trong phần phụ trợ, nhân Linux v5.0 có một lệnh gọi hệ thống duy nhất brk
được sử dụng để thực hiện cả hai: https://github.com/torvalds/linux/blob/v5.0/arch/x86/entry/syscalls/syscall_64. tbl # L23
12 common brk __x64_sys_brk
Là brk
POSIX?
brk
đã từng là POSIX, nhưng nó đã bị xóa trong POSIX 2001, do đó cần phải _GNU_SOURCE
truy cập trình bao bọc glibc.
Việc loại bỏ có thể là do phần giới thiệu mmap
, đây là một superset cho phép nhiều phạm vi được phân bổ và nhiều tùy chọn phân bổ hơn.
Tôi nghĩ rằng không có trường hợp hợp lệ mà bạn nên sử dụng brk
thay vì malloc
hoặc mmap
ngày nay.
brk
đấu với malloc
brk
là một khả năng cũ của việc thực hiện malloc
.
mmap
là cơ chế mạnh mẽ hơn mới hơn mà có khả năng tất cả các hệ thống POSIX hiện đang sử dụng để thực hiện malloc
. Dưới đây là một ví dụ phân bổ bộ nhớ runnable tối thiểummap
.
Tôi có thể trộn brk
và malloc không?
Nếu bạn malloc
được thực hiện cùng brk
, tôi không biết làm thế nào điều đó có thể không làm nổ tung mọi thứ, vì brk
chỉ quản lý một phạm vi bộ nhớ duy nhất.
Tuy nhiên tôi không thể tìm thấy bất cứ điều gì về nó trên các tài liệu glibc, ví dụ:
Mọi thứ có thể sẽ chỉ hoạt động ở đó tôi cho rằng vì mmap
có khả năng được sử dụng cho malloc
.
Xem thêm:
Thêm thông tin
Trong nội bộ, kernel quyết định xem tiến trình có thể có nhiều bộ nhớ đó không và đánh dấu các trang bộ nhớ cho việc sử dụng đó.
Điều này giải thích cách ngăn xếp so sánh với heap: Chức năng của các lệnh đẩy / pop được sử dụng trên các thanh ghi trong cụm x86 là gì?