brk() 系统调用
brk() 是 Linux 系统调用,用于 调整进程的堆(heap)大小。它用于管理进程的 数据段(Data Segment),从而影响 malloc()、free() 的底层实现。
1. brk() 的作用
- 增加或减少堆的大小
- 用于
sbrk(),它是malloc()申请堆内存的底层机制 - 影响
malloc()、free(),但不适用于mmap()分配的内存
示例
#include <unistd.h>
#include <stdio.h>
int main() {
void *heap1 = sbrk(0); // 获取当前堆顶
printf("Heap start: %p\n", heap1);
brk(heap1 + 4096); // 增加 4KB
void *heap2 = sbrk(0);
printf("Heap after brk(): %p\n", heap2);
return 0;
}
输出
Heap start: 0x55aebc756000
Heap after brk(): 0x55aebc757000
✅ brk() 增加了 4KB 的堆空间。
2. sbrk() 和 brk() 的关系
sbrk()调用brk(),以相对方式调整堆。brk(new_end)直接设置堆顶,而sbrk(size)相对移动堆顶。
示例
#include <unistd.h>
#include <stdio.h>
int main() {
void *heap1 = sbrk(0);
printf("Heap start: %p\n", heap1);
sbrk(4096); // 增加 4KB
void *heap2 = sbrk(0);
printf("Heap after sbrk(4096): %p\n", heap2);
sbrk(-4096); // 归还 4KB
void *heap3 = sbrk(0);
printf("Heap after sbrk(-4096): %p\n", heap3);
return 0;
}
输出
Heap start: 0x5634b2712000
Heap after sbrk(4096): 0x5634b2713000
Heap after sbrk(-4096): 0x5634b2712000
✅ sbrk(4096) 增加了 4KB,sbrk(-4096) 归还 4KB。
3. brk() 在 malloc() 中的作用
malloc() 可能使用 brk() 来分配内存:
- 检查
free list(空闲块链表),如果没有足够的空闲块: - 调用
sbrk(size)或brk(new_end)增加堆空间:void* malloc(size_t size) { if (size == 0) return NULL; void* new_heap = sbrk(size); if (new_heap == (void*)-1) return NULL; // 分配失败 return new_heap; }
4. brk() vs mmap()
| 方法 | 适用场景 | 分配方式 | 是否可释放 |
|---|---|---|---|
brk() / sbrk() |
小块分配(≤128KB) | 调整堆大小 | ❌ 只能扩展,不能归还 |
mmap() |
大块分配(>128KB) | 匿名映射内存 | ✅ munmap() 释放 |
示例
void* bigAlloc = mmap(NULL, 1024 * 1024, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
mmap()适用于大块内存分配,而brk()适用于 小块动态分配。
5. 为什么 free() 不归还内存?
free()将内存块加入free list,但不会调用brk(-size)。- 只有当释放的内存块正好在堆顶,
free()可能调用brk()归还内存。
示例
void* p1 = malloc(1000);
void* p2 = malloc(1000);
free(p2); // 可能归还
free(p1); // 不能归还,因 p2 仍在堆中
6. 总结
brk()直接设置堆顶,sbrk()以相对方式移动堆顶。malloc()可能使用brk()扩展堆,但释放时不会立刻归还。mmap()适用于大块分配,而brk()适用于小块管理。
mmap() 系统调用
mmap() 是 Linux 提供的一种 内存映射 机制,它可以 直接将文件或匿名内存映射到进程地址空间,用于 高效的大块内存分配。
1. mmap() 的作用
- 文件映射:将 文件 映射到进程的虚拟地址空间,可用于 文件 I/O 加速。
- 匿名映射:分配一块 无需关联文件 的内存,类似
malloc(),但 支持munmap()释放。 - 共享内存:多个进程可使用
mmap()共享一块内存,实现 进程间通信(IPC)。 - 大块内存分配:
malloc()可能使用mmap()为 大于 128KB 的分配 申请内存。
2. mmap() 的函数原型
#include <sys/mman.h>
void* mmap(void* addr, size_t length, int prot, int flags, int fd, off_t offset);
| 参数 | 作用 |
|---|---|
addr |
建议映射的地址(通常填 NULL,由系统分配) |
length |
映射的大小(字节数) |
prot |
保护权限,如 PROT_READ(可读)、PROT_WRITE(可写) |
flags |
控制标志,如 MAP_PRIVATE(私有映射)、MAP_SHARED(共享映射) |
fd |
文件描述符(-1 表示匿名映射) |
offset |
文件偏移量(fd=-1 时填 0) |
返回值:
- 成功:返回 映射的起始地址
- 失败:返回
MAP_FAILED((void*)-1)
3. mmap() 用于匿名内存分配
mmap() 可用于代替 malloc() 申请内存,并且可以 释放,而 brk() 只能扩展堆,无法释放。
示例:使用 mmap() 申请 1MB 内存
#include <sys/mman.h>
#include <stdio.h>
#include <unistd.h>
int main() {
size_t size = 1024 * 1024; // 1MB
void *ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (ptr == MAP_FAILED) {
perror("mmap failed");
return 1;
}
printf("Memory allocated at: %p\n", ptr);
// 使用 mmap 分配的内存
*(int*)ptr = 42;
printf("Value at ptr: %d\n", *(int*)ptr);
// 释放内存
munmap(ptr, size);
return 0;
}
输出
Memory allocated at: 0x7f9c3c12d000
Value at ptr: 42
✅ mmap() 成功 分配 1MB 内存,并能 用 munmap() 释放。
4. mmap() 用于文件映射
可以将 文件内容映射到内存,实现 高效文件 I/O,避免 read()/write() 系统调用开销。
示例:映射文件到内存
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/stat.h>
int main() {
int fd = open("test.txt", O_RDONLY);
if (fd == -1) {
perror("open failed");
return 1;
}
struct stat st;
fstat(fd, &st); // 获取文件大小
size_t size = st.st_size;
// 将文件映射到内存
void* ptr = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
if (ptr == MAP_FAILED) {
perror("mmap failed");
return 1;
}
printf("File content:\n%.*s\n", (int)size, (char*)ptr);
munmap(ptr, size); // 释放映射
close(fd);
return 0;
}
✅ 读取 test.txt 直接映射到内存,避免 read() 系统调用,提高 I/O 性能。
5. mmap() 的常用 flags 选项
| 标志 | 作用 |
|---|---|
MAP_ANONYMOUS |
匿名映射(不与文件关联,fd=-1 时使用) |
MAP_PRIVATE |
私有映射(修改不会影响原文件) |
MAP_SHARED |
共享映射(多个进程共享) |
MAP_FIXED |
固定映射地址(如果地址不可用,失败) |
6. mmap() vs. malloc()
| 特性 | mmap() |
malloc() |
|---|---|---|
| 适用场景 | 大块内存(>128KB) |
小块内存 |
| 底层实现 | mmap() 系统调用 |
sbrk() 调整堆 |
| 释放方式 | munmap(ptr, size) |
free(ptr) |
是否影响 brk() |
❌ 不影响堆 | ✅ 影响 brk() |
| 性能 | 适合大对象,减少碎片 | 适合小对象,管理灵活 |
示例
void* largeAlloc = mmap(NULL, 1024 * 1024, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
✅ mmap() 适合 大块分配(如 1MB)。
7. mmap() vs. brk()
| 特性 | mmap() |
brk() (sbrk()) |
|---|---|---|
| 适用场景 | 大块内存分配(如 1MB) | 小块内存管理(如 malloc()) |
| 是否可释放 | ✅ 可 munmap() 释放 |
❌ 只能扩展,不能收缩 |
| 线程安全 | ✅ 线程安全 | ⚠️ sbrk() 影响全局堆,不推荐多线程使用 |
✅ 大对象用 mmap(),小对象用 malloc()。
8. mmap() 共享内存(IPC)
多个进程可以通过 mmap() 共享一块内存,实现 进程间通信(IPC)。
示例
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
int main() {
int fd = shm_open("/my_shared_memory", O_CREAT | O_RDWR, 0666);
ftruncate(fd, 1024); // 设置共享内存大小
void* ptr = mmap(NULL, 1024, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (ptr == MAP_FAILED) {
perror("mmap failed");
return 1;
}
strcpy(ptr, "Hello from mmap!");
printf("Shared Memory Written: %s\n", (char*)ptr);
munmap(ptr, 1024);
close(fd);
shm_unlink("/my_shared_memory"); // 删除共享内存
return 0;
}
✅ 进程 A 写入共享内存,进程 B 读取共享数据。
9. 结论
mmap()用于 高效文件 I/O、匿名内存、共享内存。mmap()适合大块内存,而malloc()适合小对象。mmap()可以释放内存,而brk()不能收缩。- 高性能应用(数据库、服务器)常用
mmap()代替malloc()。