Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 78 additions & 2 deletions kernel/src/filesystem/page_cache.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use core::{
cmp::min,
sync::atomic::{AtomicUsize, Ordering},
sync::atomic::{AtomicBool, AtomicUsize, Ordering},
};

use alloc::{
Expand Down Expand Up @@ -31,6 +31,7 @@ pub struct PageCache {
id: usize,
inner: SpinLock<InnerPageCache>,
inode: Lazy<Weak<dyn IndexNode>>,
unevictable: AtomicBool,
}

#[derive(Debug)]
Expand Down Expand Up @@ -82,6 +83,18 @@ impl InnerPageCache {
let buf_offset = i * MMArch::PAGE_SIZE;
let page_index = start_page_index + i;

let page_flags = {
let cache = self
.page_cache_ref
.upgrade()
.expect("failed to get self_arc of pagecache");
if cache.unevictable.load(Ordering::Relaxed) {
PageFlags::PG_LRU | PageFlags::PG_UNEVICTABLE
} else {
PageFlags::PG_LRU
}
};

let page = page_manager_guard.create_one_page(
PageType::File(FileMapInfo {
page_cache: self
Expand All @@ -90,7 +103,7 @@ impl InnerPageCache {
.expect("failed to get self_arc of pagecache"),
index: page_index,
}),
PageFlags::PG_LRU,
page_flags,
&mut LockedFrameAllocator,
)?;

Expand All @@ -108,6 +121,62 @@ impl InnerPageCache {
Ok(())
}

/// 创建若干个“零页”并加入 PageCache。
///
/// 与 `create_pages()` 的区别:
/// - 不需要临时分配 `Vec<u8>` 作为填充缓冲区;
/// - 直接分配物理页后在页内 `fill(0)`;
///
/// 适用场景:tmpfs 等内存文件系统的“空洞读/缺页补零”。
pub fn create_zero_pages(
&mut self,
start_page_index: usize,
page_num: usize,
) -> Result<(), SystemError> {
if page_num == 0 {
return Ok(());
}

let mut page_manager_guard = page_manager_lock_irqsave();

for i in 0..page_num {
let page_index = start_page_index + i;

let page_flags = {
let cache = self
.page_cache_ref
.upgrade()
.expect("failed to get self_arc of pagecache");
if cache.unevictable.load(Ordering::Relaxed) {
PageFlags::PG_LRU | PageFlags::PG_UNEVICTABLE
} else {
PageFlags::PG_LRU
}
};

let page = page_manager_guard.create_one_page(
PageType::File(FileMapInfo {
page_cache: self
.page_cache_ref
.upgrade()
.expect("failed to get self_arc of pagecache"),
index: page_index,
}),
page_flags,
&mut LockedFrameAllocator,
)?;

let mut page_guard = page.write_irqsave();
unsafe {
page_guard.as_slice_mut().fill(0);
}

self.add_page(page_index, &page);
}

Ok(())
}

/// 从PageCache中读取数据。
///
/// ## 参数
Expand Down Expand Up @@ -404,6 +473,7 @@ impl PageCache {
}
v
},
unevictable: AtomicBool::new(false),
})
}

Expand Down Expand Up @@ -437,6 +507,12 @@ impl PageCache {
self.inner.is_locked()
}

/// Mark this page cache as unevictable (or revert). When enabled, newly created
/// pages will carry PG_UNEVICTABLE to keep the reclaimer from reclaiming them.
pub fn set_unevictable(&self, unevictable: bool) {
self.unevictable.store(unevictable, Ordering::Relaxed);
}

/// 两阶段读取:持锁收集拷贝项,解锁后拷贝到目标缓冲区,避免用户缺页导致自锁
pub fn read(&self, offset: usize, buf: &mut [u8]) -> Result<usize, SystemError> {
let (copies, ret) = {
Expand Down
60 changes: 56 additions & 4 deletions kernel/src/filesystem/procfs/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ pub enum ProcFileType {
/// /proc/<pid>/fdinfo/<fd> 文件
ProcFdInfoFile,
ProcMounts,
/// /proc/<pid>/mountinfo
ProcMountInfo,
/// /proc/version
ProcVersion,
/// /proc/cpuinfo
Expand Down Expand Up @@ -517,9 +519,31 @@ impl ProcFSInode {
let file = fd_table.get_file_by_fd(fd);
if let Some(file) = file {
let inode = file.inode();
let path = inode.absolute_path().unwrap();
let len = path.len().min(buf.len());
buf[..len].copy_from_slice(&path.as_bytes()[..len]);
let full = inode
.absolute_path()
.unwrap_or_else(|_| "unknown".to_string());

// chroot 视角:若目标在进程 fs root 下,则去掉真实前缀,变为以 "/" 为根的路径。
let pcb = ProcessManager::current_pcb();
let root_inode = pcb.fs_struct().root();
let root_prefix = root_inode
.absolute_path()
.unwrap_or_else(|_| "/".to_string());

let shown = if root_prefix != "/" {
if full == root_prefix {
"/".to_string()
} else if full.starts_with(&(root_prefix.clone() + "/")) {
full[root_prefix.len()..].to_string()
} else {
full
}
} else {
full
};

let len = shown.len().min(buf.len());
buf[..len].copy_from_slice(&shown.as_bytes()[..len]);
Ok(len)
} else {
return Err(SystemError::EBADF);
Expand Down Expand Up @@ -725,6 +749,25 @@ impl ProcFS {
.downcast_ref::<LockedProcFSInode>()
.unwrap();
fdinfo.0.lock().fdata.ftype = ProcFileType::ProcFdInfoDir;

// mounts file: /proc/<pid>/mounts (供 /proc/self/mounts 使用)
let mounts_binding = pid_dir.create("mounts", FileType::File, InodeMode::S_IRUGO)?;
let mounts_file = mounts_binding
.as_any_ref()
.downcast_ref::<LockedProcFSInode>()
.unwrap();
mounts_file.0.lock().fdata.pid = Some(pid);
mounts_file.0.lock().fdata.ftype = ProcFileType::ProcMounts;

// mountinfo file: /proc/<pid>/mountinfo
let mountinfo_binding = pid_dir.create("mountinfo", FileType::File, InodeMode::S_IRUGO)?;
let mountinfo_file = mountinfo_binding
.as_any_ref()
.downcast_ref::<LockedProcFSInode>()
.unwrap();
mountinfo_file.0.lock().fdata.pid = Some(pid);
mountinfo_file.0.lock().fdata.ftype = ProcFileType::ProcMountInfo;

//todo: 创建其他文件

return Ok(());
Expand All @@ -740,9 +783,11 @@ impl ProcFS {
// 删除进程文件夹下文件
pid_dir.unlink("status")?;
pid_dir.unlink("exe")?;
pid_dir.unlink("maps")?;
pid_dir.rmdir("fd")?;
pid_dir.rmdir("fdinfo")?;
let _ = pid_dir.unlink("mounts");
let _ = pid_dir.unlink("mountinfo");
let _ = pid_dir.unlink("maps");

// 查看进程文件是否还存在
// let pf= pid_dir.find("status").expect("Cannot find status");
Expand Down Expand Up @@ -925,6 +970,12 @@ impl IndexNode for LockedProcFSInode {
ProcFileType::ProcMaps => inode.open_maps(&mut proc_private)?,
ProcFileType::ProcExe => inode.open_exe(&mut proc_private)?,
ProcFileType::ProcMounts => inode.open_mounts(&mut proc_private)?,
ProcFileType::ProcMountInfo => {
let s = proc_mounts::generate_mountinfo_content();
proc_private.data = s.into_bytes();
proc_private.data.len() as i64
}

ProcFileType::ProcVersion => inode.open_version(&mut proc_private)?,
ProcFileType::ProcCpuinfo => inode.open_cpuinfo(&mut proc_private)?,
ProcFileType::ProcSelf => inode.open_self(&mut proc_private)?,
Expand Down Expand Up @@ -1007,6 +1058,7 @@ impl IndexNode for LockedProcFSInode {
| ProcFileType::ProcStatm
| ProcFileType::ProcMaps
| ProcFileType::ProcMounts
| ProcFileType::ProcMountInfo
| ProcFileType::ProcVersion
| ProcFileType::ProcCpuinfo => {
// 获取数据信息
Expand Down
73 changes: 67 additions & 6 deletions kernel/src/filesystem/procfs/proc_mounts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,34 @@ impl ProcFSInode {
pdata: &mut ProcfsFilePrivateData,
) -> Result<i64, SystemError> {
// 生成mount信息
let mount_content = Self::generate_mounts_content();
let mount_content = Self::generate_mounts_like_content(MountsFormat::Mounts);

pdata.data = mount_content.into_bytes();
return Ok(pdata.data.len() as i64);
}

#[inline(never)]
fn generate_mounts_content() -> String {
fn generate_mounts_like_content(fmt: MountsFormat) -> String {
let mntns = ProcessManager::current_mntns();
let mounts = mntns.mount_list().clone_inner();

// 以进程 fs root 为基准做 chroot 视图裁剪/重写
let pcb = ProcessManager::current_pcb();
let root_inode = pcb.fs_struct().root();
let root_prefix = root_inode
.absolute_path()
.unwrap_or_else(|_| "/".to_string());
let is_chrooted = root_prefix != "/";
let root_prefix_with_slash = if root_prefix.ends_with('/') {
root_prefix.clone()
} else {
root_prefix.clone() + "/"
};

let mut lines = Vec::with_capacity(mounts.len());
let mut cap = 0;
let mut mid: usize = 1;

for (mp, mfs) in mounts {
let mut line = String::new();
let fs_type = mfs.fs_type();
Expand All @@ -53,14 +68,49 @@ impl ProcFSInode {
}
};

line.push_str(&format!("{source} {m} {fs_type}", m = mp.as_str()));
// 过滤/改写 mountpoint(chroot 后应只暴露 root 下的挂载点,并重写为 chroot 视角)
let mut mountpoint = mp.as_str().to_string();
if is_chrooted {
if mountpoint == root_prefix {
mountpoint = "/".to_string();
} else if mountpoint.starts_with(&root_prefix_with_slash) {
// strip_prefix 会得到 "child/..";保持以 '/' 开头
let stripped = &mountpoint[root_prefix.len()..];
mountpoint = if stripped.is_empty() {
"/".to_string()
} else {
stripped.to_string()
};
} else {
continue;
}
}

line.push(' ');
line.push_str(&mfs.mount_flags().options_string());
match fmt {
MountsFormat::Mounts => {
line.push_str(&format!("{source} {m} {fs_type}", m = mountpoint));
line.push(' ');
line.push_str(&mfs.mount_flags().options_string());
line.push_str(" 0 0\n");
}
MountsFormat::MountInfo => {
// 极简 mountinfo:只保证 mountpoint 字段正确且不泄露 chroot 前缀
// mount ID / parent ID / major:minor / root / mountpoint / options / - / fstype / source / superopts
line.push_str(&format!(
"{id} {pid} 0:0 / {mp} {opts} - {fst} {src} {opts}\n",
id = mid,
pid = if mid == 1 { 0 } else { 1 },
mp = mountpoint,
opts = mfs.mount_flags().options_string(),
fst = fs_type,
src = source
));
}
}

line.push_str(" 0 0\n");
cap += line.len();
lines.push(line);
mid += 1;
}

let mut content = String::with_capacity(cap);
Expand All @@ -71,3 +121,14 @@ impl ProcFSInode {
return content;
}
}

#[derive(Clone, Copy)]
enum MountsFormat {
Mounts,
MountInfo,
}

/// 为 /proc/<pid>/mountinfo 生成内容(极简版,满足 gVisor chroot_test)。
pub(super) fn generate_mountinfo_content() -> String {
ProcFSInode::generate_mounts_like_content(MountsFormat::MountInfo)
}
Loading
Loading