From d678ab9f2abdfc12702a642d83291fe41925ee82 Mon Sep 17 00:00:00 2001 From: ma junfei <142782409+ekkure@users.noreply.github.com> Date: Mon, 18 Nov 2024 19:35:12 +0800 Subject: [PATCH] add blog (#617) --- ...5-\351\251\254\344\277\212\351\243\236.md" | 174 ++++++++++++++++++ ...5-\351\251\254\344\277\212\351\243\236.md" | 158 ++++++++++++++++ 2 files changed, 332 insertions(+) create mode 100644 "source/_posts/2024\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\347\254\254\344\270\200\351\230\266\346\256\265-\351\251\254\344\277\212\351\243\236.md" create mode 100644 "source/_posts/2024\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\347\254\254\344\272\214\351\230\266\346\256\265-\351\251\254\344\277\212\351\243\236.md" diff --git "a/source/_posts/2024\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\347\254\254\344\270\200\351\230\266\346\256\265-\351\251\254\344\277\212\351\243\236.md" "b/source/_posts/2024\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\347\254\254\344\270\200\351\230\266\346\256\265-\351\251\254\344\277\212\351\243\236.md" new file mode 100644 index 0000000000..49a4465494 --- /dev/null +++ "b/source/_posts/2024\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\347\254\254\344\270\200\351\230\266\346\256\265-\351\251\254\344\277\212\351\243\236.md" @@ -0,0 +1,174 @@ +--- + +title: rust体会 +date: 2024-11-11 +categories: + - rust language +tags: + - author:ekkure + - repo:https://github.com/ekkure/blog +--- +### Rust编程技巧与示例:宏、算法与类型转换 + +在Rust编程中,有许多细节和技巧可以帮助开发者更好地组织代码、优化算法性能,以及确保类型安全。本篇博客汇总了一些Rust编程的核心要点和实用代码示例,涵盖了宏的使用、排序算法、树和图的操作等内容。 + +--- + +### 1. 宏与#[macro_export]、#[macro_use] + +Rust中的宏非常强大,用于生成重复代码和提升代码的灵活性。使用`#[macro_export]`可以导出宏,使其在其他模块或包中可用;而`#[macro_use]`则在现代Rust中被推荐通过`use`语句显式引入。 + +示例宏定义: +```rust +#[rustfmt::skip] +macro_rules! my_macro { + () => { println!("Check out my macro!"); }; + ($val:expr) => { println!("Look at this other macro: {}", $val); }; +} +``` + +这里的`#[rustfmt::skip]`用于避免自动格式化,保持代码样式的灵活性和可读性。 + +--- + +### 2. Rust中的类型与特性 + +在实现数据结构或算法时,我们通常需要对泛型类型T施加一些特性约束,例如: +- `Ord`:使得元素可以比较大小,适用于排序、合并等操作。 +- `Clone`:便于复制元素值,即使是复杂类型,也可以无所有权转移地复制。 +- `Display`:实现字符串友好的格式化输出,便于打印和日志记录。 + +这些特性可以通过`where`语句在泛型实现中指定: +```rust +impl LinkedList +where T: Ord + Clone + Display +``` + +--- + +### 3. 内存操作与指针 + +Rust通过`unsafe`块支持手动管理内存和指针操作,用于高性能或底层操作。 +例如,获取节点的指针并解引用: +```rust +let node_ptr = Some(unsafe { NonNull::new_unchecked(Box::into_raw(node)) }); +res.add((*node_ptr.as_ptr()).val.clone()); +cur_a = (*node_ptr.as_ptr()).next; // 注意这里直接获取的是ta的next指针 +``` + +指针的安全解包和操作要格外小心,可以使用`Option`配合`unsafe`避免空指针风险。 + +--- + +### 4. 算法设计示例 + +#### 4.1 链表与树的操作 + +##### 插入与查找 +在链表或树结构中,我们经常用到`Option`类型来表示节点的存在与否。例如,在插入和查找二叉树中,可以选择使用`if let`语句来处理`Some`和`None`的情况: +```rust +fn insert(&mut self, value: T) { + if let Some(ref mut node) = self.root { + node.insert(value); + } else { + self.root = Some(Box::new(TreeNode::new(value))); + } +} +``` +这种写法在处理可变引用时尤其简洁。 + +#### 4.2 排序算法与Ord与PartialOrd + +选择排序等算法需要比较泛型元素的大小,通常需要`PartialOrd`特性来支持部分排序(如非全序关系的情况),而对于要求全序的场景可以使用`Ord`。 + +#### 4.3 深度优先与广度优先搜索 + +在图算法中,深度优先搜索(DFS)和广度优先搜索(BFS)是两种基础的遍历方式: +- DFS示例: + ```rust + fn dfs_util(&self, v: usize, visited: &mut HashSet, visit_order: &mut Vec) { + visited.insert(v); + visit_order.push(v); + for &nei in self.adj[v].iter() { + if !visited.contains(&nei) { + self.dfs_util(nei, visited, visit_order); + } + } + } + ``` + +- BFS示例: + ```rust + fn bfs_with_return(&self, start: usize) -> Vec { + let mut visit_order = vec![]; + let mut visited = vec![false; self.adj.len()]; + let mut queue = VecDeque::new(); + queue.push_back(start); + visited[start] = true; + + while let Some(node) = queue.pop_front() { + visit_order.push(node); + for &neighbor in &self.adj[node] { + if !visited[neighbor] { + visited[neighbor] = true; + queue.push_back(neighbor); + } + } + } + visit_order + } + ``` + +#### 4.4 平衡堆的插入与调整 + +Rust标准库中`Vec`的`swap_remove`方法可以高效地删除指定位置的元素,适用于实现优先队列等堆结构: +```rust +let result = self.items.swap_remove(1); // 移除并返回指定位置的元素 +``` + +在删除元素后,可以通过调整堆结构(如最小/最大堆)来保持堆的性质。 + +--- + +### 5. 实现栈与队列 + +使用双队列实现栈的操作逻辑: +```rust +pub struct myStack { + q1: Queue, + q2: Queue +} + +impl myStack { + pub fn push(&mut self, elem: T) { + self.q2.enqueue(elem); + while !self.q1.is_empty() { + self.q2.enqueue(self.q1.dequeue().unwrap()); + } + std::mem::swap(&mut self.q1, &mut self.q2); + } +} +``` + +这种方法利用队列的FIFO特性来模拟栈的LIFO特性。 + +--- + +### 6. 函数与内存管理 + +Rust中的`Box`和`unsafe`结合用于手动管理堆内存。`Box::from_raw`可以从裸指针重新创建`Box`,这在需要手动内存管理的场景中非常有用。 +```rust +unsafe fn raw_pointer_to_box(ptr: *mut Foo) -> Box { + let mut ret: Box = unsafe { Box::from_raw(ptr) }; + ret.b = Some(String::from("hello")); + ret +} +``` + +这种方法常用于FFI(外部函数接口)中将指针恢复为拥有所有权的Rust类型。 + +--- + +### 总结 + +Rust语言通过丰富的内存管理工具和类型系统,确保了在安全性和性能上的平衡。无论是自定义数据结构还是排序、图遍历等基础算法,Rust的特性可以为代码提供极大的灵活性和安全保障。 \ No newline at end of file diff --git "a/source/_posts/2024\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\347\254\254\344\272\214\351\230\266\346\256\265-\351\251\254\344\277\212\351\243\236.md" "b/source/_posts/2024\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\347\254\254\344\272\214\351\230\266\346\256\265-\351\251\254\344\277\212\351\243\236.md" new file mode 100644 index 0000000000..44d97b6544 --- /dev/null +++ "b/source/_posts/2024\347\247\213\345\206\254\345\255\243\345\274\200\346\272\220\346\223\215\344\275\234\347\263\273\347\273\237\350\256\255\347\273\203\350\220\245\347\254\254\344\272\214\351\230\266\346\256\265-\351\251\254\344\277\212\351\243\236.md" @@ -0,0 +1,158 @@ +--- + +title: 调度与死锁 +date: 2024-11-11 +categories: + - os +tags: + - author:ekkure + - repo:https://github.com/ekkure/blog + +--- +## 调度 + +#### **三级调度:** + +作业调度、高级调度(频次最低):主要解决:接纳多少个任务+接纳那哪些任务这两个工作 + +进程调度、低级调度(频次最高): 必须有、**核心**,**确定哪个进程可以占有CPU并执行** + +中级调度:将那些暂时不能运行的进程从内存挂起到外存,(阻塞状态下进程实体(程序 + 数据 + PCB)还在内存中,而挂起状态会把进程实体挂到外存,但是PCB会存在系统内核空间中,会记录进程在外存的状态以及位置),一般在**内存紧张**时使用 + + + +高级调度,用于批处理系统中,将任务从外存调度到内存中去。(在分时/实时系统中,任务是直接在内存,因此没有高级调度) + +分时系统:**只有进程调度** + +批处理系统:**进程调度 + 作业调度** + + + +#### 调度算法相关 + +准则:周转时间(常用于**批处理系统**)、平均周转时间、带权周转时间 + +响应时间(交互性作业、分时系统)、截止时间的保证(实时系统)、优先权准则 + +周转时间 = 完成时间 - 到达时间 + +带权周转时间 = 周转时间 / 服务时间 + + +**调度算法** + +- FCFS(first come first serve), SJ(P)F等等 +对于抢占式调度,注意**服务时间的更新,然后再比较,看谁抢** + +- 高优先权 优先调度算法 + + 静态优先权:简单,但存在饥饿现象 + + 动态优先权:eg Rp = (等待时间 + 服务时间)/ 服务时间 作为优先权 1 + tw / ts; + +- 时间片轮转 ......? + + 多级反馈队列 S1 < S2 < S3 优先权 S1 > S2 > S3 + + + +- 实时调度 + + 非抢占:轮转 || 优先权 + + 抢占:基于中断时钟,好处是减少了上下文保存切换的次数 + + ​ 立即抢占 + + 实时调度算法:EDF、LLF,还有例题 + +- 其他一些?? + + MPS:CPU共享内存, 共享缓存(单个儿独立的,容易出现绑定,忙闲不均) + + SMP中进程分配方式:静态分配和动态分配 + + ​ 调度方式: 自调度和成组调度(两种方式就对应了用户级线程和系统级线程), 专用处理机分配? + + +## 死锁 + +**一些定义**: + +- 可剥夺资源:如主存,CPU,可以在使用时被强占的资源 +- 不可剥夺资源:不可被打断抢占的资源,如驱动器,打印机 +- 永久资源(外存),临时资源(进程运行过程中临时产生的数据资源等等) + +**竞争非剥夺资源,或者竞争临时资源可导致死锁** + +### 死锁的必要条件 + +- 互斥条件:进程互斥的使用临界资源 +- 不剥夺条件(不可抢占) +- 请求-保持条件:进程在申请新的资源的同时,保持对某些资源的占有 +- 环路等待:循环等待链 + + + + + +### 解决死锁的方法 + +从严格依次降低,为 + +预防 -> 避免 -> 检测与解除 + +#### 预防 + +上面4个条件是死锁的必要条件 , Deadlock -> 4 其逆否命题为 !4 -> !Deadlock,所以我们从4个条件入手 + +1. 互斥,并没有好的办法 +2. 不抢占:不抢占变成"抢占",如果进程申请不到全部资源时,主动释放 +3. 请求保持条件:使用AND机制,但是有点浪费资源 +4. 环路等待:破除环路,资源排序,参考哲学家进餐 + +#### 避免死锁 + +**这是比较中庸的做法,既不损耗很多的效率,也比较的严格** + +##### 银行家算法 + +一种是,资源分配表,为 + +| Process | Allocation | Need | Available | +| ------- | ---------- | ---- | --------- | +| | | | | + +另一种是,计算表 + +| Work | Need | Allocation | work + Allocation | Finish | +| ---- | ---- | ---------- | ----------------- | ------ | +| | | | | | + +**对资源进行分配时,分成两步** + +1. 判断分配请求 R是否满足 R < Available && R < Need +2. 如果满足1,使用表1表示分配后的资源表T1,再次计算是否存在安全序列,如果不安全,退回至T0,否则保存T1,下次分配将从T1开始。 + +#### 检测和解除 + +使用方法:**资源分配图** + +**几个结论** + +- 不可完全简化 => 存在死锁 +- 分配图中无环 => 不会存在死锁 +- 分配图中有环 => 不一定死锁 + +简化方法,对一个资源分配图,首先考虑持有边,如果持有者线程能够完成(获得所有需要的资源),将持有边消去后,将资源返回,如果不能完成,边消去后,仍保持资源占有,直到完成。 + +然后考虑请求边,如果请求的资源有空闲的,可以把边消去,若请求线程能够完成,则可将该资源返回,否则保持占有 + +重复上述过程,直至卡住,或者全部成孤立。 + +**解除** + +通过撤销进程或者挂起进程来释放一些资源,进而推动僵持状态。 + +而具体的对哪些进程,以什么样的顺序进行操作,可以参考`Dijkstra`之类的算法,找到一种损耗最小、利益最大的方法。