@@ -3,6 +3,7 @@ use crate::task::{change_program_brk, exit_current_and_run_next, suspend_current
33use crate :: timer:: get_time_us;
44use crate :: mm:: { PageTable , translated_and_write, VirtAddr , frame_alloc, PTEFlags } ;
55use crate :: config:: { PAGE_SIZE , USER_SPACE_END } ;
6+ use alloc:: vec:: Vec ;
67
78#[ repr( C ) ]
89#[ derive( Debug ) ]
@@ -110,9 +111,8 @@ pub fn sys_trace(trace_request: usize, id: usize, data: usize) -> isize {
110111 }
111112}
112113
113- // YOUR JOB: Implement mmap.
114114pub fn sys_mmap ( start : usize , len : usize , prot : usize ) -> isize {
115- trace ! ( "kernel: sys_mmap NOT IMPLEMENTED YET! " ) ;
115+ trace ! ( "kernel: sys_mmap" ) ;
116116
117117 // 1. 校验start按页对齐(页大小为PAGE_SIZE,4KB为4096)
118118 if start % PAGE_SIZE != 0 {
@@ -125,7 +125,7 @@ pub fn sys_mmap(start: usize, len: usize, prot: usize) -> isize {
125125 }
126126
127127 // 3. 计算映射区间[start, end),处理len=0的特殊情况
128- let end = start + len ;
128+ let end = start. checked_add ( len) . unwrap_or ( usize :: MAX ) ;
129129 // 校验区间不超出用户空间(避免映射内核地址)
130130 if start >= USER_SPACE_END || end > USER_SPACE_END {
131131 return -1 ;
@@ -135,8 +135,8 @@ pub fn sys_mmap(start: usize, len: usize, prot: usize) -> isize {
135135 if len == 0 {
136136 return 0 ;
137137 }
138- // println!("kernel: sys_mmap from {:#x} to {:#x}", start, end);
139- // 5. 获取当前进程的页表(加锁保护,避免并发修改)
138+
139+ // 5. 获取当前进程的页表
140140 let page_table_token = current_user_token ( ) ;
141141 let mut page_table = PageTable :: from_token ( page_table_token) ;
142142
@@ -145,51 +145,49 @@ pub fn sys_mmap(start: usize, len: usize, prot: usize) -> isize {
145145 if ( prot & 0x2 ) != 0 { flags |= PTEFlags :: W ; } // prot第1位→写权限
146146 if ( prot & 0x4 ) != 0 { flags |= PTEFlags :: X ; } // prot第2位→执行权限
147147
148-
149148 // 6. 检查区间内是否已有映射(如有则返回错误)
150149 let mut va = start;
151150 while va < end {
152151 let vpn = VirtAddr :: from ( va) . floor ( ) ; // 虚拟页号(向下取整到页边界)
153- println ! ( "kernel: sys_mmap mapping vpn {:#x}" , vpn. 0 ) ;
154152
155153 if let Some ( pte) = page_table. translate ( vpn) {
156- // 核心修改: 判断页面是否有效(检查PTE的有效位V是否置位)
157- if pte. is_valid ( ) { // 关键:判断“页面是否valid”
154+ // 判断页面是否有效(检查PTE的有效位V是否置位)
155+ if pte. is_valid ( ) {
158156 return -1 ; // 存在已有效的映射页,冲突返回-1
159- } else {
160- // 页表项存在但无效,继续下一页检查
161- println ! ( "kernel: PTE_flage = {:?}" , pte. flags( ) ) ;
162157 }
163- } else {
164- // 页表项不存在,继续下一页检查
165- println ! ( "kernel: PTE_flage = None" ) ;
166158 }
167159
168- // 分配物理页(alloc_frame返回None表示内存不足)
169- let ppn = frame_alloc ( ) . unwrap ( ) . ppn ;
170-
171- // 建立虚拟页→物理页的映射(写入页表)
172- page_table. map ( vpn, ppn, flags) ;
160+ va += PAGE_SIZE ;
161+ }
173162
174- if let Some ( pte1) = page_table. translate ( vpn) {
175- // 检查mmap之后标志位是否设置正常
176- println ! ( "kernel: mmap_PTE_flage = {:#?}" , pte1. flags( ) ) ;
177- } else {
178- // 页表项存在但无效,继续下一页检查
179- println ! ( "kernel: mmap_PTE_flage = None" ) ;
163+ // 7. 建立映射,处理内存分配失败
164+ let mut va = start;
165+ let mut mapped_pages = Vec :: new ( ) ; // 记录已成功映射的页面,用于错误回滚
166+
167+ while va < end {
168+ let vpn = VirtAddr :: from ( va) . floor ( ) ;
169+
170+ // 分配物理页(处理内存不足情况)
171+ if let Some ( frame) = frame_alloc ( ) {
172+ let ppn = frame. ppn ;
173+
174+ // 建立虚拟页→物理页的映射
175+ page_table. map ( vpn, ppn, flags) ;
176+ mapped_pages. push ( vpn) ; // 记录成功映射的页面
177+ } else {
178+ // 内存不足,回滚已建立的映射
179+ for mapped_vpn in mapped_pages {
180+ page_table. unmap ( mapped_vpn) ;
181+ }
182+ return -1 ;
180183 }
181184
182185 va += PAGE_SIZE ;
183-
184- println ! ( "kernel: va = {:#x}" , va) ;
185- println ! ( "kernel: end = {:#x}" , end) ;
186-
187186 }
188187
189188 0 // 成功返回0
190189}
191190
192- // YOUR JOB: Implement munmap.
193191pub fn sys_munmap ( start : usize , len : usize ) -> isize {
194192 trace ! ( "kernel: sys_munmap" ) ;
195193
@@ -203,46 +201,40 @@ pub fn sys_munmap(start: usize, len: usize) -> isize {
203201 return 0 ;
204202 }
205203
206- // 3. 计算映射区间[start, end),处理len=0的特殊情况
207- let end = start + len ;
208- // 校验区间:不超出用户空间,且start <= end(避免无效区间)
204+ // 3. 计算映射区间[start, end)
205+ let end = start. checked_add ( len) . unwrap_or ( usize :: MAX ) ;
206+ // 校验区间:不超出用户空间
209207 if start > end || end > USER_SPACE_END {
210208 return -1 ;
211209 }
212210
213- // 4. 获取当前进程的页表(加锁保护,避免并发修改页表导致数据竞争)
211+ // 4. 获取当前进程的页表
214212 let page_table_token = current_user_token ( ) ;
215213 let mut page_table = PageTable :: from_token ( page_table_token) ;
216214
215+ // 5. 首先检查整个区域是否都是已映射的
216+ // 如果任何一页没有有效映射,则返回错误
217217 let mut va = start;
218- // 合并「校验+回收+取消映射」为一次遍历(优化性能,减少冗余)
219-
220- while va < end {
221- let vpn = VirtAddr :: from ( va) . floor ( ) ; // 虚拟页号(向下对齐到页边界)
218+ while va < end {
219+ let vpn = VirtAddr :: from ( va) . floor ( ) ;
222220
223- // 关键修改:仅处理已映射的页,未映射页直接跳过(不返回错误)
221+ // 检查页面是否有效映射
224222 if let Some ( pte) = page_table. translate ( vpn) {
225- // 校验页合法性:必须是有效且用户可见的页
226- if !pte. user_visible ( ) {
227- return -1 ; // 有效但非用户页,属于错误,返回-1
223+ if !pte. is_valid ( ) || !pte. user_visible ( ) {
224+ // 页面无效,返回错误
225+ return -1 ;
226+ } else {
227+ page_table. unmap ( vpn) ;
228228 }
229-
230- // 取消页表映射(清除PTE的V位)
231- page_table. unmap ( vpn) ;
232-
233- }
234-
235- va += PAGE_SIZE ; // 按页步长遍历下一页
236-
229+ }
230+ va += PAGE_SIZE ;
237231 }
238232
239- // 5. 刷新TLB:确保CPU立即丢弃旧映射(避免访问已回收的物理页)
240- // 注意:sfence.vma 需确保操作的是当前进程的页表(通过current_user_token保证)
241- // unsafe {
242- // core::arch::asm!("sfence.vma");
243- // // a0=0 表示刷新所有虚拟地址,a1=0 表示使用当前页表(ASID=0,简化场景)
244- // }
245233
234+ // 7. 刷新TLB:确保CPU立即丢弃旧映射
235+ unsafe {
236+ core:: arch:: asm!( "sfence.vma" ) ;
237+ }
246238
247239 0 // 成功返回0
248240}
0 commit comments