Golang 内存模型(一)

开始之前

首先,这是一篇菜B写的文章,可能会有理解错误的地方,发现错误请斧正,谢谢。

为了治疗我的懒癌早期,我一次就不写得太多了,这个系列想写很久了,每次都是开了个头就没有再写。这次争取把写完,弄成一个系列。

此 nil 不等彼 nil

先声明,这个标题有标题党的嫌疑。

Go 的类型系统是比较奇葩的,nil 的含义跟其它语言有些差别,这里举个例子(可以直接进入 http://play.golang.org/p/ezFhXX0dnB 运行查看结果):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package main
import "fmt"
type A struct {
}
func main() {
var a *A = nil
var ai interface{} = a
var ei interface{} = nil
fmt.Printf("ai == nil: %v\n", ai == nil)
fmt.Printf("ai == ei: %v\n", ai == ei)
fmt.Printf("ei == a: %v\n", a == ei)
fmt.Printf("ei == nil: %v\n", ei == nil)
}
// -> 输出
// ai == nil: false
// ai == ei: false
// ei == a: false
// ei == nil: true

这里 ai != nil,对于没有用过 Go 的人来说比较费解,对我来说,这个算得上一门语言设计有歧义的地方(Golang FAQ 有对于此问题的描述,可以参考一下:http://golang.org/doc/faq#nil_error)。

简单的说就是 nil 代表 “zero value”(空值),对于不同类型,它具体所代表的值不同。比如上面的 a 为“*A 类型的空值”,而 ai 为“interface{} 类型的空值”。造成理解失误的最大问题在于,struct pointerinterface 有隐式转换(var ai interface{] = a,这里有个隐式转换),至于为什么对于 Go 这种在其它转换方面要求严格,而对于 interface 要除外呢,for convenience 吧,呵呵……

碰到了这个坑,我就开始好奇了,Go 的类型系统到底是什么样的?

VirtualBox与杀毒软件冲突导致虚拟机无法启动: 错误 "supR3HardenedWinReSpawn"

今天在公司机器上装了 VirtualBox (版本 4.3.14) 和 Vagrant, 在运行 vagrant up 后总是提示虚拟机不能启动的错误。打开 VirtualBox 然后手动启动虚拟机发现如下错误框:

1
2
3
4
5
6
---------------------------
VirtualBox - Error In supR3HardenedWinReSpawn
---------------------------
Error relaunching VirtualBox VM process: 5
Command line: '81954AF5-4D2F-31EB-A142-B7AF187A1C41-suplib-2ndchild --comment Work_default_1407215688716_8404 --startvm 93cdc421-ae20-49d6-8ca4-6c5570f809cd --no-startvm-errormsgbox' (rc=-104)/>
---------------------------

原因:

  • 与 Symantec Endpoint Protection 冲突

临时解决办法:

NOTE 降级 VirtualBox 后需要重新启动计算机

另外可以参考VirtualBox 论坛的讨论

在 Go 中获取 stacktrace

打印 stacktrace
1
2
3
4
5
6
buf := make([]byte, 1<<16)
// 获取 **所有** goroutine 的 stacktrace
runtime.Stack(buf, true)
// 如果需要获取 **当前** goroutine 的 stacktrace, 第二个参数需要为 `false`
runtime.Stack(buf, true)
fmt.Println(string(buf))

太诡异了,居然要指定 buffer 的大小,用起来不方便。虽然可以给个“足够大” buffer 用来容纳 stacktrace,但是什么是“足够大”?

为了确认 runtime.Stack() 函数的 behavior,需要参考一下 Go 输出 stacktrace 的实现代码。该代码是使用 GOC 写成的。

A .goc file is a combination of a limited form of Go with C.

mprof.goclink
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
func Stack(b Slice, all bool) (n int) {
uintptr pc, sp;
// Stack pointer
sp = runtime·getcallersp(&b);
// Programer pointer
pc = (uintptr)runtime·getcallerpc(&b);
// 如果选择输出所有 goroutine 的 traceback, 挂起所有goroutine,
// 在本函数完成后恢复
if(all) {
runtime·semacquire(&runtime·worldsema, false);
m->gcing = 1;
runtime·stoptheworld();
}
if(b.len == 0)
n = 0;
else{
// 重定向输出缓冲, 打印到 `b` 这个buffer里
g->writebuf = (byte*)b.array;
// buffer具有固定大小, 因此会截断
g->writenbuf = b.len;
// proc.c: runtime·goroutineheader
runtime·goroutineheader(g);
// traceback_${arch}.c
runtime·traceback(pc, sp, 0, g);
if(all)
runtime·tracebackothers(g);
n = b.len - g->writenbuf;
g->writebuf = nil;
g->writenbuf = 0;
}
if(all) {
m->gcing = 0;
runtime·semrelease(&runtime·worldsema);
runtime·starttheworld();
}
}

因为打印到buffer会截断过长的结果,因此可以写一个包装函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
func StackTrace(all bool) string {
// Reserve 10K buffer at first
buf := make([]byte, 10240)
for {
size := runtime.Stack(buf, all)
// The size of the buffer may be not enough to hold the stacktrace,
// so double the buffer size
if size == len(buf) {
buf = make([]byte, len(buf)<<1)
continue
}
break
}
return string(buf)
}

例子可以在这里看到:Go Playground

使用阿里云镜像服务器

最近本机访问 163.comCentOS 镜像比较不稳定,体现在 ping 很低,但是 HTTP 连接很慢,yumfastestmirror 也不太理想,所以一般都禁用之。

发现阿里云的镜像服务器,立马换了,便秘立刻就通了。

yum 镜像

官方源镜像

1
2
3
4
5
6
# 禁用 fastestmirror 插件
sed -i.backup 's/^enabled=1/enabled=0/' /etc/yum/pluginconf.d/fastestmirror.conf
# 备份
mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backup
# 使用阿里云镜像
wget -O /etc/yum.repos.d/CentOS-Base-aliyun.repo http://mirrors.aliyun.com/repo/Centos-6.repo

EPEL 镜像

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 安装 EPEL 源
wget http://dl.fedoraproject.org/pub/epel/6/x86_64/epel-release-6-8.noarch.rpm
wget http://rpms.famillecollet.com/enterprise/remi-release-6.rpm
rpm -Uvh remi-release-6*.rpm epel-release-6*.rpm
# 使用阿里云镜像
if [[ ! -f /etc/yum.repos.d/epel.repo.backup ]]; then
mv /etc/yum.repos.d/epel.repo /etc/yum.repos.d/epel.repo.backup 2>/dev/null || :
fi
if [[ ! -f /etc/yum.repos.d/epel-testing.repo.backup ]]; then
mv /etc/yum.repos.d/epel-testing.repo /etc/yum.repos.d/epel-testing.repo.backup 2>/dev/null || :
fi
wget -O /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-6.repo

PyPi 镜像

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
mkdir -p ~/.pip
touch ~/.pip/pip.conf
sed -i.backup -r \
's/^index-url\s*=\s*.+$/index-url = http:\/\/mirrors.aliyun.com\/pypi\/simple\//' \
~/.pip/pip.conf
# If file not changed, write contents back to pip.conf
diff "~/.pip/pip.conf" "~/.pip/pip.conf.backup" &> /dev/null
if [ $? -eq 0 ]; then
cat > ~/.pip/pip.conf <<EOF
[global]
index-url = http://mirrors.aliyun.com/pypi/simple/
EOF
fi

RubyGems 镜像

1
2
gem source -r https://rubygems.org/
gem source -a http://mirrors.aliyun.com/rubygems/