Golang在windows与linux的部分区别


前言

Golang语言包下载分为windows,linux和MAC,在平常的使用中并没有什么问题,但是在交叉编译场景的发生,却有不得不注意的问题


一、Golang为什么分不同的操作系统版本?

在linux中,一切皆文件,内核不同,Linux操作系统使用Linux内核,Windows操作系统使用NT内核;Linux内核代码开源,NT内核代码闭源,在针对读取操作系统本身信息的Go语言包,便有了不同之处

二、包

1.syscall

syscall 是语言与系统交互的唯一手段,其重要性不言而喻,所以此包也是go语言在linux与windows最大的不同之处,下面举几个实际应用场景

  • Linux读取磁盘容量
type Disk struct {
	All  uint64 `json:"all"`
	Used uint64 `json:"used"`
	Free uint64 `json:"free"`
}

func xx(){
	fs := syscall.Statfs_t{}
	err := syscall.Statfs(path, &fs)
	if err != nil {
		return 0,err
	}
	disk.All = fs.Blocks * uint64(fs.Bsize)          //总容量
	disk.Free = fs.Bfree * uint64(fs.Bsize)        //空闲容量
	disk.Used = disk.All - disk.Free                   //已使用容量
	}

其中syscall.Statfs_t{}该结构体是Linux独有,在windows上是不存在的

  • windows读取磁盘容量
func xxx() {
    kernel32, err := syscall.LoadLibrary("Kernel32.dll")
    if err != nil {
        log.Panic(err)
    }
    defer syscall.FreeLibrary(kernel32)
    GetDiskFreeSpaceEx, err := syscall.GetProcAddress(syscall.Handle(kernel32), "GetDiskFreeSpaceExW")
 
    if err != nil {
        log.Panic(err)
    }
 
    lpFreeBytesAvailable := int64(0)
    lpTotalNumberOfBytes := int64(0)
    lpTotalNumberOfFreeBytes := int64(0)
    r, a, b := syscall.Syscall6(uintptr(GetDiskFreeSpaceEx), 4,
        uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr("C:"))),
        uintptr(unsafe.Pointer(&lpFreeBytesAvailable)),
        uintptr(unsafe.Pointer(&lpTotalNumberOfBytes)),
        uintptr(unsafe.Pointer(&lpTotalNumberOfFreeBytes)), 0, 0)
 
    log.Printf("Available  %dmb", lpFreeBytesAvailable/1024/1024.0)
    log.Printf("Total      %dmb", lpTotalNumberOfBytes/1024/1024.0)
    log.Printf("Free       %dmb", lpTotalNumberOfFreeBytes/1024/1024.0)
}

当然,go对windows读取信息的操作还是最弱的,远不及linux

再举个例子,使用Go语言获取系统IP

  • windows获取系统IP
func getAdapterList() (*syscall.IpAdapterInfo, error) {
	b := make([]byte, 1000)
	l := uint32(len(b))
	a := (*syscall.IpAdapterInfo)(unsafe.Pointer(&b[0]))
	err := syscall.GetAdaptersInfo(a, &l)
	if err == syscall.ERROR_BUFFER_OVERFLOW {
		b = make([]byte, l)
		a = (*syscall.IpAdapterInfo)(unsafe.Pointer(&b[0]))
		err = syscall.GetAdaptersInfo(a, &l)
	}
	if err != nil {
		return nil, os.NewSyscallError("GetAdaptersInfo", err)
	}
	return a, nil
}

其中syscall包的部分结构体函数又是linux中没有的

  • Linux获取系统IP
	netInterfaces, err := net.Interfaces()
	if err != nil {
		fmt.Println("net.Interfaces failed, err:", err.Error())
		return false
	}

	for i := 0; i < len(netInterfaces); i++ {
		if (netInterfaces[i].Flags & net.FlagUp) != 0 {
			addrs, _ := netInterfaces[i].Addrs()

			for _, address := range addrs {
				if ipnet, ok := address.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
					if ipnet.IP.To4() != nil {
						fmt.Println(ipnet.IP.String())
						return true
					}
				}
			}
		}
	}

当然,这里用到了net包,此方法winodws与linux都可以使用

2.runtime

runtime调度器是非常有用的东西,主要使用的是内存的相关操作,关于runtime包几个方法:

  • Gosched:让当前线程让出cpu以让其他线程运行,它不会挂起当前线程,因此当前线程未来会继续执行

  • NumCPU:返回当前系统的CPU核数量

  • GOMAXPROCS:设置最大的可同时使用的CPU核数

  • Goexit:退出当前goroutine(但是defer语句会照常执行)

  • NumGoroutine:返回真该执行和排队的任务总数

  • GOOS:目标操作系统

  • GOROOT:返回本机的GO路径

  • Linux下获取内存占用

type MemStatus struct {
    All  uint32 `json:"all"`
    Used uint32 `json:"used"`
    Free uint32 `json:"free"`
    Self uint64 `json:"self"`
}
 
func MemStat() MemStatus {
    //自身占用
    memStat := new(runtime.MemStats)
    runtime.ReadMemStats(memStat)
    mem := MemStatus{}
    mem.Self = memStat.Alloc
 
    //系统占用,仅linux/mac下有效
    sysInfo := new(syscall.Sysinfo_t)
    err := syscall.Sysinfo(sysInfo)
    if err == nil {
        mem.All = sysInfo.Totalram * uint32(syscall.Getpagesize())
        mem.Free = sysInfo.Freeram * uint32(syscall.Getpagesize())
        mem.Used = mem.All - mem.Free
    }
    return mem
}
  • window获取内存占用
var m runtime.MemStats
runtime.ReadMemStats(&m)
fmt.Printf("%+v\n", m)
fmt.Printf("os %d\n", m.Sys)

主要是 runtime.MemStats结构体,这里面字段较多,如果只是想了解占用 OS 的内存,可以查看其中的 Sys字段。当然,如同上例一样,在获取内存上,windows对于Go是不太友好的

解决方案

  • 第三方包 github.com/shirou/gopsutil
    • CPU
  import &quot;github.com/shirou/gopsutil/cpu&quot;

// cpu info
func getCpuInfo() {
   cpuInfos, err := cpu.Info()
   if err != nil {
   	fmt.Printf(&quot;get cpu info failed, err:%v&quot;, err)
   }
   for _, ci := range cpuInfos {
   	fmt.Println(ci)
   }
   // CPU使用率
   for {
   	percent, _ := cpu.Percent(time.Second, false)
   	fmt.Printf(&quot;cpu percent:%v\n&quot;, percent)
   }
}
  • Memory
import &quot;github.com/shirou/gopsutil/mem&quot;

// mem info
func getMemInfo() {
	memInfo, _ := mem.VirtualMemory()
	fmt.Printf(&quot;mem info:%v\n&quot;, memInfo)
}

但是该包所包含的功能又不尽然,如果已经可以满足即可考虑使用

  • Linux使用exec.Command("bash", "-c", command)可直接读取到想要的信息,例如,exec.Command("bash", "-c","ifconfig")读取网络信息,但需要##注意##的是返回的参数如果是多行,在使用strings包解析时许多空格或其他内容读出来会是二进制等内容,这里建议将读出的字符串写入文件,然后使用打开文件再解析的方式

总结

总的来说,除了特定场景,windows与linux的交叉编译还是没有问题的,但是当必须与冲突点打交道时,只能选择合适的方式规避,期待Go官方或者有团队的第三方解决该类问题,以上仅为暂时在开发中发现的问题~

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值