CS144-TCP协议简单实现
Minnow
初始的TCPSocket是在Linux TCP/IP栈上封装的一个类
lab4开始自己构建TCPPeer对端进行数据传输,直接使用网卡上的IP数据包进行TCP包的构建
lab5实现简易ARP协议,可以转换IP地址和MAC地址NetworkInterface
lab6实现了最长前缀匹配的链路层选路算法IP route
lab7实现了一个使用IP route、NetworkInterface的端到端的TCP报文传输
数据格式IP包
IPv4数据包构建:不支持IP选项
// IPv4 Internet datagram header (note: IP options are not supported)struct IPv4Header{ static constexpr size_t LENGTH = 20; // IPv4 header length, not including options static constexpr uint8_t DEFAULT_TTL = 128; // A reasonable default T ...
Hardware and its habits
Hardware and its habits硬件发展Pipelined CPUs
流水线+超标量+乱序执行+分支预测
分支预测失败将导致预取的指令全部丢弃
不同线程共享一个CPU核心,指令分解的微操作流水线失效
Memory References
直接访问内存的成本过高
现代计算机会增加三级缓存尽可能减小成本
Atomic Operation
现代计算机改进原子操作
识别具有原子操作的所有缓存行,所有缓存行由单个CPU所有
非原子操作进行store可以使用store buffer而非cacheline
为维护多个数据的同步,CPU提供了memory barrier
Memory Barriers
内存屏障,实现多个数据的同步
Thermal Throttling
CPU功耗升高,温度也会升高;受限于冷却设备,可能会自主截断功耗继续升高
Cache Misses
缓存未命中,会取下一级缓存或者内存中取,这实际上增加了开销
I/O Operations
C++20协程
Coroutine of C++20
C++的协程是:
对称的。一个协程暂停后,可返回 caller 或恢复任意协程。
语言级特性。编译器知道你在使用协程。然而不比库强到哪里去。
无栈(Stackless) 的。没有独立运行时栈,无惧爆栈,调度成本低。
一个协程在被命令「暂停」时,会保证将数据和当前运行位置保存在堆内存(以便恢复现场),然后转移运行权。
实现协程间切换
co_await Transfer{};struct promise { // ... std::coroutine_handle<promise> other;};std::coroutine_handle<> Transfer::await_suspend(std::coroutine_handle<promise> me){ return me.promise().other ? me.promise().other : me;}
缺点
除非编译器优化,每个协程都需要通过operator ...
futex
Futex
Futex(Fast userspace mutex,用户态快速互斥锁) ,是一种用户态与内核态共同作用的锁,其用户态部分负责锁逻辑,内核态部分负责锁调度。
当用户态线程请求锁时,先在用户态进行锁状态的判断维护
若此时不产生锁的竞争,则直接在用户态进行上锁返回;
反之,则需要进行线程的挂起操作,通过Futex系统调用请求内核介入来挂起线程,并维护阻塞队列。
当用户态线程释放锁时,先在用户态进行锁状态的判断维护
若此时没有其他线程被该锁阻塞,则直接在用户态进行解锁返回;
反之,则需要进行阻塞线程的唤醒操作,通过Futex系统调用请求内核介入来唤醒阻塞队列中的线程。
/// 快速系统调用static int futex(uint32_t *uaddr, int futex_op, uint32_t val, const struct timespec *timeout, uint32_t *uaddr2, uint32_t val3) { return syscall(SYS_futex ...
Why we need memory barrier?
Why we need memory barrier?MESI protocol state
状态
描述
监听任务
M 修改 (Modified)
该Cache line有效,数据被修改了,和内存中的数据不一致,数据只存在于本Cache中。
缓存行必须时刻监听所有试图读该缓存行相对就主存的操作,这种操作必须在缓存将该缓存行写回主存并将状态变成S(共享)状态之前被延迟执行。
E 独享、互斥 (Exclusive)
该Cache line有效,数据和内存中的数据一致,数据只存在于本Cache中。
缓存行也必须监听其它缓存读主存中该缓存行的操作,一旦有这种操作,该缓存行需要变成S(共享)状态。
S 共享 (Shared)
该Cache line有效,数据和内存中的数据一致,数据存在于很多Cache中。
缓存行也必须监听其它缓存使该缓存行无效或者独享该缓存行的请求,并将该缓存行变成无效(Invalid)。
I 无效 (Invalid)
该Cache line无效。
无
Stores Result in Unnecessary Stalls
针对某些特定地址的数据(在一 ...
BatchOS
BatchOS
实现批处理程序功能的OS
APP与OS隔离
自动加载并运行多个程序
特权级机制
ecall 具有用户态到内核态的执行环境切换能力的函数调用指令;
sret :具有内核态到用户态的执行环境切换能力的函数返回指令。
首先,操作系统需要提供相应的功能代码,能在执行 sret 前准备和恢复用户态执行应用程序的上下文。其次,在应用程序调用 ecall 指令后,能够检查应用程序的系统调用参数,确保参数不会破坏操作系统。
RISC-V异常
RISC-V S模式特权指令
sert:从S模式返回U模式
wfi:处理器在空闲时进入低功耗状态等待终端
sfence.vma:刷新TLB缓存
访问S模式CSR指令:改变系统状态
控制状态寄存器
sstatus:SPP字段给出Trap发生前CPU的特权级
sepc:记录异常发生前执行的最后一条指令的地址
scause:描述Trap的原因
stval:给出Trap的附加信息
stvec:控制Trap处理代码的入口地址
硬件切换的硬件控制机制ecall
sstatus中的SPP字段切换到CPU当前特权级
sepc修改为Trap处理完成后默认 ...
LibOS
LibOS
实现与OS无关的OS类型的程序
结构
OS在APP运行前进行初始化:建立栈空间 + .bss段清零
Bootloader在0x80000000处加载,所以OS只能在0x80200000处加载
内存布局
rustlings
RustlingsOption2
调用·vec的pop 方法时,它会从数组的末尾移除一个元素,并返回被移除的元素作为 Option<T>。因此,在这个例子中,由于数组的类型是 Vec<Option<i8>>,所以 pop 方法返回的类型是 Option<Option<i8>>
错误处理
Result<String, String>:第一个类型是Ok()中的数据类型,第二个类型是Err()中的类型
use std::fmt::Error;pub fn generate_nametag_text(name: String) -> Result<String, String> { if name.is_empty() { // Empty names aren't allowed. Err("`name` was empty; it must be nonempty.".into()) } else ...
C++内存序
C++11并发操作的内存序原子操作的关系Synchronized-with
If thread A stores a value and thread B reads that value, there’s a synchronizes-with relationship between the store in thread A and the load in thread B.
Happens-before
单线程:如果一个操作 A 排列在另一个操作 B 之前,那么这个操作 A happens-before B,一般单线程叫A sequenced-before B。
多线程:对于多线程而言, 如果一个线程中的操作A先于另一个线程中的操作B, 那么 A happens-before B(一般多线程间操作叫A inter-thread happens-before B)。
六种内存顺序typedef enum memory_order { memory_order_relaxed, // 无同步或顺序限制,只保证当前操作原子性 memory_order_consum ...