0x00000000~0xFFFFFFFF,也就是4G
0 ~ 3G-1是归用户所使用,称为用户地址空间3G ~ 4G-1是归内核使用,称为内核地址空间0x0000 0000 0000 0000 ~ 0x0000 FFFF FFFF FFFF是用户地址空间0xFFFF 0000 0000 0000 ~ 0xFFFF FFFF FFFF FFFF是内核地址空间以下是一个32位系统存储数据的示例图

从上图中可以看到每个进程都有自己独立的虚拟地址池,它们之间相互隔离,不会相互干扰或冲突,当给内核操作的虚拟地址没有在映射关系表中找到对应关系时,就会出现段错误。

代码如下:
// 虚拟地址空间布局
#include
#include
const int const_global = 1; // 常全局变量
int init_global = 2; // 初始化全局变量
int uninit_global; // 未初始化全局变量
int main(int argc, char *argv[], char *envp[])
{
static const int const_static = 3; // 常静态变量
static int init_static = 4; // 初始化静态变量
static int uninit_static; // 未初始化静态变量
const int const_local = 5; // 常局部变量
int local; // 局部变量
char *string = "hello"; // 字面值常量
int *heap = malloc(sizeof(int)); // 堆变量
printf("----------参数和环境----------\n");
printf(" 命令行参数: %p\n", argv);
printf(" 环境变量: %p\n", envp);
printf("------------栈区-------------\n");
printf(" 常局部变量: %p\n", &const_local);
printf(" 局部变量: %p\n", &local);
printf("------------堆区-------------\n");
printf(" 堆变量: %p\n", heap);
printf("------------BSS区-------------\n");
printf(" 未初始化全局变量: %p\n", &uninit_global);
printf(" 未初始化静态变量: %p\n", &uninit_static);
printf("------------数据区-------------\n");
printf(" 初始化全局变量: %p\n", &init_global);
printf(" 初始化静态变量: %p\n", &init_static);
printf("------------代码区-------------\n");
printf(" 常全局变量: %p\n", &const_global);
printf(" 常静态变量: %p\n", &const_static);
printf(" 字面值常量: %p\n", string);
printf(" 函数: %p\n", main);
printf("-------------------------------\n");
return 0;
}
mmap函数手动建立虚拟地址和物理地址之间的映射关系。/usr/includemman.h这个头文件:#include mmap函数
void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset);
start:映射区虚拟内存的起始地址,NULL系统自动选定后返回。length:映射区字节数,自动按页圆整,4k为一页。prot:映射区操作权限,可取以下值,多选使用|隔开
PROT_READ:映射区可读PROT_WRITE:映射区可写PROT_EXEC:映射区可执行PROT_NONE:映射区不可访问flags:映射标志,可取以下值,多选使用|隔开
MAP_ANONYMOUS:匿名映射,将虚拟内存映射到物理内存中而非文件,使用这个就可以忽略df和offset参数,将这两个参数设为0即可MAP_PRIVATE:对映射区的写操作只反映到缓冲区并不会真正写入文件,与MAP_SHARED二选一,不能同时存在MAP_SHARED:对映射区的写操作直接反映到文件中MAP_DENYWRITE:拒绝其他对文件的写操作MAP_FIXED:若在start上无法创建映射,则失败(无此标志系统会自动调整)fd:文件描述符,flags使用MAP_ANONYMOUS时,这里填0即可offset:文件偏移量,自动按页(4K)对齐,flags使用MAP_ANONYMOUS时,这里填0即可MAP_FAILED(-1)munmap函数
int munmap(void *start, size_t length);
start:映射区虚拟内存的起始地址length:映射区字节数,自动按页圆整,4k为一页munmap允许对映射区的一部分映射,但必须按页处理使用示例:
#include
#include
#include
int main(void)
{
// 1. 建立映射: 返回值是void*类型,这里使用的是char*,内部会自动强转的
char *start = mmap(NULL, 8192, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
if(start == MAP_FAILED)
{
perror("mmap");
return -1;
}
// 2. 写入数据
strcpy(start, "哈哈");
printf("输出: %s\n", start);
// 3. 解除一部分映射
if(munmap(start, 4096) == -1)
{
perror("munmap");
return -1;
}
// strcpy(start, "呵呵"); // 这里测试使用了已经解除的虚拟地址,出现了 段错误 (核心已转储)
// 4. 在剩下的那一部分中写入数据
start += 4096; // 首先先将start往上移
strcpy(start, "嘿嘿");
printf("输出: %s\n", start);
// 5. 解除
if(munmap(start, 4096) == -1)
{
perror("munmap");
return -1;
}
return 0;
}