跳转至

博客

每周分享第 55 期

一个月后终于复更

  1. 退出 vim 教程 https://github.com/hakluke/how-to-exit-vim
  2. SHA-1 攻击新进展 https://sha-mbles.github.io/
  3. gmane 近况 https://lars.ingebrigtsen.no/2020/01/06/whatever-happened-to-news-gmane-org/
  4. 浏览器能做的事情 https://github.com/luruke/browser-2020
  5. 一个 ext2 和 FAT 为一体的 fs https://github.com/NieDzejkob/cursedfs
  6. iptables 规则调试工具 https://github.com/x-way/iptables-tracer
  7. Qt 2020 的变化 https://www.qt.io/blog/qt-offering-changes-2020
  8. 后缀自动机可视化 https://yeah.moe/p/a8e74947/

MacBookPro 14,3 Wi-Fi 驱动问题解决方案

之前在 MacBookPro 14,3 安装 Linux 后,很多东西的驱动都有了解决方法,参考 1参考 2,触摸板和键盘等等都可以正常使用,触摸板的使用效果比我意料要好一些,虽然还是没有 macOS 原生那么好。但 Wi-Fi 一直有能扫到信号但连不上的问题,最近终于有了比较完善的解决方案,地址,也是两个月前才出来的方案,我测试了一下,确实可以很好的解决网络问题,网卡型号是 BCM43602,驱动用的是 brcmfmac。

另一方面,带 T2 的 MacBook 似乎也有了支持,见 aunali1/linux-mbp-arch,有一些尚未 upstream 的 patch,但我没有设备,就没有测试了。需要吐槽的是 ArchWiki 不怎么更新新 Model 的 MacBook 的教程,都是到处找散落的 github repo 和 gist 找别人的方案。

P.S. 可以正常工作的有:Wi-Fi,键盘,触摸板,Touchbar,内置摄像头,键盘背光,蓝牙 P.P.S MacBookPro11,2 的网卡是 BCM4360,直接用 broadcom-wl 驱动就可以。

JieLabs 是如何工作的

简介

JieLabs 是陈嘉杰、高一川、刘晓义(按姓氏拼音首字母排序)于 2020 年新型冠状病毒疫情期间开发的在线数字逻辑电路实验系统,用于清华大学 2020 年春季学期数字逻辑电路实验课程。其包括前端、后端和固件三部分,分别主要由刘晓义、陈嘉杰和高一川负责开发。核心功能实现用时一周,后续界面和稳定性优化用时两周。本文会详细地介绍 JieLabs 的工作原理和一些技术细节,希望对各位同学有所帮助。

太长;不看。

采用了如下的技术方案:

前端:React 框架 + Redux 状态管理 + Monaco 编辑器 + WebAssembly 运行 Rust 代码 + WebSocket 实时通信 + SASS 样式

后端:Actix-Web 框架 + Diesel/PostgreSQL 数据库 + Redis 消息队列 + Quartus 构建 + Kubernetes 构建容器编排

固件:Xilinx FPGA 控制 + Buildroot 系统 + Linux 内核

前端

前端大部分都是刘晓义同学编写的,也是这个项目工作量最大的一部分。除了本文,还可以阅读刘晓义同学自己写的总结。主要分以下几部分来谈前端的技术实现:

第三方库

整体上采用了时下比较流行的 React 框架,配合 Redux 进行状态管理,用 React Hooks 编写组件的逻辑。为了实现 VHDL/Verilog 代码的编辑,采用了来自 VSCode 的独立编辑器空间 Monaco,并自行编写了 VHDL 和 Verilog 语言的支持,一部分在 JS 实现,另一部分在 Rust 中实现,通过 wasm-pack 打包到 JS 中执行。另外为了实现 gzip 格式的解压缩也引入了 pako 库。

在这些第三方库里,Monaco 的体积最大,后面我们针对 JS 体积做了许多优化,在下面会再提。

Rust 在前端的应用

由于开发者里刘晓义和陈嘉杰都是 Rust 语言的爱好者,考虑到目前 Rust to WASM 的技术比较成熟,WebAssembly 的可用程度也很高,我们把一些功能挪到了 Rust 中执行:

一是布线的计算。这是一个比较纯粹的算法问题,一方面对性能有一定的要求,一方面开发者比较喜欢 Rust,所以就用 Rust 实现了。这里要特别感谢刘光哲同学对布线算法的指点。在此基础上,我们用 Rust 实现了几个论文中的布线算法(Maze Router),并且通过和 JS 代码的配合得到了一个比较优秀的效果。

二是 VHDL/Verilog 的语言支持。学过编译原理的同学应该知道,如果要实现一个能够解析代码里的信号的程序,一般是不能通过正则表达式来解决的,况且我们还实现了一些错误信息的显示。VHDL 语言支持采用了已有的比较完善的库,Verilog 由于现有的库都比较庞大,不适合放于前端,于是我们编写了一个最小的 Verilog(实际上算是 SystemVerilog)的解析,仅仅足够满足我们的需求。如果同学们遇到了一些语法上功能的缺少,欢迎提出。

Canvas 的应用

连线部分因为是动态生成的,所以也是动态绘制的,Canvas 就可以派上用场了。我们也利用了 Canvas 的特性,针对每一个网络都画在一个 Canvas 上,那么在检测鼠标位置的时候,只要检查 Canvas 在鼠标所在的点上是否颜色,就知道鼠标是否在它上面了。

前端加载速度的优化

优化前前端 JS 和 WASM 总大小大约是 4MB,对于网络不好的用户来说,它的加载时间是不能容忍的。于是我们采用了以下的措施:

  1. 打开 gzip:有很显著的效果,但因为一些未知的原因,在实际部署的时候未能打开
  2. 缩小 JS 体积:通过 Webpack Analyzer 分析程序各个部分的大小,删掉了 Monaco 中一些没有用到的功能
  3. 缩小 WASM 体积:打开 LTO 和 -Os 选项
  4. 代码分割:把不同功能的代码分割开,先让一部分代码加载进来,可以绘制一个部分功能的界面,然后再继续加载剩下的组件
  5. CDN:把一部分外部的依赖放到国内,后续如果有需求的话也可以把内部的依赖也放到国内的 CDN 上

后端

后端用 Rust 语言编写,采用了目前比较成熟的 actix-web 框架,大量使用了 async/await。除此之外,用 Redis 作为消息队列,在 Docker 容器中运行 Quartus,用 Kubernetes 进行容器的动态调度。

任务调度

对于用户提交的代码和约束,后端需要进行任务的调度,生成一个新的任务,放入到 Redis 消息队列中。另一方面,Docker 中运行的 python 脚本会从 Redis 中取任务,任务完成后把结果上传并回传给后端表示确认。如果一个任务一直没有完成,后端会进行回收并重新分配一个任务到队列中。为了防止这个过程中出现重复任务的提交,为每个提交设置了一个足够长的随机 ID。Docker 容器一开始是通过 docker-compose 进行配置,后来考虑到这个场景比较适合 kubernetes,于是使用了一下,还挺好用的。一开始用的是 minikube,搭好 docker registry,然后往里面部署几个 pod 并设置 hpa,具体可以看我的另一篇博客,后来迁移到了 kubeadm 直接配置。现在迁移到了一个 k3s 搭建的 k8s 集群上。

板子通信

第二个主要功能是进行板子的分配和通信。每个用户会创建一个 WebSocket 连接到后端,每个板子也是一个 WebSocket。当一个用户分配到一个板子的时候,它可以通过后端发送命令给对应的板子,板子的回复也会原路返回,相当于一个 WebSocket Proxy。另外为了保证资源的利用率,添加了一些限制、心跳包和认证。

状态监控

为了可以直观地看到各个数据,实现了一个简单的监控接口,接入 Telegraf+InfluxDB+Grafana 的监控系统,可以实时看到各个资源的情况,如用户、板子和任务等等,也方便我们在在线用户比较少的时候进行更新。

板子

这个平台虽然是用于数字逻辑实验课程,但实际用的板子来自数字逻辑设计课程。我们把其上一个 Altera FPGA 作为实验 FPGA,在控制的 Xilinx FPGA 上运行我们的固件,负责读取和写入 GPIO、下载 bitstream 等等功能。

每周分享第 54 期

咕了两周

  1. ES2019 https://javascript.christmas/2019/7
  2. CSS 技巧 https://github.com/chokcoco/iCSS
  3. Rust 编译器加速 https://blog.mozilla.org/nnethercote/2019/12/11/how-to-speed-up-the-rust-compiler-one-last-time-in-2019/
  4. OSXFuse 不开源 https://colatkinson.site/macos/fuse/2019/09/29/osxfuse/
  5. 嵌入式 Rust 的 fmt 优化 https://jamesmunns.com/blog/fmt-unreasonably-expensive/
  6. Docker base image 更新工具 https://github.com/containrrr/watchtower
  7. 运行 Linux 的名片 https://www.thirtythreeforty.net/posts/2019/12/my-business-card-runs-linux/

每周分享第 53 期

  1. GDB Enhanced Features https://github.com/hugsy/gef
  2. Lisp on Lua https://fennel-lang.org/
  3. Django 3.0 https://docs.djangoproject.com/en/3.0/releases/3.0/
  4. Rust Constant Propagation https://blog.rust-lang.org/inside-rust/2019/12/02/const-prop-on-by-default.html
  5. ES2019 features https://javascript.christmas/2019/7

每周分享第 52 期

  1. 传递 Rust 闭包到 C https://readhacker.news/s/4dbWL

  2. SystemVerilog Online http://sv-lang.com/

  3. Java 14 新特性 https://www.javaworld.com/article/3437797/work-begins-on-java-14.html

  4. 在线 or1k 的模拟器 https://readhacker.news/s/4dfqc

  5. 在 macOS 上运行 virt-manager https://github.com/jeffreywildman/homebrew-virt-manager

  6. 关于 SystemVerilog 的博客 http://systemverilog.io/

  7. 结合 VSCode 和 Docker 的开发环境 https://github.com/cdr/sail

每周分享第 51 期

  1. 一个 LaTeX 的 LSP https://github.com/latex-lsp/texlab

  2. Rope 数据结构 https://github.com/cessen/ropey

  3. 一个把 Vivado 工程放 git 中管理的方法 https://github.com/jhallen/vivado_setup

  4. https://github.com/athre0z/color-backtrace

  5. 拿 Arch 当路由器 https://github.com/archwrt

  6. Sourcetrail 开源了 https://www.sourcetrail.com/blog/open_source/

  7. NodeJS 正式支持 ES Module https://medium.com/@nodejs/announcing-core-node-js-support-for-ecmascript-modules-c5d6dc29b663

  8. Rust 的错误处理 https://blog.yoshuawuyts.com/error-handling-survey/

每周分享第 50 期

时间过得真快,忽然就 50 期了。。

  1. CLion 的 C++20 Concept 支持 https://blog.jetbrains.com/clion/2019/11/cpp20-concepts-in-clion/
  2. TypeScript 一些工具 https://github.com/pirix-gh/ts-toolbelt
  3. Rust 编写的 SystemVerilog Parser https://github.com/dalance/sv-parser
  4. MacBookPro 16 英寸 发布
  5. 用 Rust 写 eBPF 程序 https://blog.redsift.com/labs/putting-rust-in-the-kernel-with-ebpf/
  6. 终端里玩蜘蛛纸牌 https://github.com/chrisbouchard/klondike-rs
  7. Rust 的 coverage 工具 https://github.com/mozilla/grcov
  8. 在 Menu Bar 或者 Touch Bar 控制 AirPods Pro 模式 https://github.com/insidegui/NoiseBuddy
  9. Demangle Rust 符号的工具 https://github.com/luser/rustfilt

实现一个简单的 Decaf LSP

背景

编译原理课程在做 Decaf 的 PA,之前做了一些比较简单的尝试,包括在线 Decaf、在线 TAC VM 等等,都是套一个前端,然后整个编译到 wasm 跑前端就可以了。如果要做 LSP 的话,工作量会稍微大一些,不过也更加实用。

然后有一天,助教 @equation314 写了 decaf-vscode 一个 VSCode 对 Decaf 的语法高亮插件,我就 Fork 了一份到 jiegec/decaf-vscode,然后添加了 LSP 的支持,让它有了一些更高级的功能。

实现

LSP 服务端一般是一个命令行程序,通过 JSONRPC 进行消息通讯,然后就上午找有没有现成的框架。比较重要的是 lsp-typestower-lsp ,前者封装了 LSP 协议的各个结构体,后者提供了服务端的大概实现。不过由于后者做的不大全,所以我自己 fork 了一份添加了一些。

实际实现的时候,需要实现几个函数,分别相应客户端的请求,比如在 initialize 的时候告诉客户端我都实现了哪些东西,然后相应地提供各种信息,如 symbol,hover,folding,definition 等等。为了实现简单,我要求客户端每次修改的时候都把完整的文件传过来,虽然不是很高效,但是很简单,目前也没有啥很长的 Decaf 程序嘛。

每次拿到 Decaf 程序之后,就按照 decaf-rs 的方法,Lex 然后 Parse,然后遍历 AST,分别把需要的各个信息都存下来,当客户端在请求的时候,直接返回即可。然后就会在 VSCode 中出现,比如实现了 document symbol,在左边的 Outline 中就会出现相应的结构;实现了 hover,当移动到一些地方的时候,客户端发出请求,服务端就把相应的 hover 信息返回给客户端。整个协议并不复杂,后面实际实现其实才是比较复杂的地方。

实现的功能中,symbols hovers ranges definition 都是在得到 AST 后一次遍历都计算好,然后返回,同时在遇到错误的时候,也通过 diagnostic 的形式把检查出来的错误汇报给用户。由于 VSCode 的良好支持,基本不需要写 TypeScript 代码。

至于代码补全,现在做的比较粗糙,仅仅补全了一些内置函数:Print ReadInteger 和 ReadLine。还在考虑支持函数调用的补全,但是在补全的时候会出现语法错误,意味着需要保证在补全的时候我还能拿到之前正确的类型信息,需要一些工作量,现在还没有去做。

使用

我自己测试的方法就是两个窗口,一个是 decaf-lsp ,首先克隆下来,然后 cargo install --path . --force 来安装到全局;另一个就是我 Fork 的 decaf-vscode ,克隆下来,然后按 F5 进入 VSCode 的调试模式,它会打开一个新窗口,里面启用了 Decaf for VSCode 插件。这个时候看 Decaf 代码就可以看到上面提到的那些东西了。

总结

感觉 LSP 是一个比较好实现的 Protocol,但 Protocol 承载的 Data 才是比较困难的东西。要实现一个完整的 completion 还需要很多东西,现在只能说是个 naive implementation 吧。

刚写完就发现 Neovim 发布了 官方的 LSP client

用 Rust Procedure Macro 实现 GLL Parser

背景

在编译原理课上,PA 框架采用的是 MashPlant/lalr1 ,是一个比较好用的 Lexer + Parser 的工具,它的大概语法见 一个完整的例子 。然后之前看到了 GLL Parser,想着可不可以照着类似的语法也写一个 GLL 的 Parser Generator,也是用 Rust Procedure Macro 的方法,就开始了研究。

尝试

首先是阅读 GLL 的论文,它并不长,大概的意思就是,LL(1) 文法需要考虑 PS 冲突的情况,而 GLL 的解决方法就是“都试一下”,然后为了效率,用了 GSS 表示解析过程和 SPPF 表示解析结果。然后就开始照着论文手写了不同版本的实现,见 jiegec/gll-test

第一种就是按照论文里第一段实现直接抄过来,每个可能性作为一个 Continuation 存下来,它有自己的栈和执行位置(Label)。这样 Work 以后呢,我又想到了 async/await,用类似的方法又写了一遍,相对要简洁一些,也是很平常的递归下降的写法,而不是 Loop + Label 的形式。但这些都不能做到合并栈的目的,所以遇到十分有歧义的文法的时候会很糟糕。

然后开始按照论文中的 GSS 进行编写,基本还是按照论文进行翻译,然后一步一步做,做好以后把 GSS 画出来,和论文的图可以对的上;然后照着 GLL parse-tree generation 的论文把 SPPF 实现了,这时候就可以从 recongizer 变成一个 parser 了。

得到一份可行的代码以后,就要扩展到通用的情况上。学习了一下 MashPlant/lalr1 的实现,实现了一个 proc macro,它读取了用户的程序,从一个模板文件开始,往里面插入一些生成的代码,丢给编译器去编译。这时候就涉及到编译期和运行时的不同了,我把运行时一些通用的结构放到了 gll-pg-core 中,把编译期的代码放到了 gll-pg-macros

代码生成的时候,基本按照之前自己写的样子抄,只不过这个时候要按照用户编写的产生式进行生成了,各种名字都要规范化,变得可以复用,然后尽量减少命名空间的污染等等这些常见的写宏需要注意的操作。

不过,考虑到现在还没有实现 Lexer,所以先用了 Logos 库作为 Lexer。但我其实不大喜欢它,因为它太简单,也没有行号的信息,不过暂且先这样吧,以后可能会自己实现。

然后 0.1.0 版本就诞生了,它的样例长这样:

//! This example is taken from MashPlant/lalr1

use gll_pg_core::LogosToken;
use gll_pg_macros::gll;
use logos::Logos;

#[derive(Logos, Debug, Eq, PartialEq, Clone)]
pub enum Token {
    #[end]
    End,
    #[error]
    Error,
    #[token = " "]
    _Eps,
    #[token = "+"]
    Add,
    #[token = "-"]
    Sub,
    #[token = "*"]
    Mul,
    #[token = "/"]
    Div,
    #[token = "%"]
    Mod,
    #[token = "("]
    LPar,
    #[token = ")"]
    RPar,
    #[regex = "[0-9]+"]
    IntLit,
}

#[gll(Expr, Token)]
impl Parser {
    #[rule(Expr -> Expr Add Expr)]
    fn expr_add(l: i32, _op: LogosToken<Token>, r: i32) -> i32 {
        l + r
    }
    #[rule(Expr -> Expr Sub Expr)]
    fn expr_sub(l: i32, _op: LogosToken<Token>, r: i32) -> i32 {
        l - r
    }
    #[rule(Expr -> Expr Mul Expr)]
    fn expr_mul(l: i32, _op: LogosToken<Token>, r: i32) -> i32 {
        l * r
    }
    #[rule(Expr -> Expr Div Expr)]
    fn expr_div(l: i32, _op: LogosToken<Token>, r: i32) -> i32 {
        l / r
    }
    #[rule(Expr -> Expr Mod Expr)]
    fn expr_mod(l: i32, _op: LogosToken<Token>, r: i32) -> i32 {
        l % r
    }
    #[rule(Expr -> Sub Expr)]
    fn expr_neg(_op: LogosToken<Token>, r: i32) -> i32 {
        -r
    }
    #[rule(Expr -> LPar Expr RPar)]
    fn expr_paren(_l: LogosToken<Token>, i: i32, _r: LogosToken<Token>) -> i32 {
        i
    }
    #[rule(Expr -> IntLit)]
    fn expr_int(i: LogosToken<Token>) -> i32 {
        i.slice.parse().unwrap()
    }
}

#[test]
fn gll() {
    let mut lexer = Token::lexer("1 + 2 * 3");
    let res = Parser::parse(&mut lexer);
    // two ways to parse
    assert_eq!(res, [7, 9]);
}

可以看到,它解析的结果是一个数组,对应所有可能出现的情况。这样比较简单,但是要求中间各种类型都是 Clone,因为同一个结点可能会被用多次。它的计算方法就是在最终的 SPPF 上递归找到所有可能性,然后调用用户代码,最后放到一个 Vec 中。

记忆化

但是,上面的做法有一个很大的问题,就是,虽然 SPPF 的空间复杂度是有限的,但所有可能的解析树可以有很多,如果把每一个情况都完整的存在一个 Vec 中,空间要求是很高的,中间也有很多重复计算的情况。所以需要做记忆化,然后每次给出一个。因为依赖自己内部的状态,所以不能是 Iterator 只能是 StreamingIterator。

记忆化也花了我一番功夫,现在用了一个比较土的办法,在每个结点上记录了当前遇到过的所有可能,这个是逐渐构造的,意味着如果只需要第一种解析树,不需要额外的空间。然后逐渐扩张,如果有可以重用的结构就重用,把涉及的所有的结构都放在一个 Vec 中,用完之后一起 drop 掉。

当然了,这个时候,各种东西都变成了引用:

//! This example is taken from MashPlant/lalr1

use gll_pg_core::*;
use gll_pg_macros::gll;
use logos::Logos;

#[derive(Logos, Debug, Eq, PartialEq, Clone)]
enum Token {
    #[end]
    End,
    #[error]
    Error,
    #[token = " "]
    _Eps,
    #[token = "+"]
    Add,
    #[token = "-"]
    Sub,
    #[token = "*"]
    Mul,
    #[token = "/"]
    Div,
    #[token = "%"]
    Mod,
    #[token = "("]
    LPar,
    #[token = ")"]
    RPar,
    #[regex = "[0-9]+"]
    IntLit,
}

#[derive(Default)]
struct Parser {
    literals: Vec<i32>,
}

#[gll(Expr, Token)]
impl Parser {
    // you can omit self
    #[rule(Expr -> Expr Add Expr)]
    fn expr_add(l: &i32, _op: &LogosToken<Token>, r: &i32) -> i32 {
        *l + *r
    }
    // you can use &self
    #[rule(Expr -> Expr Sub Expr)]
    fn expr_sub(&self, l: &i32, _op: &LogosToken<Token>, r: &i32) -> i32 {
        *l - *r
    }
    // you can use &mut self as well
    // but all of these have &mut self in fact
    #[rule(Expr -> Expr Mul Expr)]
    fn expr_mul(&mut self, l: &i32, _op: &LogosToken<Token>, r: &i32) -> i32 {
        *l * *r
    }
    #[rule(Expr -> Expr Div Expr)]
    fn expr_div(l: &i32, _op: &LogosToken<Token>, r: &i32) -> i32 {
        *l / *r
    }
    #[rule(Expr -> Expr Mod Expr)]
    fn expr_mod(l: &i32, _op: &LogosToken<Token>, r: &i32) -> i32 {
        *l % *r
    }
    #[rule(Expr -> Sub Expr)]
    fn expr_neg(_op: &LogosToken<Token>, r: &i32) -> i32 {
        -*r
    }
    #[rule(Expr -> LPar Expr RPar)]
    fn expr_paren(_l: &LogosToken<Token>, i: &i32, _r: &LogosToken<Token>) -> i32 {
        *i
    }
    // so you can make your IDE happy with &mut self here
    #[rule(Expr -> IntLit)]
    fn expr_int(&mut self, i: &LogosToken<Token>) -> i32 {
        let lit = i.slice.parse().unwrap();
        self.literals.push(lit);
        lit
    }
}

#[test]
fn ambiguous() {
    let mut lexer = Token::lexer("1 + 2 + 3");
    let mut parser = Parser { literals: vec![] };
    let res = parser.parse(&mut lexer).unwrap();
    // two ways to parse
    let res: Vec<_> = res.cloned().collect();
    assert_eq!(res, vec![6, 6]);
}

这时候就是 0.3.0 版本,基本达到了我一开始想要的程度。

错误处理

在之前写编译原理 PA1 的时候,遇到的一个问题就是,如果自己的代码有错,因为宏展开以后丢失了位置信息,所以报错都会在错误的位置。一番查找以后,找到了解决方案:原样记录下原来的代码(syn::Block),然后通过 quote 宏直接拼接到最终的 TokenStream 中,这样在结果里,虽然代码还是那些代码,但部分的 Token 就有了正确的位置,这样就很方便用户代码的修改了。不过还是不方便找模板部分的代码错误,毕竟那部分确实在原来的代码中没有出现过。

对于模板中的代码错误,我最终的解决方案是 cargo-expand ,把我的测试代码和展开后的代码拼接起来,然后在茫茫的无关报错下去找我的错误的地方。虽然不是很好用,但毕竟还是 work 的。另外,宏还需要对用户代码的一些类型进行检查,比如上面的 Expr 对应 i32,这个就需要在各处都保持一致,但这个就需要自己进行检查了。使用了一下 proc_macro_diagnostic 的 API,还不是很好用,等它 stable 吧。

总结

终于自己手写了一个 Procedure Macro,感觉现有的工具已经比较成熟了,有 syn quote 以后很多操作都很方便。但代码还有很多地方可以优化,慢慢搞吧。

每周分享第 49 期

  1. libuv wrapper in C++ https://github.com/skypjack/uvw

  2. Visual Studio Online https://visualstudio.microsoft.com/zh-hans/services/visual-studio-online/

  3. OpenSSH 的 U2F 支持 https://readhacker.news/s/4carE

  4. Rust 1.39 Rust 1.39 Released With Async-Await Support, Attributes On Function Parameters

  5. Windows 也在用 Rust https://msrc-blog.microsoft.com/2019/11/07/using-rust-in-windows/

  6. 用 Chrome Dev Tools 调试 Rust https://twitter.com/ChromeDevTools/status/1192803818024710145

  7. C++20 的新 Thread 类型 https://medium.com/@vgasparyan1995/a-new-thread-in-c-20-jthread-ebd121ae8906

每周分享第 48 期

  1. 一个特殊的用于显示数字的字体 https://blog.janestreet.com/commas-in-big-numbers-everywhere/
  2. Intel 的 SPMD 编译器 https://ispc.github.io/
  3. 基于 Scala 的 notebook https://polynote.org/
  4. 解析登机牌信息 https://github.com/georgesmith46/bcbp
  5. 常用的 React Hooks 库 https://github.com/streamich/react-use
  6. jwt 工具 https://github.com/mike-engel/jwt-cli
  7. 用过程宏实现的 delegation in Rust https://github.com/chancancode/rust-delegate

每周分享第 47 期

  1. CLion 完善了 Rust 调试支持 https://blog.jetbrains.com/clion/2019/10/debugging-rust-code-in-clion/
  2. Nginx HTTP3 的 docker 镜像 https://github.com/RanadeepPolavarapu/docker-nginx-http3
  3. 手算 Ed25519 https://dang.fan/zh-Hans/posts/25519
  4. Rust 的 QuickCheck https://github.com/BurntSushi/quickcheck
  5. Rust 另一个类似 QuickCheck 的测试框架 https://github.com/AltSysrq/proptest
  6. Cookie 策略要改了 https://readhacker.news/s/4bvGG

为 Cisco WLC 配置 Telegraf

最近想到可以给 Cisco WLC 也配置一下监控,查了一下,果然有一些方法。大概研究了一下,找到了方法:

把 https://github.com/haad/net-snmp/tree/master/mibs 和 https://github.com/zampat/neteye4/tree/master/monitoring/monitoring-plugins/wireless/cisco/mibs 目录下的所有 .txt 文件放到 /usr/share/snmp/mibs 目录下。

然后把 https://github.com/zampat/neteye4/blob/master/monitoring/monitoring-plugins/wireless/cisco/telegraf.conf 下面 snmp 的配置复制到 telegraf 配置中,然后修改一下 IP 地址。

确保 Cisco WLC 的 SNMP 的 Public Community 已经配置好,然后就可以拿到数据了。

目前可以拿到 WLC 自身的一些运行˙状态信息、AP 的信息、SSID 的信息和 Client 的信息,基本满足了我们的需求。

参考:https://www.neteye-blog.com/2019/08/monitoring-a-cisco-wireless-controller/

每周分享第 46 期

  1. LibC++ 正式支持 WASI https://releases.llvm.org/9.0.0/projects/libcxx/docs/ReleaseNotes.html
  2. 从文本反推 regex https://github.com/pemistahl/grex
  3. KDE 5.17 发布 https://www.omgubuntu.co.uk/2019/10/kde-plasma-5-17-features
  4. IDA 7.4 发布 支持 Py3 https://www.hex-rays.com/products/ida/7.4/index.shtml
  5. 新的 CPU Bug https://www.phoronix.com/scan.php?page=news_item&px=Chrome-Geminilake-Bug
  6. WAST -> WASM in Rust https://docs.rs/wast/

每周分享第 45 期

  1. Android ARM32/64 环境模拟 https://github.com/zhkl0228/unidbg
  2. Calibre 4.0 https://readhacker.news/s/4aukk
  3. Rust 的 gRPC 实现 https://github.com/hyperium/tonic
  4. JAVA API 历史 https://github.com/marchof/java-almanac
  5. Elm 开发 dev server https://github.com/wking-io/elm-live
  6. 另一个静态网站生成器 https://github.com/getzola/zola
  7. Catalina 发布
  8. Notarization 的相关情况 https://eclecticlight.co/2019/06/07/notarization-in-mojave-and-catalina/ https://eclecticlight.co/2019/05/31/can-you-tell-whether-code-has-been-notarized/
  9. 椭圆曲线加密算法 https://fangpenlin.com/posts/2019/10/07/elliptic-curve-cryptography-explained/
  10. Travis CI 添加 ARM 支持 https://blog.travis-ci.com/2019-10-07-multi-cpu-architecture-support
  11. CD 上的结构 https://readhacker.news/s/4aDHB
  12. 在线 TAC 的虚拟机 https://jia.je/online_tac_vm/
  13. 在线 Decaf 编译和运行 https://jia.je/online_decaf/

每周分享第 44 期

  1. Modern Script Loading https://jasonformat.com/modern-script-loading/

  2. 如何实现 closure http://craftinginterpreters.com/closures.html

  3. 加速 uniform_int_distribution 实现 https://lemire.me/blog/2019/09/28/doubling-the-speed-of-stduniform_int_distribution-in-the-gnu-c-library/

  4. 在 FPGA 上运行 Rocket Chip 教程 https://github.com/cnrv/fpga-rocket-chip

  5. Xilinx Bitstream 逆向 https://prjxray.readthedocs.io/en/latest/

  6. JVM 利用 SIGSEGV 进行 null 检测 https://jcdav.is/2015/10/06/SIGSEGV-as-control-flow/

  7. Async/Await 进入 Rust Beta https://blog.rust-lang.org/2019/09/30/Async-await-hits-beta.html

  8. 在 Rust 里实现 Plugin http://adventures.michaelfbryan.com/posts/plugins-in-rust/

每周分享第 43 期

  1. Async in C https://github.com/naasking/async.h
  2. Mozilla 的 QUIC 实现 https://github.com/mozilla/neqo/
  3. 在 PCI 上做的攻击 https://github.com/ufrisk/pcileech
  4. 对 X 的原理介绍 https://magcius.github.io/xplain/article/index.html
  5. Ethernet 物理层的介绍 https://www.analog.com/media/en/technical-documentation/application-notes/EE-269.pdf
  6. Overleaf 更新了 TeX Live 版本 https://www.overleaf.com/blog/tex-live-upgrade-september-2019
  7. 下一代 proc macro?https://github.com/alexcrichton/proc-macro2
  8. iOS 13 API 变化 https://nshipster.com/ios-13/
  9. iOS 13.1.1 发布
  10. Rust 编译期 HashMap https://github.com/sfackler/rust-phf
  11. V8 加入顶层 Await 支持 https://chromium.googlesource.com/v8/v8.git/+/0ceee9ad28c21bc4971fb237cf87eb742fc787b8%5E%21/
  12. ASCII 码表的一种理解 https://garbagecollected.org/2017/01/31/four-column-ascii/
  13. Cloudflare 启用 HTTP/3 https://blog.cloudflare.com/http3-the-past-present-and-future/
  14. ZFS 发布 0.8.2 https://github.com/zfsonlinux/zfs/releases/tag/zfs-0.8.2

用 Nginx 作为 RTMP 服务器并提供直播服务

Nginx 除了可以做 HTTP 服务器以外,还可以做 RTMP 服务器,同时转成 HLS 再提供给用户,这样可以实现一个直播的服务器,用 OBS 推上来即可。

首先要安装 nginx-rtmp-server 模块,很多的发行版都已经包含了,它的主页是 https://github.com/arut/nginx-rtmp-module,下面很多内容也是来自于它的教程中。

接着,配置 Nginx,在 nginx.conf 的顶层中添加如下的配置:

rtmp {
    server {
            listen 1935;
            chunk_size 4096;

            application live {
                    live on;
                    record off;

                    hls on;
                    hls_path /path/to/save/hls;
                    hls_fragment 1s;
                    hls_playlist_length 10s;
            }

    }
}

这里表示 Nginx 要在 1935 监听一个 RTMP 服务器,然后把 live 下的视频切成片然后存在目录下,提供一个 m3u8 文件以供播放器使用。这里的参数都可以按照实际需求进行调整。这时候应该可以看到 nginx 正确监听 1935 端口,这是 rtmp 的默认端口。

接着,需要在一个 HTTP server 路径下把 HLS serve 出去:

        location /hls {
            # Serve HLS fragments
            types {
                application/vnd.apple.mpegurl m3u8;
                video/mp2t ts;
            }
            root /path/to/save/hls;
            add_header Cache-Control no-cache;
        }

这时候,如果你用 rtmp 推一个流(比如用 OBS)到 rtmp://SERVER_IP/live/SOMETHING,那么在对应的目录下会看到 SOMETHING 开头的一系列文件;用播放器打开 http://SERVER_IP/hls/SOMETHING.m3u8 就可以看到直播的视频流了。

如果要直接在浏览器里播放 HLS,需要用 Flowplayer,直接参考官方的例子即可:

<script>
var player = flowplayer("#player", {
        clip: {
                sources: [
                {
                        type: "application/x-mpegurl",
                        src: "https://SERVER_IP/hls/SOMETHING.m3u8"
                }]
        },
        autoplay: true,
        loop: true,
        live: true
});
</script>

上面的各个路径可以按照实际需求改动。

每周分享第 42 期

  1. Dart 2.5 发布
  2. Rust USB device 库 https://github.com/mvirkkunen/usb-device
  3. 同步视频的 timecode https://github.com/cnbeining/autotimecode
  4. xv6 今年换到了 riscv https://github.com/mit-pdos/xv6-riscv-fall19
  5. pdf 文本搜索 https://pdfgrep.org/
  6. iOS 13 正式版发布
  7. exFAT 驱动进入 Linux 5.4 Microsoft exFAT File-System Mailed In For Linux 5.4 Along With Promoted EROFS & Greybus
  8. Bash 详细教程 https://github.com/dylanaraps/pure-bash-bible
  9. Rust 编译到 iOS bitcode https://github.com/getditto/rust-bitcode
  10. JS 运行时间 in V8 https://v8.dev/blog/cost-of-javascript-2019#json
  11. USB3.0 PIPE 接口实现 https://github.com/enjoy-digital/usb3_pipe
  12. LLVM 9.0 发布 https://lists.llvm.org/pipermail/llvm-dev/2019-September/135304.html
  13. Safari 13.0 发布
  14. Rust 模板引擎 https://github.com/botika/yarte
  15. systemd 开始掌握 home 目录 https://www.phoronix.com/scan.php?page=news_item&px=systemd-homed
  16. 命令行的 cheatsheet 工具 https://github.com/denisidoro/navi

每周分享第 41 期

  1. Wget2 1.99.2 beta 发布 https://lists.gnu.org/archive/html/info-gnu/2019-09/msg00001.html
  2. 独立的 printf 实现 https://github.com/mpaland/printf
  3. Unicode 字符串长度统计 https://hsivonen.fi/string-length/
  4. C 类型推断 https://github.com/ltcmelo/psychec#generic-programming
  5. Clang 9.0 支持 Linux 内核编译 The New Features Of LLVM 9.0 & Clang 9.0 - Includes Building The Linux x86_64 Kernel
  6. 可控制的 USB 设备 https://github.com/usb-tools/Facedancer
  7. USB 监听器 https://github.com/openvizsla/ov_ftdi
  8. 修复一个截断的视频 https://github.com/ponchio/untrunc

在 macOS 上创建 ESP 镜像文件

最近 rCore 添加了 UEFI 支持,在 QEMU 里跑自然是没有问题,然后尝试放到 VMWare 虚拟机里跑,这时候问题就来了:需要一个带有 ESP 盘的 vmdk 虚拟盘。搜索了一下网络,找到了解决方案:

hdiutil create -fs fat32 -ov -size 60m -volname ESP -format UDTO -srcfolder esp uefi.cdr

其中 60m espuefi.cdr 都可以按照实际情况修改。它会把 esp 目录下的文件放到 ESP 分区中,然后得到一个镜像文件:

uefi.cdr: DOS/MBR boot sector; partition 1 : ID=0xb, start-CHS (0x3ff,254,63), end-CHS (0x3ff,254,63), startsector 1, 122879 sectors, extended partition table (last)

接着转换为 vmdk:

qemu-img convert -O vmdk uefi.cdr uefi.vmdk

这样就可以了。

每周分享第 40 期

  1. TeX Typesetting Game https://texnique.xyz/
  2. Writing Linux Kernel Module in Rust https://github.com/fishinabarrel/linux-kernel-module-rust
  3. Sipeed Longan Nano RISC-V 开发板 https://readhacker.news/s/48EBG
  4. Systemd 加载 BPF 防火墙 https://kailueke.gitlab.io/systemd-bpf-firewall-loader/
  5. 方舟编译器 https://code.opensource.huaweicloud.com/HarmonyOS/OpenArkCompiler.git
  6. XLOOKUP 函数 https://techcommunity.microsoft.com/t5/Excel-Blog/Announcing-XLOOKUP/ba-p/811376
  7. SystemVerilog to Verilog 转译 https://github.com/zachjs/sv2v
  8. USB 4.0 发布 http://www.phoronix.com/scan.php?page=news_item&px=USB-4.0-Specification-Published
  9. Android 10 发布 http://www.phoronix.com/scan.php?page=news_item&px=Android-10-Released
  10. iOS 13.1 beta 2 发布
  11. 对 iOS 设备抓包 http://blog.imaou.com/opensource/2014/12/14/pcapd_diagnostic_packet_capture.html
  12. Google 的差分隐私库 https://developers.googleblog.com/2019/09/enabling-developers-and-organizations.html
  13. Apple Music 第三方 App https://github.com/Musish/Musish

每周分享第 39 期

  1. 另一个 Pattern Matching in C++ 库 https://github.com/mpark/patterns
  2. Rust 编写的一个 shell http://www.jonathanturner.org/2019/08/introducing-nushell.html
  3. Vim 的游戏教程 https://readhacker.news/s/48nbJ
  4. 在 throw 的时候同时抓取 SO 信息 https://github.com/shobrook/rebound/
  5. 颜色生成工具 https://github.com/sharkdp/pastel
  6. 转换 Escape Code 到 HTML https://github.com/theZiz/aha
  7. iOS 13.1 beta 发布
  8. 苹果特别活动定于 9.11 日
  9. exFAT 规范发布 http://www.phoronix.com/scan.php?page=news_item&px=Microsoft-exFAT-Specification
  10. 可视化的 5 阶段流水线 RISC-V 模拟器 https://github.com/mortbopet/Ripes