Blog

  • 查看tcp 队列

    查看队列溢出

    netstat -s | egrep "listen|LISTEN"
        ;全连接队列溢出次数:
        41696 times the listen queue of a socket overflowed
        ;半连接队列溢出次数:
        41696 SYNs to LISTEN sockets dropped
    

    (more…)

  • float_and_double

    浮点数是指用符号,尾数,基数和指数这四部分来表示的小数。因为计算机内部使用的是二进制数,所以基数自然是2。

    来源

    CPU中的二进制数据(小数篇)

    计算机浮点数规格化表示

    单精度与双精度是什么意思,有什么区别?

    浮点数

    • 双进度 64 位,8byte

    • 单精度 32 位,4byte

    浮点数构成`

    浮点数构成_双精度

    浮点数构成_单精度

    1. IEEE 754標準規定:非正規形式的浮點數的指數偏移值比正規形式的浮點數的指數偏移值小1。例如,最小的正規形式的單精度浮點數的指數部分編碼值為1,指數的實際值為-126;而非規約的單精度浮點數的指數域編碼值為0,對應的指數實際值也是-126而不是-127。實際上非正規形式的浮點數仍然是有效可以使用的,只是它們的絕對值已經小於所有的規約浮點數的絕對值;即所有的非規約浮點數比規約浮點數更接近0。規約浮點數的尾數大於等於1且小於2,而非規約浮點數的尾數小於1且大於0

    2. 小数部分是原十进制数值变为二进制后再经过规格化,规格化后省去唯一的整数1之后剩下的0.0100000 00000000 00000000写进去的。

    3. 指数部分是由指数实际值-3再加上固定值(这里取IEEE754标准的固定值2**(e-1)-1,e为储存指数的比特长度,在这里是8 bit)(单精度时指数偏移度为127,目的是为了同时可以表示正和负的指数,即00000000-11111111分别对应-127 — 128)。

      # 十进制运算
      0.15625-(1.0/8)-0-(1.0/32) = 0
      # 因式分解,用二的倍数表示
      0.15625=(1.0/8)+0+(1.0/32)
      # 转为二进制
      0.15625=(1)*(0+0.00101)=(1)*(1+0.01)x2^(-3)
      # 二进制下小数位为0.01
      # 尾数为0100 0000 0000 0000 0000 000
      # 指数 -3
      (-3)-(-127)=134 => 0b0111_1100
      
      

    一个32位单精度浮点数-3.75表示的例子:

    #首先转化为2进制表示
    −3.75=−(2+1+1/2+1/4)=−1.111×2^1
    #整理符号位并进行规格化表示
    −1.111×21=(−1)(1)×(1+0.1110 0000 0000 0000 0000 000)×2^1
    # 尾数为1110 0000 0000 0000 0000 000
    # 指数为1-127=128=0b10000000
    

    精度

    浮点数的尾数是二进制的,转化之后,有一些数无法表示,这也是精度丢失的原因.

  • go 内存对齐

    文章来源

    内存对齐是指首地址对齐,而不是说每个变量大小对齐

    为减少内存对齐带来的 padding 浪费. 构建结构体时,先写大的成员

    操作系统并非一个字节一个字节访问内存,而是按2, 4, 8这样的字长来k访问。
    因此,当 CPU 从存储器读数据到寄存器,或者从寄存器写数据到存储器,IO 的数据长度通常是字长。 如:
    * 32 位系统访问粒度是 4 字节(bytes),
    * 64 位系统的是 8 字节。

    当被访问的数据长度为 n 字节且该数据地址为n字节对齐,那么操作系统就可以高效地一次定位到数据,无需多次读取、处理对齐运算等额外操作。
    数据结构应该尽可能地在自然边界上对齐。如果访问未对齐的内存,CPU 需要做两次内存访问。

    没有内存对齐,cpu读取两次,然后在寄存器中合并.

    平台对齐系数

    计算机在加载和保存数据时,如果内存地址合理地对齐的将会更有效率。
    * 2字节大小的int16类型的变量地址应该是偶数,
    * 一个4字节大小的rune类型变量的地址应该是4的倍数,
    * 一个8字节大小的float64、uint64或64-bit指针类型变量的地址应该是8字节对齐的。
    * 但是对于再大的地址对齐倍数则是不需要的,即使是complex128等较大的数据类型最多也只是8字节对齐。

    unsafe.Sizeof

    unsafe.Sizeof函数返回操作数在内存中的字节大小
    * 参数可以是任意类型的表达式
    * 并且它并不会对表达式进行求值。

    unsafe.Alignof

    unsafe.Alignof 函数返回对应参数的类型需要对齐的倍数.
    和 Sizeof 类似, Alignof 也是返回一个常量表达式, 对应一个常量.
    通常情况下布尔和数字类型需要对齐到它们本身的大小(最多8个字节), 其它的类型对齐到机器字大小.

    unsafe.Offsetof

    unsafe.Offsetof 函数的参数必须是一个字段 x.f, 然后返回 f 字段相对于 x 起始地址的偏移量, 包括可能的空洞

    go string 类型 大小 16.

    • go语言的string是一种数据类型,这个数据类型占用16字节空间,
    • 前8字节是一个指针,指向字符串值的地址,后八个字节是一个整数,标识字符串的长度;
    • 注意go语言的字符串内部并不以’\0’作为结尾,而是通过一个长度域来表示字符串的长度。

    go 数组类型, 测试过 []int8 和 []int 都是 24

    • 在Go语言中数组是一个值类型value type
    • 是真真实实的数组,而不是一个指向数组内存起始位置的指针,也不能和同类型的指针进行转化,这一点严重不同于C语言
    • 所有的值类型变量在赋值和作为参数传递时都将产生一次复制动作
      (这让我想起, channel 传递对象的时候,也是值拷贝. 需要避免拷贝巨大对象)
    • 如果将数组作为函数的参数类型,则在函数调用时该参数将发生数据复制。因此,在函数体中无法修改传入的数组的内容,因为函数内操作的只是所传入数组的一个副本。

    demo

    package main
    
    import (
        "fmt"
        "reflect"
        "unsafe"
    )
    
    type empty_struct struct {
    }
    
    type empty_struct_with_name1 struct {
        flag    bool
        name    string
        empty_s empty_struct
    }
    
    type empty_struct_with_name2 struct {
        a bool
        b empty_struct
        c string
        d empty_struct
    }
    
    func main() {
        s_val := "test"
        int_arr_val := []int{0, 1, 2, 3, 4, 5}
        var empty_s empty_struct
    
        fmt.Println("type:\t", reflect.TypeOf(s_val), "\tsize of:\t", unsafe.Sizeof(s_val), "\talign of:\t", unsafe.Alignof(s_val), "\tval:\t", s_val)
        fmt.Println("type:\t", reflect.TypeOf(int_arr_val), "\tsize of:\t", unsafe.Sizeof(int_arr_val), "\talign of:\t", unsafe.Alignof(int_arr_val), "\tval:\t", int_arr_val)
    
      //空 struct 正常size of 是 0
        fmt.Println("type:\t", reflect.TypeOf(empty_s), "\tsize of:\t", unsafe.Sizeof(empty_s), "\talign of:\t", unsafe.Alignof(empty_s), "\tval:\t", empty_s)
    
      // 空 struct 由于需要内存对齐,占了 8 字节空间
        var empty_s_name1 empty_struct_with_name1
        fmt.Println("type:\t", reflect.TypeOf(empty_s_name1), "\tsize of:\t", unsafe.Sizeof(empty_s_name1), "\talign of:\t", unsafe.Alignof(empty_s_name1), "\tval:\t", empty_s_name1)
    
      //第一个空 struct 由于需要内存对齐和 bool a 一起共同占了 8 字节空间
      //第二个空 struct 由于需要内存对齐和 独自占了 8 字节空间
        var empty_s_name2 empty_struct_with_name2
        fmt.Println("type:\t", reflect.TypeOf(empty_s_name2), "\tsize of:\t", unsafe.Sizeof(empty_s_name2), "\talign of:\t", unsafe.Alignof(empty_s_name2), "\tval:\t", empty_s_name2)
        fmt.Println("struce empty_s_name2 size of:\t", unsafe.Sizeof(empty_s_name2),
            "\ta offset of:\t", unsafe.Offsetof(empty_s_name2.a), "\tb offset of:\t", unsafe.Offsetof(empty_s_name2.b),
            "\tc offset of:\t", unsafe.Offsetof(empty_s_name2.c), "\td offset of:\t", unsafe.Offsetof(empty_s_name2.d))
    
        /*
            output :
            type:    string     size of:     16     align of:    8  val:     test
            type:    []int  size of:     24     align of:    8  val:     [0 1 2 3 4 5]
            type:    main.empty_struct  size of:     0  align of:    1  val:     {}
            type:    main.empty_struct_with_name1   size of:     32     align of:    8  val:     {false  {}}
            type:    main.empty_struct_with_name2   size of:     32     align of:    8  val:     {false {}  {}}
            struce empty_s_name2 size of:    32     a offset:    0  b offset of:     1  c offset of:     8  d offset of:     24
        */
    }
    
    
  • go loop,defer 和 闭包

    golang 中的陷阱. 这是一个有趣的例子,用到了 loop, defer 和闭包.

    例子 1,输出全是 5, 例子 2 和例子 3 会输出 5 4 3 2 1 0.

    // 1
    package main
    
    import "fmt"
    
    func main() {
        var whatever [6]struct{}
        for i := range whatever {
            defer func() {
                fmt.Println(i)
            }()
        }
        /*
            为什么全是 5,为什么不是 0, 1, 2, 3, 4, 5 这样的输出结果呢?
    
            其根本原因是闭包所导致的,有两点原因:
    
            1. 在 for 循环结束后,局部变量 i 的值已经是 5 了,并且 defer 的闭包是直接引用变量的 i。
            2. 结合defer 关键字的特性,可得知会在 main 方法主体结束后再执行。
            结合上述,最终输出的结果是已经自增完毕的 5。
    
        */
    }
    
    //2
    package main
    
    import "fmt"
    
    func main() {
        var whatever [6]struct{}
        for i := range whatever {
            y := i
            defer func() {
                fmt.Println(y)
            }()
        }
    }
    
    //3
    package main
    
    import "fmt"
    
    func main() {
        var whatever [6]struct{}
        for i := range whatever {
            defer func(i int) {
                fmt.Println(i)
            }(i)
        }
    }
    

    defer 执行顺序是先进后出的倒序执行.

    例子 2 中用局部变量声明的方式把的值拷克了.因此输出结果和例子 1 不同.

    例子 3 中,defer 执行的函数的参数是由声明时确定的.因此输出结果和例子 1 不同.

    来源: https://eddycjy.com/posts/go/go-tips-defer/

  • 使用delve 调试golang程序

    目录


    运行编译程序,并且指定参数

    • 使用 -- 用于指定命令行参数
    • 等同于指定命令 bin/go_build_main_go ckfpp -c 10240000
    [going@dev cuckoohui]$ dlv exec bin/go_build_main_go -- ckfpp -c 10240000
    Type 'help' for list of commands.
    
    

    在开始的main函数添加断点

    (dlv) b main.main
    Breakpoint 1 (enabled) set at 0x605813 for main.main() ./main.go:12
    
    

    (more…)