1. 添加系统调用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// syscall.h
#define SYS_mmap 26
#define SYS_munmap 27
#define SYS_sigaction 28

// syscall.c
extern int sys_mmap(void);
extern int sys_munmap(void);
extern int sys_sigaction(void);

[SYS_mmap] "sys_mmap",
[SYS_munmap] "sys_munmap",
[SYS_sigaction] "sys_sigaction",

[SYS_mmap] sys_mmap,
[SYS_munmap] sys_munmap,
[SYS_sigaction] sys_sigaction,

// user.h
void *mmap(void *addr, uint length, int prot, int flags, int fd, uint offset);
int munmap(void *addr, uint length);
int sigaction(int signum, void *act, void *oldact);

// usys.S
SYSCALL(mmap)
SYSCALL(munmap)
SYSCALL(sigaction)
  1. 实现mmap系统调用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
// proc.h:新增
struct mmap_region {
void *addr;
uint len;
int prot;
int flags;
int valid;
char *faulted_pages; // bitmap 记录哪些页已经分配
};
// 在struct proc中添加
struct {
void *addr;
uint len;
int prot;
int flags;
int valid;
char *faulted_pages;
} mmap_regions[16];
void *sigsegv_handler;
// sysproc.c
#define MAX_MMAP_REGIONS 16

int
sys_mmap(void)
{
void *addr;
uint length;
int prot, flags, fd;
uint offset;

if(argptr(0, (char**)&addr, sizeof(addr)) < 0)
return -1;
if(argint(1, (int*)&length) < 0)
return -1;
if(argint(2, &prot) < 0)
return -1;
if(argint(3, &flags) < 0)
return -1;
if(argint(4, &fd) < 0)
return -1;
if(argint(5, (int*)&offset) < 0)
return -1;

struct proc *p = myproc();

// 只支持匿名映射(MAP_ANONYMOUS)
if(!(flags & 0x20)) { // MAP_ANONYMOUS 通常为 0x20
cprintf("mmap: only MAP_ANONYMOUS supported\n");
return -1;
}

// 查找空闲的 mmap 区域槽位
for(int i = 0; i < MAX_MMAP_REGIONS; i++) {
if(p->mmap_regions[i].valid == 0) {
// 如果没有指定地址,在堆之后分配
if(addr == 0) {
addr = (void*)PGROUNDUP(p->sz);
}

// 确保地址页对齐
addr = (void*)PGROUNDDOWN((uint)addr);

p->mmap_regions[i].addr = addr;
p->mmap_regions[i].len = length;
p->mmap_regions[i].prot = prot;
p->mmap_regions[i].flags = flags;
p->mmap_regions[i].valid = 1;
p->mmap_regions[i].faulted_pages = 0;

// 扩展进程的虚拟地址空间(如果需要)
uint end = (uint)addr + length;
if(end > p->sz) {
p->sz = end;
}

cprintf("mmap: region %d at 0x%x, size=%d\n", i, addr, length);
return (int)addr;
}
}

cprintf("mmap: too many regions\n");
return -1;
}

int
sys_munmap(void)
{
void *addr;
uint length;

if(argptr(0, (char**)&addr, sizeof(addr)) < 0)
return -1;
if(argint(1, (int*)&length) < 0)
return -1;

struct proc *p = myproc();

// 查找对应的 mmap 区域
for(int i = 0; i < MAX_MMAP_REGIONS; i++) {
if(p->mmap_regions[i].valid && p->mmap_regions[i].addr == addr) {
// 释放 faulted_pages bitmap(如果已分配)
if(p->mmap_regions[i].faulted_pages != 0) {
kfree(p->mmap_regions[i].faulted_pages);
}

// 标记区域为无效
p->mmap_regions[i].valid = 0;

cprintf("munmap: region %d at 0x%x unmapped\n", i, addr);
return 0;
}
}

cprintf("munmap: region not found at 0x%x\n", addr);
return -1;
}

// 简单的 sigaction 实现
int
sys_sigaction(void)
{
int signum;
void *handler;
void *oldact;

if(argint(0, &signum) < 0)
return -1;
if(argptr(1, (char**)&handler, sizeof(handler)) < 0)
return -1;
if(argptr(2, (char**)&oldact, sizeof(oldact)) < 0)
return -1;

struct proc *p = myproc();

// 只处理 SIGSEGV
if(signum == 11) { // SIGSEGV
if(handler != 0) {
p->sigsegv_handler = handler;
}
if(oldact != 0) {
*(void**)oldact = p->sigsegv_handler;
}
}

return 0;
}
  1. 添加mmap页面故障处理(trap.c)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
/* 上面的代码
if(p == 0 || (tf->cs & 3) == 0){
cprintf("kernel page fault at eip 0x%x, va 0x%x\n", tf->eip, rcr2());
panic("trap");
}

uint va = rcr2();
uint addr = PGROUNDDOWN(va); // 对齐到页边界
// 修改
/*原先的:
// 检查地址是否在进程的有效范围内,地址应该小于 sz 但大于栈顶
if(addr >= p->sz || addr < PGROUNDDOWN(p->tf->esp)) {
// 无效地址:超出堆顶或在栈下方
cprintf("pid %d %s: invalid page fault at va 0x%x (sz=0x%x, esp=0x%x)\n",
p->pid, p->name, va, p->sz, p->tf->esp);
p->killed = 1;
break;
}

// 分配一页物理内存
char *mem = kalloc();
if(mem == 0){
cprintf("lazy allocation: out of memory for va 0x%x\n", va);
p->killed = 1;
break;
}

// 清零新分配的页
memset(mem, 0, PGSIZE);

// 映射到用户虚拟地址空间
if(mappages(p->pgdir, (void*)addr, PGSIZE, V2P(mem), PTE_W|PTE_U) < 0){
cprintf("lazy allocation: mappages failed for va 0x%x\n", va);
kfree(mem);
p->killed = 1;
break;
}

return;
*/
// 更改为:
if(addr < p->sz && addr >= PGROUNDDOWN(p->tf->esp)) {
// 分配一页物理内存
char *mem = kalloc();
if(mem == 0){
cprintf("lazy allocation: out of memory for va 0x%x\n", va);
p->killed = 1;
break;
}

// 清零新分配的页
memset(mem, 0, PGSIZE);

// 映射到用户虚拟地址空间
if(mappages(p->pgdir, (void*)addr, PGSIZE, V2P(mem), PTE_W|PTE_U) < 0){
cprintf("lazy allocation: mappages failed for va 0x%x\n", va);
kfree(mem);
p->killed = 1;
break;
}

return;
}

// 遍历进程的 mmap 区域
for(int i = 0; i < 16; i++) {
if(p->mmap_regions[i].valid) {
uint start = (uint)p->mmap_regions[i].addr;
uint end = start + p->mmap_regions[i].len;

// 检查故障地址是否在这个 mmap 区域内
if(addr >= start && addr < end) {
// 计算页索引
int page_idx = (addr - start) / PGSIZE;

// 检查这个页是否已经分配
int page_mapped = 0;
if(p->mmap_regions[i].faulted_pages != 0) {
int byte_idx = page_idx / 8;
int bit_idx = page_idx % 8;
page_mapped = (p->mmap_regions[i].faulted_pages[byte_idx] >> bit_idx) & 1;
}

// 如果页面尚未分配,进行分配
if(!page_mapped) {
// 分配一页物理内存
char *mem = kalloc();
if(mem == 0){
cprintf("mmap: out of memory at va 0x%x\n", va);
p->killed = 1;
break;
}

// 清零新分配的页
memset(mem, 0, PGSIZE);

// 映射到用户虚拟地址空间
if(mappages(p->pgdir, (void*)addr, PGSIZE, V2P(mem),
PTE_W|PTE_U|PTE_P) < 0){
kfree(mem);
p->killed = 1;
break;
}

// 初始化 faulted_pages 位图(如果还没分配)
if(p->mmap_regions[i].faulted_pages == 0){
p->mmap_regions[i].faulted_pages = kalloc();
if(p->mmap_regions[i].faulted_pages){
memset(p->mmap_regions[i].faulted_pages, 0, PGSIZE);
}
}

// 标记该页面已分配
if(p->mmap_regions[i].faulted_pages){
int byte_idx = page_idx / 8;
int bit_idx = page_idx % 8;
p->mmap_regions[i].faulted_pages[byte_idx] |= (1 << bit_idx);
}

return;
}
}
}
}

// 既不是堆区域,也不是 mmap 区域,杀死进程
cprintf("pid %d %s: invalid page fault at va 0x%x (sz=0x%x, esp=0x%x)\n",
p->pid, p->name, va, p->sz, p->tf->esp);
p->killed = 1;
break;
  1. 创建mmaptest.c测试程序
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#include "types.h"
#include "stat.h"
#include "user.h"
#include "fcntl.h"

#define PGSIZE 4096
#define NUM_ELEMENTS 10000

int main()
{
int *mapping;
int total_size = NUM_ELEMENTS * sizeof(int);

printf(1, "Testing mmap with %d elements (%d bytes)\n",
NUM_ELEMENTS, total_size);

// 使用 mmap 分配内存(MAP_ANONYMOUS = 0x20)
mapping = mmap(0, total_size, 0, 0x20, 0, 0);

if(mapping == (void*)-1) {
printf(2, "mmap failed\n");
exit();
}

printf(1, "Mapping at 0x%x\n", mapping);

// 写入数据(会触发页面故障)
printf(1, "Writing data...\n");
for(int i = 0; i < NUM_ELEMENTS; i++) {
mapping[i] = i * 2;
}

// 验证数据
printf(1, "Validating...\n");
for(int i = 0; i < NUM_ELEMENTS; i++) {
if(mapping[i] != i * 2) {
printf(2, "Validation failed at index %d: expected %d, got %d\n",
i, i * 2, mapping[i]);
exit();
}
}

printf(1, "All tests passed!\n");

// 清理
munmap(mapping, total_size);

exit();
}
  1. 修改Makefile
1
2
# 在_mkdir\后添加
_mmaptest\
  1. 编译测试
1
2
3
4
make clean
make
make CPUS=1 qemu-nox
mmaptest