<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
<channel>
	<title>陪她去流浪</title>
	<link>https://blog.twofei.com</link>
	<description>博客建站🔟周年快乐🎉！圣诞快乐🎄！</description>
	<lastBuildDate>Fri, 17 Apr 2026 00:43:11 HKT</lastBuildDate>
	<item>
		<title>使用字符模式访问UTM创建的虚拟机终端</title>
		<link>https://blog.twofei.com/2284/</link>
		<pubDate>Mon, 06 Apr 2026 09:55:09 HKT</pubDate>
		<description><![CDATA[<p>常规模式使用UTM创建的虚拟机模拟的是标准的显卡（可以简单理解为屏幕），如果运行是服务器版本操作系统，则无法使用鼠标操作：选中文本、复制、粘贴等。这非常不便，特别是还没配置好网络、需要粘贴SSH公钥时……</p>
<p>UTM允许使用串口连接到物理终端，这使得用户界面非常像SSH登录后字符模式界面一样。只需要像下面这样操作：</p>
<ol class="marker-period">
<li>在运行虚拟机前，打开虚拟机设置；</li>
<li>在“设备（Devices）”一栏右键删除原有的“Display”；</li>
<li>然后增加一个“串口（Serial）”设备。</li>
</ol>
<p>把增加的Serial串口设备的“Mode”改为“内置终端（Built-in<span class="space"> </span>Terminal）”即可。</p>
<div class="image-scroll-outer"><p><img src="Screenshot%202026-04-06%20at%2009.44.46.avif?og=" alt="" width="771" height="611" data-metadata="[&#34;名字&#34;,&#34;Screenshot 2026-04-06 at 09.44.46.avif&#34;,&#34;大小&#34;,&#34;33.6KB&#34;,&#34;尺寸&#34;,&#34;1928x1528&#34;,&#34;类型&#34;,&#34;image/avif&#34;]" data-thumb-hash="CAiCBIAsaaeYd4CKeGcIIIh7cpJ4CId5AQ==" data-contrast-ratio="1" loading="lazy"/></p></div>
<p>保存配置后启动，就会发现鼠标可以操作了。</p>
<div class="image-scroll-outer"><p><img src="Screenshot%202026-04-06%20at%2009.52.08.avif" alt="" width="646" height="478" data-metadata="[&#34;名字&#34;,&#34;Screenshot 2026-04-06 at 09.52.08.avif&#34;,&#34;大小&#34;,&#34;78.3KB&#34;,&#34;尺寸&#34;,&#34;1616x1196&#34;,&#34;类型&#34;,&#34;image/avif&#34;]" data-thumb-hash="CAiGBIIrm3aIh2Col/rLeIZwcpJ3B4h5AQ==" data-contrast-ratio="1" loading="lazy"/></p></div>
]]></description>
	</item>
	<item>
		<title>对exec.Cmd.CombinedOutput的一点研究</title>
		<link>https://blog.twofei.com/2280/</link>
		<pubDate>Thu, 12 Mar 2026 22:14:16 HKT</pubDate>
		<description><![CDATA[<p><code>CombinedOutput</code>会自动设置命令的<code>stdout</code>和<code>stderr</code>为内存内的缓冲区，然后等待进程运行结束。其相关代码如下：</p>
<pre><code class="language-go">// CombinedOutput runs the command and returns its combined standard
// output and standard error.
func (c *Cmd) CombinedOutput() ([]byte, error) {
	// ...省略部分不相关代码...
	var b bytes.Buffer
	c.Stdout = &amp;b
	c.Stderr = &amp;b
	err := c.Run()
	return b.Bytes(), err
}
</code></pre>
<p>这段代码虽然看起来十分简单，但是实际上极其容易造成一种误解：<strong>把<code>stdout</code>和<code>stderr</code>设置成“同一个Writer”是安全的</strong>。</p>
<p>所以我也在我自己的代码中写出了类似下面这样看起来很像又不完全像的代码：</p>
<pre><code class="language-go">func main() {
	cmd := exec.Command(`echo`, `123`)
	b := bytes.Buffer{}
	cmd.Stdout = io.MultiWriter(os.Stdout, &amp;b)
	cmd.Stderr = &amp;b
	cmd.Run()
	output := b.String()
	fmt.Println(&#34;output:&#34;, output)
	if output == `` {
		panic(`should not be empty`)
	}
}
</code></pre>
<p>在一台全新的服务器上，给出了如下的运行结果：时而正常、时而崩溃。</p>
<pre><code class="language-bash">root@iv-yeh9qbzz7kh2cbeuhzh4:~# ./go1.26.1/bin/go run a.go
123
output: 123

root@iv-yeh9qbzz7kh2cbeuhzh4:~# ./go1.26.1/bin/go run a.go
123
output:
panic: should not be empty

goroutine 1 [running]:
main.main()
	/root/a.go:20 +0x415
exit status 2
</code></pre>
<p>这让我很费解。按照正常理解，<code>stdout</code>和<code>stderr</code>在进程内是两个不同的描述符，对应到两个文件，也就意味着：它们可以被多线程同时写。所以，上述写法一定是线程不安全的，会造成数据竞态。但是Go语言本身就能把它们设置为同一个Write呢？而我，只是模仿Go的标准库写了差不多的代码。</p>
<p>如果再去看看<code>Cmd.Stdout/Stderr</code>的文档，有下面的说明：</p>
<pre><code class="language-go">type Cmd struct {
	// If Stdout and Stderr are the same writer, and have a type that can
	// be compared with ==, at most one goroutine at a time will call Write.
	Stdout io.Writer
	Stderr io.Writer
}
</code></pre>
<p>这里的文档正好解释了上面的问题：如果是同一个Writer且<code>==</code>，<code>Write</code>同一时刻只会被一个goroutine调用。这就保证了数据的安全。但这是如何保证的呢？</p>
<p>当Stdout和Stderr设置到同一个Writer时，下面的Go语言代码保证了只会创建唯一一个共享的<code>os.File</code>：</p>
<pre><code class="language-go">func (c *Cmd) childStdout() (*os.File, error) {
	return c.writerDescriptor(c.Stdout)
}

func (c *Cmd) childStderr(childStdout *os.File) (*os.File, error) {
	if c.Stderr != nil &amp;&amp; interfaceEqual(c.Stderr, c.Stdout) {
		return childStdout, nil
	}
	return c.writerDescriptor(c.Stderr)
}

// interfaceEqual protects against panics from doing equality tests on
// two interfaces with non-comparable underlying types.
func interfaceEqual(a, b any) bool {
	defer func() {
		recover()
	}()
	return a == b
}
</code></pre>
<p>而<code>os.File</code>的<code>Write</code>方法又调用了<code>poll.FD.Write</code>方法：</p>
<pre><code class="language-go">// write writes len(b) bytes to the File.
// It returns the number of bytes written and an error, if any.
func (f *File) write(b []byte) (n int, err error) {
	n, err = f.pfd.Write(b)
	runtime.KeepAlive(f)
	return n, err
}
</code></pre>
<p><code>poll.FD</code>内部一进来就对写操作进行了加锁：</p>
<pre><code class="language-go">// Write implements io.Writer.
func (fd *FD) Write(p []byte) (int, error) {
	if err := fd.writeLock(); err != nil {
		return 0, err
	}
	defer fd.writeUnlock()
	// ...
</code></pre>
<p>所以综合各种以上措施后，把stdout和stderr设置成<strong>完全相同的同一个Writer</strong>是安全的。否则，应该自行加锁。</p>
]]></description>
	</item>
	<item>
		<title>一个观察：实锤MacOS真的会偷跑流量</title>
		<link>https://blog.twofei.com/2279/</link>
		<pubDate>Tue, 10 Mar 2026 15:38:14 HKT</pubDate>
		<description><![CDATA[<p>到手不到一天的流量卡，总共220G，就已经只剩下100G，感觉哪里不对。于是上路由器看了一下实时网速，其中一台Mac电脑持续以5-10MB/s的速度跑了很久很久。心痛❤️‍🩹。</p>
<p>活动监视器的网络一栏非常离谱：右下角的数据接收速度明明显示10MB/s，但是上面列表里面的“接收字节数”一直不变（已排序）。第一反应就是有些进程被系统隐藏了。</p>
<div class="image-scroll-outer"><figure><img src="Screenshot%202026-03-10%20at%2015.14.00.avif" alt="" width="1724" height="1106" data-metadata="[&#34;名字&#34;,&#34;Screenshot 2026-03-10 at 15.14.00.avif&#34;,&#34;大小&#34;,&#34;41.4KB&#34;,&#34;尺寸&#34;,&#34;1724x1106&#34;,&#34;类型&#34;,&#34;image/avif&#34;]" data-thumb-hash="OfiFA4ArZ2d1iICIj3oJSDc3eXdwmCc=" loading="lazy"/><figcaption><p>非真实统计图，现场已经被破坏了。</p></figcaption></figure></div>
<p>然后我就尝试安装各种工具看看是哪个进程在跑流量（因为是一台非开发机，什么开发工具都没有装），然后才发现，
MacOS上的各种工具对网络的调试也太难受了：netstat、ss、lsof<span class="space"> </span>居然要么不能查看端口对应的进程，要么直接查无此端口……而且和linux/ai给出的用法有非常大的冲突。</p>
<p>然后终于找到一个名叫“nettop”的自带工具（我错了，不该安装其它的），才终于找到罪魁祸首：</p>
<pre><code class="language-bash">$ sudo nettop -m tcp -n

                                                                                    interface         state        bytes_in       bytes_out   rx_dupe    rx_ooo     re-tx   rtt_avg   rcvsize    tx_win  tc_class    tc_mgt   cc_algo P C R W
launchd.1                                                                                                             0 B             0 B       0 B       0 B       0 B
   tcp4 127.0.0.1:8021&lt;-&gt;*:*                                                              lo0        Listen                                                                                                               -     cubic - - - -
   tcp6 ::1.8021&lt;-&gt;*.*                                                                    lo0        Listen                                                                                                               -     cubic - - - -
   tcp4 *:22&lt;-&gt;*:*                                                                                   Listen                                                                                                               -     cubic - - - -
   tcp6 *.22&lt;-&gt;*.*                                                                                   Listen                                                                                                               -     cubic - - - -
apsd.603                                                                                                           1772 KiB        1128 KiB    10 KiB   150 KiB    20 KiB
   tcp4 192.168.10.230:59045&lt;-&gt;17.57.145.152:443                                          en0   Established        1772 KiB        1128 KiB    10 KiB   150 KiB    20 KiB   3.50 ms   128 KiB    44 KiB        RD         -    prague - - - -
symptomsd.669                                                                                                         0 B             0 B       0 B       0 B       0 B
   tcp6 *.64301&lt;-&gt;*.*                                                                                Listen                                                                                                              BG    prague - - - -
searchpartyd.773                                                                                                   9774 B          8585 B       0 B       0 B       0 B
rapportd.1092                                                                                                       174 KiB         150 KiB  2856 B       0 B    4845 B
   tcp6 *.64349&lt;-&gt;*.*                                                                                Listen                                                                                                               -    prague - - - -
   tcp6 *.64348&lt;-&gt;*.*                                                                                Listen                                                                                                               -    prague - - - -
   tcp6 fe80::1cde:a36f:4902:1e95%en0.57836&lt;-&gt;fe80::84a:b198:e9cd:91d7%en0.50635          en0   Established          85 KiB         104 KiB     0 B       0 B    1702 B   239.00 ms   128 KiB   128 KiB        BE         -    prague - - - -
   tcp6 fe80::1cde:a36f:4902:1e95%en0.57836&lt;-&gt;fe80::1897:c369:d7bb:aa07%en0.63315         en0   Established          88 KiB          46 KiB  2856 B       0 B    3143 B    60.56 ms   128 KiB   128 KiB        BE         -    prague - - - -
   tcp6 *.57836&lt;-&gt;*.*                                                                                Listen                                                                                                               -    prague - - - -
   tcp4 *:57836&lt;-&gt;*:*                                                                                Listen                                                                                                               -    prague - - - -
identityservice.1114                                                                                                 10 KiB        3946 B    1569 B       0 B     651 B
   tcp6 fe80::3f65:cae2:46a8:ff50%utun4.1025&lt;-&gt;fe80::ec40:c110:5d9:6c75%utun4.1026      utun4   Established        9003 B          2697 B    1308 B       0 B     415 B    45.88 ms   128 KiB   127 KiB        RV         -    prague - - - -
   tcp6 fe80::3f65:cae2:46a8:ff50%utun4.1024&lt;-&gt;fe80::ec40:c110:5d9:6c75%utun4.1024      utun4   Established        1282 B          1249 B     261 B       0 B     236 B     0.00 ms   128 KiB   128 KiB       CTL         -    prague - - - -
ControlCenter.1210                                                                                                    0 B             0 B       0 B       0 B       0 B
   tcp6 *.5000&lt;-&gt;*.*                                                                                 Listen                                                                                                               -    prague - - - -
   tcp4 *:5000&lt;-&gt;*:*                                                                                 Listen                                                                                                               -    prague - - - -
   tcp6 *.7000&lt;-&gt;*.*                                                                                 Listen                                                                                                               -    prague - - - -
   tcp4 *:7000&lt;-&gt;*:*                                                                                 Listen                                                                                                               -    prague - - - -
firefox.1451                                                                                                        238 KiB         582 KiB  3494 B    5032 B    1448 B
   tcp4 192.168.10.230:58250&lt;-&gt;34.120.208.123:443                                         en0   Established        2404 B          9443 B       0 B       0 B       0 B     2.91 ms   128 KiB    72 KiB        BE         -    prague - - - -
   tcp4 192.168.10.230:58248&lt;-&gt;151.101.129.91:443                                         en0   Established          30 KiB          24 KiB   815 B       0 B       0 B     3.59 ms   128 KiB    68 KiB        BE         -    prague - - - -
   tcp4 192.168.10.230:58247&lt;-&gt;151.101.65.91:443                                          en0   Established        2203 B          3596 B      93 B       0 B       0 B     2.69 ms   128 KiB    67 KiB        BE         -    prague - - - -
   tcp4 192.168.10.230:58245&lt;-&gt;23.217.118.24:443                                          en0   Established        1009 B          3454 B       0 B       0 B       0 B     4.81 ms   128 KiB    71 KiB        BE         -    prague - - - -
   tcp4 192.168.10.230:58231&lt;-&gt;45.12.90.213:443                                           en0   Established         176 KiB         531 KiB  2493 B    5032 B    1448 B     6.91 ms   128 KiB   372 KiB        BE         -    prague - - - -
   tcp4 192.168.10.230:58225&lt;-&gt;34.107.243.93:443                                          en0   Established        1290 B          3475 B       0 B       0 B       0 B     3.00 ms   128 KiB    72 KiB        BE         -    prague - - - -
   tcp4 192.168.10.230:58252&lt;-&gt;151.101.129.91:443                                         en0   Established        5084 B          3030 B      93 B       0 B       0 B     3.12 ms   128 KiB    67 KiB        BE         -    prague - - - -
Code Helper.1609                                                                                                     14 KiB        7824 B     854 B       0 B       0 B
   tcp4 192.168.10.230:58243&lt;-&gt;13.66.138.105:443                                          en0   Established        6949 B          3494 B     279 B       0 B       0 B     3.12 ms   128 KiB    60 KiB        BE         -    prague - - - -
ChatGPTHelper.1620                                                                                                 5961 B          4682 B     793 B       0 B       0 B
   tcp4 192.168.10.230:60744&lt;-&gt;104.18.32.47:443                                           en0   Established        5961 B          4682 B     793 B       0 B       0 B     3.69 ms   128 KiB    60 KiB        BE         -    prague - - - -
Raycast.1635                                                                                                          0 B             0 B       0 B       0 B       0 B
   tcp6 *.7265&lt;-&gt;*.*                                                                                 Listen                                                                                                               -    prague - - - -
maha-darwin-arm.1725                                                                                                  0 B             0 B       0 B       0 B       0 B
   tcp6 *.6242&lt;-&gt;*.*                                                                                 Listen                                                                                                               -    prague - - - -
nginx.1765                                                                                                            0 B             0 B       0 B       0 B       0 B
   tcp4 *:443&lt;-&gt;*:*                                                                                  Listen                                                                                                               -    prague - - - -
ChatGPT.3532                                                                                                       4919 B          5906 B       0 B       0 B       0 B
WeChat.55887                                                                                                        262 KiB          89 KiB   177 B      12 KiB  8489 B
   tcp4 127.0.0.1:14023&lt;-&gt;*:*                                                             lo0        Listen                                                                                                               -    prague - - - -
   tcp4 127.0.0.1:14022&lt;-&gt;*:*                                                             lo0        Listen                                                                                                               -    prague - - - -
   tcp4 127.0.0.1:14019&lt;-&gt;*:*                                                             lo0        Listen                                                                                                               -    prague - - - -
   tcp4 127.0.0.1:14016&lt;-&gt;*:*                                                             lo0        Listen                                                                                                               -    prague - - - -
   tcp4 127.0.0.1:14013&lt;-&gt;*:*                                                             lo0        Listen                                                                                                               -    prague - - - -
   tcp4 192.168.10.230:57856&lt;-&gt;157.255.191.88:80                                          en0   Established         262 KiB          89 KiB   177 B      12 KiB  8489 B    91.25 ms   256 KiB   324 KiB        BE         -    prague - - - -
</code></pre>
<p>这玩意儿的输出倒是非常的清晰明了，一眼就看出了哪个进程在跑流量：其中一个名为“idleassetsd”的进程（现场已破坏）。但是……搞笑的事情出现了：此进程在活动监视器里面不存在，是一个被系统隐藏了的进程！（无论是按名字搜、还是PID排序后人肉搜。）</p>
<p>网上说这是一个系统下载高清壁纸和动态壁纸的进程……啊？你是怎么敢偷偷跑我超过80G流量的？OnlyAppleCanDo。</p>
<p>另1：把Wi-Fi设置为低数据模式（low<span class="space"> </span>data<span class="space"> </span>rate）无效。<br/>
另2：我以为OpenWRT天生就很厉害，结果发现连个按设备查看网速的功能都没有，最终还是祭出了上古时代的iftop工具。</p>
]]></description>
	</item>
	<item>
		<title>关闭OpenSSH的后量子密码学安全警告</title>
		<link>https://blog.twofei.com/2274/</link>
		<pubDate>Wed, 28 Jan 2026 22:23:44 HKT</pubDate>
		<description><![CDATA[<p>继OpenSSH在v9.0中关闭了RSA/SHA1算法<sup id="fn:1f6d23a0"><a href="#fn:cae51869" class="footnote-ref" role="doc-noteref">1</a></sup>后，MacOS<span class="space"> </span>Tahoe<span class="space"> </span>Beta<span class="space"> </span>26.3中的OpenSSH(v10.0)又多出了下面的警告：</p>
<pre><code class="language-bash">$ ssh cudy
** WARNING: connection is not using a post-quantum key exchange algorithm.
** This session may be vulnerable to &#34;store now, decrypt later&#34; attacks.
** The server may need to be upgraded. See https://openssh.com/pq.html
</code></pre>
<p>大意是：当前服务器使用了非后量子安全的密钥交换算法，虽然现在能破解此算法的量子计算机还没有被发明出来，但是数据可以先被存储起来，等能破解此密钥的量子计算机发明出来以后再被解密。即：先窃取、后解密。</p>
<p>我以为我一直最喜欢的ED25519算法已经不能用了，其实不完全算是，看了上面的链接发现是又新出来了两个算法：</p>
<ol class="marker-period">
<li>
<p>sntrup761x25519-sha512</p>
<pre><code class="language--">sntrup761   x   25519   -   sha512
│                │          │
│                │          └─ 哈希函数
│                └─ 经典 ECDH
└─ NTRU 家族后量子 KEM
</code></pre>
</li>
<li>
<p>mlkem768x25519-sha256</p>
<pre><code class="language--">mlkem768   x   25519   -   sha256
│               │          │
│               │          └─ 哈希函数
│               └─ 经典椭圆曲线 DH
└─ 后量子 KEM（NIST 标准）
</code></pre>
</li>
</ol>
<p>ED25519没有被废除，只是又引入了两个长度超级超级长的组合算法。前者是固定的32字节，而后两者的长度竟然达到了≈1024字节。注意是字节，不是位。逆天。但是最终在交换时的密码是它们结合起来后再经SHA256/SHA512用KDF算法派生出来的新密钥。</p>
<p>太强大了。但是对我目前的内网主机来说用处应该不大，目前也没有必要升级使用，所以我决定关闭警告提示。</p>
<p>做法非常简单，只需要在“<code>~/.ssh/config</code>”的最最最前面加上以下配置就可以了：</p>
<pre><code class="language-text">Host *
	WarnWeakCrypto no
</code></pre>
<p>加在最前面的目的是为了对所有机器生效。</p>
<div class="footnotes" role="doc-endnotes">
<hr/>
<ol>
<li id="fn:cae51869">
<p><a href="/881/">MacOS<span class="space"> </span>Ventura<span class="space"> </span>系统<span class="space"> </span>ssh<span class="space"> </span>不再支持<span class="space"> </span>ssh-rsa<span class="space"> </span>的原因及解决办法</a> <a href="#fn:1f6d23a0" class="footnote-backref" role="doc-backlink">^</a></p>
</li>
</ol>
</div>
]]></description>
	</item>
	<item>
		<title>在OpenWRT上使用Samba是真的容易啊</title>
		<link>https://blog.twofei.com/2265/</link>
		<pubDate>Tue, 06 Jan 2026 10:54:28 HKT</pubDate>
		<description><![CDATA[<p>曾经尝试过多次在Linux上用Samba分享文件，均以失败告终，各种权限设置，搞不懂，真麻烦，屡战屡败。
MacOS本身一直没有正经的方式挂载ext4文件系统，我一直很纳闷。有MacFUSE的时候，一直靠它续命，但是自从MacOS强制开启了SIP后，普通用户（甚至是我）也很难说服自己再继续用MacFUSE了。所以后来我自己用Go的WebDAV几行代码写了个服务器，一直用到现在仍然在服役<sup id="fn:9f47476e"><a href="#fn:de21a223" class="footnote-ref" role="doc-noteref">1</a></sup>。坏消息是，只有MacOS支持WebDAV，iOS不支持。</p>
<p>厌倦了Intel<span class="space"> </span>NUC的风扇狂转，突发奇想把32TB的硬盘柜插在了路由器屁股后面，一句话安装了USB存储模块<sup id="fn:9e4745db"><a href="#fn:df21a3b6" class="footnote-ref" role="doc-noteref">2</a></sup>后，居然秒识别？？！那我就得想办法榨干它的性能价值了！然后就是如何把它分享给局域网，OpenWRT的包管理这么好用，应该有好用的UI？安装Samba的时候竟然让我选Samba还是Ksmbd<sup id="fn:9d474448"><a href="#fn:e021a549" class="footnote-ref" role="doc-noteref">3</a></sup>。前者让我不适；第一次听说后者，仔细研究了一下：</p>
<ol class="marker-period">
<li>太好了，内核模块，性能提升，没有历史包袱（旧协议支持）；</li>
<li>能用<span class="space"> </span><code>ksmbd.adduser &lt;username&gt;</code><span class="space"> </span>一键添加用户；</li>
<li>第一次让我明白Samba的用户系统是和Linux分开的；<sup id="fn:a4474f4d"><a href="#fn:e121a6dc" class="footnote-ref" role="doc-noteref">4</a></sup></li>
</ol>
<p>小而美的东西，依旧让我着迷：</p>
<div class="image-scroll-outer"><p><img src="pkgs.avif" alt="" width="641" height="144" data-metadata="[&#34;名字&#34;,&#34;pkgs.avif&#34;,&#34;大小&#34;,&#34;17.1 KiB&#34;,&#34;尺寸&#34;,&#34;1604x362&#34;,&#34;类型&#34;,&#34;image/avif&#34;]" data-thumb-hash="PAgCAoJZBqhKmDVo4JYJbpk=" data-contrast-ratio="0.45213848" loading="lazy"/></p></div>
<p>然后用<code>luci-app-ksmbd</code>界面上操作分享，简直不要太简单：</p>
<div class="image-scroll-outer"><p><img src="ksmbd.avif?og=" alt="" width="686" height="560" data-metadata="[&#34;名字&#34;,&#34;ksmbd.avif&#34;,&#34;大小&#34;,&#34;41.4 KiB&#34;,&#34;尺寸&#34;,&#34;1716x1402&#34;,&#34;类型&#34;,&#34;image/avif&#34;]" data-thumb-hash="+vcFBoB3h4dxh4eAdwh3F3iCey6Q2AM=" data-contrast-ratio="0.30327344" class="border" loading="lazy"/></p></div>
<p>虽然我很希望SFTP(ssh)能一统天下，但是为了跨平台兼容性考虑，我还是折服了。</p>
<div class="footnotes" role="doc-endnotes">
<hr/>
<ol>
<li id="fn:de21a223">
<p>访达（Finder）在枚举WebDAV的较大目录时，会超级卡。但是神经的是，我在日志里面并没有看到任何奇怪的请求，不知道它在发什么神经。访（不）达真的很垃圾。 <a href="#fn:9f47476e" class="footnote-backref" role="doc-backlink">^</a></p>
</li>
<li id="fn:df21a3b6">
<p><a href="https://openwrt.org/docs/guide-user/storage/usb-drives-quickstart">[OpenWrt<span class="space"> </span>Wiki]<span class="space"> </span>Quick<span class="space"> </span>Start<span class="space"> </span>for<span class="space"> </span>Adding<span class="space"> </span>a<span class="space"> </span>USB<span class="space"> </span>drive</a> <a href="#fn:9e4745db" class="footnote-backref" role="doc-backlink">^</a></p>
</li>
<li id="fn:e021a549">
<p><a href="https://openwrt.org/docs/guide-user/services/nas/ksmbd">[OpenWrt<span class="space"> </span>Wiki]<span class="space"> </span>ksmbd</a> <a href="#fn:9d474448" class="footnote-backref" role="doc-backlink">^</a></p>
</li>
<li id="fn:e121a6dc">
<p>但是如果Ksmbd的用户名在Linux上有同名的，则会继承其权限位（即：划等号）。 <a href="#fn:a4474f4d" class="footnote-backref" role="doc-backlink">^</a></p>
</li>
</ol>
</div>
]]></description>
	</item>
	<item>
		<title>Cudy TR3000路由器刷OpenWRT系统笔记</title>
		<link>https://blog.twofei.com/2260/</link>
		<pubDate>Mon, 29 Dec 2025 05:30:11 HKT</pubDate>
		<description><![CDATA[<p>这大概是鸽了好些年的内容了，老早就该实操并记录的，因为没有找到比较好的机子。</p>
<p>起因是：最近些年一直旅居比较多，家里的宽带太贵被我停掉了，换成了随身WiFi➕️旅行/便携/迷你路由器的组合套装。没错，我选择了大家在推特上推荐得很多的一套组合：中兴F50随身WiFi➕️Cudy<span class="space"> </span>TR3000路由器，网速快时能跑到<span class="space"> </span>150Mb+，差强人意。</p>
<div class="image-scroll-outer"><figure><img src="IMG_0052.avif?og=" alt="" width="614" height="345" data-metadata="[&#34;名字&#34;,&#34;IMG_0052.avif&#34;,&#34;大小&#34;,&#34;44.7 KiB&#34;,&#34;尺寸&#34;,&#34;2048x1152&#34;,&#34;类型&#34;,&#34;image/avif&#34;]" data-thumb-hash="5PcJFIJweoNHyZmaVqqFUFcJdQ==" loading="lazy"/><figcaption><p>图是盗的推特上别人的。[doge]</p></figcaption></figure></div>
<p>我的套装中，除了这两个外，还有一个树莓派Zero<span class="space"> </span>2W盒子，非常小巧，作用：旁路由网关。长期使用了一年左右，发现其在大流量时不稳定，容易死机。所以闲鱼卖掉了，这才准备折腾OpenWRT的，从此又可以少一个设备了。<sup id="fn:e9e8d375"><a href="#fn:2b0d1772" class="footnote-ref" role="doc-noteref">1</a></sup></p>
<div class="image-scroll-outer"><p><img src="/1483/IMG_7076.avif" alt="" width="453" height="254" data-metadata="[&#34;名字&#34;,&#34;IMG_7076.avif&#34;,&#34;大小&#34;,&#34;84.2 KiB&#34;,&#34;尺寸&#34;,&#34;3024x1696&#34;,&#34;类型&#34;,&#34;image/avif&#34;,&#34;时间&#34;,&#34;2024-11-23T16:30:39+08:00&#34;,&#34;设备&#34;,&#34;Apple / iPhone XS Max&#34;,&#34;参数&#34;,&#34;f/1.8, 4.2 mm, 1/60s, ISO/125&#34;]" data-thumb-hash="VvgNDIDHuaiGeHePd3n6h+p++A==" data-contrast-ratio="1" loading="lazy"/></p></div>
<h2>准备内容</h2>
<h3>Cudy<span class="space"> </span>TR3000<span class="space"> </span>一台</h3>
<p>建议上256MB大闪存版，这样的话，安装完标准版本布局后还剩下大约200MB可自由使用。对于128MB的版本，安装完后仅剩下~40MB可使用。两者的差价仅20元左右。本文的内容目前只针对256MB大闪存版。</p>
<p>另外，Cudy从2025年第44周开始生产的设备使用了新的闪存芯片，OpenWRT从<code>24.10.5</code>(版本号虽然看似是去年的，实质是2025年12月发布的)才开始支持，以往的旧版本是刷不进去的，刷写时会被警告，强刷也没有效果。具体的生产批次可以从外壳背后的序列号(SN)看到，形如：<code>SN: TR300025XX...</code>，其中的<code>25XX</code>即是生产年份和周数。</p>
<h3>固件：中间固件、OpenWRT固件</h3>
<p>正常来说，固件更新只需要一次：从设备管理后台选择“固件升级”，选择新的固件，等待升级完成。但是Cudy有点儿不一样，它需要两次。为什么？因为：Cudy官方默认只允许固件升级到带RSA签名的官方固件。显然，社区没有Cudy的签名私钥是不可能给出正确签名的固件的，所以不能一步到位。但是官方比较良心，给出了一个所谓的“中间固件”，它是签过名的。并且，它的后台允许通过它升级到未签名的OpenWRT固件。</p>
<p>中间固件官方下载网盘：<a href="https://drive.google.com/drive/folders/1eQ9v8c0UwivRTR7QlUpD2R36WGYqC8sh">TR3000<span class="space"> </span>256MB<span class="space"> </span>Flash<span class="space"> </span>V1<span class="space"> </span>(not<span class="space"> </span>for<span class="space"> </span>TR3000<span class="space"> </span>V1)<span class="space"> </span>-<span class="space"> </span>Google<span class="space"> </span>云端硬盘</a>。文件名：<code>cudy_tr3000-256mb-v1-sysupgrade.bin</code>。变更过，OpenWRT官方引用的名字已经不对了。</p>
<p>OpenWRT固件官方下载地址：<a href="https://firmware-selector.openwrt.org/?version=24.10.5&amp;target=mediatek%2Ffilogic&amp;id=cudy_tr3000-256mb-v1">OpenWrt<span class="space"> </span>Firmware<span class="space"> </span>Selector</a>。搜索“Cudy<span class="space"> </span>TR3000<span class="space"> </span>256mb<span class="space"> </span>v1”即可找到预编译好的固件。点击“SysUpgrade”即可下载到固件，名为：<code>openwrt-24.10.5-mediatek-filogic-cudy_tr3000-256mb-v1-squashfs-sysupgrade.bin</code>。从名字中的“squashfs”可以看到这是一个持久的文件系统，而不是另外一个“kernel”按钮下载到的“initramfs”。后者用于直接在内存中运行整个文件系统，用于系统维护，而不是长久使用。</p>
<p>第一次下载这个OpenWRT固件的时候太震惊我了，因为它仅仅只有9MB大小！其中：Linux<span class="space"> </span>Kernel<span class="space"> </span>4.5MB，OpenWRT<span class="space"> </span>根文件系统<span class="space"> </span>4.5MB。要知道，我一个用Go写的几百上千行的小程序，已经11MB，辣鸡啊。</p>
<h3>有线网络环境</h3>
<p>中间固件、最终的OpenWRT默认均不开启WiFi网络，所以如果没有有线网络环境的话，升级过程是无法完成的。</p>
<p>如果是常规的台式机电脑，基本上都配有RJ45规格的网口。找一根网线把它和Cudy背后的LAN口连起来即可在没有WiFi的情况下直接访问管理后台。</p>
<p>我没有台式机，只有一台MacBook，它没有网口。所以我翻出了一个古老的TP-LINK迷你路由器，型号为<code>TL-WR702N</code><sup id="fn:e6e8cebc"><a href="#fn:2a0d15df" class="footnote-ref" role="doc-noteref">2</a></sup>。官方早已停产，但是在闲鱼上二手的价格不足¥10即可拿下，货源非常多买一个备用还是非常不错的。把此路由器的工作模式换成“AP(Access<span class="space"> </span>Point)”即可实现：有线网络↔️无线网络。只做数据接入转发，无DHCP、NAT等任何功能。它的出厂默认应该就是AP默认，如果不是的话，连上背后的WiFi后改一下即可；或者按一下重置按钮恢复出厂设置。</p>
<p>因为这台AP一直处于上电模式，所以WiFi一直存在。所以更建议在升级的过程中使用此WiFi访问Cudy路由器的后台，而不是直接连接Cudy的WiFi。</p>
<h3>相关网页</h3>
<ul class="marker-asterisk">
<li>OpenWRT官方的Cudy产品硬件介绍页面：<a href="https://openwrt.org/toh/cudy/tr3000#oem_easy_installation">[OpenWrt<span class="space"> </span>Wiki]<span class="space"> </span>Cudy<span class="space"> </span>TR3000</a></li>
<li>Cudy官方的TR3000固件下载页面：<a href="https://www.cudy.com/en-us/pages/download-center/tr3000-1-0">Downloads<span class="space"> </span>for<span class="space"> </span>TR3000<span class="space"> </span>1.0<span class="space"> </span>–<span class="space"> </span>Cudy</a></li>
</ul>
<h2>固件升级</h2>
<p>前面介绍的准备工作内容准备好后就可以着手固件升级了。</p>
<p>首先是中间固件的刷写：</p>
<ol class="marker-period">
<li>
<p>进入Cudy的路由器后台<span class="space"> </span><a href="http://192.168.10.1/">http://192.168.10.1/</a>，依次找到“高级设置”➡️“系统”➡️“固件升级”。</p>
</li>
<li>
<p>浏览并找到固件文件：<code>cudy_tr3000-256mb-v1-sysupgrade.bin</code>。</p>
<div class="image-scroll-outer"><p><img src="select-intermediate-openwrt.avif" alt="" width="480" height="301" data-metadata="[&#34;名字&#34;,&#34;select-intermediate-openwrt.avif&#34;,&#34;大小&#34;,&#34;19.5 KiB&#34;,&#34;尺寸&#34;,&#34;1200x754&#34;,&#34;类型&#34;,&#34;image/avif&#34;]" data-thumb-hash="vOcFDIL3Zohog3mIaI/4bIOvRg==" data-contrast-ratio="1" loading="lazy"/></p></div>
<p>然后点“继续”就可以开始刷写中间固件了。</p>
</li>
<li>
<p>等待几分钟就可以刷写成功了。（建议尽量用有线网络环境（含AP）连接刷新，否则可能看不到图二刷写结束的画面时WiFi就被断掉了。但是没关系，盲等待几分钟也可以完成刷写过程。）</p>
<div class="image-scroll-outer"><p><img src="flashing1.avif" alt="" width="300" height="206" data-metadata="[&#34;名字&#34;,&#34;flashing1.avif&#34;,&#34;大小&#34;,&#34;9.8 KiB&#34;,&#34;尺寸&#34;,&#34;500x344&#34;,&#34;类型&#34;,&#34;image/avif&#34;]" data-thumb-hash="/QcGDYLHWbZ0h3iKdtiIj/ePivuI" data-contrast-ratio="0.001" class="border" loading="lazy"/></p></div>
<div class="image-scroll-outer"><p><img src="completed1.avif" alt="" width="300" height="230" data-metadata="[&#34;名字&#34;,&#34;completed1.avif&#34;,&#34;大小&#34;,&#34;8.6 KiB&#34;,&#34;尺寸&#34;,&#34;448x344&#34;,&#34;类型&#34;,&#34;image/avif&#34;]" data-thumb-hash="/fcFDYL2aJl1eJmLddhoj9ePan+3" data-contrast-ratio="0.001" class="border" loading="lazy"/></p></div>
</li>
<li>
<p>中间固件是没有启动WiFi的，只能通过有线网络访问。新的管理后台地址：<a href="http://192.168.1.1/">http://192.168.1.1/</a>。用户名是<code>root</code>，没有密码。</p>
<div class="image-scroll-outer"><p><img src="login1.avif" alt="" width="480" height="164" data-metadata="[&#34;名字&#34;,&#34;login1.avif&#34;,&#34;大小&#34;,&#34;6.2 KiB&#34;,&#34;尺寸&#34;,&#34;1200x412&#34;,&#34;类型&#34;,&#34;image/avif&#34;]" data-thumb-hash="N9gNAoqHh4d/dzd3d4B9+Dg=" data-contrast-ratio="1" loading="lazy"/></p></div>
<p>中间固件其实也是个OpenWRT变种，体验上和完整的标准版没有太大差异，可以把玩一下。但是它的文件体积比标准版本大了一半，我不太理解，明明它的唯一用途就是拿来升级到正式的OpenWRT系统？（有点想阴阳IE浏览器只是拿来下载Chrome的一样）</p>
<div class="image-scroll-outer"><p><img src="op1.avif" alt="" width="782" height="341" data-metadata="[&#34;名字&#34;,&#34;op1.avif&#34;,&#34;大小&#34;,&#34;26.3 KiB&#34;,&#34;尺寸&#34;,&#34;1956x854&#34;,&#34;类型&#34;,&#34;image/avif&#34;]" data-thumb-hash="9ggSK4KHd4dweCiIaI+J9pg=" data-contrast-ratio="0.38212252" class="border" loading="lazy"/></p></div>
<p>注意其中的版本号，是<code>23.05-SNAPSHOT</code>。因为它和最终的版本长相几乎完全一样，所以好像以为没有升级成功。</p>
</li>
</ol>
<p>然后就是通过中间固件升级到最终正式版本OpenWRT系统了：</p>
<ol class="marker-period">
<li>
<p>在新系统后台内依次找到“系统”➡️“备份与更新”➡️“更新固件”，并选择固件：<code>openwrt-24.10.5-mediatek-filogic-cudy_tr3000-256mb-v1-squashfs-sysupgrade.bin</code>。注意核对名字，然后确认上传。</p>
<div class="image-scroll-outer"><p><img src="select2.avif" alt="" width="720" height="169" data-metadata="[&#34;名字&#34;,&#34;select2.avif&#34;,&#34;大小&#34;,&#34;10.8 KiB&#34;,&#34;尺寸&#34;,&#34;1200x282&#34;,&#34;类型&#34;,&#34;image/avif&#34;]" data-thumb-hash="/fcFCoAISZkpSolpHy714WI=" data-contrast-ratio="1" loading="lazy"/></p></div>
</li>
<li>
<p>备份选项：无须备份现有配置</p>
<div class="image-scroll-outer"><p><img src="backup.avif" alt="" width="720" height="352" data-metadata="[&#34;名字&#34;,&#34;backup.avif&#34;,&#34;大小&#34;,&#34;27.0 KiB&#34;,&#34;尺寸&#34;,&#34;1200x588&#34;,&#34;类型&#34;,&#34;image/avif&#34;]" data-thumb-hash="/PcBA4Agi6s2OHlfHw/x8RA=" data-contrast-ratio="1" loading="lazy"/></p></div>
</li>
<li>
<p>等待刷写新系统。期间红灯会闪烁。</p>
<div class="image-scroll-outer"><p><img src="flashing2.avif" alt="" width="720" height="136" data-metadata="[&#34;名字&#34;,&#34;flashing2.avif&#34;,&#34;大小&#34;,&#34;13.2 KiB&#34;,&#34;尺寸&#34;,&#34;1200x228&#34;,&#34;类型&#34;,&#34;image/avif&#34;]" data-thumb-hash="/AcGAYCAd7h+ZMaLAAAAAAA=" data-contrast-ratio="1" loading="lazy"/></p></div>
</li>
<li>
<p>等待几分钟后刷新一下浏览器，就可以进入新的后台，还是没有密码。</p>
<div class="image-scroll-outer"><p><img src="login2.avif" alt="" width="720" height="244" data-metadata="[&#34;名字&#34;,&#34;login2.avif&#34;,&#34;大小&#34;,&#34;7.3 KiB&#34;,&#34;尺寸&#34;,&#34;1200x408&#34;,&#34;类型&#34;,&#34;image/avif&#34;]" data-thumb-hash="N9gNAoqHiIh/hyeHd4B9+Dg=" data-contrast-ratio="1" loading="lazy"/></p></div>
<p>看起来是不是长得一模一样？</p>
</li>
<li>
<p>不过，版本号变了，现在是<code>24.10.5</code>。</p>
<div class="image-scroll-outer"><p><img src="final2.avif" alt="" width="780" height="360" data-metadata="[&#34;名字&#34;,&#34;final2.avif&#34;,&#34;大小&#34;,&#34;28.4 KiB&#34;,&#34;尺寸&#34;,&#34;1952x900&#34;,&#34;类型&#34;,&#34;image/avif&#34;]" data-thumb-hash="9ggOK4KHh4dweCiIaI+Z9pg=" data-contrast-ratio="0.3722807" class="border" loading="lazy"/></p></div>
</li>
</ol>
<h2>初印象</h2>
<h3>主题不对？</h3>
<p>默认的主题看起来可能不像OpenWRT？没错。更常见的应该是这个：<a href="https://github.com/jerrykuku/luci-theme-argon" title="Argon is a clean and tidy OpenWrt LuCI theme that allows users to customize their login interface with images or videos. It also supports automatic and manual switching between light and dark modes.">jerrykuku/luci-theme-argon</a>。</p>
<h3>SSH访问</h3>
<p>SSH应该是默认就打开的。如果不是：“系统”➡️“管理”➡️“SSH<span class="space"> </span>Access”启用即可。</p>
<pre><code class="language-text">Last login: Mon Dec 29 02:04:08 on ttys011
Downloads → ssh root@192.168.1.1


BusyBox v1.36.1 (2025-10-19 16:37:45 UTC) built-in shell (ash)

  _______                     ________        __
 |       |.-----.-----.-----.|  |  |  |.----.|  |_
 |   -   ||  _  |  -__|     ||  |  |  ||   _||   _|
 |_______||   __|_____|__|__||________||__|  |____|
          |__| W I R E L E S S   F R E E D O M
 -----------------------------------------------------
 OpenWrt 24.10.4, r28959-29397011cc
 -----------------------------------------------------
root@cudy:~#
</code></pre>
<h3>建立WiFi</h3>
<p>在“Network”➡️“Wireless”处可以看到当前板子的硬件支持的无线硬件（长得像个信号塔、天线一样的东西）。在Cudy<span class="space"> </span>TR3000上，<code>radio0</code>是2.4GHz设备、<code>radio1</code>是5GHz设备。</p>
<p>点右边的“Add”，可以新建一个热点。可以想建多少个就建多少个！！！太神奇了。</p>
<div class="image-scroll-outer"><p><img src="wireless_overview.avif" alt="" class="padding  border" width="940" height="326" data-metadata="[&#34;名字&#34;,&#34;wireless_overview.avif&#34;,&#34;大小&#34;,&#34;27.0 KiB&#34;,&#34;尺寸&#34;,&#34;1880x652&#34;,&#34;类型&#34;,&#34;image/avif&#34;]" data-thumb-hash="+/cFCoBZj6WJZ6d3D3j4cIk=" data-contrast-ratio="0.017588932" loading="lazy"/></p></div>
<p>另外，我的宽带接入是走的随身WiFi，所以我的radio1下面有一个<code>Mode：Client</code>的WiFi，这是用来做宽带接入的（即：WISP），其它两个才是家庭网络WiFi。</p>
<h3>那……啥？</h3>
<p>不行，我这个文章是用来安装非常标准的OpenWRT的，有需要的话，可以换其它的发行版，比如：ImmortalWRT、KWRT……</p>
<p>我不一样，我还是喜欢干净纯净的系统，我选择自己修改iptables，用自己写的隧道工具🤪。</p>
<p>在OpenWRT内开启之后，所有连接到WiFi的设备就自动翻🧱了。对象再也不会嫌弃每次都要手动改网关和DNS才能做到了。</p>
<h2>恢复原厂固件（网络方式）</h2>
<p>啊……不是刚刚才安装完成吗？这就要恢复了？</p>
<p>开机时，Bootloader会检测机身左侧的Reset键是否被按下，如果有被按下，则会尝试下载新固件并安装。过程如下：</p>
<ol class="marker-period">
<li>它会把路由器自身的IP地址设置成<code>192.168.1.112</code>；</li>
<li>向固定目标为<code>192.168.1.88:96</code>端口处的TFTP服务器下载名为<code>recovery.bin</code>的固件；</li>
<li>下载成功后便开始全新安装新的系统。</li>
</ol>
<p>由于是通过网络下载固件，所以还是需要用网线把Cudy连接到电脑上：</p>
<ol class="marker-period">
<li>如果电脑有LAN口，则直接用网络连接即可；</li>
<li>如果没有LAN口（大多数笔记本），则可以用AP把LAN转成WiFi后连接。</li>
</ol>
<p>搭建一个TFTP服务器，把<code>recovery.bin</code>放在其工作目录内即可。记得把电脑的IP地址手动改成<code>192.168.1.88</code>哦。</p>
<p>由于我是在MacBook上使用的，很多教程推荐使用的Windows版tftp64.exe我无法使用。并且在试用过几个开源版本后我都不满意，所以我自己也写了一个，足够简单、小巧、易用。开源地址：</p>
<p><a href="https://github.com/movsb/tts">movsb/tts:<span class="space"> </span>A<span class="space"> </span>tiny/trivial²<span class="space"> </span>TFTP<span class="space"> </span>server<span class="space"> </span>that<span class="space"> </span>just<span class="space"> </span>works.</a></p>
<p>在右边的Release下载页面即可下载到主流各平台的预编译的二进制。随意找个空目录，放置好需要被下载的文件，然后运行即可。</p>
<p>值得注意的是，网络很多教程和视频说需要按下RESET键10秒。在我实践看来，这是完全没必要的，仅仅需要按下RESET并插上电源，直到TFTP服务器接收到下载请求了即可松开，这只需要大概2秒钟的时间。向TFTP请求下载时其使用的块大小选项只有1KB左右，而recovery文件大概有30MB，所以还是需要一定的时间来下载的。不过好在我前面写的TFTP服务器有实时进度显示，不用担心是卡住了。</p>
<p>等待几分钟便会下载完成，并且会继续花几分钟时间来重写系统。等待红灯闪烁完毕白灯(或红灯)常亮即代表系统重装完成。</p>
<h2>没了</h2>
<p>嗯。</p>
<div class="footnotes" role="doc-endnotes">
<hr/>
<ol>
<li id="fn:2b0d1772">
<p><a href="/1483/">电子垃圾➕1（树莓派<span class="space"> </span>Zero<span class="space"> </span>2<span class="space"> </span>W）</a> <a href="#fn:e9e8d375" class="footnote-backref" role="doc-backlink">^</a></p>
</li>
<li id="fn:2a0d15df">
<p><a href="/2093/">我见过的最小巧的路由器：TP-LINK<span class="space"> </span>TL-WR702N</a> <a href="#fn:e6e8cebc" class="footnote-backref" role="doc-backlink">^</a></p>
</li>
</ol>
</div>
]]></description>
	</item>
	<item>
		<title>实现一个简单的TFTP文件服务器</title>
		<link>https://blog.twofei.com/2259/</link>
		<pubDate>Sun, 28 Dec 2025 01:08:41 HKT</pubDate>
		<description><![CDATA[<p>TFTP(Trivial<span class="space"> </span>File<span class="space"> </span>Transfer<span class="space"> </span>Protocol)是一个非常早期的基于UDP协议的可靠文件传输协议。其可靠性基于最简单的停等协议实现（即：Stop-and-WAIT<span class="space"> </span>ARQ协议），和TCP的三次握手阶段一样，必须等到对方回复后才会继续发送下一个数据包。可见其性能非常一般。</p>
<p>但是由于它是基于UDP的，甚至可以直接构造IP包，所以可以离谱到裸机都可以跑，完全不需要操作系统或复杂的网络栈，实现也非常简单。所以，它能在嵌入式设备上“发光发热”就不足为奇了。</p>
<p>由于不知道具体的协议，硬是给我如何给我的Cudy<span class="space"> </span>TR3000路由器恢复原厂固件带来了麻烦。我甚至搞不懂为什么要给电脑设置手动IP地址。更好奇的是<span class="space"> </span>U-Boot<span class="space"> </span>如何能“塞”进去一个网络栈以下载固件？</p>
<p>看了4️⃣篇RFCs文档才知道了协议的具体细节。加上在一头雾水的情况了试用了几个别人写的，真是一言难尽。所以就决定自己写个最简能用的就行。我为什么不用系统的/自己安装？</p>
<ol class="marker-period">
<li>
<p>MacOS上好像是自带的，但是要启动为一个后台服务，甚至还要launchctl来启动，太抽象了</p>
</li>
<li>
<p>Linux作为嵌入式开发主力设备，居然不自带，虽然前几天用过一次并成功了，还是觉得麻烦</p>
</li>
<li>
<p>Windows上有一个比较常见的，但是实在丑拒，另外，我没有<span class="space"> </span>Windows<span class="space"> </span>系统。</p>
<div class="image-scroll-outer"><p><img src="tftpd64.avif?og=" alt="" width="368" height="317" data-metadata="[&#34;名字&#34;,&#34;tftpd64.avif&#34;,&#34;大小&#34;,&#34;17.9 KiB&#34;,&#34;尺寸&#34;,&#34;920x794&#34;,&#34;类型&#34;,&#34;image/avif&#34;]" data-thumb-hash="+/cFBoBXaGh0d3hziPh3qId4iJFQJQg=" data-contrast-ratio="0.7304323" loading="lazy"/></p></div>
</li>
</ol>
<p>其实最大的问题是：不能跨平台使用。除了<span class="space"> </span>Windows<span class="space"> </span>开发，谁现在还用<span class="space"> </span>Windows<span class="space"> </span>开发啊？</p>
<p>我的项目地址在这里：<a href="https://github.com/movsb/tts">movsb/tts:<span class="space"> </span>A<span class="space"> </span>tiny/trivial²<span class="space"> </span>TFTP<span class="space"> </span>server<span class="space"> </span>that<span class="space"> </span>just<span class="space"> </span>works.</a>。</p>
<p>零依赖，零配置，开箱应用，用完即弃。</p>
<p>我的目的明明是为了救砖，你们为什么搞那么复杂？</p>
<hr/>
<p>服务器运行示例：</p>
<pre><code class="language-text">Downloads → sudo ~/code/tts/tts
2025/12/29 22:46:52 192.168.10.230:59750 ReadFile: 2.docx [{tsize 0} {blksize 1024} {rollover 0}]
2025/12/29 22:46:52 192.168.10.230:59750 Ignored unsupported option: {tsize 0}
2025/12/29 22:46:52 192.168.10.230:59750 Ignored unsupported option: {rollover 0}
2025/12/29 22:46:53 192.168.10.230:59750 Sent block: 1/14 (7%)
2025/12/29 22:46:53 192.168.10.230:59750 Sent block: 2/14 (14%)
2025/12/29 22:46:53 192.168.10.230:59750 Sent block: 3/14 (21%)
2025/12/29 22:46:53 192.168.10.230:59750 Sent block: 4/14 (28%)
2025/12/29 22:46:53 192.168.10.230:59750 Sent block: 5/14 (35%)
2025/12/29 22:46:53 192.168.10.230:59750 Sent block: 6/14 (42%)
2025/12/29 22:46:53 192.168.10.230:59750 Sent block: 7/14 (50%)
2025/12/29 22:46:53 192.168.10.230:59750 Sent block: 8/14 (57%)
2025/12/29 22:46:53 192.168.10.230:59750 Sent block: 9/14 (64%)
2025/12/29 22:46:53 192.168.10.230:59750 Sent block: 10/14 (71%)
2025/12/29 22:46:53 192.168.10.230:59750 Sent block: 11/14 (78%)
2025/12/29 22:46:53 192.168.10.230:59750 Sent block: 12/14 (85%)
2025/12/29 22:46:53 192.168.10.230:59750 Sent block: 13/14 (92%)
2025/12/29 22:46:53 192.168.10.230:59750 Sent block: 14/14 (100%)
2025/12/29 22:46:53 192.168.10.230:59750 Sent completed successfully.
2025/12/29 22:46:53 192.168.10.230:59750 Connection closed.
</code></pre>
<p>客户端运行示例：</p>
<pre><code class="language-text">tmp → tftp 192.168.10.230 69
tftp&gt; options on
Support for RFC2347 style options are now enabled.
Support for non-RFC defined options are now enabled.

The following options are available:
	options on	: enable support for RFC2347 style options
	options off	: disable support for RFC2347 style options
	options extra	: toggle support for non-RFC defined options
tftp&gt; blocksize 1024
Blocksize is now 1024 bytes.
tftp&gt; get 2.docx
Received 14328 bytes during 1.0 seconds in 14 blocks
tftp&gt;
</code></pre>
]]></description>
	</item>
	<item>
		<title>修复Go程序在OpenWRT系统中无法正确处理时区的问题</title>
		<link>https://blog.twofei.com/2254/</link>
		<pubDate>Thu, 18 Dec 2025 01:32:03 HKT</pubDate>
		<description><![CDATA[<p>OpenWRT在System页面设置好时区后会把配置数据写到<span class="space"> </span><code>/etc/config/system</code><span class="space"> </span>中：</p>
<pre><code class="language-text">root@cudy:~# cat /etc/config/system

config system
	option timezone &#39;CST-8&#39;
	option zonename &#39;Asia/Shanghai&#39;
</code></pre>
<p>中国的时区明明是<code>UTC+8</code>，这里的<code>CST-8</code>是个非常奇怪且少见的表示法（来自于<span class="space"> </span>POSIX）<sup id="fn:1e40c646"><a href="#fn:898e38eb" class="footnote-ref" role="doc-noteref">1</a></sup>：</p>
<ul class="marker-asterisk">
<li>“CST”是个任意可能的名字，并不一定代表中国标准时间（China<span class="space"> </span>Standard<span class="space"> </span>Time）；</li>
<li>“-8”才是用于准确计算的部分，表示本初子午线往东“+8”时区。</li>
</ul>
<p>时区会同时被写入到<code>/tmp/TZ</code>这个文件中：</p>
<pre><code class="language-bash">root@cudy:~# ls -lh /tmp/TZ
-rw-r--r--    1 root     root           6 Dec 21 00:29 /tmp/TZ
root@cudy:~# cat /tmp/TZ
CST-8
</code></pre>
<p>但是Go的时区处理函数并不会读取这个位置<sup id="fn:1d40c4b3"><a href="#fn:8a8e3a7e" class="footnote-ref" role="doc-noteref">2</a></sup><sup id="fn:1c40c320"><a href="#fn:8b8e3c11" class="footnote-ref" role="doc-noteref">3</a></sup>：</p>
<pre><code class="language-go">// Many systems use /usr/share/zoneinfo, Solaris 2 has
// /usr/share/lib/zoneinfo, IRIX 6 has /usr/lib/locale/TZ,
// NixOS has /etc/zoneinfo.
var platformZoneSources = []string{
	&#34;/usr/share/zoneinfo/&#34;,
	&#34;/usr/share/lib/zoneinfo/&#34;,
	&#34;/usr/lib/locale/TZ/&#34;,
	&#34;/etc/zoneinfo&#34;,
}
</code></pre>
<p>甚至也不在环境变量“TZ”中。虽然会fallback到<span class="space"> </span><code>/etc/localtime</code><span class="space"> </span>中，但是在OpenWRT上，这是一个无效的软连接。</p>
<p>尝试在现有的Linux系统中拷贝一个能用的时区数据库文件（<a href="shanghai">shanghai</a>）并软连接过去，发现是可以使用的：</p>
<pre><code class="language-bash">root@cudy:~# ls -lh /etc/localtime
lrwxrwxrwx    1 root     root          14 Dec 21 00:29 /etc/localtime -&gt; /root/shanghai
</code></pre>
<p>好了，把它加在启动脚本里面开机自动软连接即可：</p>
<div class="image-scroll-outer"><p><img src="Screenshot%202025-12-21%20at%2023.09.19.avif?og=" alt="" width="802" height="256" data-metadata="[&#34;名字&#34;,&#34;Screenshot 2025-12-21 at 23.09.19.avif&#34;,&#34;大小&#34;,&#34;18.7 KiB&#34;,&#34;尺寸&#34;,&#34;1604x512&#34;,&#34;类型&#34;,&#34;image/avif&#34;]" data-thumb-hash="yvcBAoCNhmd1mQiIZntvpvc=" data-contrast-ratio="1" loading="lazy"/></p></div>
<p>或者，直接下载上述文件并覆盖<code>/etc/localtime</code>：</p>
<pre><code class="language-bash">$ wget https://blog.twofei.com/2254/shanghai
$ mv shanghai /etc/localtime
</code></pre>
<p>用C或者Zig写的程序貌似都有此问题，以上的做法能够一次性全部解决。</p>
<p>但是也有一个问题：时区不会随着在System页面上的修改而自动修改。不经常出国的话完全没有问题。我也不想安装全量的zoneinfo包。</p>
<div class="footnotes" role="doc-endnotes">
<hr/>
<ol>
<li id="fn:898e38eb">
<p><a href="https://stackoverflow.com/a/70801873/3628322">timezone<span class="space"> </span>-<span class="space"> </span>What<span class="space"> </span>is<span class="space"> </span>the<span class="space"> </span>correct<span class="space"> </span>POSIX-style<span class="space"> </span>TZ<span class="space"> </span>format:<span class="space"> </span>&lt;+04&gt;-4<span class="space"> </span>vs<span class="space"> </span>UNK-4<span class="space"> </span>-<span class="space"> </span>Stack<span class="space"> </span>Overflow</a> <a href="#fn:1e40c646" class="footnote-backref" role="doc-backlink">^</a></p>
</li>
<li id="fn:8a8e3a7e">
<p><a href="https://github.com/golang/go/blob/7ecb1f36acab7b48d77991d58d456a34074a2d0e/src/time/zoneinfo_unix.go#L21-L26">go/src/time/zoneinfo_unix.go<span class="space"> </span>at<span class="space"> </span>master<span class="space"> </span>·<span class="space"> </span>golang/go</a> <a href="#fn:1d40c4b3" class="footnote-backref" role="doc-backlink">^</a></p>
</li>
<li id="fn:8b8e3c11">
<p><a href="https://stackoverflow.com/a/54036829/3628322">go<span class="space"> </span>-<span class="space"> </span>Why<span class="space"> </span>running<span class="space"> </span>Golang&#39;s<span class="space"> </span>time.Now()<span class="space"> </span>at<span class="space"> </span>OpenWRT<span class="space"> </span>always<span class="space"> </span>get<span class="space"> </span>UTC<span class="space"> </span>time?<span class="space"> </span>-<span class="space"> </span>Stack<span class="space"> </span>Overflow</a> <a href="#fn:1c40c320" class="footnote-backref" role="doc-backlink">^</a></p>
</li>
</ol>
</div>
]]></description>
	</item>
	<item>
		<title>判断网站访问者IP地址是否来源于中国</title>
		<link>https://blog.twofei.com/1954/</link>
		<pubDate>Mon, 03 Nov 2025 00:29:34 HKT</pubDate>
		<description><![CDATA[<p>赛博菩萨<span class="space"> </span>Cloudflare<span class="space"> </span>的<span class="space"> </span>R2<span class="space"> </span>存储外出流量不要钱，还有免费的<span class="space"> </span>10GB<span class="space"> </span>存储空间，所以我很早就把存储搬到国外了。但是我在国内实际测试发现<span class="space"> </span>R2<span class="space"> </span>访问不稳定，所以为了区别加速，就需要判断访问者的<span class="space"> </span>IP<span class="space"> </span>地址是否来源于中国。如果是，就走国内的对象存储；如果不是，通通走<span class="space"> </span>R2。</p>
<p>在不借助外部工具、服务的情况下，可以自己从<span class="space"> </span><strong>亚太互联网络信息中心（Asia-Pacific<span class="space"> </span>Network<span class="space"> </span>Information<span class="space"> </span>Centre，APNIC）</strong><span class="space"> </span>找到所有分配给中国的地址段，然后判断目标<span class="space"> </span>IP<span class="space"> </span>地址是否在范围内即可。（部分群体对这个数据库应该非常熟悉）</p>
<p>纯文本数据库地址：<a href="http://ftp.apnic.net/apnic/stats/apnic/delegated-apnic-latest">http://ftp.apnic.net/apnic/stats/apnic/delegated-apnic-latest</a>。</p>
<p>这个数据库表格的数据始终是最新的。数据格式大概长这样：</p>
<pre><code class="language-text">apnic|AU|ipv4|1.0.0.0|256|20110811|assigned
apnic|CN|ipv4|1.0.1.0|256|20110414|allocated
apnic|CN|ipv4|1.0.2.0|512|20110414|allocated
apnic|AU|ipv4|1.0.4.0|1024|20110412|allocated
apnic|CN|ipv4|1.0.8.0|2048|20110412|allocated
apnic|JP|ipv4|1.0.16.0|4096|20110412|allocated
apnic|CN|ipv4|1.0.32.0|8192|20110412|allocated
</code></pre>
<p>过滤出包含“<code>|CN|ipv4|</code>”的行就能得到中国所有的<span class="space"> </span>IPv4<span class="space"> </span>地址网段（CIDR）。</p>
<p>CIDR（即<span class="space"> </span>IPv4<span class="space"> </span>➕️<span class="space"> </span>Prefix<span class="space"> </span>length）一共是<span class="space"> </span>5️⃣<span class="space"> </span>个字节，为了节省空间，我把它们直接用二进制方式写到文件中了，而不是用<span class="space"> </span>ASCII<span class="space"> </span>一行一个的方式。</p>
<p>文件最终的大小是<span class="space"> </span>40KB，足够小巧到可以无压力地直接嵌入到程序二进制内。远小于网上找到的各种什么<span class="space"> </span>geoip<span class="space"> </span>地址库（比如：GeoLite.mmdb）。当然，我这种做法仅限于判断<span class="space"> </span>IP<span class="space"> </span>所属国家是否是中国，更不含具体的省市等信息。但是对于我的需求来说，足够了，而且数据还是精确的。</p>
<p>判断<span class="space"> </span>IP<span class="space"> </span>地址是否在<span class="space"> </span>CIDR<span class="space"> </span>集合内有很多现成的高效的实现，比如最常用的：前缀树/基数树/Trie。</p>
<p>参考我的实现：<a href="https://github.com/movsb/taoblog/tree/43aabbb9c33be542a6d71eb5a4c1a0ec465531fd/modules/geo/geoip">taoblog/modules/geo/geoip<span class="space"> </span>at<span class="space"> </span>master<span class="space"> </span>·<span class="space"> </span>movsb/taoblog</a>。</p>
<p>作为一个长期较稳定运行超过十年的网站，我会尽量少地依赖外部工具或服务。</p>
]]></description>
	</item>
	<item>
		<title>把 mattn/go-sqlite3 换成了 ncruces/go-sqlite3</title>
		<link>https://blog.twofei.com/1984/</link>
		<pubDate>Thu, 16 Oct 2025 17:28:29 HKT</pubDate>
		<description><![CDATA[<p>前者是<span class="space"> </span>CGO<span class="space"> </span>绑定，后者是<span class="space"> </span>WASM<span class="space"> </span>运行时。相关的对比及性能测试可以参考：<a href="https://github.com/cvilsmeier/go-sqlite-bench">cvilsmeier/go-sqlite-bench:<span class="space"> </span>Benchmarks<span class="space"> </span>for<span class="space"> </span>Golang<span class="space"> </span>SQLite<span class="space"> </span>Drivers</a>。</p>
<p>前两天有朋友找到我说要部署一份我的博客系统，让我编译一个<span class="space"> </span>Linux/AMD64<span class="space"> </span>版本（实际上我一直是用的这个版本，只不过是<span class="space"> </span>GitHub<span class="space"> </span>Actions<span class="space"> </span>编译到容器里面的，但可以<span class="space"> </span>cp<span class="space"> </span>出来），我再次尝试在<span class="space"> </span>MacOS<span class="space"> </span>上直接<span class="space"> </span><code>go build</code>，再一次又被<span class="space"> </span>CGO<span class="space"> </span>气死了：没有看到任何<span class="space"> </span>build<span class="space"> </span>tags<span class="space"> </span>的<span class="space"> </span><code>sqlite3.Error</code><span class="space"> </span>竟然无法编译，找不到定义。</p>
<p>而恰好前几天又正好在群里和雨帆⛵️聊起<span class="space"> </span>sqlite3<span class="space"> </span>的问题，仔细看了前面的对比以及文档，发现<span class="space"> </span>WASM<span class="space"> </span>版本<span class="space"> </span>CGO<span class="space"> </span>版本竟然性能几乎无异，所以就动手切换了。实际上改动的代码只有几行<sup id="fn:a43497b1"><a href="#fn:6911a506" class="footnote-ref" role="doc-noteref">1</a></sup>，因为它俩都是兼容<span class="space"> </span><code>database/sql</code><span class="space"> </span>接口标准的。</p>
<div class="table-wrapper"><div class="scrollable"><table class="yaml">
<thead>
</thead>
<tbody>
<tr><th>使用的库</th><th>测试项</th><th>测试结果</th><th>Delta</th></tr>
<tr><td rowspan="2"><p><code>mattn/go-sqlite3</code></p>
</td><td>首页</td><td><pre><code class="language-bash">$ wrk https://blog.mac.twofei.com/
Running 10s test @ https://blog.mac.twofei.com/
  2 threads and 10 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency    49.88ms   14.55ms 110.15ms   70.69%
    Req/Sec   100.23     15.07   140.00     73.00%
  2008 requests in 10.06s, 18.91MB read
Requests/sec:    199.69
Transfer/sec:      1.88MB
</code></pre>
</td><td>N/A</td></tr>
<tr><td>文章</td><td><pre><code class="language-bash">$ wrk https://blog.mac.twofei.com/1/
Running 10s test @ https://blog.mac.twofei.com/1/
  2 threads and 10 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     7.50ms    2.57ms  37.84ms   71.15%
    Req/Sec   670.53     42.64   737.00     86.00%
  13358 requests in 10.01s, 81.92MB read
Requests/sec:   1334.46
Transfer/sec:      8.18MB
</code></pre>
</td><td>N/A</td></tr>
<tr><td rowspan="2"><p><code>ncruces/go-sqlite3</code></p>
</td><td>首页</td><td><pre><code class="language-bash">$ wrk https://blog.mac.twofei.com/
Running 10s test @ https://blog.mac.twofei.com/
  2 threads and 10 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency    94.73ms   27.77ms 210.57ms   72.21%
    Req/Sec    52.77     12.82    90.00     70.71%
  1056 requests in 10.07s, 9.94MB read
Requests/sec:    104.88
Transfer/sec:      0.99MB
</code></pre>
</td><td>-47%</td></tr>
<tr><td>文章</td><td><pre><code class="language-bash">$ wrk https://blog.mac.twofei.com/1/
Running 10s test @ https://blog.mac.twofei.com/1/
  2 threads and 10 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency    12.22ms    4.09ms  42.32ms   70.56%
    Req/Sec   411.22     27.02   474.00     74.00%
  8201 requests in 10.02s, 50.30MB read
Requests/sec:    818.50
Transfer/sec:      5.02MB
</code></pre>
</td><td>-38%</td></tr>
</tbody>
</table></div></div>
<p>“首页”因为几乎没有缓存的原因，相比于“单篇文章”来说性能非常差，在我的预期内。但是从<span class="space"> </span>CGO<span class="space"> </span>换成<span class="space"> </span>WASM<span class="space"> </span>竟然整体性能掉了一半让我震惊。换还是不换呢❓</p>
<p>换，当然要换，对于我这样的小破站来说，即便是只有几百<span class="space"> </span>QPS<span class="space"> </span>的性能也完全没问题。最主要的是：编译速度/跨平台编译容易程度都极大地提高了。还有一点，几百<span class="space"> </span>QPS<span class="space"> </span>显然是另有隐情。</p>
<p>所以我就<span class="space"> </span>profile<span class="space"> </span>了一下<span class="space"> </span>cpu：</p>
<div class="image-scroll-outer"><p><img src="d943abc4703703f12c3f6750800a3b15.avif" alt="" width="3748" height="1842" data-metadata="[&#34;名字&#34;,&#34;d943abc4703703f12c3f6750800a3b15.avif&#34;,&#34;大小&#34;,&#34;180.1 KiB&#34;,&#34;尺寸&#34;,&#34;3748x1842&#34;,&#34;类型&#34;,&#34;image/avif&#34;]" data-thumb-hash="LhgGC4KQkHSWlcZoh3af9JY=" data-contrast-ratio="1" loading="lazy"/></p></div>
<p>好了，知道问题所在了：七年前（至少）写的查“相关文章”的接口没有缓存（大量<span class="space"> </span><code>IN</code>、<code>OR</code>、<code>!=</code><span class="space"> </span>语句），瓶颈全在这儿。</p>
<p>加了缓存后，效果改善就“很大”了：</p>
<pre><code class="language-bash">$ wrk https://blog.mac.twofei.com/1/ 
Running 10s test @ https://blog.mac.twofei.com/1/
  2 threads and 10 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency   447.34us    1.11ms  54.96ms   94.86%
    Req/Sec    20.70k    14.85k   36.16k    51.98%
  415804 requests in 10.10s, 320.85MB read
  Non-2xx or 3xx responses: 383097
Requests/sec:  41167.80
Transfer/sec:     31.77MB
</code></pre>
<p>直接起飞到了<span class="space"> </span>4万<span class="space"> </span>QPS。但是仔细观察，发现错误非常多，nginx<span class="space"> </span>的<span class="space"> </span>upstream<span class="space"> </span>不够用了。我直接访问<span class="space"> </span>localhost：</p>
<pre><code class="language-bash">$ wrk http://localhost:2564/1/
Running 10s test @ http://localhost:2564/1/
  2 threads and 10 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     1.34ms    1.33ms  35.59ms   95.15%
    Req/Sec     4.03k   216.01     4.33k    94.06%
  80920 requests in 10.10s, 493.67MB read
Requests/sec:   8012.03
Transfer/sec:     48.88MB
</code></pre>
<p>这次就只有<span class="space"> </span>8000+<span class="space"> </span>了（全部成功）。</p>
<p>好了，今天的优化至此结束。</p>
<p>测试机器：MacBook<span class="space"> </span>Pro<span class="space"> </span>2023<span class="space"> </span>14-inch,<span class="space"> </span>M2,<span class="space"> </span>32GB.</p>
<div class="divider"><span>2025-10-18<span class="space"> </span>00:06:08</span></div>
<p>再补一下（实时）堆内存占用，压测过后的数值。</p>
<div class="table-wrapper"><div class="scrollable"><table class="yaml">
<thead>
</thead>
<tbody>
<tr><th>使用的库</th><th>内存占用</th><th>QPS</th></tr>
<tr><td><p><code>matth/go-sqlite3</code></p>
</td><td>29MB</td><td>9378</td></tr>
<tr><td><p><code>ncruces/go-sqlite3</code></p>
</td><td>39MB</td><td>8176</td></tr>
</tbody>
</table></div></div>
<p>完全可以接受的程度。</p>
<div class="divider"><span>2026-04-12<span class="space"> </span>03:23:01</span></div>
<p>作者又把WASM版本的SQLite3用wasm2go直接翻译成了go代码，升级后重新跑了一遍，性能有所提升，内存有较大下降：</p>
<pre><code class="language-bash">$ wrk http://localhost:2564/1/
Running 10s test @ http://localhost:2564/1/
  2 threads and 10 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     1.19ms  692.07us  11.88ms   79.97%
    Req/Sec     4.40k   236.63     4.73k    76.00%
  87512 requests in 10.00s, 549.08MB read
Requests/sec:   8748.33
Transfer/sec:     54.89MB
</code></pre>
<p>我才发现我这个测试是基于本地文件系统是真实数据的。我改成内存数据库库以及demo选项后，性能直接起飞：</p>
<pre><code class="language-bash">$ ./taoblog server --demo

$ wrk http://localhost:2564/1/
Running 10s test @ http://localhost:2564/1/
  2 threads and 10 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency   649.46us  335.92us   4.26ms   74.67%
    Req/Sec     7.92k   273.85     8.31k    79.50%
  157551 requests in 10.00s, 295.85MB read
Requests/sec:  15751.83
Transfer/sec:     29.58MB
</code></pre>
<p>直接翻了一倍。太强了。</p>
<div class="footnotes" role="doc-endnotes">
<hr/>
<ol>
<li id="fn:6911a506">
<p><a href="https://github.com/movsb/taoblog/commit/2ab42791dd320b0e5cbfd8f16e8606ed931b9167">mattn/go-sqlite3<span class="space"> </span>(cgo)<span class="space"> </span>➡️<span class="space"> </span>ncruces/go-sqlite3<span class="space"> </span>(wasm)<span class="space"> </span>·<span class="space"> </span>movsb/taoblog@2ab4279</a> <a href="#fn:a43497b1" class="footnote-backref" role="doc-backlink">^</a></p>
</li>
</ol>
</div>
]]></description>
	</item>
</channel>
</rss>
