Skip to content

Commit d678ab9

Browse files
authored
add blog (#617)
1 parent 0e33145 commit d678ab9

2 files changed

+332
-0
lines changed
Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
---
2+
3+
title: rust体会
4+
date: 2024-11-11
5+
categories:
6+
- rust language
7+
tags:
8+
- author:ekkure
9+
- repo:https://github.com/ekkure/blog
10+
---
11+
### Rust编程技巧与示例:宏、算法与类型转换
12+
13+
在Rust编程中,有许多细节和技巧可以帮助开发者更好地组织代码、优化算法性能,以及确保类型安全。本篇博客汇总了一些Rust编程的核心要点和实用代码示例,涵盖了宏的使用、排序算法、树和图的操作等内容。
14+
15+
---
16+
17+
### 1. 宏与#[macro_export]、#[macro_use]
18+
19+
Rust中的宏非常强大,用于生成重复代码和提升代码的灵活性。使用`#[macro_export]`可以导出宏,使其在其他模块或包中可用;而`#[macro_use]`则在现代Rust中被推荐通过`use`语句显式引入。
20+
21+
示例宏定义:
22+
```rust
23+
#[rustfmt::skip]
24+
macro_rules! my_macro {
25+
() => { println!("Check out my macro!"); };
26+
($val:expr) => { println!("Look at this other macro: {}", $val); };
27+
}
28+
```
29+
30+
这里的`#[rustfmt::skip]`用于避免自动格式化,保持代码样式的灵活性和可读性。
31+
32+
---
33+
34+
### 2. Rust中的类型与特性
35+
36+
在实现数据结构或算法时,我们通常需要对泛型类型T施加一些特性约束,例如:
37+
- `Ord`:使得元素可以比较大小,适用于排序、合并等操作。
38+
- `Clone`:便于复制元素值,即使是复杂类型,也可以无所有权转移地复制。
39+
- `Display`:实现字符串友好的格式化输出,便于打印和日志记录。
40+
41+
这些特性可以通过`where`语句在泛型实现中指定:
42+
```rust
43+
impl<T> LinkedList<T>
44+
where T: Ord + Clone + Display
45+
```
46+
47+
---
48+
49+
### 3. 内存操作与指针
50+
51+
Rust通过`unsafe`块支持手动管理内存和指针操作,用于高性能或底层操作。
52+
例如,获取节点的指针并解引用:
53+
```rust
54+
let node_ptr = Some(unsafe { NonNull::new_unchecked(Box::into_raw(node)) });
55+
res.add((*node_ptr.as_ptr()).val.clone());
56+
cur_a = (*node_ptr.as_ptr()).next; // 注意这里直接获取的是ta的next指针
57+
```
58+
59+
指针的安全解包和操作要格外小心,可以使用`Option`配合`unsafe`避免空指针风险。
60+
61+
---
62+
63+
### 4. 算法设计示例
64+
65+
#### 4.1 链表与树的操作
66+
67+
##### 插入与查找
68+
在链表或树结构中,我们经常用到`Option`类型来表示节点的存在与否。例如,在插入和查找二叉树中,可以选择使用`if let`语句来处理`Some``None`的情况:
69+
```rust
70+
fn insert(&mut self, value: T) {
71+
if let Some(ref mut node) = self.root {
72+
node.insert(value);
73+
} else {
74+
self.root = Some(Box::new(TreeNode::new(value)));
75+
}
76+
}
77+
```
78+
这种写法在处理可变引用时尤其简洁。
79+
80+
#### 4.2 排序算法与Ord与PartialOrd
81+
82+
选择排序等算法需要比较泛型元素的大小,通常需要`PartialOrd`特性来支持部分排序(如非全序关系的情况),而对于要求全序的场景可以使用`Ord`
83+
84+
#### 4.3 深度优先与广度优先搜索
85+
86+
在图算法中,深度优先搜索(DFS)和广度优先搜索(BFS)是两种基础的遍历方式:
87+
- DFS示例:
88+
```rust
89+
fn dfs_util(&self, v: usize, visited: &mut HashSet<usize>, visit_order: &mut Vec<usize>) {
90+
visited.insert(v);
91+
visit_order.push(v);
92+
for &nei in self.adj[v].iter() {
93+
if !visited.contains(&nei) {
94+
self.dfs_util(nei, visited, visit_order);
95+
}
96+
}
97+
}
98+
```
99+
100+
- BFS示例:
101+
```rust
102+
fn bfs_with_return(&self, start: usize) -> Vec<usize> {
103+
let mut visit_order = vec![];
104+
let mut visited = vec![false; self.adj.len()];
105+
let mut queue = VecDeque::new();
106+
queue.push_back(start);
107+
visited[start] = true;
108+
109+
while let Some(node) = queue.pop_front() {
110+
visit_order.push(node);
111+
for &neighbor in &self.adj[node] {
112+
if !visited[neighbor] {
113+
visited[neighbor] = true;
114+
queue.push_back(neighbor);
115+
}
116+
}
117+
}
118+
visit_order
119+
}
120+
```
121+
122+
#### 4.4 平衡堆的插入与调整
123+
124+
Rust标准库中`Vec``swap_remove`方法可以高效地删除指定位置的元素,适用于实现优先队列等堆结构:
125+
```rust
126+
let result = self.items.swap_remove(1); // 移除并返回指定位置的元素
127+
```
128+
129+
在删除元素后,可以通过调整堆结构(如最小/最大堆)来保持堆的性质。
130+
131+
---
132+
133+
### 5. 实现栈与队列
134+
135+
使用双队列实现栈的操作逻辑:
136+
```rust
137+
pub struct myStack<T> {
138+
q1: Queue<T>,
139+
q2: Queue<T>
140+
}
141+
142+
impl<T> myStack<T> {
143+
pub fn push(&mut self, elem: T) {
144+
self.q2.enqueue(elem);
145+
while !self.q1.is_empty() {
146+
self.q2.enqueue(self.q1.dequeue().unwrap());
147+
}
148+
std::mem::swap(&mut self.q1, &mut self.q2);
149+
}
150+
}
151+
```
152+
153+
这种方法利用队列的FIFO特性来模拟栈的LIFO特性。
154+
155+
---
156+
157+
### 6. 函数与内存管理
158+
159+
Rust中的`Box``unsafe`结合用于手动管理堆内存。`Box::from_raw`可以从裸指针重新创建`Box`,这在需要手动内存管理的场景中非常有用。
160+
```rust
161+
unsafe fn raw_pointer_to_box(ptr: *mut Foo) -> Box<Foo> {
162+
let mut ret: Box<Foo> = unsafe { Box::from_raw(ptr) };
163+
ret.b = Some(String::from("hello"));
164+
ret
165+
}
166+
```
167+
168+
这种方法常用于FFI(外部函数接口)中将指针恢复为拥有所有权的Rust类型。
169+
170+
---
171+
172+
### 总结
173+
174+
Rust语言通过丰富的内存管理工具和类型系统,确保了在安全性和性能上的平衡。无论是自定义数据结构还是排序、图遍历等基础算法,Rust的特性可以为代码提供极大的灵活性和安全保障。
Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
---
2+
3+
title: 调度与死锁
4+
date: 2024-11-11
5+
categories:
6+
- os
7+
tags:
8+
- author:ekkure
9+
- repo:https://github.com/ekkure/blog
10+
11+
---
12+
## 调度
13+
14+
#### **三级调度:**
15+
16+
作业调度、高级调度(频次最低):主要解决:接纳多少个任务+接纳那哪些任务这两个工作
17+
18+
进程调度、低级调度(频次最高): 必须有、**核心****确定哪个进程可以占有CPU并执行**
19+
20+
中级调度:将那些暂时不能运行的进程从内存挂起到外存,(阻塞状态下进程实体(程序 + 数据 + PCB)还在内存中,而挂起状态会把进程实体挂到外存,但是PCB会存在系统内核空间中,会记录进程在外存的状态以及位置),一般在**内存紧张**时使用
21+
22+
23+
24+
高级调度,用于批处理系统中,将任务从外存调度到内存中去。(在分时/实时系统中,任务是直接在内存,因此没有高级调度)
25+
26+
分时系统:**只有进程调度**
27+
28+
批处理系统:**进程调度 + 作业调度**
29+
30+
31+
32+
#### 调度算法相关
33+
34+
准则:周转时间(常用于**批处理系统**)、平均周转时间、带权周转时间
35+
36+
响应时间(交互性作业、分时系统)、截止时间的保证(实时系统)、优先权准则
37+
38+
周转时间 = 完成时间 - 到达时间
39+
40+
带权周转时间 = 周转时间 / 服务时间
41+
42+
43+
**调度算法**
44+
45+
- FCFS(first come first serve), SJ(P)F等等
46+
对于抢占式调度,注意**服务时间的更新,然后再比较,看谁抢**
47+
48+
- 高优先权 优先调度算法
49+
50+
静态优先权:简单,但存在饥饿现象
51+
52+
动态优先权:eg Rp = (等待时间 + 服务时间)/ 服务时间 作为优先权 1 + tw / ts;
53+
54+
- 时间片轮转 ......?
55+
56+
多级反馈队列 S1 < S2 < S3 优先权 S1 > S2 > S3
57+
58+
59+
60+
- 实时调度
61+
62+
非抢占:轮转 || 优先权
63+
64+
抢占:基于中断时钟,好处是减少了上下文保存切换的次数
65+
66+
​ 立即抢占
67+
68+
实时调度算法:EDF、LLF,还有例题
69+
70+
- 其他一些??
71+
72+
MPS:CPU共享内存, 共享缓存(单个儿独立的,容易出现绑定,忙闲不均)
73+
74+
SMP中进程分配方式:静态分配和动态分配
75+
76+
​ 调度方式: 自调度和成组调度(两种方式就对应了用户级线程和系统级线程), 专用处理机分配?
77+
78+
79+
## 死锁
80+
81+
**一些定义**
82+
83+
- 可剥夺资源:如主存,CPU,可以在使用时被强占的资源
84+
- 不可剥夺资源:不可被打断抢占的资源,如驱动器,打印机
85+
- 永久资源(外存),临时资源(进程运行过程中临时产生的数据资源等等)
86+
87+
**竞争非剥夺资源,或者竞争临时资源可导致死锁**
88+
89+
### 死锁的必要条件
90+
91+
- 互斥条件:进程互斥的使用临界资源
92+
- 不剥夺条件(不可抢占)
93+
- 请求-保持条件:进程在申请新的资源的同时,保持对某些资源的占有
94+
- 环路等待:循环等待链
95+
96+
97+
98+
99+
100+
### 解决死锁的方法
101+
102+
从严格依次降低,为
103+
104+
预防 -> 避免 -> 检测与解除
105+
106+
#### 预防
107+
108+
上面4个条件是死锁的必要条件 , Deadlock -> 4 其逆否命题为 !4 -> !Deadlock,所以我们从4个条件入手
109+
110+
1. 互斥,并没有好的办法
111+
2. 不抢占:不抢占变成"抢占",如果进程申请不到全部资源时,主动释放
112+
3. 请求保持条件:使用AND机制,但是有点浪费资源
113+
4. 环路等待:破除环路,资源排序,参考哲学家进餐
114+
115+
#### 避免死锁
116+
117+
**这是比较中庸的做法,既不损耗很多的效率,也比较的严格**
118+
119+
##### 银行家算法
120+
121+
一种是,资源分配表,为
122+
123+
| Process | Allocation | Need | Available |
124+
| ------- | ---------- | ---- | --------- |
125+
| | | | |
126+
127+
另一种是,计算表
128+
129+
| Work | Need | Allocation | work + Allocation | Finish |
130+
| ---- | ---- | ---------- | ----------------- | ------ |
131+
| | | | | |
132+
133+
**对资源进行分配时,分成两步**
134+
135+
1. 判断分配请求 R是否满足 R < Available && R < Need
136+
2. 如果满足1,使用表1表示分配后的资源表T1,再次计算是否存在安全序列,如果不安全,退回至T0,否则保存T1,下次分配将从T1开始。
137+
138+
#### 检测和解除
139+
140+
使用方法:**资源分配图**
141+
142+
**几个结论**
143+
144+
- 不可完全简化 => 存在死锁
145+
- 分配图中无环 => 不会存在死锁
146+
- 分配图中有环 => 不一定死锁
147+
148+
简化方法,对一个资源分配图,首先考虑持有边,如果持有者线程能够完成(获得所有需要的资源),将持有边消去后,将资源返回,如果不能完成,边消去后,仍保持资源占有,直到完成。
149+
150+
然后考虑请求边,如果请求的资源有空闲的,可以把边消去,若请求线程能够完成,则可将该资源返回,否则保持占有
151+
152+
重复上述过程,直至卡住,或者全部成孤立。
153+
154+
**解除**
155+
156+
通过撤销进程或者挂起进程来释放一些资源,进而推动僵持状态。
157+
158+
而具体的对哪些进程,以什么样的顺序进行操作,可以参考`Dijkstra`之类的算法,找到一种损耗最小、利益最大的方法。

0 commit comments

Comments
 (0)