Skip to main content

4 posts tagged with "golang"

View All Tags

golang compile

· 3 min read

golang 的 lex和parse 在src\cmd\compile\internal\gc\main.go开始

核心步骤

  • parseFiles lex and parse
  • typecheck 语法树的遍历做类型检查
  • ssa

之后会经过link 连接和加载器ld

func Main(archInit func(*Arch)) {
lines := parseFiles(flag.Args()) // lex and parse
...
typecheckok = true

// Process top-level declarations in phases.

// Phase 1: const, type, and names and types of funcs.
// This will gather all the information about types
// and methods but doesn't depend on any of it.
//
// We also defer type alias declarations until phase 2
// to avoid cycles like #18640.
// TODO(gri) Remove this again once we have a fix for #25838.

// Don't use range--typecheck can add closures to xtop.
timings.Start("fe", "typecheck", "top1")
for i := 0; i < len(xtop); i++ {
n := xtop[i]
if op := n.Op; op != ODCL && op != OAS && op != OAS2 && (op != ODCLTYPE || !n.Left.Name.Param.Alias) {
xtop[i] = typecheck(n, ctxStmt)
}
}

// Phase 2: Variable assignments.
// To check interface assignments, depends on phase 1.

// Don't use range--typecheck can add closures to xtop.
timings.Start("fe", "typecheck", "top2")
for i := 0; i < len(xtop); i++ {
n := xtop[i]
if op := n.Op; op == ODCL || op == OAS || op == OAS2 || op == ODCLTYPE && n.Left.Name.Param.Alias {
xtop[i] = typecheck(n, ctxStmt)
}
}

// Phase 3: Type check function bodies.
// Don't use range--typecheck can add closures to xtop.
timings.Start("fe", "typecheck", "func")
var fcount int64
for i := 0; i < len(xtop); i++ {
n := xtop[i]
if op := n.Op; op == ODCLFUNC || op == OCLOSURE {
Curfn = n
decldepth = 1
saveerrors()
typecheckslice(Curfn.Nbody.Slice(), ctxStmt)
checkreturn(Curfn)
if nerrors != 0 {
Curfn.Nbody.Set(nil) // type errors; do not compile
}
// Now that we've checked whether n terminates,
// we can eliminate some obviously dead code.
deadcode(Curfn)
fcount++
}
}
// With all types checked, it's now safe to verify map keys. One single
// check past phase 9 isn't sufficient, as we may exit with other errors
// before then, thus skipping map key errors.
}
(gdb) bt
#0 cmd/go/internal/load.LoadImport (path=..., srcDir=..., parent=0xc000147200, stk=0xc0001db698, importPos=..., mode=1, ~r6=<optimized out>) at /home/dinosaur/newgo/go/src/cmd/go/internal/load/pkg.go:530
#1 0x000000000079fa1b in cmd/go/internal/load.(*Package).load (p=0xc000147200, stk=0xc0001db698, bp=0xc0001b8a80, err=...) at /home/dinosaur/newgo/go/src/cmd/go/internal/load/pkg.go:1707
#2 0x0000000000799827 in cmd/go/internal/load.loadImport (pre=0x0, path=..., srcDir=..., parent=0xc000146d80, stk=0xc0001db698, importPos=..., mode=1, ~r7=<optimized out>)
at /home/dinosaur/newgo/go/src/cmd/go/internal/load/pkg.go:578
#3 0x000000000079890a in cmd/go/internal/load.LoadImport (path=..., srcDir=..., parent=0xc000146d80, stk=0xc0001db698, importPos=..., mode=1, ~r6=<optimized out>) at /home/dinosaur/newgo/go/src/cmd/go/internal/load/pkg.go:531
#4 0x000000000079fa1b in cmd/go/internal/load.(*Package).load (p=0xc000146d80, stk=0xc0001db698, bp=0xc0001b8700, err=...) at /home/dinosaur/newgo/go/src/cmd/go/internal/load/pkg.go:1707
#5 0x0000000000799827 in cmd/go/internal/load.loadImport (pre=0x0, path=..., srcDir=..., parent=0xc000146900, stk=0xc0001db698, importPos=..., mode=1, ~r7=<optimized out>)
at /home/dinosaur/newgo/go/src/cmd/go/internal/load/pkg.go:578
#6 0x000000000079890a in cmd/go/internal/load.LoadImport (path=..., srcDir=..., parent=0xc000146900, stk=0xc0001cf698, importPos=..., mode=1, ~r6=<optimized out>) at /home/dinosaur/newgo/go/src/cmd/go/internal/load/pkg.go:531
#7 0x000000000079fa1b in cmd/go/internal/load.(*Package).load (p=0xc000146900, stk=0xc0001db698, bp=0xc0001b8380, err=...) at /home/dinosaur/newgo/go/src/cmd/go/internal/load/pkg.go:1707
#8 0x00000000007a4c66 in cmd/go/internal/load.GoFilesPackage (gofiles=..., ~r1=<optimized out>) at /home/dinosaur/newgo/go/src/cmd/go/internal/load/pkg.go:2230
#9 0x00000000007a3c54 in cmd/go/internal/load.PackagesAndErrors (patterns=..., ~r1=...) at /home/dinosaur/newgo/go/src/cmd/go/internal/load/pkg.go:2056
#10 0x00000000007a417d in cmd/go/internal/load.PackagesForBuild (args=..., ~r1=...) at /home/dinosaur/newgo/go/src/cmd/go/internal/load/pkg.go:2123
#11 0x0000000000842528 in cmd/go/internal/work.runBuild (cmd=<optimized out>, args=...) at /home/dinosaur/newgo/go/src/cmd/go/internal/work/build.go:348
#12 0x0000000000932219 in main.main () at /home/dinosaur/newgo/go/src/cmd/go/main.go:189

golang interface 比较

· 2 min read

来源

Interface values are comparable. Two interface values are equal if they have identical dynamic types and equal dynamic values or if both have value nil.
A value x of non-interface type X and a value t of interface type T are comparable when values of type X are comparable and X implements T. They are equal if t's dynamic type is identical to X and t's dynamic value is equal to x.

A comparison of two interface values with identical dynamic types causes a run-time panic if values of that type are not comparable. This behavior applies not only to direct interface value comparisons but also when comparing arrays of interface values or structs with interface-valued fields.

interface 是可以比较的,当两个interface满足以下之一的时候两者相等:

  • 两个interface的动态类型和动态值两两相等
  • 两个interface值都是nil

当一个是interface,一个不是interface的时候,满足以下条件才能可比较:

  • x 是类型X的值,t是类型T的值。只有X是可比较且X是T的实现的时候,x和t是可比较的

那么当一个是interface,一个不是interface的时候,可比较的时候,怎么样才能相等呢?

  • 当t的动态类型和X相同且t的动态值与x相同

当比较两个interface的时候,如果他们的类型是不可比较的,那么会产生运行时panic,这种panic不仅仅会发生在interface直接比较。还会发生在interface数组比较或者包含interface作为structs中的字段时候的structs之间的比较。

相关分析

go-micro hello world

· One min read

最近在学一点点go 相关的内容,遇到了很多坑

什么是mDNS

相关阅读

gomicro 遇到的编译不过的问题(时间是2019/9/19)

我但是用的go 版本是1.10,然后编译的时候报crypto/ed25519 这个包找不到

  • 怎么解决?

升级到go 1.13以上版本,好像1.13才有这个包

相关记录

// In Go 1.13, the ed25519 package was promoted to the standard library as
// crypto/ed25519, and this package became a wrapper for the standard library one.
//
// +build !go1.13

整个调用流程和抓包

// todo

golang_gc 相关问题

· 4 min read

golang gc 关闭fd

4月还是5月的时候写了个golang 的程序,因为要保证最多只有一个进程存在所以进程启动就去获取锁,没有获取文件锁的进程就退出。每分钟我会启动一次进程。目的就是为了进程保活。

使用文件锁就是为了他的特性:

  • 如果文件关闭,那么锁也会被回收

遇到的问题

  • 问题是:过了半天之后ps aux 看启动的进程,发现居然有7-8个。按照预想应该只有一个。 代码大概是长这样的
func lockFile(){
name := "lockfiletest.lock"
file, err := os.OpenFile(name, syscall.O_CREAT|syscall.O_RDWR|syscall.O_CLOEXEC, 0666) //①打开文件
 ...
err = syscall.FcntlFlock(file.Fd(), syscall.F_SETLK, &flockT)  //②加锁
...
}
func main(){
err :=lockFile()
if err!=nil{
os.Exit(2) // ③加锁失败退出
}
}

很简单的逻辑,就是获取文件锁,获取失败则退出

找问题

  • 问题出在哪里呢?  

想了很久很久:难道是我用的库哪里fork了进程?文件被哪个第三方包关闭了?

想了很久很久一直怀疑第三方包有问题,但是最后经过google很多次后定位到是gc 的问题。

相关链接

在下面的例子里面编译后会在手动执行runtime.GC()后文件被回收

package main

import (
"os"
"log"
"time"
"runtime"
)

func openFile(path string) error {
_, err := os.Open(path)
return err
}

func main() {
if err := openFile(os.Args[1]); err != nil {
log.Fatal(err)
}
// trigger GC below will also recycle the non-referenced fd opened before
runtime.GC()
time.Sleep(time.Hour)
}
  • 怎么看进程打开的文件呢?  

    通过proc文件系统就可以了,proc文件系统几乎把linux内核所有的统计量都导出来了哦

## 8808 就是我的nginx 的master 的pid
ll /proc/8808/fd/
total 0
dr-x------ 2 root root 0 8月 10 06:16 ./
dr-xr-xr-x 9 root root 0 8月 9 07:35 ../
lrwx------ 1 root root 64 8月 10 06:16 0 -> /dev/null
lrwx------ 1 root root 64 8月 10 06:16 1 -> /dev/null
l-wx------ 1 root root 64 8月 10 06:16 2 -> /usr/local/nginx/logs/error.log*
lrwx------ 1 root root 64 8月 10 06:16 3 -> socket:[78178946]
l-wx------ 1 root root 64 8月 10 06:16 4 -> /usr/local/nginx/logs/access.log*
l-wx------ 1 root root 64 8月 10 06:16 5 -> /usr/local/nginx/logs/error.log*
lrwx------ 1 root root 64 8月 10 06:16 6 -> socket:[78180730]
lrwx------ 1 root root 64 8月 10 06:16 7 -> socket:[78178947]

怎么解决

第一:我们的问题是什么?   其实问题很简单:

  • 我们的fd这个对象被回收了
  • gc的调用fd对象回调函数
  • 回调函数把fd对象对应的文件描述符关闭了

解决方案:

把fd 弄成全局变量,全局变量一直被引用所以不会被gc回收掉

var file *File  // 加了一行变成全局变量
func lockFile(){
name := "lockfiletest.lock"
file, err := os.OpenFile(name, syscall.O_CREAT|syscall.O_RDWR|syscall.O_CLOEXEC, 0666) //①打开文件
 ...
err = syscall.FcntlFlock(file.Fd(), syscall.F_SETLK, &flockT)  //②加锁
...
}