Skip to content

Commit 53df56c

Browse files
authored
feat(fs): 添加文件预读功能支持 (#1391)
- 新增readahead模块实现文件预读算法 - 修改page_cache模块支持预读标记 - 在File结构体中添加预读状态管理 - 为PageFlags添加PG_READAHEAD标志位 Signed-off-by: xbohodx <[email protected]>
1 parent 94c1f4c commit 53df56c

File tree

7 files changed

+477
-12
lines changed

7 files changed

+477
-12
lines changed

kernel/src/filesystem/page_cache.rs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -62,16 +62,13 @@ impl InnerPageCache {
6262
self.pages.remove(&offset)
6363
}
6464

65-
fn create_pages(&mut self, start_page_index: usize, buf: &[u8]) -> Result<(), SystemError> {
66-
assert!(buf.len().is_multiple_of(MMArch::PAGE_SIZE));
67-
68-
let page_num = buf.len() / MMArch::PAGE_SIZE;
69-
70-
let len = buf.len();
71-
if len == 0 {
65+
pub fn create_pages(&mut self, start_page_index: usize, buf: &[u8]) -> Result<(), SystemError> {
66+
if buf.is_empty() {
7267
return Ok(());
7368
}
7469

70+
let page_num = ((buf.len() - 1) >> MMArch::PAGE_SHIFT) + 1;
71+
7572
let mut page_manager_guard = page_manager_lock_irqsave();
7673

7774
for i in 0..page_num {
@@ -90,12 +87,15 @@ impl InnerPageCache {
9087
&mut LockedFrameAllocator,
9188
)?;
9289

90+
let page_len = core::cmp::min(MMArch::PAGE_SIZE, buf.len() - buf_offset);
91+
9392
let mut page_guard = page.write_irqsave();
9493
unsafe {
95-
page_guard.copy_from_slice(&buf[buf_offset..buf_offset + MMArch::PAGE_SIZE]);
94+
let dst = page_guard.as_slice_mut();
95+
dst[..page_len].copy_from_slice(&buf[buf_offset..buf_offset + page_len]);
9696
}
9797

98-
self.add_page(page_index, &page);
98+
self.add_page(start_page_index + i, &page);
9999
}
100100

101101
Ok(())

kernel/src/filesystem/vfs/file.rs

Lines changed: 71 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use system_error::SystemError;
77
use super::{FileType, IndexNode, InodeId, Metadata, SpecialNodeData};
88
use crate::process::pid::PidPrivateData;
99
use crate::{
10+
arch::MMArch,
1011
driver::{
1112
base::{block::SeekFrom, device::DevicePrivateData},
1213
tty::tty_device::TtyFilePrivateData,
@@ -18,6 +19,11 @@ use crate::{
1819
},
1920
ipc::{kill::kill_process, pipe::PipeFsPrivateData},
2021
libs::{rwlock::RwLock, spinlock::SpinLock},
22+
mm::{
23+
page::PageFlags,
24+
readahead::{page_cache_async_readahead, page_cache_sync_readahead, FileReadaheadState},
25+
MemoryManagementArch,
26+
},
2127
process::{cred::Cred, resource::RLimitID, ProcessControlBlock, ProcessManager, RawPid},
2228
};
2329

@@ -151,6 +157,8 @@ pub struct File {
151157
close_on_exec: AtomicBool,
152158
/// owner
153159
pid: SpinLock<Option<Arc<ProcessControlBlock>>>,
160+
/// 预读状态
161+
ra_state: SpinLock<FileReadaheadState>,
154162
}
155163

156164
impl File {
@@ -182,6 +190,7 @@ impl File {
182190
cred: ProcessManager::current_pcb().cred(),
183191
close_on_exec: AtomicBool::new(close_on_exec),
184192
pid: SpinLock::new(None),
193+
ra_state: SpinLock::new(FileReadaheadState::new()),
185194
};
186195

187196
return Ok(f);
@@ -251,6 +260,58 @@ impl File {
251260
self.do_write(offset, len, buf, false)
252261
}
253262

263+
fn file_readahead(&self, offset: usize, len: usize) -> Result<(), SystemError> {
264+
let page_cache = match self.inode.page_cache() {
265+
Some(page_cahce) => page_cahce,
266+
None => return Ok(()),
267+
};
268+
269+
let start_page = offset >> MMArch::PAGE_SHIFT;
270+
let end_page = (offset + len - 1) >> MMArch::PAGE_SHIFT;
271+
272+
let (async_trigger_page, missing_page) = {
273+
let page_cache_guard = page_cache.lock_irqsave();
274+
let mut async_trigger_page = None;
275+
let mut missing_page = None;
276+
277+
for index in start_page..=end_page {
278+
match page_cache_guard.get_page(index) {
279+
Some(page)
280+
if page
281+
.read_irqsave()
282+
.flags()
283+
.contains(PageFlags::PG_READAHEAD) =>
284+
{
285+
async_trigger_page = Some((index, page.clone()));
286+
break;
287+
}
288+
None => {
289+
missing_page = Some(index);
290+
break;
291+
}
292+
_ => {}
293+
}
294+
}
295+
(async_trigger_page, missing_page)
296+
};
297+
298+
if let Some((index, page)) = async_trigger_page {
299+
let mut ra_state = self.ra_state.lock().clone();
300+
let req_pages = end_page - index + 1;
301+
page.write_irqsave().remove_flags(PageFlags::PG_READAHEAD);
302+
303+
page_cache_async_readahead(&page_cache, &self.inode, &mut ra_state, index, req_pages)?;
304+
*self.ra_state.lock() = ra_state;
305+
} else if let Some(index) = missing_page {
306+
let mut ra_state = self.ra_state.lock().clone();
307+
let req_pages = end_page - index + 1;
308+
309+
page_cache_sync_readahead(&page_cache, &self.inode, &mut ra_state, index, req_pages)?;
310+
*self.ra_state.lock() = ra_state;
311+
}
312+
Ok(())
313+
}
314+
254315
pub fn do_read(
255316
&self,
256317
offset: usize,
@@ -264,6 +325,10 @@ impl File {
264325
return Err(SystemError::ENOBUFS);
265326
}
266327

328+
if self.file_type == FileType::File && !self.mode().contains(FileMode::O_DIRECT) {
329+
self.file_readahead(offset, len)?;
330+
}
331+
267332
let len = if self.mode().contains(FileMode::O_DIRECT) {
268333
self.inode
269334
.read_direct(offset, len, buf, self.private_data.lock())
@@ -272,11 +337,15 @@ impl File {
272337
.read_at(offset, len, buf, self.private_data.lock())
273338
}?;
274339

340+
if len > 0 {
341+
let last_page_readed = (offset + len - 1) >> MMArch::PAGE_SHIFT;
342+
self.ra_state.lock().prev_index = last_page_readed as i64;
343+
}
344+
275345
if update_offset {
276346
self.offset
277347
.fetch_add(len, core::sync::atomic::Ordering::SeqCst);
278348
}
279-
280349
Ok(len)
281350
}
282351

@@ -522,6 +591,7 @@ impl File {
522591
cred: self.cred.clone(),
523592
close_on_exec: AtomicBool::new(self.close_on_exec.load(Ordering::SeqCst)),
524593
pid: SpinLock::new(None),
594+
ra_state: SpinLock::new(self.ra_state.lock().clone()),
525595
};
526596
// 调用inode的open方法,让inode知道有新的文件打开了这个inode
527597
// TODO: reopen is not a good idea for some inodes, need a better design

kernel/src/libs/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,4 @@ pub mod name;
3131
pub mod decompress;
3232

3333
pub mod pod;
34+
pub mod ranges;

kernel/src/libs/ranges.rs

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
use crate::alloc::vec::Vec;
2+
3+
/// 合并连续的整数为范围
4+
///
5+
/// ## 参数
6+
/// - `set`: 整数集合,必须已排序
7+
///
8+
/// ## 返回值
9+
/// - `Vec<(start, count)>`: 范围列表
10+
///
11+
/// ## 示例
12+
/// ```
13+
/// merge_ranges(&[1, 2, 3, 5, 6, 8])
14+
/// // 返回 [(1, 3), (5, 2), (8, 1)]
15+
/// ```
16+
pub fn merge_ranges(set: &[usize]) -> Vec<(usize, usize)> {
17+
if set.is_empty() {
18+
return Vec::new();
19+
}
20+
21+
let mut ranges = Vec::new();
22+
let mut start = set[0];
23+
let mut count = 1;
24+
25+
for &page_index in &set[1..] {
26+
if page_index == start + count {
27+
count += 1; // 连续,扩展范围
28+
} else {
29+
ranges.push((start, count)); // 保存当前范围
30+
start = page_index;
31+
count = 1;
32+
}
33+
}
34+
35+
ranges.push((start, count)); // 最后一个范围
36+
ranges
37+
}
38+
39+
#[cfg(test)]
40+
mod tests {
41+
use super::*;
42+
43+
#[test]
44+
fn test_empty_input() {
45+
// 边界情况:空输入
46+
let result = merge_ranges(&[]);
47+
assert_eq!(result, vec![]);
48+
}
49+
50+
#[test]
51+
fn test_single_page() {
52+
// 边界情况:单个页面
53+
let result = merge_ranges(&[5]);
54+
assert_eq!(result, vec![(5, 1)]);
55+
}
56+
57+
#[test]
58+
fn test_fully_consecutive() {
59+
// 完全连续的页面
60+
let result = merge_ranges(&[1, 2, 3, 4, 5]);
61+
assert_eq!(result, vec![(1, 5)]);
62+
}
63+
64+
#[test]
65+
fn test_no_consecutive() {
66+
// 完全不连续的页面
67+
let result = merge_ranges(&[1, 3, 5, 7, 9]);
68+
assert_eq!(result, vec![(1, 1), (3, 1), (5, 1), (7, 1), (9, 1)]);
69+
}
70+
71+
#[test]
72+
fn test_mixed_ranges() {
73+
// 文档示例:混合连续和不连续
74+
let result = merge_ranges(&[1, 2, 3, 5, 6, 8]);
75+
assert_eq!(result, vec![(1, 3), (5, 2), (8, 1)]);
76+
}
77+
78+
#[test]
79+
fn test_two_consecutive_pairs() {
80+
// 两对连续的范围
81+
let result = merge_ranges(&[0, 1, 10, 11]);
82+
assert_eq!(result, vec![(0, 2), (10, 2)]);
83+
}
84+
85+
#[test]
86+
fn test_start_from_zero() {
87+
// 从 0 开始的连续范围
88+
let result = merge_ranges(&[0, 1, 2]);
89+
assert_eq!(result, vec![(0, 3)]);
90+
}
91+
92+
#[test]
93+
fn test_large_gap() {
94+
// 大间隔的不连续页面
95+
let result = merge_ranges(&[0, 100, 200]);
96+
assert_eq!(result, vec![(0, 1), (100, 1), (200, 1)]);
97+
}
98+
99+
#[test]
100+
fn test_long_consecutive_sequence() {
101+
// 长连续序列(模拟大文件预读)
102+
let input: Vec<usize> = (0..128).collect();
103+
let result = merge_ranges(&input);
104+
assert_eq!(result, vec![(0, 128)]);
105+
}
106+
107+
#[test]
108+
fn test_alternating_pattern() {
109+
// 交替的连续和间断模式
110+
let result = merge_ranges(&[1, 2, 4, 5, 7, 8, 10]);
111+
assert_eq!(result, vec![(1, 2), (4, 2), (7, 2), (10, 1)]);
112+
}
113+
114+
#[test]
115+
fn test_real_world_sparse() {
116+
// 真实场景:稀疏的页面缓存(部分页面已存在)
117+
let result = merge_ranges(&[0, 5, 6, 7, 12, 20, 21]);
118+
assert_eq!(result, vec![(0, 1), (5, 3), (12, 1), (20, 2)]);
119+
}
120+
}

kernel/src/mm/mod.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ pub mod mmio_buddy;
3333
pub mod no_init;
3434
pub mod page;
3535
pub mod percpu;
36+
pub mod readahead;
3637
pub mod syscall;
3738
pub mod sysfs;
3839
pub mod truncate;
@@ -554,13 +555,13 @@ pub trait MemoryManagementArch: Clone + Copy + Debug {
554555

555556
/// @brief 读取指定虚拟地址的值,并假设它是类型T的指针
556557
#[inline(always)]
557-
unsafe fn read<T>(address: VirtAddr) -> T {
558+
unsafe fn read<T: Sized>(address: VirtAddr) -> T {
558559
return ptr::read(address.data() as *const T);
559560
}
560561

561562
/// @brief 将value写入到指定的虚拟地址
562563
#[inline(always)]
563-
unsafe fn write<T>(address: VirtAddr, value: T) {
564+
unsafe fn write<T: Sized>(address: VirtAddr, value: T) {
564565
ptr::write(address.data() as *mut T, value);
565566
}
566567

kernel/src/mm/page.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -434,6 +434,7 @@ bitflags! {
434434
const PG_RECLAIM = 1 << 18;
435435
const PG_SWAPBACKED = 1 << 19;
436436
const PG_UNEVICTABLE = 1 << 20;
437+
const PG_READAHEAD = 1 << 21;
437438
}
438439
}
439440

0 commit comments

Comments
 (0)