C和Go相互调用

C可以调用Go,并且Go可以调用C, 如果更进一步呢, C-->Go-->C 或者 Go-->C-->Go的调用如何实现?

本文通过两个简单的例子帮助你了解这两种复杂的调用关系。本文不涉及两者之间的复杂的数据转换,官方文章C? Go? Cgo!wiki/cgocmd/cgo有一些介绍。

阅读全文

跳出Go module的泥潭

说明: go module增在快速的成长中,最近每个版本中(1.11 ~ 1.13)都有很大的变动。 建议你阅读官方的wiki了解go module最新的知识: go/wiki/Modules

最新扩展阅读(go 1.13):Go module 再回顾

Go 1.11 前天已经正式发布了,这个版本包含了两个最重要的feature就是 moduleweb assembly。虽然也有一些简单的教程介绍了go module的特性,但是基本上都是hello world的例子,在实践的过程中, 很多人都在“拼命的挣扎”,包括我自己, 从一些qq群、github的issue, twitter上都可以看到大家茫然或者抱怨的语句。

虽然有三个帮助文件go help modgo help modulesgo help module-get可以了解一些go module的用法,但是感觉Go开发组对module这一特性还是没有很好的做一个全面的介绍,很多情况还得靠大家看源代码或者去猜,比如module下载的文件夹、版本格式的完整声明,module的最佳实践等,并且当前Go 1.11的实现中还有一些bug,给大家在使用的过程中带来了很大的困难。

我也在摸索中前行, 记录了摸索过程中的一些总结,希望能给还在挣扎中的Gopher一些帮助。

Introduction to Go Modules 是一篇很好的go module 入门介绍, 如果你仔细阅读了它,应该就不需要看本文了。

阅读全文

局部敏感哈希介绍

传统的Hash当源数据有些许的变化的时候生成的哈希值差异也非常的大, 比如:

1
2
3
4
5
6
7
8
9
10
func main() {
s1 := []byte("你好世界")
s2 := []byte("你好,世界")
hash1 := md5.Sum(s1)
hash2 := md5.Sum(s2)
fmt.Println(hex.EncodeToString(hash1[:]))
fmt.Println(hex.EncodeToString(hash2[:]))
}

s1的哈希值是65396ee4aad0b4f17aacd1c6112ee364、s2的哈希值是27444ee2d245c3e8e11ed8b9b035c43b,源数据仅仅是一个逗号的区别,但是哈希值完全不一样。这是我们使用Hash的常见的场景,输出的哈希值经常被称为消息摘要(message digest)或摘要(digest)。

局部敏感哈希(Locality-sensitive hashing, 简称LSH)则不同, LSH则希望相似的源数据计算出来的哈希值越相近越好。
LSH经常用在判重、文章摘要、聚类、相似搜索、近邻查找等场景, 用来减少高维度的数据的维度,相近的数据放在同一个桶中。 比如大规模异常滥用检测:基于局部敏感哈希算法——来自Uber Engineering的实践

阅读全文

创建最小的Go docker 镜像

虽然曾有一些文章介绍了如何创建一个最小的Go Docker镜像,我也曾写过一篇文章,但是随着Go的新的版本的发布, 以及docker本身的进化,有些技巧已经发生了变化, 本文介绍了最新的创建超小的Go镜像的方法。

阅读全文

[译]Go HttpServer 最佳实践

这是 Cloudflare 的 Filippo Valsorda 2016年发表在Gopher Academy的一篇文章, 虽然过去两年了,但是依然很有意义。

先前 crypto/tls 太慢而net/http也很年轻, 所以对于Go web server来说, 通常我们明智的做法把它放在反向代理的后面, 如nginx等,现在不需要了。

在Cloudflare我们最近试验了直接暴漏纯Go的服务作为主机。 Go 1.8的net/httpcrypto/tls 提供了稳定的、高性能并且灵活的功能。

然后,需要做一些调优的工作,本文我们将展示怎么去调优和使web服务器更稳定。

阅读全文

完全静态编译一个Go程序

在Docker化的今天, 我们经常需要静态编译一个Go程序,以便方便放在Docker容器中。 即使你没有引用其它的第三方包,只是在程序中使用了标准库net,你也会发现你编译后的程序依赖glibc,这时候你需要glibc-static库,并且静态连接。

不同的Go版本下静态编译方式还有点不同,在go 1.10下, 下面的方式会尽可能做到静态编译:

1
CGO_ENABLED=0 go build -a -ldflags '-extldflags "-static"' .

有一个提案请求给编译加一个static,如果接收了的话也许在将来的go中直接使用static

参考文档

  1. http://blog.wrouesnel.com/articles/Totally%20static%20Go%20builds/
  2. https://github.com/golang/go/issues/9344
  3. https://github.com/golang/go/issues/26492

ldd、objdump、nm、strings、strip等工具

最近在做Docker镜像的时候发现镜像文件非常大,需要找出程序的依赖库,减少程序的大小,所以整理了一下相关的工具。基本上这些工具都在GNU Binutils中。

GNU Binary Utilities或binutils是一整套的编程语言工具程序,用来处理许多格式的目标文件。当前的版本原本由在Cygnus Solutions的程序员以Binary File Descriptor library(libbfd)所撰写。这个工具程序通常搭配GCC、make、和GDB这些程序来使用。

它包含20个左右的工具,本文介绍了我在创建Docker镜像的时候的使用的几种工具。

ldd

ldd不是GNU Binutils工具集中的一个工具,但是却是一个非常有用的工具, 它可以显示程序或者共享库所需的共享库。

例如:

1
2
3
4
5
# ldd main
linux-vdso.so.1 => (0x00007ffc88fd4000)
libpthread.so.0 => /lib64/libpthread.so.0 (0x00007faee13b8000)
libc.so.6 => /lib64/libc.so.6 (0x00007faee0feb000)
/lib64/ld-linux-x86-64.so.2 (0x00007faee15d4000)

依照ldd得手册, 有时候ldd会通过执行程序来获取依赖信息,对于来源不明的程序,执行这些程序可能会带来风险,所以对于来源不明的程序,可以使用objdump来分析。

objdump

onjdump可以显示目标文件的信息,可以通过参数控制要显示的内容。

比如-p可以显示文件头内容, 通过grep可以查看依赖的库。

1
2
3
4
# objdump -p main|grep GLIBC
0x09691a75 0x00 02 GLIBC_2.2.5
0x09691972 0x00 03 GLIBC_2.3.2
0x09691a75 0x00 04 GLIBC_2.2.5

甚至可以查看-T可以查看动态符号表的内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# objdump -T main|grep GLIBC
0000000000000000 DO *UND* 0000000000000000 GLIBC_2.2.5 stderr
0000000000000000 DO *UND* 0000000000000000 GLIBC_2.2.5 fwrite
0000000000000000 DO *UND* 0000000000000000 GLIBC_2.2.5 vfprintf
0000000000000000 DO *UND* 0000000000000000 GLIBC_2.2.5 fputc
0000000000000000 DO *UND* 0000000000000000 GLIBC_2.2.5 abort
0000000000000000 DO *UND* 0000000000000000 GLIBC_2.2.5 pthread_mutex_lock
0000000000000000 DO *UND* 0000000000000000 GLIBC_2.3.2 pthread_cond_wait
0000000000000000 DO *UND* 0000000000000000 GLIBC_2.2.5 pthread_mutex_unlock
0000000000000000 DO *UND* 0000000000000000 GLIBC_2.3.2 pthread_cond_broadcast
0000000000000000 DO *UND* 0000000000000000 GLIBC_2.2.5 pthread_create
0000000000000000 DO *UND* 0000000000000000 GLIBC_2.2.5 nanosleep
0000000000000000 DO *UND* 0000000000000000 GLIBC_2.2.5 pthread_detach
......

nm

nm显示目标文件的符号。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# nm go/bin/glide |more
0000000000908680 r andMask
0000000000901d00 r bswapMask
00000000009036c0 r BSWAP_SHUFB_CTL
0000000000b000e0 B bufio.ErrAdvanceTooFar
0000000000b000f0 B bufio.ErrBufferFull
0000000000b00100 B bufio.ErrFinalToken
0000000000b00110 B bufio.ErrInvalidUnreadByte
0000000000b00120 B bufio.ErrInvalidUnreadRune
0000000000b00130 B bufio.ErrNegativeAdvance
0000000000b00140 B bufio.ErrNegativeCount
0000000000b00160 B bufio.errNegativeRead
0000000000b00170 B bufio.errNegativeWrite
0000000000b00150 B bufio.ErrTooLong
00000000004d9140 T bufio.init
0000000000b21120 B bufio.initdone.
00000000004d6510 T bufio.(*Reader).Buffered
00000000004d59d0 T bufio.(*Reader).Discard
00000000004d5590 T bufio.(*Reader).fill
00000000004d57c0 T bufio.(*Reader).Peek
00000000004d5b70 T bufio.(*Reader).Read
......

strings

strings显示文件中的可打印字符。

1
2
3
4
# strings main|grep GLIBC
GLIBC_2.2.5
GLIBC_2.3.2
GLIBC_2.2.5

strip

通过上面的工具,可以分析出文件的依赖库,创建Docker镜像的时候只需把所需的依赖库加进去即可。

如果程序本身比较大,可以将程序压缩,去掉不需要的一些数据, 比如使用strip进行裁剪。

你可以通过参数控制要丢掉的哪些符号。
比如去除符号表和行号信息:

1
strip main

libtool

Linux下的GNU Libtool是一种属于GNU建构系统的GNU程序设计工具,用来产生便携式的库。这里引用libtool手册的说明:

1
libtool --mode=compile gcc -g -O -c foo.c

MacOS下的libtool时另外一个工具,可以用来创建库:

1
2
libtool -dynamic -o c.dylib a.o b.o
libtool -static -o c.a a.o b.o

ar

可以对静态库做创建、修改和提取的操作。

1
2
3
ar rv libNAME.a file1.o file2.o
ar -d lib.a conflict.o
ar -x lib.a

otool

比nm更强大,mac还有一个对应的图形化工具——MachOView。

查看依赖动态库:

1
otool -L a.out

查看反汇编代码段:

1
otool -v -t a.out

解决 error creating overlay mount to /var/lib/docker/overlay2

最近在centos7.1使用docker运行redis镜像,出现下面的错误:

1
2
/usr/bin/docker-current: Error response from daemon: error creating overlay mount to /var/lib/docker/overlay2/65f3c109fb903539820f84856d2725af784f2f03f95b1f0214e34184e4d61ff7-init/merged: invalid argument.
See '/usr/bin/docker-current run --help'.

在网上搜索一番后,一个可行的方案如下(改变storage driver类型, 禁用selinux):

  1. 停止docker服务
1
systemctl stop docker
  1. 清理镜像
1
rm -rf /var/lib/docker
  1. 修改存储类型
1
vi /etc/sysconfig/docker-storage

把空的DOCKER_STORAGE_OPTIONS参数改为overlay:

1
DOCKER_STORAGE_OPTIONS="--storage-driver overlay"
  1. 禁用selinux
1
vi /etc/sysconfig/docker

去掉option的--selinux-enabled

  1. 启动docker应该就可以了
1
systemctl start docker

方案抄自 Ysssssssssssssss的博客 和 redis的讨论: error creating overlay mount to .../merged: invalid argument., 基本可以确定是启用selinux导致的。