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

在上一篇文章之后,我又有了一些进展:UART ,简易的shell ,修复了之前写的 xmodem 中的 BUG,一个可以从 UART 接收一个 kernel 写入到内存中再跳转过去的 bootloader 。 首先是 UART ,就是通过两个 GPIO pin 进行数据传输,首先在 memory mapped IO 上进行相应的初始化,然后包装了 io::Read 和 io::Write (这里实现一开始有 BUG,后来修复了),然后很快地完成了一个仅仅能 echo 的 kernel 。 然后实现了 CONSOLE ,一个对 MiniUart 和单例封装,就可以用 kprint!/kprintln! 宏来输出到 UART ,接着实现了一个 echo 的 shell ,读入一行输出一行。然后实现退格键和方向键,这里的难点在于要控制光标并且用读入的或者空格覆盖掉屏幕上已经显示而不应该显示的内容。接着,利用 skeleton 中的 Command 做了一个简单的 echo 命令。 接着,利用之前编写的 tty ,配合上新编写的 bootloader ,实现通过 UART 把新的 kernel 通过 XMODEM 协议发送到设备,写入 0x80000 启动地址并且调转到新加载的 kernel 中执行。 最后,又实现了 uptime (输出设备启动到现在的时间)和 exit (跳转回 bootloader ,可以上传新的 kernel )。并添加了 TUNA 作为 shell 启动时输出的 BANNER 。

Read More

近来做 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