首页
网站导航
关于
Search
1
解决Typecho Joe主题访问fastly.jsdelivr.net速度慢的方案 | 快速优化技巧
2,252 阅读
2
解决WSL2内存不释放问题的最佳指南
1,529 阅读
3
如何在 Typecho Joe 主题的文章中增加目录
1,186 阅读
4
GO语言环境的搭建教程 - 完全指南
1,042 阅读
5
如何解决Win11电脑桌面上方显示横线问题 | 窗口11教程
1,026 阅读
默认分类
编程语言
GO语言
PHP
Node
javascript
html
rust
java
Css
Python
资源分享
chrome插件
阅读思考
运维架构
redis
Nginx
linux
memcached
mongodb
mysql
windows
docker
k8s
Mq
apache
CI
Git
swoole
elk
系统设计
thinkPhp
beego
登录
Search
标签搜索
重要
go基础
git 命令
go包
phpstorm
sublime
thinkphp6
mysql问题
软件分享
redis命令
php基础
thinkphp3.2
php第三扩展包
小蚯蚓博客
累计撰写
335
篇文章
累计收到
48
条评论
首页
栏目
默认分类
编程语言
GO语言
PHP
Node
javascript
html
rust
java
Css
Python
资源分享
chrome插件
阅读思考
运维架构
redis
Nginx
linux
memcached
mongodb
mysql
windows
docker
k8s
Mq
apache
CI
Git
swoole
elk
系统设计
thinkPhp
beego
页面
网站导航
关于
搜索到
128
篇与
的结果
2023-12-29
Go语言中CGO的使用指南
概述 在使用 cgo 的时候,首先要开启 cgo 的配置 CGO_ENABLED=1 使用 C 代码 然后如下所示,既可以开始简单的使用 C 代码。 package main /* #include <stdio.h> void Cprint(){ printf("hello from c \n"); } void CprintStr(char* str){ printf("Hello %s \n",str); } */ import "C" func main() { C.Cprint() // 不能直接传字符串,需要相应的转一下 // 有些比较简单的,比如int类型,底层会自动转,也可以不手动转类型 C.CprintStr(C.CString("1232456")) } 但是在项目中,不大可能所有的 C 代码都写在头部,需要做一下拆分。 头部 hello.h 文件, 需要放到 lib 文件夹,此时 go 里需要用 cgo 需要指定头文件位置 头文件的实现放到 hello.c 文件里 main.go 代码如下所示: package main /* #cgo CFLAGS: -I./lib #include "hello.h" */ import "C" func main() { C.Cprint() // 不能直接传字符串,需要相应的转一下 C.CprintStr(C.CString("1232456")) } hello.h 代码如下所示 #include <stdio.h> void Cprint(); void CprintStr(char* str); hello.c 代码如下所示 #include "hello.h" void Cprint(){ printf("hello from c \n"); } void CprintStr(char* str){ printf("Hello %s \n",str); } 代码结构如下: 使用 C++ 代码 和使用 C 嵌入差不多,不过 C++代码不会,如果后续用到的话再学吧。
2023年12月29日
117 阅读
0 评论
0 点赞
2023-12-24
go build 编译详解
go build 参数 -o 参数 假设代码内容如下 package main import "fmt" func main() { fmt.Println("hello world") } 编译之后的可执行文件名称,例如 go build -o hello.exe 这样生成的二进制文件的名称就是 hello.exe -a 参数 强制重新编译所有包(包含标准库) go build -a -p 参数 编译时使用的 cpu 数量,如下所示,指定编译使用 2 个cpu go build -p 2 -v 参数 显示待编译包的名称,这个见 -x,这个和 -x 参数一起用,不然没有显示任何内容 -x 参数 显示正在执行的编译命令。go build 添加 -x -v 选项,可以输出构建的执行细节。-v 用于输出当前正在编译的包,而-x 则用于输出 go build 执行的每一个命令。 go build -x -v -n 参数 仅显示编译的命令,但是不执行 go build -n ![[go build 编译详解-20231224172831941.webp|800]] -work 参数 显示临时的工作目录,编译后不删 go build -work 输出 WORK=D:\project\go\goTempDir\go-build3416180188,编译后这个目录还会保存 -race 参数 启用数据竞争检查(仅amd64)。-race 命令行选项可以在构建时开启竞态检查。在程序运行时,如果发现对数据的并发竞态访问,就会给出警告。 go build -race -gcflags 参数 编译器参数。go build 实质上是通过调用 go 自带的 compile 工具对 go 代码进行编译的。 在 linux 下的位置为 $GOROOT/pkg/tool/linux_amd64/compile。 在 windows 下的位置为 $GOROOT/pkg/tool/windows_amd64/compile.exe。 go build 可以经过 -gcflags 向 compile 工具传递编译所需的命令标志选项集合。这些命令行标志选项是传递给 go 编译器的,因此可以使用下面的命令查看编译器支持的选项集合: go tool compile -help 下面是一些常用的命令行标志选项: -l:关闭内敛。 -N:关闭代码优化 -m:输出逃逸分析的分析决策过程(哪些变量在栈上分配,哪些变量在堆上分配)。 -S:输出汇编代码。 在运行调试器对程序进行调试之前,我们通常使用 -N -l 两个选项关闭对代码的内联和优化,这样能得到更多的调试信息。这个在使用 gdp 调试的是就要 go build -gcflags="-N -l" -ldflags 参数 链接器参数。go build 实质上是通过调用 go 自带的 link 工具对 go 代码进行编译的。 在 linux 下的位置为 $GOROOT/pkg/tool/linux_amd64/link。 在 windows 下的位置为 $GOROOT/pkg/tool/windows_amd64/link.exe。 使用下面的命令可以查看链接器支持的选项集合: go tool link -help 下面三个是常用的命令行选项标志: -X:设定包中 string 类型变量的值。 -s:不生成符号表。 -w:不生成 DWARF(Debugging With Attributed Record Formats)调试信息。 -H:设置可执行文件格式,eg: -H windowgui,告诉编译器是 gui 程序,不要控制台黑框打开 -X 选项的例子:可以在编译时指定程序的版本。 package main import ( "fmt" "os" ) // 全局变量 var version string func main() { if len(os.Args) > 1 && os.Args[1] == "version" { fmt.Println(version) } } 编译指定变量值,一定要带上包名称,如果多个的话,用空格隔开 -X main.version=v0.1 -X main.author=wqy go build -ldflags="-X main.version=v0.1" 执行二进制文件 ./main.exe version # 输出 v0.1 默认情况下,go build 构建出的可执行文件中都是包含 符号表 和 DWARF 格式的调试信息,这虽然让最终二进制文件的体积都增加了,但是符号表和调试信息对于生产环境下程序异常的现场保存和在线调试都有着重要意义。 如果不在意这些信息,或者对应用的大小比较敏感,那么可以通过-s 和-w 选项将这些信息从最终的二进制文件中剔除。 go build -ldflags="-s -w -X main.version=v0.1" 查看汇编代码 以下命令会输出汇编代码 go tool compile -S main.go 或者用 go run -gcflags="-S" main.go 交叉编译 所谓交叉编译,指的是在一个平台下编译出其他平台所需的可执行文件。 如果只是单纯的 go 代码,没有使用 cgo 调用 c 代码,交叉编译非常方便,只需要使用 GOOS, GOARCH 环境变量指定编译目标平台即可。 GOOS=linux go build # 32位 GOOS=linux GOARCH=386 go build 包含 CGO 代码时,需要指定交叉编译器,我平时用 zig,详细使用,可以查阅相关文档。 GOOS=Linux GOARCH=386 CC="zig cc -target x86-Linux" CXX="zig c++ -target x86-Linux" go build -o test 频繁敲写这些代码比较麻烦,可以边写 Makefile 文件,文件名称就命名为 Makefile 或 MAKEFILE dep: @go mod tidy # 代表执行 make linux 之前先执行 dep linux:dep @GOOS=linux GOARCH=amd64 go build -o main main.go @echo "linux exec is build" # 代表执行 make windows 之前先执行 dep windows:dep @GOOS=windows GOARCH=amd64 go build -o main.exe main.go @echo "windows exec is build" 写好 Makefile 文件后,只需要执行 make linux 或者 make windows 命令即可。 如果想要确认文件是否是对应的架构,可以使用 upx 压缩二进制命令,该命令在压缩的时候,会输出架构 upx -9 main 输出 条件编译 方案一 将平台信息加入文件名尾部 hello_windows_386.go,后面的 386 代表是在 32 位系统架构下,也可以是 amd64,可以不指定。 使用的时候,可以使用 go build -x 查看编译过程,查看使用哪个文件。 假设 main 包有 version_windows.go 文件,内容如下 import "fmt" var OS = "windows" func PrintOs() { fmt.Println("windows io") } version_linux.go 文件,内容如下 package main var OS = "linux" func PrintOs() { fmt.Println("linux io") } 然后,main 包在 window 平台下就可以使用该变量了,window 平台编译的时候会使用该变量 package main import "fmt" func main() { fmt.Println(OS) PrintOs() } 在 window 下编译,go build,执行编译后二进制文件,输出结果会是 windows。在 window 下交叉编译 GOOS=linux go build,然后去 linux 下执行二进制文件,输出结果是 linux。 通过这种方式,就可以实现在不同系统底下,调用不同的内容。 方案二 使用 go build 标签来指定这个文件是哪个操作系统下生效。 需要注意的是,标签和 package 语句之间需要有空行。并且这个新写法是 go 1.17 版本后生效,旧的写法和这个写法不一样,是 //+build windows 如果要再指定系统位数,可以这样 //go build linux && amd64,用 && 符号,或者的话用 ||,非的用 !。老版本用 , 和 空格 来代表与或非。例如 // go build windows || linux // go build windows && amd64 // go build windows && !386 // +build windows,amd64 #并且的关系 // +build windows linux #或者关系 方案三 使用 tag 标签。 然后编译的时候,指定 tag 即可。其实方案二,应该是系统默认会传操作系统的 tag,不用我们手动 -tags 参数指定 如果 tag 是这样,代表是有 release 和 linux 标签才可以,-tags 参数后面用空格隔开代表是并且的关系。
2023年12月24日
97 阅读
0 评论
0 点赞
2023-12-24
如何使用Go生成DLL或SO文件
环境准备 首先,必须要有 gcc 工具,windows 下,可以下载 tdm-gcc。 然后,go env 查看环境变量 # 必须开启 set CGO_ENABLED=1 # 表示是64位 set GOARCH=amd64 编写代码 编写 go 代码,有几个注意点: 必须在 main 包 必须有 main 函数,即使是空的 导出的方法,增加注释 // export Add package main import "C" func init() { // 这个函数可以在初始化的时候做一些事情 } // export Add func Add(a int, b int) int { return a + b } func main() { // 这个函数必须有,但是没啥用 } 然后执行: -ldflags "-s -w",表示将一些调试的符号给删除 -buildmode=c-shared,表示要打包动态链接库 -o api64.dll,表示导出文件 go build -ldflags "-s -w" -buildmode=c-shared -o api64.dll .\api.go 如果要编译 32 位的,可以这样 go env -w GOARCH=386 然后重新打包 go build -ldflags "-s -w" -buildmode=c-shared -o api32.dll .\api.go
2023年12月24日
127 阅读
0 评论
0 点赞
2023-12-24
Go调用SO动态库文件的两种方式
本文章,介绍 go 调用 so 里的函数。 生成 so 文件 首先编写 add.c 源代码,里面有个简单的 add 函数,用于加法运算。 #include "stdio.h" int add(int a,int b) { printf("a+b=%d\n", a+b); return a+b; } 然后生成 so 文件 gcc -fPIC -shared -o add.so add.c 方式 1-通过 cgo 调用 思路是,编写 c 代码调用 so 库,然后 go 再调用 c 代码。 c 调用 so 文件 同一个目录,创建 goLoadSo.c 文件,内容如下,里面加载刚刚生成的 ./add.so #include <stdio.h> #include <dlfcn.h> int main(){ // 手动加载指定位置的so动态库 void* handle = dlopen("./add.so",RTLD_LAZY); int (*add)(int a,int b); // 根据动态链接库操作句柄与符号,返回符号对应的地址 add = dlsym(handle,"add"); int sum = add(5,6); printf("5+6=%d\n", sum); dlclose(handle); return sum; } 然后编译测试一下,生成 goLoadSo # -ldl 表示调用动态库 gcc -o goLoadSo goLoadSo.c -ldl 执行 goLoadSo ./goLoadSo # 得到结果 a+b=11 5+6=11 go 调用 c 文件 同一个目录,创建 goLoad.go 文件,内容如下,需要注意的是 import "C" 和上面的注释中间不能有空行。 package main /* #cgo LDFLAGS: -ldl #include "./goloadSo.c" int LoadSo(); */ import "C" import "fmt" func main () { fmt.Println(C.LoadSo()) } 然后编译 go build goLoad.go 然后执行 ./goLoad # 输出 a+b=11 5+6=11 11 方式 2-通过第三方包调用 下载包 go get github.com/ebitengine/purego,这个包可以直接调用 so 文件。 package main import ( "fmt" "github.com/ebitengine/purego" ) func main () { libc, err := purego.Dlopen("./add.so", purego.RTLD_LAZY|-purego.RTLD_LOCAL) if err !=nil{ panic(err) } defer purego.Dlclose(libc) // 注册 var add func(int,int)int purego.RegisterLibFunc(&add,libc,"add") fmt.Println(add(2,3)) } 执行代码 go run purego.go # 输出 a+b=5 5
2023年12月24日
184 阅读
0 评论
0 点赞
2023-12-23
go 调用 windows ddl 的 4 种方式
概述 以下举例程序的效果是会弹出消息框。 四种方式 方式 1-立即加载 func LoadDll() { user32Dll, err := syscall.LoadLibrary("user32.dll") if err != nil { panic(err) } // 获取dll里的函数 proc, err := syscall.GetProcAddress(user32Dll, "MessageBoxW") if err != nil { panic(err) } // 宽字节 fromString, err := syscall.UTF16PtrFromString("你好世界") if err != nil { panic(err) } // https://learn.microsoft.com/zh-cn/windows/win32/api/winuser/nf-winuser-messageboxw // 参数和返回值,见上面地址 r1, _, _ := syscall.SyscallN(proc, 0, uintptr(unsafe.Pointer(fromString)), // 因为syscalN适配所有类型的调用,所以统一转这个类型 uintptr(unsafe.Pointer(fromString)), 0, ) if int(r1) == 1 { fmt.Println("用户点击了确定") } } 方式 2-懒加载 func LoadDll2() { u32Dll := syscall.NewLazyDLL("user32.dll") proc := u32Dll.NewProc("MessageBoxW") // 宽字节 fromString, err := syscall.UTF16PtrFromString("你好世界") if err != nil { panic(err) } r1, _, _ := proc.Call( 0, uintptr(unsafe.Pointer(fromString)), // 因为syscalN适配所有类型的调用,所以统一转这个类型 uintptr(unsafe.Pointer(fromString)), 0, ) if int(r1) == 1 { fmt.Println("用户点击了确定") } r0, _, _ := syscall.SyscallN(proc.Addr(), 0, uintptr(unsafe.Pointer(fromString)), // 因为syscalN适配所有类型的调用,所以统一转这个类型 uintptr(unsafe.Pointer(fromString)), 0, ) if int(r0) == 1 { fmt.Println("用户点击了确定") } } 方式 3-对第 1 种的封装 func LoadDll3() { u32dll, err := syscall.LoadDLL("user32.dll") if err != nil { panic(err) } proc, err := u32dll.FindProc("MessageBoxW") // 宽字节 fromString, err := syscall.UTF16PtrFromString("你好世界") if err != nil { panic(err) } r1, _, _ := proc.Call( 0, uintptr(unsafe.Pointer(fromString)), // 因为syscalN适配所有类型的调用,所以统一转这个类型 uintptr(unsafe.Pointer(fromString)), 0, ) if int(r1) == 1 { fmt.Println("用户点击了确定") } r0, _, _ := syscall.SyscallN(proc.Addr(), 0, uintptr(unsafe.Pointer(fromString)), // 因为syscalN适配所有类型的调用,所以统一转这个类型 uintptr(unsafe.Pointer(fromString)), 0, ) if int(r0) == 1 { fmt.Println("用户点击了确定") } } 方式 4-使用 windows 包 需要安装 go get golang.org/x/sys/windows,实际上是对 NewLazyDLL 的封装,用起来很方便 func LoadDll4() { u32Dll := windows.NewLazySystemDLL("user32.dll") proc := u32Dll.NewProc("MessageBoxW") // 宽字节 fromString, err := syscall.UTF16PtrFromString("你好世界") if err != nil { panic(err) } r1, _, _ := proc.Call( 0, uintptr(unsafe.Pointer(fromString)), // 因为syscalN适配所有类型的调用,所以统一转这个类型 uintptr(unsafe.Pointer(fromString)), 0, ) if int(r1) == 1 { fmt.Println("用户点击了确定") } r0, _, _ := syscall.SyscallN(proc.Addr(), 0, uintptr(unsafe.Pointer(fromString)), // 因为syscalN适配所有类型的调用,所以统一转这个类型 uintptr(unsafe.Pointer(fromString)), 0, ) if int(r0) == 1 { fmt.Println("用户点击了确定") } }
2023年12月23日
61 阅读
0 评论
0 点赞
2023-12-15
Go-Cache 缓存包 - 高效的Golang 缓存解决方案
第三方包github.com/patrickmn/go-cache 代码实现示例 package cache import ( gocache "github.com/patrickmn/go-cache" "strings" "sync" "time" ) const ( BaseKey = "platform-idstore" ) var ( once sync.Once instance *cache ) var NewCache = New() type Cache interface { Set(k string, v interface{}, d time.Duration) Get(k string) (interface{}, bool) Delete(k string) MatchDelete(matchKey string) Flush() } type cache struct { client gocache.Cache } func (c *cache) addBaseKey(k string) (key string) { return BaseKey + ":" + k } func (c *cache) Set(k string, v interface{}, d time.Duration) { key := c.addBaseKey(k) c.client.Set(key, v, d) } func (c *cache) Get(k string) (interface{}, bool) { key := c.addBaseKey(k) return c.client.Get(key) } func (c *cache) Delete(k string) { key := c.addBaseKey(k) c.client.Delete(key) } func (c *cache) MatchDelete(matchKey string) { keys := c.getMatchKeys(matchKey) for _, key := range keys { c.client.Delete(key) } } func (c *cache) getMatchKeys(match string) (keys []string){ items := c.client.Items() for key, _ := range items { matchKey := c.addBaseKey(match) if strings.Index(key, matchKey) == 0 { keys = append(keys, key) } } return } func (c *cache) Flush() { items := c.client.Items() for key, _ := range items { if strings.Index(key, BaseKey) == 0 { c.Delete(strings.Replace(key, BaseKey + ":", "", 1)) } } } func New() Cache { once.Do(func() { instance = &cache{ client: *gocache.New(1*time.Hour, 6*time.Hour), } }) return instance }
2023年12月15日
108 阅读
1 评论
0 点赞
2023-12-15
Go语言Sort包的深入解析及常见用法
SliceStable 实例讲解 本文章向大家介绍GO SliceStable实例讲解,主要分析其语法、参数、返回值和注意事项,并结合实例形式分析了其使用技巧,希望通过本文能帮助到大家理解应用这部分内容。 GO语言"sort"包中"SliceStable"函数的用法及代码示例。 用法: func SliceStable(x any, less func(i, j int) bool) SliceStable 使用提供的 less 函数对切片 x 进行排序,保持相等元素的原始顺序。如果 x 不是切片,它会Panics。 less 函数必须满足与接口类型的 Less 方法相同的要求。 例子: package main import ( "fmt" "sort" ) func main() { people := []struct { Name string Age int }{ {"Alice", 25}, {"Elizabeth", 75}, {"Alice", 75}, {"Bob", 75}, {"Alice", 75}, {"Bob", 25}, {"Colin", 25}, {"Elizabeth", 25}, } // Sort by name, preserving original order sort.SliceStable(people, func(i, j int) bool { return people[i].Name < people[j].Name }) fmt.Println("By name:", people) // Sort by age preserving name order sort.SliceStable(people, func(i, j int) bool { return people[i].Age < people[j].Age }) fmt.Println("By age,name:", people) } 输出: By name: [{Alice 25} {Alice 75} {Alice 75} {Bob 75} {Bob 25} {Colin 25} {Elizabeth 75} {Elizabeth 25}] By age,name: [{Alice 25} {Bob 25} {Colin 25} {Elizabeth 25} {Alice 75} {Alice 75} {Bob 75} {Elizabeth 75}]
2023年12月15日
163 阅读
1 评论
0 点赞
2023-12-15
Go 语言网络请求教程和技术指南
post 请求 // 网络客户端 type NetClient interface { Post(url string, req, resp interface{}, header map[string]string) error } func generateUuid() string { u := uuid.NewV4().String() return u } type native struct { c *http.Client } func setHeaders(h http.Header, kv map[string]string) { h.Set("Content-Type", "application/json") h.Set("Access-token", "service") h.Set("Trace-Id", generateUuid()) for key, value := range kv { h.Set(key, value) } } func (n *native) rawPost(url string, data []byte, header map[string]string) (code int, body []byte, err error) { var resp *http.Response reader := bytes.NewReader(data) req, err := http.NewRequest("POST", url, reader) if err != nil { return } // 设置请求头 setHeaders(req.Header, header) resp, err = n.c.Do(req) if resp != nil { defer resp.Body.Close() } if err != nil { return } code = resp.StatusCode body, err = ioutil.ReadAll(resp.Body) return } func (n *native) Post(url string, req, resp interface{}, header map[string]string) (err error) { var reqBytes []byte if reqBytes, err = json.Marshal(req); err != nil { return err } var ( code int body []byte ) if code, body, err = n.rawPost(url, reqBytes, header); err != nil { return err } if code != http.StatusOK { return fmt.Errorf("http response status code !=200,code:%d", code) } return json.Unmarshal(body, resp) }
2023年12月15日
99 阅读
0 评论
0 点赞
2023-12-07
GORM简单使用指南 | Golang ORM 框架
// 删除表 gorm.Migrator().DropTable(dst...) // 自动创建表 db.Migrator().AutoMigrate(dst...) // 转成sql字符串 tx.ToSQL()
2023年12月07日
128 阅读
0 评论
0 点赞
2023-12-04
Go语言Exc包使用指南
输出到文件 cmd := exec.Command( appCfg.MysqlCfg.DumpFilePath, "-h", appCfg.MysqlCfg.Host, "-u", appCfg.MysqlCfg.User, fmt.Sprintf("-p%s", appCfg.MysqlCfg.Password), "-P", appCfg.MysqlCfg.Port, "-B", appCfg.MysqlCfg.Database, ) stdout, err := os.OpenFile("./backup/databases_backup.sql", os.O_CREATE|os.O_WRONLY, 0600) if err != nil { err = fmt.Errorf("创建./backup/databases_backup.sql文件失败,Err%s", err.Error()) return } defer stdout.Close() // 重定向标准输出到文件 cmd.Stdout = stdout // 执行命令 if err = cmd.Start(); err != nil { err = fmt.Errorf("准备执行备份数据库失败,Err%s", err.Error()) return } if err = cmd.Wait(); err != nil { err = fmt.Errorf("执行mysqldump备份数据库失败,Err%s", err.Error()) return } 标准输出 cmd := exec.Command("zip", "-r", "./backup/backup_project.zip", appCfg.ToZipDir) // // 不需要cmd.Run() _, err = cmd.Output() if err != nil { err = fmt.Errorf("执行 zip -r ./backup/backup_project.zip 命令失败,Err%s", err.Error()) return } return
2023年12月04日
134 阅读
0 评论
0 点赞
1
2
3
...
13