Skip to content

Commit

Permalink
doc: ipc and task doc
Browse files Browse the repository at this point in the history
  • Loading branch information
BITcyman committed Aug 18, 2023
1 parent 7d0080e commit f56e7fe
Show file tree
Hide file tree
Showing 11 changed files with 293 additions and 38 deletions.
10 changes: 10 additions & 0 deletions kernel/src/ipc/futex.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,25 @@ use crate::error::{AlienError, AlienResult};
use crate::task::{Task, TASK_MANAGER};
use crate::timer::read_timer;

/// 用于记录一个进程等待一个 futex 的相关信息
pub struct FutexWaiter {
/// 进程的控制块
task: Option<Arc<Task>>,
/// 进程等待 futex 的等待时间
wait_time: Option<usize>,
/// 超时事件的标志位,标识该进程对于 futex 等待是否超时
timeout_flag: Arc<Mutex<bool>>,
}

/// 用于管理 futex 等待队列的数据结构
///
/// 包含一个 futex id -> futexWait Vec 的 map
pub struct FutexWaitManager {
map: BTreeMap<usize, Vec<FutexWaiter>>,
}

impl FutexWaiter {
/// 创建一个新的 `FutexWaiter` 保存等待在某 futex 上的一个进程 有关等待的相关信息
pub fn new(task: Arc<Task>, wait_time: Option<usize>, timeout_flag: Arc<Mutex<bool>>) -> Self {
Self {
task: Some(task),
Expand All @@ -43,12 +51,14 @@ impl FutexWaiter {
}
}

/// 唤醒该进程,返回该进程的控制块
pub fn wake(&mut self) -> Arc<Task> {
self.task.take().unwrap()
}
}

impl FutexWaitManager {
/// 创建一个新的 futex 管理器,保存 futex 和在其上等待队列的映射关系
pub fn new() -> Self {
Self {
map: BTreeMap::new(),
Expand Down
9 changes: 9 additions & 0 deletions kernel/src/ipc/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
//! IPC 进程间通信
//!
//! [`futex`] 子模块指明了 Alien 中的 futex (快速用户空间互斥体)结构。
//! [`pipe`] 子模块指明了 Alien 中管道结构。
//! [`shm`] 子模块指明了 Alien 中的共享内存结构。
//! [`signal`] 子模块指明了 Alien 中使用的信号机制。

use alloc::sync::Arc;
use core::sync::atomic::{AtomicI32, Ordering};

Expand All @@ -23,6 +30,7 @@ pub mod shm;
pub mod signal;

lazy_static! {
/// 一个全局变量,用于记录和管理 futex 的等待队列
pub static ref FUTEX_WAITER: Mutex<FutexWaitManager> = Mutex::new(FutexWaitManager::new());
}

Expand Down Expand Up @@ -265,6 +273,7 @@ pub fn get_robust_list(pid: usize, head_ptr: usize, len_ptr: usize) -> isize {
0
}

/// 唤醒所有当前正在等待 futex 但因为超时或者信号而需要被唤醒的进程
pub fn solve_futex_wait() {
let mut futex_waiter = FUTEX_WAITER.lock();
futex_waiter.wake_for_timeout();
Expand Down
64 changes: 54 additions & 10 deletions kernel/src/ipc/pipe.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,15 @@
//! 管道是一种最基本的IPC机制,作用于有血缘关系的进程之间,完成数据传递。
//!
//! `Alien` 中对于管道的设计参考了`rCore`的相关设计。创建管道时会同时创建一个环形缓冲区,
//! 管道的两个端口抽象成文件,对两个端口直接的相关的文件操作(读操作或者写操作)都被设计
//! 成对缓冲区进行数据处理(向缓冲区中传入数据或接收数据)。
//!
//! 管道文件创建时,依据 Alien 所使用的 rvfs 中对文件 `File` 的规定,我们只需为管道文件规定好
//! [`pipe_release`]、[`pipe_write`]、[`pipe_read`]、[`pipe_exec`]、[`pipe_llseek`]、
//! [`pipe_read_is_hang_up`]、[`pipe_write_is_hang_up`]、[`pipe_ready_to_read`]
//! 、[`pipe_ready_to_write`] 几个操作函数,即可快速的创建管道文件,并将其放入进程的文件描述
//! 符表中。

use alloc::boxed::Box;
use alloc::sync::{Arc, Weak};
use core::intrinsics::forget;
Expand All @@ -12,25 +24,27 @@ use crate::config::PIPE_BUF;
use crate::fs::file::KFile;
use crate::task::{current_task, do_suspend};

/// 管道是一种最基本的IPC机制,作用于有血缘关系的进程之间,完成数据传递。
///
/// `Alien` 中对于管道的设计参考了`rCore`的相关设计。创建管道时会同时创建一个环形缓冲区,
/// 管道的两个端口抽象成文件,对两个端口直接的相关的文件操作(读操作或者写操作)都被设计成对缓冲区进行数据处理(向缓冲区中传入数据或接收数据)。
/// 管道结构
pub struct Pipe;

/// 环形缓冲区,用于在内存中维护管道的相关信息。
pub struct RingBuffer {
/// 缓冲区的数据部分
pub buf: [u8; PIPE_BUF],
/// 缓冲区头部,用于指明当前的读位置
pub head: usize,
/// 缓冲区尾部,用于指明当前的写位置
pub tail: usize,
/// 记录 在 读端 进行等待的进程
pub read_wait: Option<Weak<KFile>>,
// record whether there is a process waiting for reading
/// 记录 在 写端 进行等待的进程
pub write_wait: Option<Weak<KFile>>,
// record whether there is a process waiting for writing
/// 引用计数
pub ref_count: usize,
}

impl RingBuffer {
/// 创建一片新的管道缓冲区,在 `Pipe::new` 中被调用
pub fn new() -> RingBuffer {
RingBuffer {
buf: [0; PIPE_BUF],
Expand All @@ -41,28 +55,36 @@ impl RingBuffer {
ref_count: 2,
}
}

/// 用于返回当前的缓冲区是否为空
pub fn is_empty(&self) -> bool {
self.head == self.tail
}

/// 用于返回当前的缓冲区是否为满
pub fn is_full(&self) -> bool {
(self.tail + 1) % PIPE_BUF == self.head
}
/// return the number of bytes that can be read

/// 返回当前缓冲区中能够被读的字节数
pub fn available_read(&self) -> usize {
if self.head <= self.tail {
self.tail - self.head
} else {
PIPE_BUF - self.head + self.tail
}
}
/// return the number of bytes that can be written

/// 返回当前缓冲区中还能够写入的字节数
pub fn available_write(&self) -> usize {
if self.head <= self.tail {
PIPE_BUF - self.tail + self.head - 1
} else {
self.head - self.tail - 1
}
}

/// 向缓冲区中写入数据,返回写入的字节数
pub fn write(&mut self, buf: &[u8]) -> usize {
let mut count = 0;
while !self.is_full() && count < buf.len() {
Expand All @@ -72,6 +94,8 @@ impl RingBuffer {
}
count
}

/// 从缓冲区中读取数据,返回读取的字节数
pub fn read(&mut self, buf: &mut [u8]) -> usize {
let mut count = 0;
while !self.is_empty() && count < buf.len() {
Expand All @@ -81,23 +105,29 @@ impl RingBuffer {
}
count
}

/// 清除缓冲区中的内容,当前实现为将 head 和 tail 重置为 0
pub fn clear(&mut self) {
self.head = 0;
self.tail = 0;
}

/// 返回是否有进程在 写端等待
pub fn is_write_wait(&self) -> bool {
self.write_wait.is_some() && self.write_wait.as_ref().unwrap().upgrade().is_some()
}

/// 返回是否有进程在 读端等待
pub fn is_read_wait(&self) -> bool {
self.read_wait.is_some() && self.read_wait.as_ref().unwrap().upgrade().is_some()
}
}

impl Pipe {
/// 创建一个管道,初始化环形缓冲区,返回一对文件,分别对应读端文件和写端文件。
/// 过程包括创建一个环形缓冲区、创建并初始化写端文件和读端文件,
/// 将两个文件与环形缓冲区相连,使得通过两个端文件快速对缓冲区进行读写。
///
/// 过程包括创建一个环形缓冲区、创建并初始化写端文件和读端文件、
/// 将两个文件与环形缓冲区相连、使得通过两个端文件快速对缓冲区进行读写等。
pub fn new() -> (Arc<KFile>, Arc<KFile>) {
let mut buf = Box::new(RingBuffer::new());
let mut tx_file = File::new(
Expand Down Expand Up @@ -168,6 +198,7 @@ impl Pipe {
}
}

/// 管道文件的写操作,效果等同于 RingBuffer::write
fn pipe_write(file: Arc<File>, user_buf: &[u8], _offset: u64) -> StrResult<usize> {
warn!("pipe_write: {:?}, mode:{:?}", user_buf.len(), file.f_mode);
let inode = file.f_dentry.access_inner().d_inode.clone();
Expand Down Expand Up @@ -215,6 +246,7 @@ fn pipe_write(file: Arc<File>, user_buf: &[u8], _offset: u64) -> StrResult<usize
Ok(count)
}

/// 管道文件的写操作,效果等同于 RingBuffer::read
fn pipe_read(file: Arc<File>, user_buf: &mut [u8], _offset: u64) -> StrResult<usize> {
debug!("pipe_read: {:?}", user_buf.len());
let inode = file.f_dentry.access_inner().d_inode.clone();
Expand Down Expand Up @@ -261,6 +293,7 @@ fn pipe_read(file: Arc<File>, user_buf: &mut [u8], _offset: u64) -> StrResult<us
Ok(count)
}

/// 管道文件的释放操作,用于关闭管道文件时
fn pipe_release(file: Arc<File>) -> StrResult<()> {
warn!("pipe_release: file");
assert_eq!(Arc::strong_count(&file), 1);
Expand All @@ -286,13 +319,19 @@ fn pipe_release(file: Arc<File>) -> StrResult<()> {
Ok(())
}

/// 管道文件 [`pipe_exec`] 操作中 `func` 参数的类型
pub enum PipeFunc {
/// 读就绪操作
AvailableRead,
/// 写就绪操作
AvailableWrite,
/// 某端是否被悬挂操作,当其中的布尔值为 true 时,表示检查的是读端;否则为写端。具体可见 [`pipe_read_is_hang_up`] 和 [`pipe_write_is_hang_up`]
Hangup(bool),
/// 未知操作
Unknown,
}

/// 管道文件的 exec 操作,用于被其他文件操作函数调用
fn pipe_exec(file: Arc<File>, func: PipeFunc) -> bool {
let inode = file.f_dentry.access_inner().d_inode.clone();
let inode_inner = inode.access_inner();
Expand Down Expand Up @@ -323,23 +362,28 @@ fn pipe_exec(file: Arc<File>, func: PipeFunc) -> bool {
res
}

/// 管道文件的读就绪操作,用于检查管道文件是否准备好读,效果等同于 RingBuffer::available_read
fn pipe_ready_to_read(file: Arc<File>) -> bool {
pipe_exec(file, PipeFunc::AvailableRead)
}

/// 管道文件的写就绪操作,用于检查管道文件是否准备好写,效果等同于 RingBuffer::available_write
fn pipe_ready_to_write(file: Arc<File>) -> bool {
pipe_exec(file, PipeFunc::AvailableWrite)
}

/// 管道文件的 "读端是否处于悬挂状态" 操作,用于检查管道文件的写端是否已经被关闭,效果等同于 !RingBuffer::is_write_wait
fn pipe_read_is_hang_up(file: Arc<File>) -> bool {
let pipe_hang_up = pipe_exec(file, PipeFunc::Hangup(true));
pipe_hang_up
}

/// 管道文件的 "写端是否处于悬挂状态" 操作,用于检查管道文件的读端是否已经被关闭,效果等同于 !RingBuffer::is_read_wait
fn pipe_write_is_hang_up(file: Arc<File>) -> bool {
pipe_exec(file, PipeFunc::Hangup(false))
}

/// (待实现)用于移动管道文件的文件指针,目前仅返回错误
fn pipe_llseek(_file: Arc<File>, _whence: SeekFrom) -> StrResult<u64> {
Err("ESPIPE")
}
30 changes: 27 additions & 3 deletions kernel/src/ipc/shm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,9 @@
//! 为了在多个进程间交换信息,内核专门留出了一块内存区。这段内存区可以由需要访问的进程将其映射到自己的私有地址空间。
//! 因此,进程就可以直接读写这一内存区而不需要进行数据的拷贝,从而大大提高了效率。
//!
//! 共享内存并未提供同步机制,也就是说,在第一个进程结束对共享内存的写操作之前,并无自动机制可以阻止第二个进程开始对它进行读取。
//! 所以我们通常需要用其他的机制来同步对共享内存的访问,如互斥锁和信号量等
//! Alien 中的共享内存同时提供了同步机制,也就是说,所有的 [`ShmMemoryInner`] 结构被包裹在一个 Mutex 中,
//! 最后封装成 [`ShmMemory`] 结构
//!

use alloc::collections::btree_map::BTreeMap;
use alloc::vec::Vec;

Expand All @@ -21,31 +20,42 @@ use crate::config::FRAME_SIZE;
use crate::memory::{frames_alloc, FrameTracker};
use crate::task::current_task;

/// 共享内存被 Mutex 封装后的结构
#[derive(Debug)]
pub struct ShmMemory {
/// 封装后的共享内存
inner: Mutex<ShmMemoryInner>,
}

/// 未加入同步机制前的 共享内存
#[derive(Debug)]
pub struct ShmMemoryInner {
/// 引用计数器
ref_count: usize,
/// 共享内存数据部分
pub frames: Vec<FrameTracker>,
/// 共享内存的状态
state: ShmMemoryState,
}

/// 共享内存的信息,在创建一块共享内存时,需要将对应的信息加入到进程控制块中的 `shm` 字段下
#[derive(Debug, Clone)]
pub struct ShmInfo {
/// 共享内存的虚拟地址首地址
pub start_va: usize,
/// 共享内存的虚拟地址尾地址
pub end_va: usize,
}

impl ShmInfo {
/// 创建新的共享内存信息
pub fn new(start_va: usize, end_va: usize) -> Self {
Self { start_va, end_va }
}
}

impl ShmMemory {
/// 创建新的共享内存
pub fn new(frames: Vec<FrameTracker>) -> Self {
Self {
inner: Mutex::new(ShmMemoryInner {
Expand All @@ -55,38 +65,52 @@ impl ShmMemory {
}),
}
}

/// 同步获取内部的共享内存信息
pub fn access_inner(&self) -> MutexGuard<ShmMemoryInner> {
self.inner.lock()
}

/// 返回共享内存数据区的长度(字节数)
pub fn len(&self) -> usize {
self.access_inner().frames.len() * FRAME_SIZE
}

/// 引用计数器加一
pub fn add_ref(&self) {
self.access_inner().ref_count += 1;
}

/// 获取当前共享内存的引用数
pub fn get_ref(&self) -> usize {
self.access_inner().ref_count
}

/// 删除当前的共享内存
pub fn delete(&self) {
self.access_inner().state = ShmMemoryState::Deleted;
}

/// 查询当前的共享内存是否被删除
pub fn is_deleted(&self) -> bool {
self.access_inner().state == ShmMemoryState::Deleted
}

/// 引用计数器减一
pub fn dec_ref(&self) {
self.access_inner().ref_count -= 1;
}
}

/// 记录共享内存当前状态的结构
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
pub enum ShmMemoryState {
Init,
Used,
Deleted,
}

/// 用于记录共享内存分配情况的全局变量,可使用其获取已经被创建的一块共享内存
pub static SHM_MEMORY: Mutex<BTreeMap<usize, ShmMemory>> = Mutex::new(BTreeMap::new());

/// 一个系统调用,用于创建一块共享内存,方便进程间通信。
Expand Down
Loading

0 comments on commit f56e7fe

Please sign in to comment.