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()
。