Skip to main content

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

qemu调试内核

· 2 min read

相关参考

第一步

$ cd linux-4.16
$ make menuconfig
$ make -j8
  • 开启debug 信息
Kernel hacking  ---> 
[*] Kernel debugging
Compile-time checks and compiler options --->
[*] Compile the kernel with debug info
[*] Provide GDB scripts for kernel debugging
  • 关闭地址随机化 选中Processor type and features 然后关闭
 [*] 64-bit kernel                                                │ │  
│ │ General setup ---> │ │
│ │ [*] Enable loadable module support ---> │ │
│ │ -*- Enable the block layer ---> │ │
│ │ Processor type and features --->

反选 KASLR, 也就是关闭地址随机化

│ │    [ ]   Randomize the address of the kernel image (KASLR)   

为什么需要关闭地址随机

第二步

编译busybox,注意是静态链接,然后构建initramfs根文件系统

第三步

通过qemu 跑这个系统

qemu-system-x86_64  -kernel  /home/dinosaur/Downloads/linux-4.16/arch/x86/boot/bzImage  -hda qemu_rootfs.img  -append "root=/dev/sda rootfstype=ext4 rw"   -gdb tcp::1234

gdb 调试

gdb vmlinux
(gdb) target remote localhost:1234
b vfs_write

然后输出

(gdb) bt
#0 vfs_write (file=0xffff880006431700, buf=0x66506a <error: Cannot access memory at address 0x66506a>,
count=46, pos=0xffffc900000b7f08) at fs/read_write.c:529
#1 0xffffffff811a08cd in SYSC_write (count=<optimized out>, buf=<optimized out>, fd=<optimized out>)
at fs/read_write.c:589
#2 SyS_write (fd=<optimized out>, buf=6705258, count=46) at fs/read_write.c:581
#3 0xffffffff81001c8b in do_syscall_64 (regs=0xffff880006431700) at arch/x86/entry/common.c:287
#4 0xffffffff81a00071 in entry_SYSCALL_64 () at arch/x86/entry/entry_64.S:237
#5 0x0000000000000000 in ?? ()

tcpdump_resp

· 3 min read

最近使用tcpdump 抓包发现居然支持RESP 协议的解析

tcpdump -i lo port 6379

当我在redis-cli 敲 set a a时候,tcpdump返回

seq 1023993873:1023993900, ack 4077734227, win 342, options [nop,nop,TS val 2912390058 ecr 2912384753], length 27: RESP "set" "a" "a"
  • 如何实现?

去githup 上拿下来编译之后,发现是调用resp_print解析RESP协议的,而且是固定端口的,当端口是6379的时候会尝试用redis的协议解析

void
tcp_print(netdissect_options *ndo,
const u_char *bp, u_int length,
const u_char *bp2, int fragmented)
{
...
if (ndo->ndo_packettype) {
switch (ndo->ndo_packettype) {
case PT_ZMTP1:
zmtp1_print(ndo, bp, length);
break;
case PT_RESP: // 指定解析类型 -T
resp_print(ndo, bp, length);
break;
}
return;
}
...
else if (IS_SRC_OR_DST_PORT(REDIS_PORT)) //REDIS_PORT=6379
resp_print(ndo, bp, length);
...
}
(gdb) bt
#0 resp_print (ndo=0x7fffffffca40, bp=0x7ffff6e82088 "*3\r\n$3\r\nset\r\n$1\r\na\r\n$1\r\na\r\n", length=27) at ./print-resp.c:214
#1 0x000000000045ae3f in tcp_print (ndo=0x7fffffffca40, bp=0x7ffff6e82088 "*3\r\n$3\r\nset\r\n$1\r\na\r\n$1\r\na\r\n", length=27, bp2=0x7ffff6e82054 "E", fragmented=0) at ./print-tcp.c:723
#2 0x0000000000420f52 in ip_print_demux (ndo=0x7fffffffca40, bp=0x7ffff6e82068 "\242D\030\353=\b\350\021\363\rIS\200\030\001V\376C", length=59, ver=4, fragmented=0, ttl_hl=64, nh=6 '\006',
iph=0x7ffff6e82054 "E") at ./print-ip-demux.c:100
#3 0x0000000000420b1b in ip_print (ndo=0x7fffffffca40, bp=0x7ffff6e82054 "E", length=79) at ./print-ip.c:493
#4 0x000000000041bf64 in ethertype_print (ndo=0x7fffffffca40, ether_type=2048, p=0x7ffff6e82054 "E", length=79, caplen=79, src=0x7fffffffc680, dst=0x7fffffffc690) at ./print-ether.c:490
#5 0x000000000041bb03 in ether_print_common (ndo=0x7fffffffca40, p=0x7ffff6e82054 "E", length=79, caplen=79, print_switch_tag=0x0, switch_tag_len=0, print_encap_header=0x0, encap_header_arg=0x0)
at ./print-ether.c:345
#6 0x000000000041bc44 in ether_print (ndo=0x7fffffffca40, p=0x7ffff6e82046 "", length=93, caplen=93, print_encap_header=0x0, encap_header_arg=0x0) at ./print-ether.c:401
#7 0x000000000041bc94 in ether_if_print (ndo=0x7fffffffca40, h=0x7fffffffc7e0, p=0x7ffff6e82046 "") at ./print-ether.c:416
#8 0x00000000004078fe in pretty_print_packet (ndo=0x7fffffffca40, h=0x7fffffffc7e0, sp=0x7ffff6e82046 "", packets_captured=1) at ./print.c:414
#9 0x0000000000406d84 in print_packet (user=0x7fffffffca40 "", h=0x7fffffffc7e0, sp=0x7ffff6e82046 "") at ./tcpdump.c:2984
#10 0x00007ffff7755ef6 in ?? () from /usr/lib/x86_64-linux-gnu/libpcap.so.0.8
#11 0x00007ffff775a4a3 in ?? () from /usr/lib/x86_64-linux-gnu/libpcap.so.0.8
#12 0x00007ffff775f1fd in pcap_loop () from /usr/lib/x86_64-linux-gnu/libpcap.so.0.8
#13 0x00000000004060b1 in main (argc=5, argv=0x7fffffffddd8) at ./tcpdump.c:2438

其他端口解析RESP

如果你的redis-server 不是在6379,只要加上-T RESP 即可在其他端口解析RESP协议

sudo tcpdump -i lo port 7777 -T RESP
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on lo, link-type EN10MB (Ethernet), capture size 262144 bytes
13:07:31.737440 IP localhost.42466 > localhost.7777: Flags [P.], seq 2285148837:2285148864, ack 2982472773, win 342, options [nop,nop,TS val 2913788220 ecr 2913780890], length 27: RESP "set" "a" "a"
13:07:31.737680 IP localhost.7777 > localhost.42466: Flags [P.], seq 1:6, ack 27, win 342, options [nop,nop,TS val 2913788220 ecr 2913788220], length 5: RESP "OK"
13:07:31.737706 IP localhost.42466 > localhost.7777: Flags [.], ack 6, win 342, options [nop,nop,TS val 2913788220 ecr 2913788220], length 0

Hello World

· One min read

将之前的内容迁移到hexo

php隐式转换大坑

· 3 min read

php是弱类型语言,其中一个坑是隐式转换

什么时候会触发隐式转换

这个我只知道比较的时候如果有相应的隐式转换。

例子

下面有个例子

php 代码

<?php
var_dump('1abc'== 1);
// 返回 true

经过_is_numeric_string_ex转换后,将1abc转换成了1

(gdb) p *lval
$4 = 1

堆栈如下

(gdb) bt
#0 _is_numeric_string_ex (str=0x7fffef602b58 "1abc", length=4, lval=0x7fffffff99a0, dval=0x7fffffff99a0, allow_errors=1, oflow_info=0x0) at /home/dinosaur/Downloads/php-7.2.2/Zend/zend_operators.c:3001
#1 0x0000000000938c52 in is_numeric_string_ex (str=0x7fffef602b58 "1abc", length=4, lval=0x7fffffff99a0, dval=0x7fffffff99a0, allow_errors=1, oflow_info=0x0)
at /home/dinosaur/Downloads/php-7.2.2/Zend/zend_operators.h:142
#2 0x0000000000938c94 in is_numeric_string (str=0x7fffef602b58 "1abc", length=4, lval=0x7fffffff99a0, dval=0x7fffffff99a0, allow_errors=1)
at /home/dinosaur/Downloads/php-7.2.2/Zend/zend_operators.h:146
#3 0x000000000094502b in compare_function (result=0x7fffffff9b78, op1=0x7fffffff9aa8, op2=0x7fffffff9ac8) at /home/dinosaur/Downloads/php-7.2.2/Zend/zend_operators.c:2069
#4 0x0000000000945a32 in is_equal_function (result=0x7fffffff9b78, op1=0x7fffffff9aa8, op2=0x7fffffff9ac8) at /home/dinosaur/Downloads/php-7.2.2/Zend/zend_operators.c:2159
#5 0x00000000009274e3 in zend_try_ct_eval_binary_op (result=0x7fffffff9b78, opcode=17, op1=0x7fffffff9aa8, op2=0x7fffffff9ac8) at /home/dinosaur/Downloads/php-7.2.2/Zend/zend_compile.c:6880
#6 0x0000000000927a0d in zend_compile_binary_op (result=0x7fffffff9b70, ast=0x7fffef686090) at /home/dinosaur/Downloads/php-7.2.2/Zend/zend_compile.c:6999
#7 0x000000000092b8d1 in zend_compile_expr (result=0x7fffffff9b70, ast=0x7fffef686090) at /home/dinosaur/Downloads/php-7.2.2/Zend/zend_compile.c:8235
#8 0x000000000091b84f in zend_compile_args (ast=0x7fffef6860a8, fbc=0x167f050) at /home/dinosaur/Downloads/php-7.2.2/Zend/zend_compile.c:3202
#9 0x000000000091baaf in zend_compile_call_common (result=0x7fffffff9d20, args_ast=0x7fffef6860a8, fbc=0x167f050) at /home/dinosaur/Downloads/php-7.2.2/Zend/zend_compile.c:3282
#10 0x000000000091e44b in zend_compile_call (result=0x7fffffff9d20, ast=0x7fffef6860d8, type=0) at /home/dinosaur/Downloads/php-7.2.2/Zend/zend_compile.c:4009
#11 0x000000000092bc3e in zend_compile_var (result=0x7fffffff9d20, ast=0x7fffef6860d8, type=0) at /home/dinosaur/Downloads/php-7.2.2/Zend/zend_compile.c:8339
#12 0x000000000092b841 in zend_compile_expr (result=0x7fffffff9d20, ast=0x7fffef6860d8) at /home/dinosaur/Downloads/php-7.2.2/Zend/zend_compile.c:8217
#13 0x000000000092b513 in zend_compile_stmt (ast=0x7fffef6860d8) at /home/dinosaur/Downloads/php-7.2.2/Zend/zend_compile.c:8186
#14 0x000000000092b0de in zend_compile_top_stmt (ast=0x7fffef6860d8) at /home/dinosaur/Downloads/php-7.2.2/Zend/zend_compile.c:8072
#15 0x000000000092b0c0 in zend_compile_top_stmt (ast=0x7fffef686018) at /home/dinosaur/Downloads/php-7.2.2/Zend/zend_compile.c:8067
#16 0x00000000008ec355 in zend_compile (type=2) at Zend/zend_language_scanner.l:601
#17 0x00000000008ec4e6 in compile_file (file_handle=0x7fffffffca10, type=8) at Zend/zend_language_scanner.l:635
#18 0x00000000007296f0 in phar_compile_file (file_handle=0x7fffffffca10, type=8) at /home/dinosaur/Downloads/php-7.2.2/ext/phar/phar.c:3320
#19 0x00007fffeeeca612 in opcache_compile_file (file_handle=0x7fffffffca10, type=8, key=0x7fffef16dd4c <accel_globals+556> "test.php:240416:240464", op_array_p=0x7fffffffa318)
at /home/dinosaur/Downloads/php-7.2.2/ext/opcache/ZendAccelerator.c:1600
#20 0x00007fffeeecb722 in persistent_compile_file (file_handle=0x7fffffffca10, type=8) at /home/dinosaur/Downloads/php-7.2.2/ext/opcache/ZendAccelerator.c:1941
#21 0x000000000094ccb4 in zend_execute_scripts (type=8, retval=0x0, file_count=3) at /home/dinosaur/Downloads/php-7.2.2/Zend/zend.c:1490
#22 0x00000000008b0b4a in php_execute_script (primary_file=0x7fffffffca10) at /home/dinosaur/Downloads/php-7.2.2/main/main.c:2590
#23 0x0000000000a3fd23 in do_cli (argc=2, argv=0x1441f40) at /home/dinosaur/Downloads/php-7.2.2/sapi/cli/php_cli.c:1011
#24 0x0000000000a40ee0 in main (argc=2, argv=0x1441f40) at /home/dinosaur/Downloads/php-7.2.2/sapi/cli/php_cli.c:1404


这是什么规则呢? 1 如果一个操作数是string ,一个是number ,会将string 转换成int ,如果转换发现不是数字就转换成0 然后他们就相等了

规则

操作数1操作数2规则
string,resource 或 numberstring,resource 或 number将字符串和资源转换成数字,按普通数学比较

相关阅读

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)  //②加锁
...
}

First Blog Post

· One min read
Gao Wei
Docusaurus Core Team

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet