近来做 Stanford CS140e 的一些进展和思考

最近,受各路安利,剁手买下了 这个淘宝商家的树莓派的套餐C ,还买了许多 LED 灯泡、杜邦线和电阻,开始按照 CS 140e 学习 Rust 并且用 Rust 编译写一个简易的操作系统。Assignment 0 的目标就是编写一个向 GPIO 16 连接的 LED 灯闪烁。首先当然就是愉快地按照教程下载 bootloader ,下载交叉编译工具链,顺带装一个 Raspbian 到机器上,随时可以当成一个低性能的 ARM/ARM64 (实际上,Raspbian 只用了armv7l,没有用 64bit)机器来用,以后如果配上 @scateu 团购的 Motorola Laptop Dock 的话就是一个几百块的笔记本了。把课程上的文件丢上去,可以看到绿色的活动指示灯闪烁,后面又把 CP2102 模块连上去,又能看到 Blink on, Blink off 的输出。然后按照要求,自己先码一段 C 语言,实现 blinky: #define GPIO_BASE (0x3F000000 + 0x200000) volatile unsigned *GPIO_FSEL1 = (volatile unsigned *)(GPIO_BASE + 0x04); volatile unsigned *GPIO_SET0 = (volatile unsigned *)(GPIO_BASE + 0x1C); volatile unsigned *GPIO_CLR0 = (volatile unsigned *)(GPIO_BASE + 0x28); static void spin_sleep_us(unsigned int us) { for (unsigned int i = 0; i < us * 6; i++) { asm volatile("nop"); } } static void spin_sleep_ms(unsigned int ms) { spin_sleep_us(ms * 1000); } int main(void) { // STEP 1: Set GPIO Pin 16 as output.

Read More

再次吐槽 VS 关于 scanf 和 scanf_s 的问题

继上次的吐槽后,今天再次遇到同学因为 scanf 在 VS 下的 deprecation error 感到十分迷茫,在知乎上求助又因为拍照的原因被说,我就在此再次吐槽一下 VS 这对初学者很不友善很不友善的两点。 一点就是上面提到的这个,另一点就是程序结束后任意键以退出这一功能要做得更加醒目一点 。前者由于大多数新手在学习 C/C++ 的时候都会跟着书上或者网上的代码敲一遍输入输出的代码,很容易就会撞到这个问题。后者则会让新手习惯性地以为程序闪退了,没有出结果,而不知道其实是程序执行结束后关闭而已。

Read More

我正在使用的两个 Emacs 的 Patch

我在本地对 emacs.rb 进行了修改: diff --git a/Formula/emacs.rb b/Formula/emacs.rb index d0138cd..de3c5ff 100644 --- a/Formula/emacs.rb +++ b/Formula/emacs.rb @@ -4,6 +4,14 @@ class Emacs < Formula url "https://ftp.gnu.org/gnu/emacs/emacs-25.3.tar.xz" sha256 "253ac5e7075e594549b83fd9ec116a9dc37294d415e2f21f8ee109829307c00b" + patch do + url "https://gist.githubusercontent.com/aatxe/260261daf70865fbf1749095de9172c5/raw/214b50c62450be1cbee9f11cecba846dd66c7d06/patch-multicolor-font.diff" + end + + patch do + url "https://debbugs.gnu.org/cgi/bugreport.cgi?filename=0001-Fix-child-frame-placement-issues-bug-29953.patch;bug=29953;att=1;msg=8" + end + bottle do sha256 "d5ce62eb55d64830264873a363a99f3de58c35c0bd1602cb7fd0bc37137b0c9d" => :high_sierra sha256 "4d7ff7f96c9812a9f58cd45796aef789a1b5d26c58e3e68ecf520fab34af524d" => :sierra 主要涉及到两个 Patch : 启用对 Multicolor font ,比如 Emoji 的支持。由于一些 ethic problems 暂时在 Emacs 中被禁用了,所以自己启用回来。 打上我前几天上报的 BUG #29953 的修复。已经在上游 Merge 到 emacs-26 分支中,这个修复会在下一个版本中。 有了第一个,就可以正常显示 Emoji (对不起,RMS);有了第二个,就解决了 pyim 和 lsp-ui-peek 用 child-frame 显示的一些问题了。

Read More

NAT64 初尝试

最近宿舍里有线网络的 IPv4 总是拿不到地址,只能连无线网,不禁对计算机系学生的可怕的设备数量有了深刻的认识。不过,作为一个有道德(误)的良好青年,还是不要给已经枯竭的 IPv4 地址填堵了,还是赶紧玩玩 IPv6 的网络吧。然后在 TUNA 群里受青年千人续本达 (@heroxbd) 的安利,本地搭建一下 NAT64+DNS64 的环境。不过考虑到宿舍还是拿不到有线的 IPv4 地址,我就先利用苹果先前在强制 iOS 的应用支持 NAT64 网络的同时,在 macOS 上为了方便开发者调试,提供的便捷的建立 NAT64 网络的能力。 首先在设置中按住 Option 键打开 Sharing , 点击 Internet Sharing ,勾上 Create NAT64 Network 然后把网络共享给设备。然后在手机上关掉 Wi-Fi 和 Cellular ,发现还能正常上网。此时可以打开 Wireshark 验证我们的成果了: 在手机上打开浏览器,浏览千度,得到如下的 Wireshark 截图: 这里,2001:2:0:aab1::1 是本机在这个子网中的地址,2001:2::aab1:cda2:5de:87f6:fd78 是我的 iOS 设备的地址,然后 iOS 向 macOS 发出了 DNS请求, macOS 发送 DNS 请求后得到 baidu.com 的 IPv4 地址之一为 111.13.101.208 : 上图中,我们可以看到, baidu.com 的 AAAA 记录是 2001:2:0:1baa::6f0d:65d0 ,这个就是 DNS64 转译的地址,前面为网关的 prefix ,后面就是对应的 IPv4 地址: 0x6f=111, 0x0d=13, 0x65=101, 0xd0=208 ,当客户端向这个地址发包的时候,网关发现前缀符合条件,把最后的这部分 IPv4 地址取出来,自己把包发送到真实的地址上去,再把返回来的包再转为 IPv6 的地址返还给客户端。可以验证,剩下的几个地址也符合这个转译规则。

Read More

有趣的 Java 日期格式化问题

今天在群里看到有人说, Java 的日期格式化有问题,如果用 YYYY-MM-dd ,今天的日期就会显示 2018-12-31 。我立马在本地用 Java REPL (aka Groovy) 跑了一下,果然如此: $ date = new Date() ===> Sun Dec 31 10:51:26 CST 2017 $ import java.text.SimpleDateFormat ===> java.text.SimpleDateFormat $ new SimpleDateFormat("YYYY-MM-dd").format(date) ===> 2018-12-31 解决方案是,把格式换为 yyyy-MM-dd ,确实就可以了。于是我就去研究了一下文档: Class SimpleDateFormat ,发现了问题: y 代表 year ,而 Y 代表 week year 。根据 week year ,因为今年最后的一个星期在明年的部分更多,于是这个星期被归在了明年,所以这一周属于 2018 ,这就可以解释之前的那个输出问题了。

Read More

在 macOS 上试用 Gentoo/Prefix

前几天参加了许朋程主讲的Tunight,对Gentoo有了一定的了解,不过看到如此复杂的安装过程和长久的编译时间,又看看我的CPU,只能望而却步了。不过,有Gentoo/Prefix这个工具,使得我们可以在其它的操作系统(如macOS,Solaris等)上在一个 $EPREFIX 下跑 Portage ,也就是把 Portage 运行在别的操作系统,当作一个包管理器,并且可以和别的操作系统并存。 首先还是祭出官网:Project:Prefix。 首先设定好环境变量 $EPREFIX ,之后所有的东西都会安装到这个目录下,把 bootstrap-prefix.sh 下载到 $EPREFIX ,然后 ./bootstrap-prefix.sh ,会进行一系列的问题,一一回答即可。建议在运行前设置好 GENTOO_MIRRORS=http://mirrors.tuna.tsinghua.edu.cn/gentoo 由于TUNA没有对gentoo_prefix做镜像,只能把distfiles切换到TUNA的镜像上。 然后。。。 stage1… stage2.. stage3. emerge -e @world BOOM 经过 n 次跑挂以后,终于搞完了 stage3 ,然后 SHELL=bash ./bootstrap-prefix.sh $EPREFIX startscript 生成 startprefix ,在外面的SHELL中向切进来的时候运行这个即可。 然后就可以使用Gentoo/Prefix了。注意!此时的 $PATH 仅限于 $EPREFIX 下几个目录和 /usr/bin /bin 所以很多东西都会出问题(Emacs, Vim, Fish etc)。小心不要把自己的目录什么的搞挂了。 然后就可以假装试用Gentoo了! 哈哈哈哈哈哈哈 死于编译 libgcrypt 和 llvm 。

Read More

之前时间,巨硬发布了LSP(Language Server Protocol),目的是解决目前IDE和各语言的m+n问题。想法很好,不过直到最近,终于有我觉得可以用的工具出来了,并且已经代替了我在使用的其它的插件。 由于我最近主要就是做做程设作业,做做OJ这些,主要就是和C++打交道。所以我当然就开始找一些比较成熟的C++的LSP server。有一个 Sourcegraph 维护的 langserver.org ,上面有着目前的各个语言和编辑器/IDE的支持情况,我刚才提到的cquery也会加入到这个列表里去。从这个列表里可以看到,我用的比较多的Python和Haskell都已经有不错的的LSP server,我已经开始在本地体验pyls和hie了,感觉做得挺不错的。 回到C++,我的主力编辑器是Emacs,其次是CLion,而Emacs上的LSP支持 lsp-mode也在快速发展,与之配合的lsp-ui 也出现了很多很棒的功能。 下面开始编译并配置cquery: git clone https://github.com/jacobdufault/cquery --recursive cd cquery ./waf configure # to use system clang, append --use-system-clang ./waf build 然后配置Emacs: (use-package lsp-mode :ensure t :diminish lsp-mode :commands (lsp-mode) :config (lsp-define-stdio-client lsp-pyls "python" #'get-project-root '("/usr/local/bin/pyls"))) (use-package lsp-ui :commands lsp-ui-mode :init (add-hook 'lsp-mode-hook 'lsp-ui-mode)) (use-package cquery :load-path "path_to_cquery/emacs" :config (setq cquery-executable "path_to_cquery/build/app" cquery-resource-dir "path_to_cquery/clang_resource_dir")) 接下来,需要配置 基于Clang的 工具都需要的 Compilation Database 。Sacrasm对这个有一个非常完整的总结 ,可以查看里面的方法。我这里推荐在CMake项目中用CMake自带的,加上nickdiego/compiledb-generator 应付基于Makefile/Autotools的项目。如果都不适用,就按照cquery的README写一个简单的.

Read More

今晚参加了 Tunight ,会长给我们讲了 Nginx 的一些内部运作的机制和原理。中间的时候,会长展示的代码中用到了线程池方面的一些函数,但是大多地方只有调用 ngx_pcalloc 而没有看到相应的对象释放的过程,于是在演示的最后,会长应大家要求对 Nginx 魔幻的线程池实现做了现场代码分析。 在分析的中途遇到了很多坑,最后才终于理清了内存池的工作原理。这里直接解释结论吧。以下代码均摘自 Nginx 1.13.7 ,代码都可以在官方仓库找到。 首先分析一下创建一个内存池的函数: ngx_pool_t * ngx_create_pool(size_t size, ngx_log_t *log) { ngx_pool_t *p; p = ngx_memalign(NGX_POOL_ALIGNMENT, size, log); if (p == NULL) { return NULL; } p->d.last = (u_char *) p + sizeof(ngx_pool_t); p->d.end = (u_char *) p + size; p->d.next = NULL; p->d.failed = 0; size = size - sizeof(ngx_pool_t); p->max = (size < NGX_MAX_ALLOC_FROM_POOL) ? size : NGX_MAX_ALLOC_FROM_POOL; p->current = p; p->chain = NULL; p->large = NULL; p->cleanup = NULL; p->log = log; return p; } 现在开始分段分析这个函数:在这里,一个内存池用一个 ngx_pool_t (aka struct ngx_pool_s) 类型的数据进行包装,所有的关于内存池的操作都基于相应的内存池对象。 ngx_log_t 表示输出信息的对象,与内存池无关,后面也不会讨论它。

Read More

刚刚在HN上看到了这么一个文章:Interactive Workflows for C++ with Jupyter HN ,终于可以在Jupyter Notebook里跑C++代码了,很开心,于是开始自己研究了起来怎么本地跑。 首先当然是更新一波jupyter,安装一波cling: pip3 install -U jupyter brew install cling 然后根据官方教程里的要求执行: cd /usr/local/share/cling/Jupyter/kernel pip3 install -e . jupyter kernelspec install cling-cpp11 jupyter kernelspec install cling-cpp14 jupyter kernelspec install cling-cpp17 jupyter kernelspec install cling-cpp1z 结果发现找不到jupyter-kernelspec,遂重装了一下jupyter-client这个包,果然就可以了。打开一个notebook测试: jupyter notebook 然后创建一个C++14的Notebook,结果发现一直Kernel rebooting,错误信息是说找不到../Cellar/cling/0.5/lib/libclingJupyter.dylib。这一看就是路径处理的问题,当前目录肯定不是/usr/local,肯定出现了什么问题,然后研究发现cling-kernel.py中对cling判断是否是个连接,如果是连接则按照连接去找cling的安装目录,但是!没有考虑到这个连接是个相对路径的问题(Homebrew你背锅吗)。于是我愉快地改了代码并提交了PR。修复了以后就可以用了。 以下是一个小小的例子: >> jupyter console --kernel cling-cpp14 Jupyter console 5.2.0 cling-X In [1]: #include <stdio.h> Out[1]: In [2]: char *s = "Hello, world!"; input_line_4:2:12: warning: ISO C++11 does not allow conversion from string literal to 'char *' [-Wwritable-strings] char *s = "Hello, world!

Read More

用CPUID获取评测机器的CPU

受用 CPUID 检测各大 OJ 测评机所用的 CPU(以及日常黑 BZOJ)的启发,我决定去测试一下徐老师自己写的OJ(名为Tyche)所跑的机器是什么CPU。于是我改造一下代码,用以下代码测评: #include <stdint.h>#include <iostream>#include <time.h>#include <cpuid.h>#include <sys/time.h>static void cpuid(uint32_t func, uint32_t sub, uint32_t data[4]) { __cpuid_count(func, sub, data[0], data[1], data[2], data[3]); } int main() { uint32_t data[4]; char str[48]; for(int i = 0; i < 3; ++i) { cpuid(0x80000002 + i, 0, data); for(int j = 0; j < 4; ++j) reinterpret_cast<uint32_t*>(str)[i * 4 + j] = data[j]; } struct timeval stop, start; gettimeofday(&start, NULL); while(1) { gettimeofday(&stop, NULL); if(stop.

Read More