请求处理器
在之前的内核线程中,我们看到所有内核服务线程结构都是统一的,但其中包含一个processor成员用于处理具体的请求:
/// 解析请求类型并进行具体地处理
pub trait Processor: Send + Sync {
/// 处理当前请求,完毕后唤醒相应的等待协程
fn process_request(&self, request: Request);
}
process_request
方法完成给定请求的整个处理过程,并唤醒这个请求的等待协程。下面我们以文件系统请求处理器为例来说明:
/// 文件系统请求处理器
pub struct FsProcessor;
impl FsProcessor {
pub fn new() -> Arc<Self> {
Arc::new(FsProcessor {})
}
}
文件系统请求处理器不含任何成员,只为他实现Processor trait
:
impl Processor for FsProcessor {
/// 处理一个文件系统请求,并不响应请求(在内核入口函数中响应)
fn process_request(&self, request: Request) {
let fs_req = FsReqDescription::from_bytes(&request);
match fs_req {
// Read请求,进程Pid读文件表中fd对应的文件到buf中
FsReqDescription::Read(pid, fd, buf_ptr, buf_len, result_ptr) => {
// 解析buf
...
// 获取磁盘文件
...
// 读文件
let read_size = if !file.readable() {
println!("[Fs server] Error reading file, not readable!");
usize::MAX
} else {
file.read(buf)
};
// 将read_size写入到用户态中
....
}
// Write请求,进程Pid将buf中的数据写入文件表中fd对应的文件
FsReqDescription::Write(pid, fd, buf_ptr, buf_len, result_ptr) => {
// 解析buf
....
// 获取磁盘文件
...
// 写文件
let write_size = if !file.writable() {
println!("[Fs server] Error writing file, not readable!");
usize::MAX
} else {
file.write(buf)
};
// 将write_size写入到用户态中
....
}
// 处理Open请求,打开一个磁盘文件并加入pid进程的文件表中
FsReqDescription::Open(pid, path_ptr, flags, fd_ptr) => {
// 解析路径
.....
// 可以创建文件
let file = if flags.contains(OpenFlags::CREATE) {
// 文件已存在
....
// 不存在,创建文件
} else {
ROOT_INODE
.create(path)
.map(|inode| Arc::new(OSInode::new(readable, writable, inode)))
}
// 不能创建文件,打开文件
} else {
ROOT_INODE.find(path).map(|inode| {
if flags.contains(OpenFlags::TRUNC) {
inode.clear();
}
Arc::new(OSInode::new(readable, writable, inode))
})
};
// 结果写回用户态
....
}
}
}
}
处理器首先调用之前提到过的from_bytes
方法将字节解析为文件系统请求,再根据请求的不同类型进行不同的处理。
在实际的处理过程中,调用了文件系统的相关接口,这些接口都会最终调用底层的块设备驱动,因此我们认为这些操作并不是完全可靠的。