Posted on 2022-09-12 05:57:09 default
摘要:在之前的文章中,我们分析了 Move 中资源的创建在 Move 虚拟机中的实现方式,这篇内容分析资源创建后的修改和销毁。
在 Move 中,当使用 move_to
为某个账户创建了一个资源之后,就可以将这个资源借用出来,并修改它。
例如下面的 Move 代码:
public fun set_value(addr: address, value: u64) acquires Counter {
let counter = borrow_global_mut<Counter>(addr);
counter.i = value;
}
阅读全文
Posted on 2022-09-12 05:55:19 default
摘要:Move 中的资源类型,我们都知道它其实就是 Move 中的一个自定义结构体类型,只不过我们在结构体上加了一些限制:
Posted on 2022-09-12 05:54:12 default
摘要:下面的内容分为两部分,第一部分介绍了Move虚拟机的结构: 作为一个栈式虚拟机,它的操作数栈、调用栈。另外还介绍了Move虚拟机中,虚拟机提供了数据结构来支持函数调用和返回。
第二部分介绍了Move虚拟机中比较关键的字节码指令的作用。
阅读全文Posted on 2019-05-09 16:24:45 default
摘要:下面的思维导图整理分布式系统所设计的常用概念,并对重要知识点做了简单解析。
阅读全文Posted on 2017-05-17 03:24:16 python
摘要: 阅读全文Posted on 2016-04-16 06:19:41 os
摘要:在前一篇博客中,我们介绍了Linux内核中内存映射的实现,以及用户态调用mmap系统调用的原理。接下来,我们会通过Linux内核模块的方式,映射两种物理内存:物理地址空间和内核模块申请的内存,演示内核模块中如何与用户态进程之间共享内存。
在开始之前,介绍一下/dev/mem
这个字符设备,打开/dev/mem
之后,通过mmap系统调用,就可以映射物理地址空间。需要注意的是,这里的物理地址空间,是站在CPU角度看到的地址空间,而不仅仅是RAM空间,还有外设的IO空间,例如BIOS ROM、Video ROM、PCI BUS等。
下面的内核模块代码工作的行为类似于/dev/mem
,打开对应的设备文件后,同样可以映射CPU地址空间。完整的代码在文章最后。
Posted on 2015-12-01 11:30:27 os
摘要:在unix/linux平台下读写文件,一般有两种方式。第一种是首先open文件,接着使用read系统调用读取文件的全部或一部分。于是内核将文件的内容从磁盘上读取到内核页高速缓冲,再从内核高速缓冲读取到用户进程的地址空间。这么做需要在内核和用户空间之间做四次数据拷贝。而且当多个进程同时读取一个文件时,则每一个进程在自己的地址空间都有这个文件的副本,这样也造成了物理内存的浪费。如下图所示:
第二种方式是使用内存映射的方式。首先open文件,接着调用mmap系统调用,将文件的内容的全部或一部分直接映射到进程的地址空间,映射完成后,进程可以像访问普通内存一样做memcpy等操作,不必再调用read/write等操作。mmap并不分配物理地址空间,它只是会占用进程的虚拟内存空间。而第一种方式则需要进程预先分配好物理内存,内核才能将页高速缓冲中的文件数据拷贝到用户进程指定的内存空间中。
使用第二种方式,当多个进程同时访问读取一个文件时,每个进程都将文件内容在内核中的页高速缓冲映射到自己的地址空间。当第一个进程访问内核中的页缓冲时,进程的机器指令会触发一个缺页中断。内核将文件的这一页数据读入到页高速缓冲,并更新进程的页表,使页表指向内核缓冲中的这个页。之后有其他进程再次访问同一页时,该页已经在内存中,内核只需要将进程的页表登记并指向内核中的页高速缓冲即可。如下图所示:
阅读全文Posted on 2015-06-30 17:18:28 os
Nim语言有很多语言上先进的特性和接近Python的语法,Rust定位成C++的直接竞争者。 但是请认真思考:这两个语言从一出生开始,都没有解决,而且以后也很难解决本世纪软件业的一次重大危机:多核编程危机。 它们的出现就不是冲着解决多核编程问题来的,基因决定了,靠这两门语言解决不了多核编程的问题。
怎么解决多核编程的问题?
屏蔽硬件上的复杂特性,例如缓存、一致性、内存屏障、原子操作,给程序员简单的并发特性,在编程时存在尽量少的心智负担。
GO可以在内存中创建成千上万的协程,并且提供了协程间通信的基础设施,单凭这两点,Nim和Rust都没有做到。
Nim语言目前没有实现自身的汇编器和链接器,Nim代码首先被编译为C代码,再把C代码编译为本地机器码。 Golang自带汇编器和链接器。
Rust官方最初的目标是像Erlang一样可以创建大量的协程,但是这个目标被官方抛弃了,所以Rust里面是并发执行体不是协程,是OS级别的线程。在高并发场景下,1000个OS的线程同时运行效率就变得非常差。或者可以选择异步模型,但是又面临回调地狱,并且要小心同步IO和CPU密集型计算阻塞当前线程。如果使用第三库必须经过改造以适合异步模型。
因为Rust官方明白,实现完整高效的的协程调度,难度很大。这方面Go做的很好,其他静态编译类型的语言都没有超过它。
我们可以说Nim和Rust的定位不同,要解决各自的目标问题。 但是很多人拿Nim和Go对比的时候,根本没有,而且也不敢把这两种语言的特性和Go的核心特性来对比。
多核编程,是目前遇到的问题,而且是难以解决的问题,谁能解决的高效和优雅,谁就能在未来获胜。
许世伟说过,他在C++中实现协程和协程调度,到头来也只是对Golang的拙劣的模仿,我想Rust官方最初的想法也大概如此吧。
而且我相信Ken Thompson和Russ Rox这两位大师的眼界。
知乎上关于Rust高并发框架实现的问题:http://www.zhihu.com/question/30325880
Posted on 2015-05-14 09:41:57 golang
摘要:本文分析了Golang的socket文件描述符和goroutine阻塞调度的原理。代码中大部分是Go代码,小部分是汇编代码。完整理解本文需要Go语言知识,并且用Golang写过网络程序。更重要的是,需要提前理解goroutine的调度原理。
…
网络轮询器是Golang中针对每个socket文件描述符建立的轮询机制。 此处的轮询并不是一般意义上的轮询,而是Golang的runtime在调度goroutine或者GC完成之后或者指定时间之内,调用epoll_wait获取所有产生IO事件的socket文件描述符。当然在runtime轮询之前,需要将socket文件描述符和当前goroutine的相关信息加入epoll维护的数据结构中,并挂起当前goroutine,当IO就绪后,通过epoll返回的文件描述符和其中附带的goroutine的信息,重新恢复当前goroutine的执行。
阅读全文Posted on 2015-04-24 08:55:17 golang
摘要:在Golang汇编快速指南这篇博客中,简单介绍了Golang中汇编的简单语法以及特殊之处。下面介绍Golang中的内置函数和相关操作代码的汇编实现,可以作为上篇博客的补充和实践。
汇编中过程调用的参数是通过栈来传递的,在栈上的布局如下:
参数3
参数2
参数1 <-FP
保存PC <-SP
...
...
package main
import (
"fmt"
)
type new_int int
var (
gobal_1 = "this is global var"
)
func main() {
auto_1 := "this is auto_1"
s0 := new(new_int)
s1 := make([]int, 10)
s2 := make([]int, 10)
append(s2, 9999)
fmt.Println(s1, s2)
fmt.Println(s0)
}
下面是汇编代码:
"".main t=1 size=1936 value=0 args=0x0 locals=0x128
// 定义函数main,栈帧大小为296字节,0字节的参数(无参数)
0x0000 00000 (builtin.go:13) TEXT "".main+0(SB),$296-0
// 将线程本地存储(thread local storage)传送到CX
0x0000 00000 (builtin.go:13) MOVQ (TLS),CX
// 下面是检查栈帧的大小是否超过目前分配的小
0x0009 00009 (builtin.go:13) LEAQ -168(SP),AX
0x0011 00017 (builtin.go:13) CMPQ AX,16(CX)
0x0015 00021 (builtin.go:13) JHI ,30
// 如果超过调用runtime.morestack_noctxt
0x0017 00023 (builtin.go:13) CALL ,runtime.morestack_noctxt(SB)
0x001c 00028 (builtin.go:13) JMP ,0
// 扩大栈帧
0x001e 00030 (builtin.go:13) SUBQ $296,SP
0x0025 00037 (builtin.go:13) FUNCDATA $0,gclocals·e14c7473fe07b0ccdc0fdfa1a770087b+0(SB)
0x0025 00037 (builtin.go:13) FUNCDATA $1,gclocals·7a70fcb413ec620f2a7a8c3ba5f394c1+0(SB)
// 局部变量auto_1
0x0025 00037 (builtin.go:14) LEAQ go.string."this is auto_1"+0(SB),BX
// string在golang中是由数据本身和其长度组成的
// 将数据的地址移动到BP
0x002c 00044 (builtin.go:14) MOVQ (BX),BP
// 移动BP到栈指针152字节的位置
0x002f 00047 (builtin.go:14) MOVQ BP,"".auto_1+152(SP)
// 将字符串长度移动到BP
0x0037 00055 (builtin.go:14) MOVQ 8(BX),BP
// 移动BP到栈指针160字节的位置
0x003b 00059 (builtin.go:14) MOVQ BP,"".auto_1+160(SP)
// new_int类型的初始化
// 将类型本身移动到BX
0x0043 00067 (builtin.go:17) MOVQ $type."".new_int+0(SB),BX
// 将BX移动到栈顶
0x004a 00074 (builtin.go:17) MOVQ BX,(SP)
0x004e 00078 (builtin.go:17) PCDATA $0,$1
// 调用runtime.newobject
0x004e 00078 (builtin.go:17) CALL ,runtime.newobject(SB)
// 将返回的结果移动到BX
0x0053 00083 (builtin.go:17) MOVQ 8(SP),BX
// 移动BP到栈指针80字节的位置
0x0058 00088 (builtin.go:17) MOVQ BX,"".s1+80(SP)
0x005d 00093 (builtin.go:17) NOP ,
// 创建slice s1
// 将类型移动到BX
0x005d 00093 (builtin.go:18) MOVQ $type.[]int+0(SB),BX
// 将BX(类型)移动到栈顶
0x0064 00100 (builtin.go:18) MOVQ BX,(SP)
// 将长度参数len移动到相对于栈顶8字节的位置
0x0068 00104 (builtin.go:18) MOVQ $10,8(SP)
// 将容量参数cap移动到相对于栈顶16字节的位置
0x0071 00113 (builtin.go:18) MOVQ $10,16(SP)
0x007a 00122 (builtin.go:18) PCDATA $0,$2
// 调用runtime.makeslice
0x007a 00122 (builtin.go:18) CALL ,runtime.makeslice(SB)
// 创建出的s1的参数分别保存在相对栈顶24, 32, 40字节的位置
0x007f 00127 (builtin.go:18) MOVQ 24(SP),DX
0x0084 00132 (builtin.go:18) MOVQ 32(SP),CX
0x0089 00137 (builtin.go:18) MOVQ 40(SP),BX
// CX中保存的是slice的len参数
0x008e 00142 (builtin.go:20) MOVQ CX,"".s2_len+64(SP)
0x0093 00147 (builtin.go:20) NOP ,
// 将slice的array, len, cap传送给s2
0x0093 00147 (builtin.go:21) MOVQ DX,"".s2+168(SP)
0x009b 00155 (builtin.go:21) MOVQ CX,"".s2+176(SP)
0x00a3 00163 (builtin.go:21) MOVQ BX,"".s2+184(SP)
0x00ab 00171 (builtin.go:21) MOVQ BX,AX
// 从BX中减去CX的值,结果保存在BX
0x00ae 00174 (builtin.go:21) SUBQ CX,BX
0x00b1 00177 (builtin.go:21) CMPQ BX,$1
0x00b5 00181 (builtin.go:21) JGE ,262
// 将类型传送到栈顶
0x00b7 00183 (builtin.go:21) MOVQ $type.[]int+0(SB),BX
0x00be 00190 (builtin.go:21) MOVQ BX,(SP)
// 将旧的slice传送到栈的第二个参数
0x00c2 00194 (builtin.go:21) MOVQ DX,"".autotmp_0001+240(SP)
0x00ca 00202 (builtin.go:21) MOVQ DX,8(SP)
// 将长度传送到栈上第三个参数(s2的长度)
0x00cf 00207 (builtin.go:21) MOVQ CX,"".autotmp_0001+248(SP)
0x00d7 00215 (builtin.go:21) MOVQ CX,16(SP)
// 将33行的BX的值重新保存到24(SP),即s2的array
0x00dc 00220 (builtin.go:21) MOVQ AX,"".autotmp_0001+256(SP)
0x00e4 00228 (builtin.go:21) MOVQ AX,24(SP)
// 将s2的长度置为1
0x00e9 00233 (builtin.go:21) MOVQ $1,32(SP)
// 调用growslice
0x00f2 00242 (builtin.go:21) PCDATA $0,$2
0x00f2 00242 (builtin.go:21) CALL ,runtime.growslice(SB)
// 3个返回值(sliceStruct)
0x00f7 00247 (builtin.go:21) MOVQ 40(SP),DX
0x00fc 00252 (builtin.go:21) MOVQ 48(SP),CX
0x0101 00257 (builtin.go:21) MOVQ 56(SP),AX
// 增加返回的结构中len参数长度,增加1
0x0106 00262 (builtin.go:21) MOVQ CX,SI
0x0109 00265 (builtin.go:21) INCQ ,SI
// 变址寻址,DX+CX*8
0x010c 00268 (builtin.go:21) LEAQ (DX)(CX*8),BX
// 将立即数9999传递到(BX)的内存位置
0x0110 00272 (builtin.go:21) MOVQ $9999,(BX)
0x0117 00279 (builtin.go:21) NOP ,
0x0117 00279 (builtin.go:21) MOVQ DX,"".autotmp_0001+240(SP)
0x011f 00287 (builtin.go:21) MOVQ SI,"".autotmp_0001+248(SP)
0x0127 00295 (builtin.go:21) MOVQ AX,"".autotmp_0001+256(SP)
// 将新的slice的参数传递给s2
0x012f 00303 (builtin.go:21) MOVQ DX,"".s2+168(SP)
0x0137 00311 (builtin.go:21) MOVQ SI,"".s2+176(SP)
0x013f 00319 (builtin.go:21) MOVQ AX,"".s2+184(SP)
0x0147 00327 (builtin.go:21) NOP ,
阅读全文