<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
	<channel>
		<title>Archive for 2025-08 on chengzhycn&#39;s blog</title>
		<link>https://blog.jinzhi.site/posts/2025-08/</link>
		<description>Recent content in Archive for 2025-08 on chengzhycn&#39;s blog</description>
		<generator>Hugo</generator>
		<language>en-us</language>
		
		
		
		
			<lastBuildDate>Sun, 31 Aug 2025 13:29:48 +0800</lastBuildDate>
		
			<atom:link href="https://blog.jinzhi.site/posts/2025-08/index.xml" rel="self" type="application/rss+xml" />
			<item>
				<title>Lua - An Overview</title>
				<link>https://blog.jinzhi.site/posts/2025-08/lua-an-overview/</link>
				<pubDate>Sun, 31 Aug 2025 13:29:48 +0800</pubDate>
				<guid>https://blog.jinzhi.site/posts/2025-08/lua-an-overview/</guid>
				<description>&lt;h2 id=&#34;lua-和-luajit&#34;&gt;Lua 和 LuaJIT&lt;/h2&gt;&#xA;&lt;h3 id=&#34;lua&#34;&gt;Lua&lt;/h3&gt;&#xA;&lt;p&gt;Lua 是一个开源项目，由巴西里约热内卢天主教大学的 Roberto Ierusalimschy、Luiz Henrique de Figueiredo 和 Waldemar Celes 创建。它的版本控制相对简单明了。&lt;/p&gt;&#xA;&lt;h4 id=&#34;lua-的版本体系&#34;&gt;Lua 的版本体系&lt;/h4&gt;&#xA;&lt;p&gt;Lua 的版本号通常是 &lt;code&gt;X.Y&lt;/code&gt; 的形式，例如 &lt;code&gt;5.1&lt;/code&gt;, &lt;code&gt;5.2&lt;/code&gt;, &lt;code&gt;5.3&lt;/code&gt;, &lt;code&gt;5.4&lt;/code&gt;。&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;&lt;code&gt;X&lt;/code&gt; (主版本号/Major Version):&lt;/strong&gt; 表示一个&lt;strong&gt;重大更新&lt;/strong&gt;，通常会引入不兼容的更改（breaking changes），新的核心特性，或者对虚拟机架构的显著改进。从 &lt;code&gt;5.x&lt;/code&gt; 到 &lt;code&gt;5.y&lt;/code&gt;，&lt;code&gt;y&lt;/code&gt; 增加通常意味着语法、API 或语义的更改，可能需要修改现有代码。&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;例子:&lt;/strong&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;Lua 5.0:&lt;/strong&gt; 引入了协程 (coroutines)。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Lua 5.1:&lt;/strong&gt; 引入了模块系统 (module system)、&lt;code&gt;vararg&lt;/code&gt; 参数的改进。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Lua 5.2:&lt;/strong&gt; 引入了 &lt;code&gt;goto&lt;/code&gt; 语句、环境 (environments) 的重新设计、新的 &lt;code&gt;_ENV&lt;/code&gt; 上值。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Lua 5.3:&lt;/strong&gt; 引入了整数类型、位操作 (bitwise operations)、UTF-8 支持。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Lua 5.4:&lt;/strong&gt; 引入了新的垃圾回收器、弱表 (weak tables) 的改进。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;&lt;code&gt;Y&lt;/code&gt; (次版本号/Minor Version) 或补丁版本 (Patch Version):&lt;/strong&gt; 在主版本中，通常用于表示错误修复、性能优化或次要的功能增强。这些更改通常是&lt;strong&gt;向后兼容&lt;/strong&gt;的（backward compatible），不会破坏现有代码运行。有时候，一个版本号会是 &lt;code&gt;X.Y.Z&lt;/code&gt; 的形式，&lt;code&gt;Z&lt;/code&gt; 就代表补丁版本。&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;例子:&lt;/strong&gt; &lt;code&gt;Lua 5.3.1&lt;/code&gt;, &lt;code&gt;Lua 5.3.2&lt;/code&gt;, &lt;code&gt;Lua 5.3.3&lt;/code&gt; 等等。这些通常是修复 bug。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;hr&gt;&#xA;&lt;h3 id=&#34;luajit&#34;&gt;LuaJIT&lt;/h3&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://github.com/LuaJIT/LuaJIT&#34;&gt;https://github.com/LuaJIT/LuaJIT&lt;/a&gt;&lt;/p&gt;</description>
			</item>
			<item>
				<title>KAIST-CS431: Safe Memory Reclamation</title>
				<link>https://blog.jinzhi.site/posts/2025-08/kaist-cs431-safe-memory-reclamation/</link>
				<pubDate>Thu, 28 Aug 2025 13:09:43 +0800</pubDate>
				<guid>https://blog.jinzhi.site/posts/2025-08/kaist-cs431-safe-memory-reclamation/</guid>
				<description>&lt;p&gt;无锁数据结构和传统的数据结构在内存管理有一点区别是：当数据不用了，需要释放内存时，传统数据结构直接调用&lt;code&gt;free()&lt;/code&gt;就可以了，但是无锁数据结构不行。举个例子，如下图所示，T1正在读取b1的时候，T2释放了b1，T1中的指针就变成了野指针，造成不安全的内存访问。&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://blog.jinzhi.site/images/notes/kaist-cs431-safe-memory-reclamation/image-20220410114205442.png&#34; alt=&#34;image-20220410114205442&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;因此，在无锁数据结构中，内存的释放操作一般要延后执行，即保证没有线程能够访问到该片内存后，再执行内存回收。这种方式被称作Safe Memory Reclamation（SMR）。&lt;/p&gt;&#xA;&lt;p&gt;从上述例子中，我们可以从两个方面去理解一个SMR算法：&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;&lt;strong&gt;如何保护正在使用的数据不被释放？&lt;/strong&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;什么时候能安全地释放已经被标记为需要释放的内存？&lt;/strong&gt;&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;下面介绍两种常用的SMR算法：&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;Hazard Pointers（Protected Pointers）&lt;/li&gt;&#xA;&lt;li&gt;Epoch-Based Reclamation（基于代际的内存释放）&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://blog.jinzhi.site/images/notes/kaist-cs431-safe-memory-reclamation/image-20220411074041712.png&#34; alt=&#34;image-20220411074041712&#34;&gt;&lt;/p&gt;&#xA;&lt;h2 id=&#34;hazard-pointers&#34;&gt;Hazard Pointers&lt;/h2&gt;&#xA;&lt;p&gt;Hazard Pointers的基本思想是&lt;strong&gt;线程访问数据块时会将指针记录下来，其它线程尝试释放内存时会感知到数据块引用的存在，从而延迟释放内存，直到所有线程对该数据块的访问结束。&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://blog.jinzhi.site/images/notes/kaist-cs431-safe-memory-reclamation/image-20220411083606925.png&#34; alt=&#34;image-20220411083606925&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;如上图所示，使用hazard pointers有如下几个步骤：&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;在线程访问共享数据时，先调用&lt;code&gt;reserve()&lt;/code&gt;方法，将指针记录在一个list内；&lt;/li&gt;&#xA;&lt;li&gt;T2线程尝试释放释放b1时，调用&lt;code&gt;retire()&lt;/code&gt;，因为检测到还有线程访问b1，延迟释放内存；&lt;/li&gt;&#xA;&lt;li&gt;因为内存没有真正释放，T1能在b1 retire后继续访问b1。访问结束后，T1调用&lt;code&gt;unreserve()&lt;/code&gt;将指针从list中移除；&lt;/li&gt;&#xA;&lt;li&gt;T3调用&lt;code&gt;gc()&lt;/code&gt;（可以周期性运行，也可以事件触发），遍历所有retired的数据块，如果list内没有指向数据块的指针，即可以真正回收内存。&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;Hazard Pointers有些致命的缺点：&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;不够快。因为&lt;code&gt;reserve()&lt;/code&gt;保护的指针需要立即被&lt;code&gt;gc()&lt;/code&gt;感知到，所以，两个函数都需要引入开销非常大的store-load fence。在遍历整个数据结构时，每个数据块都会调用&lt;code&gt;reserve()&lt;/code&gt;，导致遍历效率降低；&lt;/li&gt;&#xA;&lt;li&gt;并不是所有的数据结构都适用Hazard Pointers。在前文的Lock-free Linked List中，我们也提过Harris-Michael‘s traversing是为了专门支持Hazard Pointers的。Harris&amp;rsquo;s traversing会retire一个连续的数据块，这样，即便T1线程保护了其中一个数据块，&lt;code&gt;gc()&lt;/code&gt;仍然可以回收其它的数据块内存，T1线程访问next指针时，仍然会出现问题。&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;h2 id=&#34;epoch-based-reclamation&#34;&gt;Epoch-Based Reclamation&lt;/h2&gt;&#xA;&lt;p&gt;Hazard Pointers只保护无锁数据结构中真正被访问的那部分数据，这也导致了上文中Hazard Pointers的两个缺陷。与Hazard Pointers不同的是，&lt;strong&gt;EBR保护的是数据结构中所有可能潜在的访问&lt;/strong&gt;。这样也就不需要频繁地在线程间同步。&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://blog.jinzhi.site/images/notes/kaist-cs431-safe-memory-reclamation/image-20220413090336739.png&#34; alt=&#34;image-20220413090336739&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;每个线程在访问数据结构前都会开启一个代际（epoch）。T1在代际e中retire了b1和b2。那么什么时候能安全地reclaim b1和b2的内存呢？&lt;/p&gt;&#xA;&lt;p&gt;如上图，我们在这个EBR算法中规定了一个&lt;strong&gt;代际一致性规则：并发的代际之间最多只能相差1。&lt;strong&gt;也就是说，只要T1还没有调用&lt;code&gt;set_quiescent()&lt;/code&gt;结束掉e代，那么其它线程通过&lt;code&gt;set_active()&lt;/code&gt;开启的代际号只能是e+1。而所有的e+1代的线程是有可能引用到b1和b2的。当e代结束，T2开启e+2代时，因为e+2代和e代不存在&lt;/strong&gt;同时发生&lt;/strong&gt;的可能性（通过代际一致性规则保证），所以在e+2代是不可能访问到e代retire的b1和b2的。到了e+3代，表示所有可能访问到b1和b2的e+1代都已经结束，此时即可安全地回收b1和b2内存。&lt;/p&gt;&#xA;&lt;p&gt;不同的代际一致性规则，可以安全回收的代际是不一样的。&lt;/p&gt;</description>
			</item>
			<item>
				<title>KAIST-CS431: Concurrent Data Structure</title>
				<link>https://blog.jinzhi.site/posts/2025-08/kaist-cs431-concurrent-data-structure/</link>
				<pubDate>Thu, 28 Aug 2025 13:09:25 +0800</pubDate>
				<guid>https://blog.jinzhi.site/posts/2025-08/kaist-cs431-concurrent-data-structure/</guid>
				<description>&lt;h2 id=&#34;并发数据结构的关键点&#34;&gt;并发数据结构的关键点&lt;/h2&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;安全性：&lt;/strong&gt; 安全是并发程序对CDS的最基本的要求&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Sequential specification：多线程对数据结构的操作要能像一个队列一样&lt;/li&gt;&#xA;&lt;li&gt;Synchronization：如在栈操作中，不同的线程进行pushing和popping操作时是要同步的，不能这边线程执行完了，另外一边无法感知到这边的操作。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;通过锁或者更加底层的同步原语来保护并发数据结构&lt;/strong&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;可扩展性：&lt;/strong&gt; 随着CPU核数/并发度的增长，有着良好的性能增长&#xA;&lt;ul&gt;&#xA;&lt;li&gt;理想情况下，性能增长应该是线性的，但现实情况往往因为各种限制因素达不到线性增长&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;通过减少锁保护范围（更细粒度的锁）来减少竞争&lt;/strong&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;hand-over-hand locking, lock coupling, read-write locking&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;通过避免写操作降低缓存失效&lt;/strong&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Progress:&lt;/strong&gt; guaranteeing the completion (or progress) of operations&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Lock freedom: progress of at least one&lt;/li&gt;&#xA;&lt;li&gt;Wait freedom: progress of everyone&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;无锁策略&#34;&gt;无锁策略&lt;/h2&gt;&#xA;&lt;p&gt;Lock-Free 和 Wait-Free 都旨在解决传统锁（如互斥锁 mutex）带来的性能和活性问题，但采用了不同的策略和提供了不同的保证。&lt;/p&gt;&#xA;&lt;h3 id=&#34;lock-free&#34;&gt;Lock-Free&lt;/h3&gt;&#xA;&lt;p&gt;Lock-Free 是一个相对较弱的保证，但仍然非常强大和有用。它的核心思想是：&lt;strong&gt;总会有一个线程能够前进，即使其他线程被任意延迟或阻塞。&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;核心特点：&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;没有死锁 (Deadlock-Free)：&lt;/strong&gt; 由于没有线程需要等待其他线程释放锁，所以不会发生死锁。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;没有活锁 (Livelock-Free)：&lt;/strong&gt; 虽然有可能发生活锁（即线程反复尝试但总是失败），但通常通过回退策略（如指数退避）或设计良好的原子操作序列可以避免。然而，严格意义上的 Lock-Free 并不直接保证 Livelock-Free。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;进度保证 (Progress Guarantee)：&lt;/strong&gt; 只要系统不是完全停滞，总有一个或多个线程可以完成操作。这意味着整个系统的吞吐量不会因为某个线程的无限期暂停而归零。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;饥饿可能 (Starvation Possible)：&lt;/strong&gt; 某个特定的线程可能会无限期地重试它的操作，而从未成功（即所谓的“饥饿”）。这是 Lock-Free 与 Wait-Free 的一个主要区别。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;实现方式：&lt;/strong&gt; 主要依赖于原子操作，如&lt;code&gt;CAS (Compare-And-Swap)&lt;/code&gt;，&lt;code&gt;FAA (Fetch-And-Add)&lt;/code&gt;，&lt;code&gt;LL/SC (Load-Link/Store-Conditional)&lt;/code&gt;等。这些操作通常由硬件提供，能够以原子方式读取、修改和写入内存位置，而无需使用操作系统级别的锁。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;常见数据结构：&lt;/strong&gt; 无锁队列 (Lock-Free Queue)，无锁栈 (Lock-Free Stack)，无锁哈希表 (Lock-Free Hash Table) 等。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;&lt;strong&gt;工作原理示例（CAS）：&lt;/strong&gt;&lt;/p&gt;</description>
			</item>
			<item>
				<title>KAIST-CS431: Lock Implementations</title>
				<link>https://blog.jinzhi.site/posts/2025-08/kaist-cs431-lock-implementations/</link>
				<pubDate>Thu, 28 Aug 2025 12:52:58 +0800</pubDate>
				<guid>https://blog.jinzhi.site/posts/2025-08/kaist-cs431-lock-implementations/</guid>
				<description>&lt;h2 id=&#34;naive-spin-lock&#34;&gt;Naive Spin Lock&lt;/h2&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://blog.jinzhi.site/images/notes/kaist-cs431-lock-implementations/image-20220324081153922.png&#34; alt=&#34;image-20220324081153922&#34;&gt;&lt;/p&gt;&#xA;&lt;h3 id=&#34;naive-spin-lock的缺陷&#34;&gt;Naive Spin Lock的缺陷&lt;/h3&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;无法保证公平性，可能有的倒霉蛋空转了一辈子也无法cas成功，无法做到按竞争线程先来后到的次序占有锁。&lt;/li&gt;&#xA;&lt;li&gt;扩展性差，大量对同一内存区域的自旋引发性能问题。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;对naive-spin-lock的优化&#34;&gt;对Naive Spin Lock的优化&lt;/h2&gt;&#xA;&lt;p&gt;关键思路：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;通过release/acquire同步机制保证互斥量的排他性&#xA;&lt;ul&gt;&#xA;&lt;li&gt;从一个临界区的结束（release）到另一个临界区的开始（acquire）&lt;/li&gt;&#xA;&lt;li&gt;在ticket lock里面是&lt;code&gt;curr&lt;/code&gt;成员，CLH/MCS lock是每个waiter的一个新内存区域&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;通过排队和等待不同的区域释放保证公平性&#xA;&lt;ul&gt;&#xA;&lt;li&gt;通过公平的指令来排队（如swap, fetch-and-add）&lt;/li&gt;&#xA;&lt;li&gt;Ticket lock：通过&lt;code&gt;next&lt;/code&gt;来排队，&lt;code&gt;curr&lt;/code&gt;来等待锁&lt;/li&gt;&#xA;&lt;li&gt;CLH/MCS lock：通过&lt;code&gt;tail&lt;/code&gt;来排队，等待每个锁调用者申请的不同区域中值的变化来获取锁&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;ticket-lock&#34;&gt;Ticket Lock&lt;/h3&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://blog.jinzhi.site/images/notes/kaist-cs431-lock-implementations/image-20220323075751550.png&#34; alt=&#34;image-20220323075751550&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;ticket lock在锁的结构体里面新增了一个原子变量&lt;code&gt;next&lt;/code&gt;，每次需要竞争锁时，需要先从&lt;code&gt;next&lt;/code&gt;中拿到一个ticket，然后再去监听&lt;code&gt;curr&lt;/code&gt;，只有&lt;code&gt;curr&lt;/code&gt;被更新成当前的ticket值后，才能去占领锁。&lt;/p&gt;&#xA;&lt;h4 id=&#34;优点&#34;&gt;优点&lt;/h4&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;利用公平指令排队解决了公平性问题&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h4 id=&#34;缺点&#34;&gt;缺点&lt;/h4&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;API相对较复杂（调用者感知ticket）（为什么不直接用curr+1？）&lt;/li&gt;&#xA;&lt;li&gt;没有解决spinlock中所有线程监听同一个原子变量的问题&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;clh-lock&#34;&gt;CLH Lock&lt;/h3&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://blog.jinzhi.site/images/notes/kaist-cs431-lock-implementations/image-20220323082114800.png&#34; alt=&#34;image-20220323082114800&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;为了减少缓存一致性带来的开销，CLH lock被发明了。CLH是三个人首字母的缩写：Craig, Landin, and Hagersten。&lt;/p&gt;&#xA;&lt;p&gt;CLH lock给所有等待线程分配了一个Node，每个Node初始为true，在锁内维护一个链表，所有竞争锁的线程都会获取到前一个线程的Node，并将&lt;code&gt;tail&lt;/code&gt;指针指向自己的Node。&lt;/p&gt;&#xA;&lt;p&gt;不同于spin lock和Ticket lock，CLH lock的临界区是前一个Node中的原子变量。在锁释放时，当前线程会将自己的Node值置为false，而排队的下一个线程监听到这个变化，则可以安全地占有锁了。&lt;/p&gt;&#xA;&lt;h4 id=&#34;优点-1&#34;&gt;优点&lt;/h4&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;线程监听的不同临界区，减少缓存一致性的开销&lt;/li&gt;&#xA;&lt;li&gt;链表排队，也能保证公平性&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h4 id=&#34;缺点-1&#34;&gt;缺点&lt;/h4&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;O(n)的空间复杂度开销，n是临界区数目&lt;/li&gt;&#xA;&lt;li&gt;每个线程都是在前驱节点的Node上自旋，如果跨NUMA会有性能问题&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;mcs-lock&#34;&gt;MCS Lock&lt;/h3&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://blog.jinzhi.site/images/notes/kaist-cs431-lock-implementations/image-20220324080334262.png&#34; alt=&#34;image-20220324080334262&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;MCS lock也是以三个人名命名的：John M. Mellor-Crummey and Michael L. Scott。MCS lock和LCH lock最大的不同是：CLH lock是在前驱节点上自旋，MCS则是在自己节点上自旋。&lt;/p&gt;&#xA;&lt;p&gt;在CLH的Node结构里面，MCS又添加了一个&lt;code&gt;next&lt;/code&gt;字段，新的线程竞争锁时，会将自己的Node添加到前驱节点的&lt;code&gt;next&lt;/code&gt;字段中。&lt;/p&gt;</description>
			</item>
			<item>
				<title>KAIST-CS431: Promising Semantics</title>
				<link>https://blog.jinzhi.site/posts/2025-08/kaist-cs431-promising-semantics/</link>
				<pubDate>Wed, 27 Aug 2025 13:40:27 +0800</pubDate>
				<guid>https://blog.jinzhi.site/posts/2025-08/kaist-cs431-promising-semantics/</guid>
				<description>&lt;p&gt;本章是基于作者的研究“Promising semantics”: &lt;a href=&#34;https://sf.snu.ac.kr/promise-concurrency/&#34;&gt;https://sf.snu.ac.kr/promise-concurrency/&lt;/a&gt; ，提出的一种对宽松内存（relaxed-memory）并发的建模方法。&lt;/p&gt;&#xA;&lt;p&gt;主要观点有4个：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;modeling load hoisting w/ &lt;strong&gt;multi-valued memory&lt;/strong&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;允许一个线程从某个位置读取到一个旧值&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;modeling read-modify-write w/ &lt;strong&gt;message adjacency&lt;/strong&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;禁止对单个值同时进行多个read-modify-write操作&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;modeling coherence &amp;amp; ordering w/ &lt;strong&gt;views&lt;/strong&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;限制线程的行为&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;modeling store hoisting w/ &lt;strong&gt;promises&lt;/strong&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Allowing a thread to speculatively write a value&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;个人理解，**即便是编译器/硬件的指令重排，也是需要遵循一定的规则的，不能随意乱排。**作者从值读取、存储、read-modify-write多种角度对线程的行为进行了建模，是为了解释哪些情况下出现多线程执行出现哪些结果是可能的，哪些是不被允许的。&lt;/p&gt;&#xA;&lt;p&gt;hoisting load/store在网上没有搜到解释，但是有个gcc的优化提到了这个概念。大概意思时将存值/取值操作从原先的指令顺序中调整位置，优化执行效率。&lt;/p&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://gcc.gnu.org/news/hoist.html&#34;&gt;Load/Store Hoisting - GNU Project&lt;/a&gt;&lt;/p&gt;&#xA;&lt;h2 id=&#34;multi-value-memory&#34;&gt;multi-value memory&lt;/h2&gt;&#xA;&lt;p&gt;内存是一系列消息（message）所在的位置，而消息可以看作是&lt;strong&gt;值和时间戳&lt;/strong&gt;的组合。线程很有可能在读取时从内存中读到一个旧值。（effectively hoisting loads）&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://blog.jinzhi.site/images/notes/kaist-cs431-promising-semantics/image-20220318080820894.png&#34; alt=&#34;image-20220318080820894&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;在作者举的例子中，r1=r2=0是被允许的，因为r1 r2都有可能从Y X中读到一个旧值。从后文的view角度理解，因为在独立的线程中，X和Y的赋值并没有改变当前线程中相应Y和X的view，所以，r1 r2的读取操作是可以读到旧值的。&lt;/p&gt;&#xA;&lt;h2 id=&#34;message-adjacency&#34;&gt;message adjacency&lt;/h2&gt;&#xA;&lt;p&gt;上面说了，消息是值和时间戳范围的组合。read-modify-write操作修改了消息的值，在时间轴上应该和前值紧邻在一起（no gap）。&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://blog.jinzhi.site/images/notes/kaist-cs431-promising-semantics/image-20220318081606125.png&#34; alt=&#34;image-20220318081606125&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;可以看到，两次fetch_add操作后，从X的视角上看，0、1、2是紧邻的。第二次fetch_add操作，只能紧贴着1操作，而不能插到0和1之间。&lt;/p&gt;&#xA;&lt;h2 id=&#34;views&#34;&gt;views&lt;/h2&gt;&#xA;&lt;p&gt;这个semantics对我是启发性最强的一章。&lt;/p&gt;&#xA;&lt;p&gt;multi-valued memory允许了太多不在预期中的行为，因此我们需要做些限制，保证一致性和同步。&lt;/p&gt;&#xA;&lt;p&gt;View分为三种，分别是：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Per-thread view：一致性&lt;/li&gt;&#xA;&lt;li&gt;Per-message view：release/acquire同步&lt;/li&gt;&#xA;&lt;li&gt;Global view：SC同步&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;per-thread-view&#34;&gt;Per-thread view&lt;/h3&gt;&#xA;&lt;p&gt;Per-thread view表示线程对消息的确认。&lt;/p&gt;</description>
			</item>
			<item>
				<title>KAIST-CS431: Nondeterminisms of Shared-memory Concurrency</title>
				<link>https://blog.jinzhi.site/posts/2025-08/kaist-cs431-nondeterminisms-of-shared-memory-concurrency/</link>
				<pubDate>Wed, 27 Aug 2025 13:31:59 +0800</pubDate>
				<guid>https://blog.jinzhi.site/posts/2025-08/kaist-cs431-nondeterminisms-of-shared-memory-concurrency/</guid>
				<description>&lt;h2 id=&#34;nondeterminism&#34;&gt;Nondeterminism&lt;/h2&gt;&#xA;&lt;h3 id=&#34;thread-interleaving&#34;&gt;thread interleaving&lt;/h3&gt;&#xA;&lt;p&gt;&lt;strong&gt;interleaving semantics：&lt;/strong&gt; 将多线程的指令交替组合成好像是单线程执行一样。&lt;/p&gt;&#xA;&lt;p&gt;不同线程间的Load/store指令是穿插执行的，导致最终的行为有多种多样的可能。&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-rust&#34; data-lang=&#34;rust&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;no&#34;&gt;COUNTER&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;AtomicUsize&lt;/span&gt;::&lt;span class=&#34;n&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// thread A &amp;amp; B&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;c&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;no&#34;&gt;COUNTER&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;load&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;no&#34;&gt;COUNTER&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;store&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;c&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如上示例，两个线程A和B同时对COUNTER进行+1操作，预期结果当然是2。但是&lt;strong&gt;由于线程调度的不确定性&lt;/strong&gt;可能出现如下的执行顺序：&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;[COUNTER=0] A load, B load, A store, B store [COUNTER=1]&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;导致结果不符合预期。&lt;/p&gt;&#xA;&lt;h4 id=&#34;解决方案&#34;&gt;解决方案&lt;/h4&gt;&#xA;&lt;p&gt;使用原子的reading &amp;amp; writing，禁止掉这种不符预期的执行顺序。&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-rust&#34; data-lang=&#34;rust&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// thread A &amp;amp; B&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;c&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;no&#34;&gt;COUNTER&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;fetch_and_add&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;“Read-modify-write”&lt;/strong&gt;, e.g. swap, compare-and-swap, fetch-and-add&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;reordering&#34;&gt;reordering&lt;/h3&gt;&#xA;&lt;p&gt;&lt;strong&gt;同一个线程中的指令会因为硬件和编译器的优化发生指令重排。&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;DATA = 42;       ||   if FLAG.load() {&#xA;FLAG.store(1);   ||       assert(DATA == 42);&#xA;                 ||   }&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;如上图示例，预期是当&lt;code&gt;FLAG.load()&lt;/code&gt;为1时，&lt;code&gt;DATA == 42&lt;/code&gt;。但是因为指令重排，左边线程中，&lt;code&gt;FLAG.store(1)&lt;/code&gt;可能发生在赋值语句前面；右边线程中assert语句也可能发生在if语句前面。&lt;/p&gt;</description>
			</item>
			<item>
				<title>KAIST-CS431: Lock Based API</title>
				<link>https://blog.jinzhi.site/posts/2025-08/kaist-cs431-lock-based-api/</link>
				<pubDate>Wed, 27 Aug 2025 12:58:54 +0800</pubDate>
				<guid>https://blog.jinzhi.site/posts/2025-08/kaist-cs431-lock-based-api/</guid>
				<description>&lt;h2 id=&#34;标准库中的并发api&#34;&gt;标准库中的并发API&lt;/h2&gt;&#xA;&lt;p&gt;Rust 标准库中基于锁的 API 主要围绕几个核心原语构建，这些原语提供了不同级别的并发控制和用途。它们都包含在 &lt;code&gt;std::sync&lt;/code&gt; 模块中。以下是一些最常用的基于锁的 API：&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;&lt;strong&gt;&lt;code&gt;std::sync::Mutex&amp;lt;T&amp;gt;&lt;/code&gt; (互斥锁)&lt;/strong&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;用途：&lt;/strong&gt; 最常见的互斥锁，用于保护共享数据，确保一次只有一个线程可以访问该数据。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;特点：&lt;/strong&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;当线程尝试获取已被锁定的 Mutex 时，它会阻塞直到锁被释放。&lt;/li&gt;&#xA;&lt;li&gt;提供内部可变性（&lt;code&gt;&amp;amp;T&lt;/code&gt; -&amp;gt; &lt;code&gt;&amp;amp;mut T&lt;/code&gt;）通过 RAII (Resource Acquisition Is Initialization) 机制，即 &lt;code&gt;MutexGuard&lt;/code&gt;。当 &lt;code&gt;MutexGuard&lt;/code&gt; 离开作用域时，锁会自动释放。&lt;/li&gt;&#xA;&lt;li&gt;是“poisoning”感知的：如果持有锁的线程在锁被释放前发生 panic，&lt;code&gt;Mutex&lt;/code&gt; 会被标记为 poisoned。后续尝试获取锁的线程会得到一个 &lt;code&gt;PoisonError&lt;/code&gt;，其中包含原始的 &lt;code&gt;MutexGuard&lt;/code&gt;，允许它们决定如何处理被中断的数据。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;何时使用：&lt;/strong&gt; 当你需要独占访问某个共享资源时，例如全局计数器、数据结构或配置设置。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;&lt;code&gt;std::sync::RwLock&amp;lt;T&amp;gt;&lt;/code&gt; (读写锁)&lt;/strong&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;用途：&lt;/strong&gt; 允许多个读取者同时访问共享数据，但只允许一个写入者独占访问数据。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;特点：&lt;/strong&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;读取者（&lt;code&gt;read()&lt;/code&gt;）：&lt;/strong&gt; 允许多个线程并行获取读锁。只要没有写入者持有锁，所有读锁请求都会成功。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;写入者（&lt;code&gt;write()&lt;/code&gt;）：&lt;/strong&gt; 只允许一个线程获取写锁。当有写入者持有锁时，所有读锁和写锁请求都会阻塞。&lt;/li&gt;&#xA;&lt;li&gt;也提供 RAII 机制，通过 &lt;code&gt;RwLockReadGuard&lt;/code&gt; 和 &lt;code&gt;RwLockWriteGuard&lt;/code&gt;。&lt;/li&gt;&#xA;&lt;li&gt;同样是“poisoning”感知的。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;何时使用：&lt;/strong&gt; 当你的数据被频繁读取但很少写入时，&lt;code&gt;RwLock&lt;/code&gt; 可以提供比 &lt;code&gt;Mutex&lt;/code&gt; 更好的并发性能。例如，一个缓存系统或一个配置对象。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;&lt;code&gt;std::sync::Once&lt;/code&gt; (只运行一次)&lt;/strong&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;用途：&lt;/strong&gt; 确保某个代码块（一个初始化函数）在程序生命周期中只被执行一次，即使有多个线程同时尝试触发它。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;特点：&lt;/strong&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;code&gt;call_once()&lt;/code&gt; 方法会执行一个闭包。第一次调用会实际执行闭包，后续的调用会等待第一次调用完成，但不会再次执行闭包。&lt;/li&gt;&#xA;&lt;li&gt;通常用于惰性初始化全局数据或单例模式。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;何时使用：&lt;/strong&gt; 初始化全局静态变量（例如日志系统、配置加载器）或实现单例模式。通常与 &lt;code&gt;lazy_static&lt;/code&gt; crate (在稳定版 Rust 中) 或 &lt;code&gt;std::sync::OnceLock&lt;/code&gt; (在 1.70+ 版本中，见下文) 结合使用。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;&lt;code&gt;std::sync::Barrier&lt;/code&gt; (屏障)&lt;/strong&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;用途：&lt;/strong&gt; 用于同步一组线程，确保所有线程都到达某个预定义点后才能继续执行。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;特点：&lt;/strong&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;通过 &lt;code&gt;wait()&lt;/code&gt; 方法实现等待。当调用 &lt;code&gt;wait()&lt;/code&gt; 的线程数量达到预设值时，所有等待的线程都会同时被释放。&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;wait()&lt;/code&gt; 返回一个 &lt;code&gt;BarrierWaitResult&lt;/code&gt;，指示当前线程是否是最后一个到达屏障的。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;何时使用：&lt;/strong&gt; 需要协调多个并行任务的执行，例如在某个阶段结束后开始下一阶段，或者在所有子任务完成后进行汇总。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;hr&gt;&#xA;&lt;p&gt;&lt;strong&gt;Rust 1.70+ 中引入的更现代的基于锁的 API：&lt;/strong&gt;&lt;/p&gt;</description>
			</item>
			<item>
				<title>Wasm Internals - Overview</title>
				<link>https://blog.jinzhi.site/posts/2025-08/wasm-internals-overview/</link>
				<pubDate>Tue, 26 Aug 2025 12:29:03 +0800</pubDate>
				<guid>https://blog.jinzhi.site/posts/2025-08/wasm-internals-overview/</guid>
				<description>&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://rsms.me/wasm-intro&#34;&gt;https://rsms.me/wasm-intro&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://hacks.mozilla.org/2017/02/a-cartoon-intro-to-webassembly/&#34;&gt;https://hacks.mozilla.org/2017/02/a-cartoon-intro-to-webassembly/&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;wasm-的历史发展&#34;&gt;Wasm 的历史发展&lt;/h2&gt;&#xA;&lt;h3 id=&#34;早期wasm-mvp---2017&#34;&gt;早期（Wasm MVP - 2017）&lt;/h3&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;诞生背景：&lt;/strong&gt; Wasm 的设计目标是为了替代 asm.js，提供更小、更快、更安全的 Web 二进制格式。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;核心模块的初步定义：&lt;/strong&gt; MVP（Minimum Viable Product）阶段定义了 Wasm Core Module 的基本结构：函数、内存、表、导入、导出、全局变量等。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;主要用例：&lt;/strong&gt; 游戏引擎、音视频编解码、计算密集型任务等。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;限制：&lt;/strong&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;没有模块化系统：&lt;/strong&gt; 模块之间没有标准的链接机制，只能通过宿主环境（如 JavaScript）进行协调。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;缺乏垃圾回收（GC）：&lt;/strong&gt; 需要手动内存管理或使用语言自带的 GC 机制（如 Emscripten 的 mimalloc）。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;没有线程：&lt;/strong&gt; 无法直接利用多核 CPU。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;没有宿主 API 标准化：&lt;/strong&gt; 模块与宿主环境的交互方式高度依赖宿主（如浏览器），没有统一的接口定义。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;没有组件模型：&lt;/strong&gt; 模块重用和组合非常困难。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;中期mvp-之后---持续演进&#34;&gt;中期（MVP 之后 - 持续演进）&lt;/h3&gt;&#xA;&lt;p&gt;Wasm 社区和工作组认识到 MVP 的局限性，并开始着手扩展 Wasm 的能力，这直接影响了 Core Module 的能力：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;多值（Multiple Returns &amp;amp; Parameters）：&lt;/strong&gt; 允许函数返回多个值，接收多个参数，提高表达能力。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;引用类型（Reference Types）：&lt;/strong&gt; 引入了 &lt;code&gt;externref&lt;/code&gt; 和 &lt;code&gt;funcref&lt;/code&gt;，允许 Wasm 直接引用宿主对象和函数，而无需通过数字 ID 传递，为未来的 GC 和组件模型打下基础。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;固定大小的 SIMD（Fixed-width SIMD）：&lt;/strong&gt; 引入了新的指令集，允许在 Wasm 中进行向量化操作，进一步提升某些计算密集型任务的性能。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;线程（Threads）：&lt;/strong&gt; 引入了共享内存和原子操作，允许 Wasm 模块在多线程环境下运行，极大地提升了并行计算能力。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;内存增长和限制（Memory Growth and Limits）：&lt;/strong&gt; 提供了更灵活的内存管理机制。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Tail Calls（尾调用）：&lt;/strong&gt; 优化了函数调用的性能。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;近期和未来wasm-component-model&#34;&gt;近期和未来（Wasm Component Model）&lt;/h3&gt;&#xA;&lt;p&gt;这是 Wasm 发展中最重要的方向之一，旨在解决 Core Module 在模块化和互操作性方面的根本性问题：&lt;/p&gt;</description>
			</item>
			<item>
				<title>Wasm Internals: Stack Machine</title>
				<link>https://blog.jinzhi.site/posts/2025-08/wasm-internals-stack-machine/</link>
				<pubDate>Wed, 20 Aug 2025 01:04:28 +0800</pubDate>
				<guid>https://blog.jinzhi.site/posts/2025-08/wasm-internals-stack-machine/</guid>
				<description>&lt;p&gt;Wasm 中的“栈机”（Stack Machine），这正是其核心执行模型之一。Wasm 是一种基于栈的虚拟机，这意味着它的所有操作都通过从一个操作数栈中弹出值、执行操作并将结果压回栈中来完成。它没有传统的“寄存器”概念。&lt;/p&gt;&#xA;&lt;h2 id=&#34;什么是栈机&#34;&gt;什么是栈机？&lt;/h2&gt;&#xA;&lt;p&gt;在计算机科学中，栈机是一种计算模型，其中指令操作数被隐含地从一个被称为“操作数栈”的内存区域中获取，并且结果被隐含地压回这个栈。这种模型与基于寄存器或基于累加器的模型形成对比。&lt;/p&gt;&#xA;&lt;h2 id=&#34;wasm-栈机的工作原理&#34;&gt;Wasm 栈机的工作原理&lt;/h2&gt;&#xA;&lt;p&gt;Wasm 模块中的函数是由一系列指令组成的。这些指令会操作一个中央的操作数栈。&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;strong&gt;操作数栈（Operand Stack）&lt;/strong&gt;：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;这是 Wasm 执行函数时最核心的数据结构。&lt;/li&gt;&#xA;&lt;li&gt;所有的操作数（如整数、浮点数）和操作结果都临时存储在这个栈上。&lt;/li&gt;&#xA;&lt;li&gt;指令不会像在注册机中那样直接指定操作数的位置（如“将 R1 的值加到 R2”）。相反，它们会假定操作数已经在栈的顶部。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;strong&gt;局部变量（Local Variables）&lt;/strong&gt;：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;除了操作数栈，每个函数调用还有一个独立的“局部变量”区域。&lt;/li&gt;&#xA;&lt;li&gt;局部变量是命名的存储位置，可以在函数的整个执行过程中被访问和修改。&lt;/li&gt;&#xA;&lt;li&gt;虽然局部变量不是栈的一部分，但有很多指令允许你将局部变量的值压入栈中，或者将栈顶部的值存储到局部变量中。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;strong&gt;参数（Parameters）&lt;/strong&gt;：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;函数的参数在函数被调用时，会被初始化为局部变量的一部分（通常是前几个局部变量）。&lt;/li&gt;&#xA;&lt;li&gt;它们也可以被认为是函数执行上下文的一部分。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;strong&gt;指令的操作&lt;/strong&gt;：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;压栈（Push）&lt;/strong&gt;：很多指令会将值压入栈中。例如：&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;code&gt;i32.const 42&lt;/code&gt;：将整数常量 &lt;code&gt;42&lt;/code&gt; 压入栈。&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;local.get &amp;lt;idx&amp;gt;&lt;/code&gt;：获取索引为 &lt;code&gt;&amp;lt;idx&amp;gt;&lt;/code&gt; 的局部变量的值并压入栈。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;弹栈（Pop）&lt;/strong&gt;：大多数操作指令会从栈顶弹出所需数量的操作数。例如：&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;code&gt;i32.add&lt;/code&gt;：弹出栈顶的两个 &lt;code&gt;i32&lt;/code&gt; 整数，将它们相加，然后将结果压回栈。&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;if&lt;/code&gt;/&lt;code&gt;else&lt;/code&gt;/&lt;code&gt;loop&lt;/code&gt; 等控制流指令的条件值也会从栈中弹出。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;复合操作&lt;/strong&gt;：一些指令可能弹出一个值，执行一些副作用（如内存写入），而不压入任何新值。例如：&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;code&gt;i32.store&lt;/code&gt;：弹出内存地址和要存储的值，将值写入内存。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;h2 id=&#34;栈机模型的优势与特点&#34;&gt;栈机模型的优势与特点&lt;/h2&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;strong&gt;紧凑性（Compactness）&lt;/strong&gt;：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;指令更短，因为它们不需要编码操作数的位置。例如，一个加法操作，在寄存器机中可能需要指定两个源寄存器和一个目标寄存器；在栈机中，它只是一个简单的 &lt;code&gt;add&lt;/code&gt; 指令。&lt;/li&gt;&#xA;&lt;li&gt;这有助于生成更小的二进制代码，对于 Web 环境中的快速下载和解析非常有利。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;strong&gt;简化编译器后端（Simplified Compiler Backends）&lt;/strong&gt;：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;IR（中间表示）到指令的映射通常更直接。许多高级语言的语义本身就可以很容易地映射到栈操作。&lt;/li&gt;&#xA;&lt;li&gt;这使得将 C/C++/Rust 等语言编译到 Wasm 变得相对容易。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;strong&gt;易于验证（Easy to Validate）&lt;/strong&gt;：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Wasm 在加载时会进行严格的类型检查和结构验证。栈机模型使得验证其类型安全变得相对容易。例如，当检查 &lt;code&gt;i32.add&lt;/code&gt; 指令时，验证器只需确保栈顶有两个 &lt;code&gt;i32&lt;/code&gt; 类型的值。&lt;/li&gt;&#xA;&lt;li&gt;这对于沙盒环境中的安全性至关重要。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;strong&gt;独立于目标架构（Architecture-Independent）&lt;/strong&gt;：&lt;/p&gt;</description>
			</item>
	</channel>
</rss>
