diff --git a/docs/src/ch01-02-process-object.md b/docs/src/ch01-02-process-object.md index ecb805a..9f7f86f 100644 --- a/docs/src/ch01-02-process-object.md +++ b/docs/src/ch01-02-process-object.md @@ -1,108 +1,312 @@ #### 对象管理器:Process 对象 -## 权限 +## 句柄——操作内核对象的桥梁 -[权限]: https://github.com/zhangpf/fuchsia-docs-zh-CN/blob/master/zircon/docs/rights.md +在1.1中我们用Rust语言实现了一个最核心的内核对象,在本小节我们将逐步了解与内核对象相关的三个重要概念中的其他两个:**句柄(Handle)和权限(Rights)**。 -内核对象的“[权限](https://fuchsia.dev/docs/concepts/kernel/rights)”指定允许对内核对象进行哪些操作。权限与句柄相关联,并传达对关联句柄或与句柄关联的对象执行操作的特权。单个进程可能对具有不同权限的同一个内核对象有两个不同的句柄。 +句柄是允许用户程序引用内核对象引用的一种内核结构,它可以被认为是与特定内核对象的会话或连接。 -## 句柄 +通常情况下,多个进程通过不同的句柄同时访问同一个对象。对象可能有多个句柄(在一个或多个进程中)引用它们。但单个句柄只能绑定到单个进程或绑定到内核。 -[句柄]: https://github.com/zhangpf/fuchsia-docs-zh-CN/blob/master/zircon/docs/handles.md +### 定义句柄 +在 object 模块下定义一个子模块: -句柄是允许用户程序引用内核对象引用的一种内核结构,它可以被认为是与特定内核对象的会话或连接。 +```rust +// src/object/mod.rs +mod handle; -通常情况下,多个进程通过不同的句柄同时访问同一个对象。对象可能有多个句柄(在一个或多个进程中)引用它们。但单个句柄只能绑定到单个进程或绑定到内核。 +pub use self::handle::*; +``` + +定义句柄: -当句柄绑定到内核时,我们说它是“在传输中”('in-transit')。 +```rust +// src/object/handle.rs +use super::{KernelObject, Rights}; +use alloc::sync::Arc; + +/// 内核对象句柄 +#[derive(Clone)] +pub struct Handle { + pub object: Arc, + pub rights: Rights, +} +``` -在用户模式下,句柄只是某个系统调用返回的特定数字。只有“不在传输中”的句柄对用户模式可见。 +一个Handle包含object和right两个字段,object是实现了`KernelObject`Trait的内核对象,Rights是该句柄的权限,我们将在下面提到它。 -代表句柄的整数只对其所属的那个进程有意义。另一个进程中的相同数字可能不会映射到任何句柄,或者它可能映射到指向完全不同的内核对象的句柄。 +Arc是一个可以在多线程上使用的引用计数类型,这个计数会随着 `Arc` 的创建或复制而增加,并当 `Arc` 生命周期结束被回收时减少。当这个计数变为零之后,这个计数变量本身以及被引用的变量都会从堆上被回收。 -句柄的整数值是任何 32 位数字,但对应于**ZX_HANDLE_INVALID**的值将始终为 0。除此之外,有效句柄的整数值将始终具有句柄集的两个最低有效位. 可以使用**ZX_HANDLE_FIXED_BITS_MASK**访问代表这些位的掩码。 +我们为什么要在这里使用Arc智能指针呢? -句柄可以从一个进程移动到另一个进程,方法是将它们写入通道(使用[`channel_write()`](https://fuchsia.dev/docs/reference/syscalls/channel_write)),或者使用 [`process_start()`](https://fuchsia.dev/docs/reference/syscalls/process_start)传递一个句柄作为新进程中第一个线程的参数。对于几乎所有的对象,当最后一个打开的引用对象的句柄关闭时,对象要么被销毁,要么被置于可能无法撤消的最终状态。 +绝大多数内核对象的析构都发生在句柄数量为 0 时,也就是最后一个指向内核对象的Handle被关闭,该对象也随之消亡,抑或进入一种无法撤销的最终状态。很明显,这与Arc天然的契合。 +## 控制句柄的权限——Rights +上文的Handle中有一个字段是rights,也就是句柄的权限。顾名思义,权限规定该句柄对引用的对象可以进行何种操作。 -在 `Cargo.toml` 中加入 `bitflags` 库: +当不同的权限和同一个对象绑定在一起时,也就形成了不同的句柄。 -```rust,noplaypen -[dependencies] -{{#include ../../code/ch01-02/Cargo.toml:bitflags}} -``` +### 定义权限 -在 object 模块下定义两个子模块: +在 object 模块下定义一个子模块: -```rust,noplaypen +```` // src/object/mod.rs -{{#include ../../code/ch01-02/src/object/mod.rs:mod}} -``` +mod rights; -定义权限: +pub use self::rights::*; +```` -```rust,noplaypen +权限就是u32的一个数字 + + +``` // src/object/rights.rs -{{#include ../../code/ch01-02/src/object/rights.rs:rights}} +use bitflags::bitflags; + +bitflags! { + /// 句柄权限 + pub struct Rights: u32 { + const DUPLICATE = 1 << 0; + const TRANSFER = 1 << 1; + const READ = 1 << 2; + const WRITE = 1 << 3; + const EXECUTE = 1 << 4; + ... + } + ``` -定义句柄: +[**bitflags**](https://docs.rs/bitflags/1.2.1/bitflags/) 是一个 Rust 中常用来比特标志位的 crate 。它提供了 一个 `bitflags!` 宏,如上面的代码段所展示的那样,借助 `bitflags!` 宏我们将一个 `u32` 的 rights 包装为一个 `Rights` 结构体。注意,在使用之前我们需要引入该 crate 的依赖: -```rust,noplaypen -// src/object/handle.rs -{{#include ../../code/ch01-02/src/object/handle.rs:handle}} ``` +# Cargo.toml -## 存储内核对象句柄 +[dependencies] +bitflags = "1.2" +``` -> 添加成员变量 handles: BTreeMap -> -> 实现 create,add_handle,remove_handle 函数 +定义好权限之后,我们回到句柄相关方法的实现。 -使用上一节的方法,实现一个空的 Process 对象: +首先是最简单的部分,创建一个handle,很显然我们需要提供两个参数,分别是句柄关联的内核对象和句柄的权限。 -```rust,noplaypen -// src/task/process.rs -{{#include ../../code/ch01-02/src/task/process.rs:process}} +``` +impl Handle { + /// 创建一个新句柄 + pub fn new(object: Arc, rights: Rights) -> Self { + Handle { object, rights } + } +} +``` + +### 测试 + +好啦,让我们来测试一下! + +``` +#[cfg(test)] +mod tests { + use super::*; + use crate::object::DummyObject; + + #[test] + fn new_obj_handle() { + let obj = DummyObject::new(); + let handle1 = Handle::new(obj.clone(), Rights::BASIC); + } } ``` -插入、删除句柄函数: +## 句柄存储的载体——Process + +实现完了句柄之后,我们开始考虑,句柄是存储在哪里的呢? + +通过前面的讲解,很明显Process拥有内核对象句柄,也就是说,句柄存储在Process中,所以我们先来实现一个Process: -```rust,noplaypen +### 实现空的process对象 + +``` // src/task/process.rs -impl Process { -{{#include ../../code/ch01-02/src/task/process.rs:add_remove_handle}} +/// 进程对象 +pub struct Process { + base: KObjectBase, + inner: Mutex, +} +// 宏的作用:补充 +impl_kobject!(Process); + +struct ProcessInner { + handles: BTreeMap, } + +pub type HandleValue = u32; ``` -## 定义内核错误及 `Result` 类型 +handles使用BTreeMap存储的key是HandleValue,value就是句柄。通过HandleValue实现对句柄的增删操作。HandleValue实际上就是u32类型是别名。 + +把内部对象ProcessInner用自旋锁Mutex包起来,保证了互斥访问,因为Mutex会帮我们处理好并发问题,这一点已经在1.1节中详细说明。 + +接下来我们实现创建一个Process的方法: + +``` +impl Process { + /// 创建一个新的进程对象 + pub fn new() -> Arc { + Arc::new(Process { + base: KObjectBase::default(), + inner: Mutex::new(ProcessInner { + handles: BTreeMap::default(), + }), + }) + } +} +``` -```rust,noplaypen -// src/error.rs -{{#include ../../code/ch01-02/src/error.rs:error_begin}} +#### 单元测试 - // ...... +我们已经实现了创建一个Process的方法,下面我们写一个单元测试: -{{#include ../../code/ch01-02/src/error.rs:error_end}} ``` +#[test] + fn new_proc() { + let proc = Process::new(); + assert_eq!(proc.type_name(), "Process"); + assert_eq!(proc.name(), ""); + proc.set_name("proc1"); + assert_eq!(proc.name(), "proc1"); + assert_eq!( + format!("{:?}", proc), + format!("Process({}, \"proc1\")", proc.id()) + ); + + let obj: Arc = proc; + assert_eq!(obj.type_name(), "Process"); + assert_eq!(obj.name(), "proc1"); + obj.set_name("proc2"); + assert_eq!(obj.name(), "proc2"); + assert_eq!( + format!("{:?}", obj), + format!("Process({}, \"proc2\")", obj.id()) + ); + } +``` + +### Process相关方法 + +#### 插入句柄 -```rust,noplaypen -// src/error.rs -{{#include ../../code/ch01-02/src/error.rs:result}} +在Process中添加一个新的handle,返回值是一个handleValue,也就是u32: + +``` +pub fn add_handle(&self, handle: Handle) -> HandleValue { + + let mut inner = self.inner.lock(); + let value = (0 as HandleValue..) + .find(|idx| !inner.handles.contains_key(idx)) + .unwrap(); + // 插入BTreeMap + inner.handles.insert(value, handle); + value + } ``` -## 根据句柄查找内核对象 +#### 移除句柄 + +删除Process中的一个句柄: + +``` +pub fn remove_handle(&self, handle_value: HandleValue) { + self.inner.lock().handles.remove(&handle_value); +} +``` -> 实现 get_object_with_rights 等其它相关函数 -> -> 实现 handle 单元测试 +#### 根据句柄查找内核对象 -```rust,noplaypen +``` // src/task/process.rs impl Process { -{{#include ../../code/ch01-02/src/task/process.rs:get_object_with_rights}} + /// 根据句柄值查找内核对象,并检查权限 + pub fn get_object_with_rights( + &self, + handle_value: HandleValue, + desired_rights: Rights, + ) -> ZxResult> { + let handle = self + .inner + .lock() + .handles + .get(&handle_value) + .ok_or(ZxError::BAD_HANDLE)? + .clone(); + // check type before rights + let object = handle + .object + .downcast_arc::() + .map_err(|_| ZxError::WRONG_TYPE)?; + if !handle.rights.contains(desired_rights) { + return Err(ZxError::ACCESS_DENIED); + } + Ok(object) + } +} +``` + +#### ZxResult + +ZxResult是表示Zircon状态的i32值,值空间划分如下: + +- 0:ok +- 负值:由系统定义(也就是这个文件) +- 正值:被保留,用于协议特定的错误值,永远不会被系统定义。 + +``` +pub type ZxResult = Result; + +#[allow(non_camel_case_types, dead_code)] +#[repr(i32)] +#[derive(Debug, Clone, Copy)] +pub enum ZxError { + OK = 0, + ... + + /// 一个不指向handle的特定的handle value + BAD_HANDLE = -11, + + /// 操作主体对于执行这个操作来说是错误的类型 + /// 例如: 尝试执行 message_read 在 thread handle. + WRONG_TYPE = -12, + + // 权限检查错误 + // 调用者没有执行该操作的权限 + ACCESS_DENIED = -30, } ``` + +ZxResult相当于Result,也就相当于我们自己定义了一种错误。 + +### 单元测试 + +目前为止,我们已经实现了Process最基础的方法,下面我们来运行一个单元测试: + +``` +fn proc_handle() { + let proc = Process::new(); + let handle = Handle::new(proc.clone(), Rights::DEFAULT_PROCESS); + let handle_value = proc.add_handle(handle); + + let object1: Arc = proc + .get_object_with_rights(handle_value, Rights::DEFAULT_PROCESS) + .expect("failed to get object"); + assert!(Arc::ptr_eq(&object1, &proc)); + + proc.remove_handle(handle_value); + } +``` + +## 总结 + +在这一节中我们实现了内核对象的两个重要的概念,句柄(Handle)和权限(Rights),同时实现了句柄存储的载体——Process,并且实现了Process的基本方法,这将是我们继续探索zCore的基础。 + +在下一节中,我们将介绍内核对象的传输器——管道(Channel)。