|
|
<!DOCTYPE HTML>
|
|
|
<html lang="cn" class="sidebar-visible no-js light">
|
|
|
<head>
|
|
|
<!-- Book generated using mdBook -->
|
|
|
<meta charset="UTF-8">
|
|
|
<title>🚧 对象管理器:Process 对象 - 简明 zCore 教程</title>
|
|
|
|
|
|
|
|
|
<!-- Custom HTML head -->
|
|
|
|
|
|
|
|
|
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
|
|
<meta name="description" content="">
|
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
|
<meta name="theme-color" content="#ffffff" />
|
|
|
|
|
|
<link rel="icon" href="favicon.svg">
|
|
|
<link rel="shortcut icon" href="favicon.png">
|
|
|
<link rel="stylesheet" href="css/variables.css">
|
|
|
<link rel="stylesheet" href="css/general.css">
|
|
|
<link rel="stylesheet" href="css/chrome.css">
|
|
|
<link rel="stylesheet" href="css/print.css" media="print">
|
|
|
|
|
|
<!-- Fonts -->
|
|
|
<link rel="stylesheet" href="FontAwesome/css/font-awesome.css">
|
|
|
<link rel="stylesheet" href="fonts/fonts.css">
|
|
|
|
|
|
<!-- Highlight.js Stylesheets -->
|
|
|
<link rel="stylesheet" href="highlight.css">
|
|
|
<link rel="stylesheet" href="tomorrow-night.css">
|
|
|
<link rel="stylesheet" href="ayu-highlight.css">
|
|
|
|
|
|
<!-- Custom theme stylesheets -->
|
|
|
|
|
|
</head>
|
|
|
<body>
|
|
|
<!-- Provide site root to javascript -->
|
|
|
<script type="text/javascript">
|
|
|
var path_to_root = "";
|
|
|
var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "navy" : "light";
|
|
|
</script>
|
|
|
|
|
|
<!-- Work around some values being stored in localStorage wrapped in quotes -->
|
|
|
<script type="text/javascript">
|
|
|
try {
|
|
|
var theme = localStorage.getItem('mdbook-theme');
|
|
|
var sidebar = localStorage.getItem('mdbook-sidebar');
|
|
|
|
|
|
if (theme.startsWith('"') && theme.endsWith('"')) {
|
|
|
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
|
|
|
}
|
|
|
|
|
|
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
|
|
|
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
|
|
|
}
|
|
|
} catch (e) { }
|
|
|
</script>
|
|
|
|
|
|
<!-- Set the theme before any content is loaded, prevents flash -->
|
|
|
<script type="text/javascript">
|
|
|
var theme;
|
|
|
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
|
|
|
if (theme === null || theme === undefined) { theme = default_theme; }
|
|
|
var html = document.querySelector('html');
|
|
|
html.classList.remove('no-js')
|
|
|
html.classList.remove('light')
|
|
|
html.classList.add(theme);
|
|
|
html.classList.add('js');
|
|
|
</script>
|
|
|
|
|
|
<!-- Hide / unhide sidebar before it is displayed -->
|
|
|
<script type="text/javascript">
|
|
|
var html = document.querySelector('html');
|
|
|
var sidebar = 'hidden';
|
|
|
if (document.body.clientWidth >= 1080) {
|
|
|
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
|
|
|
sidebar = sidebar || 'visible';
|
|
|
}
|
|
|
html.classList.remove('sidebar-visible');
|
|
|
html.classList.add("sidebar-" + sidebar);
|
|
|
</script>
|
|
|
|
|
|
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
|
|
<div class="sidebar-scrollbox">
|
|
|
<ol class="chapter"><li class="chapter-item expanded affix "><a href="index.html">简明 zCore 教程</a></li><li class="chapter-item expanded affix "><a href="zcore-intro.html">🚧 zCore 整体结构和设计模式</a></li><li class="chapter-item expanded affix "><a href="fuchsia.html">🚧 Fuchsia OS 和 Zircon 微内核</a></li><li class="chapter-item expanded "><a href="ch01-00-object.html"><strong aria-hidden="true">1.</strong> 内核对象</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="ch01-01-kernel-object.html"><strong aria-hidden="true">1.1.</strong> ✅ 初识内核对象</a></li><li class="chapter-item expanded "><a href="ch01-02-process-object.html" class="active"><strong aria-hidden="true">1.2.</strong> 🚧 对象管理器:Process 对象</a></li><li class="chapter-item expanded "><a href="ch01-03-channel-object.html"><strong aria-hidden="true">1.3.</strong> 🚧 对象传送器:Channel 对象</a></li></ol></li><li class="chapter-item expanded "><a href="ch02-00-task.html"><strong aria-hidden="true">2.</strong> 任务管理</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="ch02-01-zircon-task.html"><strong aria-hidden="true">2.1.</strong> 🚧 Zircon 任务管理体系</a></li><li class="chapter-item expanded "><a href="ch02-02-process-job-object.html"><strong aria-hidden="true">2.2.</strong> 🚧 进程管理:Process 与 Job 对象</a></li><li class="chapter-item expanded "><a href="ch02-03-thread-object.html"><strong aria-hidden="true">2.3.</strong> 🚧 线程管理:Thread 对象</a></li></ol></li><li class="chapter-item expanded "><a href="ch03-00-memory.html"><strong aria-hidden="true">3.</strong> 内存管理</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="ch03-01-zircon-memory.html"><strong aria-hidden="true">3.1.</strong> 🚧 Zircon 内存管理模型</a></li><li class="chapter-item expanded "><a href="ch03-02-vmo.html"><strong aria-hidden="true">3.2.</strong> 🚧 物理内存:VMO 对象</a></li><li class="chapter-item expanded "><a href="ch03-03-vmo-paged.html"><strong aria-hidden="true">3.3.</strong> 🚧 物理内存:按页分配的 VMO</a></li><li class="chapter-item expanded "><a href="ch03-04-vmar.html"><strong aria-hidden="true">3.4.</strong> 🚧 虚拟内存:VMAR 对象</a></li></ol></li><li class="chapter-item expanded "><a href="ch04-00-userspace.html"><strong aria-hidden="true">4.</strong> 用户程序</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="ch04-01-user-program.html"><strong aria-hidden="true">4.1.</strong> 🚧 Zircon 用户程序</a></li><li class="chapter-item expanded "><a href="ch04-02-context-switch.html"><strong aria-hidden="true">4.2.</strong> 🚧 上下文切换</a></li><li class="chapter-item expanded "><a href="ch04-03-syscall.html"><strong aria-hidden="true">4.3.</strong> 🚧 系统调用</a></li></ol></li><li class="chapter-item expanded "><a href="ch05-00-signal-and-waiting.html"><strong aria-hidden="true">5.</strong> 信号和等待</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="ch05-01-wait-signal.html"><strong aria-hidden="true">5.1.</strong> 🚧 等待内核对象的信号</a></li><li class="chapter-item expanded "><a href="ch05-02-port-object.html"><strong aria-hidden="true">5.2.</strong> 🚧 同时等待多个信号:Port 对象</a></li><li class="chapter-item expanded "><a href="ch05-03-more-signal-objects.html"><strong aria-hidden="true">5.3.</strong> 🚧 实现更多:EventPair, Timer 对象</a></li><li class="chapter-item expanded "><a href="ch05-04-futex-object.html"><strong aria-hidden="true">5.4.</strong> 🚧 用户态同步互斥:Futex 对象</a></li></ol></li><li class="chapter-item expanded "><a href="ch06-00-hal.html"><strong aria-hidden="true">6.</strong> 硬件抽象层</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="ch06-01-zcore-hal-unix.html"><strong aria-hidden="true">6.1.</strong> ✅ UNIX硬件抽象层</a></li></ol></li></ol> </div>
|
|
|
<div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
|
|
|
</nav>
|
|
|
|
|
|
<div id="page-wrapper" class="page-wrapper">
|
|
|
|
|
|
<div class="page">
|
|
|
|
|
|
<div id="menu-bar-hover-placeholder"></div>
|
|
|
<div id="menu-bar" class="menu-bar sticky bordered">
|
|
|
<div class="left-buttons">
|
|
|
<button id="sidebar-toggle" class="icon-button" type="button" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
|
|
|
<i class="fa fa-bars"></i>
|
|
|
</button>
|
|
|
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
|
|
|
<i class="fa fa-paint-brush"></i>
|
|
|
</button>
|
|
|
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
|
|
|
<li role="none"><button role="menuitem" class="theme" id="light">Light (default)</button></li>
|
|
|
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
|
|
|
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
|
|
|
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
|
|
|
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
|
|
|
</ul>
|
|
|
<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
|
|
|
<i class="fa fa-search"></i>
|
|
|
</button>
|
|
|
</div>
|
|
|
|
|
|
<h1 class="menu-title">简明 zCore 教程</h1>
|
|
|
|
|
|
<div class="right-buttons">
|
|
|
<a href="print.html" title="Print this book" aria-label="Print this book">
|
|
|
<i id="print-button" class="fa fa-print"></i>
|
|
|
</a>
|
|
|
<a href="https://github.com/rcore-os/zCore-Tutorial" title="Git repository" aria-label="Git repository">
|
|
|
<i id="git-repository-button" class="fa fa-github"></i>
|
|
|
</a>
|
|
|
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
<div id="search-wrapper" class="hidden">
|
|
|
<form id="searchbar-outer" class="searchbar-outer">
|
|
|
<input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
|
|
|
</form>
|
|
|
<div id="searchresults-outer" class="searchresults-outer hidden">
|
|
|
<div id="searchresults-header" class="searchresults-header"></div>
|
|
|
<ul id="searchresults">
|
|
|
</ul>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
|
|
|
<script type="text/javascript">
|
|
|
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
|
|
|
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
|
|
|
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
|
|
|
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
|
|
|
});
|
|
|
</script>
|
|
|
|
|
|
<div id="content" class="content">
|
|
|
<main>
|
|
|
<h4 id="对象管理器process-对象"><a class="header" href="#对象管理器process-对象">对象管理器:Process 对象</a></h4>
|
|
|
<h2 id="权限"><a class="header" href="#权限">权限</a></h2>
|
|
|
<p>内核对象的“<a href="https://fuchsia.dev/docs/concepts/kernel/rights">权限</a>”指定允许对内核对象进行哪些操作。权限与句柄相关联,并传达对关联句柄或与句柄关联的对象执行操作的特权。单个进程可能对具有不同权限的同一个内核对象有两个不同的句柄。</p>
|
|
|
<h2 id="句柄"><a class="header" href="#句柄">句柄</a></h2>
|
|
|
<p>句柄是允许用户程序引用内核对象引用的一种内核结构,它可以被认为是与特定内核对象的会话或连接。</p>
|
|
|
<p>通常情况下,多个进程通过不同的句柄同时访问同一个对象。对象可能有多个句柄(在一个或多个进程中)引用它们。但单个句柄只能绑定到单个进程或绑定到内核。</p>
|
|
|
<p>当句柄绑定到内核时,我们说它是“在传输中”('in-transit')。</p>
|
|
|
<p>在用户模式下,句柄只是某个系统调用返回的特定数字。只有“不在传输中”的句柄对用户模式可见。</p>
|
|
|
<p>代表句柄的整数只对其所属的那个进程有意义。另一个进程中的相同数字可能不会映射到任何句柄,或者它可能映射到指向完全不同的内核对象的句柄。</p>
|
|
|
<p>句柄的整数值是任何 32 位数字,但对应于<strong>ZX_HANDLE_INVALID</strong>的值将始终为 0。除此之外,有效句柄的整数值将始终具有句柄集的两个最低有效位. 可以使用<strong>ZX_HANDLE_FIXED_BITS_MASK</strong>访问代表这些位的掩码。</p>
|
|
|
<p>句柄可以从一个进程移动到另一个进程,方法是将它们写入通道(使用<a href="https://fuchsia.dev/docs/reference/syscalls/channel_write"><code>channel_write()</code></a>),或者使用 <a href="https://fuchsia.dev/docs/reference/syscalls/process_start"><code>process_start()</code></a>传递一个句柄作为新进程中第一个线程的参数。对于几乎所有的对象,当最后一个打开的引用对象的句柄关闭时,对象要么被销毁,要么被置于可能无法撤消的最终状态。</p>
|
|
|
<p>在 <code>Cargo.toml</code> 中加入 <code>bitflags</code> 库:</p>
|
|
|
<pre><code class="language-rust noplaypen">[dependencies]
|
|
|
bitflags = "1.2"
|
|
|
</code></pre>
|
|
|
<p>在 object 模块下定义两个子模块:</p>
|
|
|
<pre><code class="language-rust noplaypen">// src/object/mod.rs
|
|
|
mod handle;
|
|
|
mod rights;
|
|
|
|
|
|
pub use self::handle::*;
|
|
|
pub use self::rights::*;
|
|
|
</code></pre>
|
|
|
<p>定义权限:</p>
|
|
|
<pre><code class="language-rust noplaypen">// src/object/rights.rs
|
|
|
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;
|
|
|
const MAP = 1 << 5;
|
|
|
const GET_PROPERTY = 1 << 6;
|
|
|
const SET_PROPERTY = 1 << 7;
|
|
|
const ENUMERATE = 1 << 8;
|
|
|
const DESTROY = 1 << 9;
|
|
|
const SET_POLICY = 1 << 10;
|
|
|
const GET_POLICY = 1 << 11;
|
|
|
const SIGNAL = 1 << 12;
|
|
|
const SIGNAL_PEER = 1 << 13;
|
|
|
const WAIT = 1 << 14;
|
|
|
const INSPECT = 1 << 15;
|
|
|
const MANAGE_JOB = 1 << 16;
|
|
|
const MANAGE_PROCESS = 1 << 17;
|
|
|
const MANAGE_THREAD = 1 << 18;
|
|
|
const APPLY_PROFILE = 1 << 19;
|
|
|
const SAME_RIGHTS = 1 << 31;
|
|
|
|
|
|
const BASIC = Self::TRANSFER.bits | Self::DUPLICATE.bits | Self::WAIT.bits | Self::INSPECT.bits;
|
|
|
const IO = Self::READ.bits | Self::WRITE.bits;
|
|
|
|
|
|
const DEFAULT_CHANNEL = Self::BASIC.bits & !Self::DUPLICATE.bits | Self::IO.bits | Self::SIGNAL.bits | Self::SIGNAL_PEER.bits;
|
|
|
/// GET_PROPERTY | SET_PROPERTY
|
|
|
const PROPERTY = Self::GET_PROPERTY.bits | Self::SET_PROPERTY.bits;
|
|
|
/// BASIC | IO | PROPERTY | ENUMERATE | DESTROY | SIGNAL | MANAGE_PROCESS | MANAGE_THREAD
|
|
|
const DEFAULT_PROCESS = Self::BASIC.bits | Self::IO.bits | Self::PROPERTY.bits | Self::ENUMERATE.bits | Self::DESTROY.bits | Self::SIGNAL.bits | Self::MANAGE_PROCESS.bits | Self::MANAGE_THREAD.bits;
|
|
|
}
|
|
|
}
|
|
|
</code></pre>
|
|
|
<p>定义句柄:</p>
|
|
|
<pre><code class="language-rust noplaypen">// src/object/handle.rs
|
|
|
use super::{DummyObject, KernelObject, Rights};
|
|
|
use alloc::sync::Arc;
|
|
|
|
|
|
/// 内核对象句柄
|
|
|
#[derive(Clone)]
|
|
|
pub struct Handle {
|
|
|
pub object: Arc<dyn KernelObject>,
|
|
|
pub rights: Rights,
|
|
|
}
|
|
|
|
|
|
impl Handle {
|
|
|
/// 创建一个新句柄
|
|
|
pub fn new(object: Arc<dyn KernelObject>, rights: Rights) -> Self {
|
|
|
Handle { object, rights }
|
|
|
}
|
|
|
}
|
|
|
</code></pre>
|
|
|
<h2 id="存储内核对象句柄"><a class="header" href="#存储内核对象句柄">存储内核对象句柄</a></h2>
|
|
|
<blockquote>
|
|
|
<p>添加成员变量 handles: BTreeMap<HandleValue, Handle></p>
|
|
|
<p>实现 create,add_handle,remove_handle 函数</p>
|
|
|
</blockquote>
|
|
|
<p>使用上一节的方法,实现一个空的 Process 对象:</p>
|
|
|
<pre><code class="language-rust noplaypen">// src/task/process.rs
|
|
|
/// 进程对象
|
|
|
pub struct Process {
|
|
|
base: KObjectBase,
|
|
|
inner: Mutex<ProcessInner>,
|
|
|
}
|
|
|
|
|
|
impl_kobject!(Process);
|
|
|
|
|
|
struct ProcessInner {
|
|
|
handles: BTreeMap<HandleValue, Handle>,
|
|
|
}
|
|
|
|
|
|
pub type HandleValue = u32;
|
|
|
|
|
|
impl Process {
|
|
|
/// 创建一个新的进程对象
|
|
|
pub fn new() -> Arc<Self> {
|
|
|
Arc::new(Process {
|
|
|
base: KObjectBase::default(),
|
|
|
inner: Mutex::new(ProcessInner {
|
|
|
handles: BTreeMap::default(),
|
|
|
}),
|
|
|
})
|
|
|
}
|
|
|
}
|
|
|
</code></pre>
|
|
|
<p>插入、删除句柄函数:</p>
|
|
|
<pre><code class="language-rust noplaypen">// src/task/process.rs
|
|
|
impl Process {
|
|
|
/// 添加一个新的对象句柄
|
|
|
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();
|
|
|
inner.handles.insert(value, handle);
|
|
|
value
|
|
|
}
|
|
|
|
|
|
/// 删除一个对象句柄
|
|
|
pub fn remove_handle(&self, handle_value: HandleValue) {
|
|
|
self.inner.lock().handles.remove(&handle_value);
|
|
|
}
|
|
|
}
|
|
|
</code></pre>
|
|
|
<h2 id="定义内核错误及-result-类型"><a class="header" href="#定义内核错误及-result-类型">定义内核错误及 <code>Result</code> 类型</a></h2>
|
|
|
<pre><code class="language-rust noplaypen">// src/error.rs
|
|
|
/// Zircon statuses are signed 32 bit integers. The space of values is
|
|
|
/// divided as follows:
|
|
|
/// - The zero value is for the OK status.
|
|
|
/// - Negative values are defined by the system, in this file.
|
|
|
/// - Positive values are reserved for protocol-specific error values,
|
|
|
/// and will never be defined by the system.
|
|
|
#[allow(non_camel_case_types, dead_code)]
|
|
|
#[repr(i32)]
|
|
|
#[derive(Debug, Clone, Copy)]
|
|
|
pub enum ZxError {
|
|
|
OK = 0,
|
|
|
|
|
|
// ======= Internal failures =======
|
|
|
/// The system encountered an otherwise unspecified error
|
|
|
/// while performing the operation.
|
|
|
INTERNAL = -1,
|
|
|
|
|
|
/// The operation is not implemented, supported,
|
|
|
/// or enabled.
|
|
|
NOT_SUPPORTED = -2,
|
|
|
|
|
|
// ......
|
|
|
|
|
|
/// Connection was aborted.
|
|
|
CONNECTION_ABORTED = -76,
|
|
|
}
|
|
|
</code></pre>
|
|
|
<pre><code class="language-rust noplaypen">// src/error.rs
|
|
|
///
|
|
|
pub type ZxResult<T> = Result<T, ZxError>;
|
|
|
</code></pre>
|
|
|
<h2 id="根据句柄查找内核对象"><a class="header" href="#根据句柄查找内核对象">根据句柄查找内核对象</a></h2>
|
|
|
<blockquote>
|
|
|
<p>实现 get_object_with_rights 等其它相关函数</p>
|
|
|
<p>实现 handle 单元测试</p>
|
|
|
</blockquote>
|
|
|
<pre><code class="language-rust noplaypen">// src/task/process.rs
|
|
|
impl Process {
|
|
|
/// 根据句柄值查找内核对象,并检查权限
|
|
|
pub fn get_object_with_rights<T: KernelObject>(
|
|
|
&self,
|
|
|
handle_value: HandleValue,
|
|
|
desired_rights: Rights,
|
|
|
) -> ZxResult<Arc<T>> {
|
|
|
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::<T>()
|
|
|
.map_err(|_| ZxError::WRONG_TYPE)?;
|
|
|
if !handle.rights.contains(desired_rights) {
|
|
|
return Err(ZxError::ACCESS_DENIED);
|
|
|
}
|
|
|
Ok(object)
|
|
|
}
|
|
|
}
|
|
|
</code></pre>
|
|
|
|
|
|
</main>
|
|
|
|
|
|
<nav class="nav-wrapper" aria-label="Page navigation">
|
|
|
<!-- Mobile navigation buttons -->
|
|
|
<a rel="prev" href="ch01-01-kernel-object.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
|
|
<i class="fa fa-angle-left"></i>
|
|
|
</a>
|
|
|
|
|
|
<a rel="next" href="ch01-03-channel-object.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
|
|
<i class="fa fa-angle-right"></i>
|
|
|
</a>
|
|
|
|
|
|
<div style="clear: both"></div>
|
|
|
</nav>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
|
|
<a rel="prev" href="ch01-01-kernel-object.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
|
|
<i class="fa fa-angle-left"></i>
|
|
|
</a>
|
|
|
|
|
|
<a rel="next" href="ch01-03-channel-object.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
|
|
<i class="fa fa-angle-right"></i>
|
|
|
</a>
|
|
|
</nav>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<script type="text/javascript">
|
|
|
window.playground_copyable = true;
|
|
|
</script>
|
|
|
|
|
|
<script src="ace.js" type="text/javascript" charset="utf-8"></script>
|
|
|
<script src="editor.js" type="text/javascript" charset="utf-8"></script>
|
|
|
<script src="mode-rust.js" type="text/javascript" charset="utf-8"></script>
|
|
|
<script src="theme-dawn.js" type="text/javascript" charset="utf-8"></script>
|
|
|
<script src="theme-tomorrow_night.js" type="text/javascript" charset="utf-8"></script>
|
|
|
|
|
|
<script src="elasticlunr.min.js" type="text/javascript" charset="utf-8"></script>
|
|
|
<script src="mark.min.js" type="text/javascript" charset="utf-8"></script>
|
|
|
<script src="searcher.js" type="text/javascript" charset="utf-8"></script>
|
|
|
|
|
|
<script src="clipboard.min.js" type="text/javascript" charset="utf-8"></script>
|
|
|
<script src="highlight.js" type="text/javascript" charset="utf-8"></script>
|
|
|
<script src="book.js" type="text/javascript" charset="utf-8"></script>
|
|
|
|
|
|
<!-- Custom JS scripts -->
|
|
|
|
|
|
|
|
|
</body>
|
|
|
</html>
|