我们专注攀枝花网站设计 攀枝花网站制作 攀枝花网站建设
成都网站建设公司服务热线:400-028-6601

网站建设知识

十年网站开发经验 + 多家企业客户 + 靠谱的建站团队

量身定制 + 运营维护+专业推广+无忧售后,网站问题一站解决

go语言调用系统 go调用系统命令

go语言如何调用c函数

直接嵌入c源代码到go代码里面

成都创新互联公司坚持“要么做到,要么别承诺”的工作理念,服务领域包括:成都网站设计、做网站、企业官网、英文网站、手机端网站、网站推广等服务,满足客户于互联网时代的铁力网站设计、移动媒体设计的需求,帮助企业找到有效的互联网解决方案。努力成为您成熟可靠的网络建设合作伙伴!

package main

/*

#include stdio.h

void myhello(int i) {

printf("Hello C: %d\n", i);

}

*/

import "C"

import "fmt"

func main() {

C.myhello(C.int(12))

fmt.Println("Hello Go");

}

需要注意的是C代码必须放在注释里面

import "C"语句和前面的C代码之间不能有空行

运行结果

$ go build main.go ./main

Hello C: 12

Hello Go

分开c代码到单独文件

嵌在一起代码结构不是很好看,很多人包括我,还是喜欢把两个分开,放在不同的文件里面,显得干净,go源文件里面是go的源代码,c源文件里面是c的源代码。

$ ls

hello.c hello.h main.go

$ cat hello.h

void hello(int);

$ cat hello.c

#include stdio.h

void hello(int i) {

printf("Hello C: %d\n", i);

}

$ cat main.go

package main

// #include "hello.h"

import "C"

import "fmt"

func main() {

C.hello(C.int(12))

fmt.Println("Hello Go");

}

编译运行

$ go build ./main

Hello C: 12

Hello Go

编译成库文件

如果c文件比较多,最好还是能够编译成一个独立的库文件,然后go来调用库。

$ find mylib main

mylib

mylib/hello.h

mylib/hello.c

main

main/main.go

编译库文件

$ cd mylib

# gcc -fPIC -shared -o libhello.so hello.c

编译go程序

$ cd main

$ cat main.go

package main

// #cgo CFLAGS: -I../mylib

// #cgo LDFLAGS: -L../mylib -lhello

// #include "hello.h"

import "C"

import "fmt"

func main() {

C.hello(C.int(12))

fmt.Println("Hello Go");

}

$ go build main.go

运行

$ export LD_LIBRARY_PATH=../mylib

$ ./main

Hello C: 12

Hello Go

在我们的例子中,库文件是编译成动态库的,main程序链接的时候也是采用的动态库

$ ldd main

linux-vdso.so.1 = (0x00007fffc7968000)

libhello.so = ../mylib/libhello.so (0x00007f513684c000)

libpthread.so.0 = /lib64/libpthread.so.0 (0x00007f5136614000)

libc.so.6 = /lib64/libc.so.6 (0x00007f5136253000)

/lib64/ld-linux-x86-64.so.2 (0x000055d819227000)

理论上讲也是可以编译成整个一静态链接的可执行程序,由于我的机器上缺少静态链接的系统库,比如libc.a,所以只能编译成动态链接。

Go语言——goroutine并发模型

参考:

Goroutine并发调度模型深度解析手撸一个协程池

Golang 的 goroutine 是如何实现的?

Golang - 调度剖析【第二部分】

OS线程初始栈为2MB。Go语言中,每个goroutine采用动态扩容方式,初始2KB,按需增长,最大1G。此外GC会收缩栈空间。

BTW,增长扩容都是有代价的,需要copy数据到新的stack,所以初始2KB可能有些性能问题。

更多关于stack的内容,可以参见大佬的文章。 聊一聊goroutine stack

用户线程的调度以及生命周期管理都是用户层面,Go语言自己实现的,不借助OS系统调用,减少系统资源消耗。

Go语言采用两级线程模型,即用户线程与内核线程KSE(kernel scheduling entity)是M:N的。最终goroutine还是会交给OS线程执行,但是需要一个中介,提供上下文。这就是G-M-P模型

Go调度器有两个不同的运行队列:

go1.10\src\runtime\runtime2.go

Go调度器根据事件进行上下文切换。

调度的目的就是防止M堵塞,空闲,系统进程切换。

详见 Golang - 调度剖析【第二部分】

Linux可以通过epoll实现网络调用,统称网络轮询器N(Net Poller)。

文件IO操作

上面都是防止M堵塞,任务窃取是防止M空闲

每个M都有一个特殊的G,g0。用于执行调度,gc,栈管理等任务,所以g0的栈称为调度栈。g0的栈不会自动增长,不会被gc,来自os线程的栈。

go1.10\src\runtime\proc.go

G没办法自己运行,必须通过M运行

M通过通过调度,执行G

从M挂载P的runq中找到G,执行G

一学就会,手把手教你用Go语言调用智能合约

智能合约调用是实现一个 DApp 的关键,一个完整的 DApp 包括前端、后端、智能合约及区块 链系统,智能合约的调用是连接区块链与前后端的关键。

我们先来了解一下智能合约调用的基础原理。智能合约运行在以太坊节点的 EVM 中。因此要 想调用合约必须要访问某个节点。

以后端程序为例,后端服务若想连接节点有两种可能,一种是双 方在同一主机,此时后端连接节点可以采用 本地 IPC(Inter-Process Communication,进 程间通信)机制,也可以采用 RPC(Remote Procedure Call,远程过程调用)机制;另 一种情况是双方不在同一台主机,此时只能采用 RPC 机制进行通信。

提到 RPC, 读者应该对 Geth 启动参数有点印象,Geth 启动时可以选择开启 RPC 服务,对应的 默认服务端口是 8545。。

接着,我们来了解一下智能合约运行的过程。

智能合约的运行过程是后端服务连接某节点,将 智能合约的调用(交易)发送给节点,节点在验证了交易的合法性后进行全网广播,被矿工打包到 区块中代表此交易得到确认,至此交易才算完成。

就像数据库一样,每个区块链平台都会提供主流 开发语言的 SDK(Software Development Kit,软件开发工具包),由于 Geth 本身就是用 Go 语言 编写的,因此若想使用 Go 语言连接节点、发交易,直接在工程内导入 go-ethereum(Geth 源码) 包就可以了,剩下的问题就是流程和 API 的事情了。

总结一下,智能合约被调用的两个关键点是节点和 SDK。

由于 IPC 要求后端与节点必须在同一主机,所以很多时候开发者都会采用 RPC 模式。除了 RPC,以太坊也为开发者提供了 json- rpc 接口,本文就不展开讨论了。

接下来介绍如何使用 Go 语言,借助 go-ethereum 源码库来实现智能合约的调用。这是有固定 步骤的,我们先来说一下总体步骤,以下面的合约为例。

步骤 01:编译合约,获取合约 ABI(Application Binary Interface,应用二进制接口)。 单击【ABI】按钮拷贝合约 ABI 信息,将其粘贴到文件 calldemo.abi 中(可使用 Go 语言IDE 创建该文件,文件名可自定义,后缀最好使用 abi)。

最好能将 calldemo.abi 单独保存在一个目录下,输入“ls”命令只能看到 calldemo.abi 文件,参 考效果如下:

步骤 02:获得合约地址。注意要将合约部署到 Geth 节点。因此 Environment 选择为 Web3 Provider。

在【Environment】选项框中选择“Web3 Provider”,然后单击【Deploy】按钮。

部署后,获得合约地址为:0xa09209c28AEf59a4653b905792a9a910E78E7407。

步骤 03:利用 abigen 工具(Geth 工具包内的可执行程序)编译智能合约为 Go 代码。abigen 工具的作用是将 abi 文件转换为 Go 代码,命令如下:

其中各参数的含义如下。 (1)abi:是指定传入的 abi 文件。 (2)type:是指定输出文件中的基本结构类型。 (3)pkg:指定输出文件 package 名称。 (4)out:指定输出文件名。 执行后,将在代码目录下看到 funcdemo.go 文件,读者可以打开该文件欣赏一下,注意不要修改它。

步骤 04:创建 main.go,填入如下代码。 注意代码中 HexToAddress 函数内要传入该合约部署后的地址,此地址在步骤 01 中获得。

步骤 04:设置 go mod,以便工程自动识别。

前面有所提及,若要使用 Go 语言调用智能合约,需要下载 go-ethereum 工程,可以使用下面 的指令:

该指令会自动将 go-ethereum 下载到“$GOPATH/src/github.com/ethereum/go-ethereum”,这样还算 不错。不过,Go 语言自 1.11 版本后,增加了 module 管理工程的模式。只要设置好了 go mod,下载 依赖工程的事情就不必关心了。

接下来设置 module 生效和 GOPROXY,命令如下:

在项目工程内,执行初始化,calldemo 可以自定义名称。

步骤 05:运行代码。执行代码,将看到下面的效果,以及最终输出的 2020。

上述输出信息中,可以看到 Go 语言会自动下载依赖文件,这就是 go mod 的神奇之处。看到 2020,相信读者也知道运行结果是正确的了。

【golang详解】go语言GMP(GPM)原理和调度

Goroutine调度是一个很复杂的机制,下面尝试用简单的语言描述一下Goroutine调度机制,想要对其有更深入的了解可以去研读一下源码。

首先介绍一下GMP什么意思:

G ----------- goroutine: 即Go协程,每个go关键字都会创建一个协程。

M ---------- thread内核级线程,所有的G都要放在M上才能运行。

P ----------- processor处理器,调度G到M上,其维护了一个队列,存储了所有需要它来调度的G。

Goroutine 调度器P和 OS 调度器是通过 M 结合起来的,每个 M 都代表了 1 个内核线程,OS 调度器负责把内核线程分配到 CPU 的核上执行

模型图:

避免频繁的创建、销毁线程,而是对线程的复用。

1)work stealing机制

当本线程无可运行的G时,尝试从其他线程绑定的P偷取G,而不是销毁线程。

2)hand off机制

当本线程M0因为G0进行系统调用阻塞时,线程释放绑定的P,把P转移给其他空闲的线程执行。进而某个空闲的M1获取P,继续执行P队列中剩下的G。而M0由于陷入系统调用而进被阻塞,M1接替M0的工作,只要P不空闲,就可以保证充分利用CPU。M1的来源有可能是M的缓存池,也可能是新建的。当G0系统调用结束后,根据M0是否能获取到P,将会将G0做不同的处理:

如果有空闲的P,则获取一个P,继续执行G0。

如果没有空闲的P,则将G0放入全局队列,等待被其他的P调度。然后M0将进入缓存池睡眠。

如下图

GOMAXPROCS设置P的数量,最多有GOMAXPROCS个线程分布在多个CPU上同时运行

在Go中一个goroutine最多占用CPU 10ms,防止其他goroutine被饿死。

具体可以去看另一篇文章

【Golang详解】go语言调度机制 抢占式调度

当创建一个新的G之后优先加入本地队列,如果本地队列满了,会将本地队列的G移动到全局队列里面,当M执行work stealing从其他P偷不到G时,它可以从全局G队列获取G。

协程经历过程

我们创建一个协程 go func()经历过程如下图:

说明:

这里有两个存储G的队列,一个是局部调度器P的本地队列、一个是全局G队列。新创建的G会先保存在P的本地队列中,如果P的本地队列已经满了就会保存在全局的队列中;处理器本地队列是一个使用数组构成的环形链表,它最多可以存储 256 个待执行任务。

G只能运行在M中,一个M必须持有一个P,M与P是1:1的关系。M会从P的本地队列弹出一个可执行状态的G来执行,如果P的本地队列为空,就会想其他的MP组合偷取一个可执行的G来执行;

一个M调度G执行的过程是一个循环机制;会一直从本地队列或全局队列中获取G

上面说到P的个数默认等于CPU核数,每个M必须持有一个P才可以执行G,一般情况下M的个数会略大于P的个数,这多出来的M将会在G产生系统调用时发挥作用。类似线程池,Go也提供一个M的池子,需要时从池子中获取,用完放回池子,不够用时就再创建一个。

work-stealing调度算法:当M执行完了当前P的本地队列队列里的所有G后,P也不会就这么在那躺尸啥都不干,它会先尝试从全局队列队列寻找G来执行,如果全局队列为空,它会随机挑选另外一个P,从它的队列里中拿走一半的G到自己的队列中执行。

如果一切正常,调度器会以上述的那种方式顺畅地运行,但这个世界没这么美好,总有意外发生,以下分析goroutine在两种例外情况下的行为。

Go runtime会在下面的goroutine被阻塞的情况下运行另外一个goroutine:

用户态阻塞/唤醒

当goroutine因为channel操作或者network I/O而阻塞时(实际上golang已经用netpoller实现了goroutine网络I/O阻塞不会导致M被阻塞,仅阻塞G,这里仅仅是举个栗子),对应的G会被放置到某个wait队列(如channel的waitq),该G的状态由_Gruning变为_Gwaitting,而M会跳过该G尝试获取并执行下一个G,如果此时没有可运行的G供M运行,那么M将解绑P,并进入sleep状态;当阻塞的G被另一端的G2唤醒时(比如channel的可读/写通知),G被标记为,尝试加入G2所在P的runnext(runnext是线程下一个需要执行的 Goroutine。), 然后再是P的本地队列和全局队列。

系统调用阻塞

当M执行某一个G时候如果发生了阻塞操作,M会阻塞,如果当前有一些G在执行,调度器会把这个线程M从P中摘除,然后再创建一个新的操作系统的线程(如果有空闲的线程可用就复用空闲线程)来服务于这个P。当M系统调用结束时候,这个G会尝试获取一个空闲的P执行,并放入到这个P的本地队列。如果获取不到P,那么这个线程M变成休眠状态, 加入到空闲线程中,然后这个G会被放入全局队列中。

队列轮转

可见每个P维护着一个包含G的队列,不考虑G进入系统调用或IO操作的情况下,P周期性的将G调度到M中执行,执行一小段时间,将上下文保存下来,然后将G放到队列尾部,然后从队列中重新取出一个G进行调度。

除了每个P维护的G队列以外,还有一个全局的队列,每个P会周期性地查看全局队列中是否有G待运行并将其调度到M中执行,全局队列中G的来源,主要有从系统调用中恢复的G。之所以P会周期性地查看全局队列,也是为了防止全局队列中的G被饿死。

除了每个P维护的G队列以外,还有一个全局的队列,每个P会周期性地查看全局队列中是否有G待运行并将其调度到M中执行,全局队列中G的来源,主要有从系统调用中恢复的G。之所以P会周期性地查看全局队列,也是为了防止全局队列中的G被饿死。

M0

M0是启动程序后的编号为0的主线程,这个M对应的实例会在全局变量rutime.m0中,不需要在heap上分配,M0负责执行初始化操作和启动第一个G,在之后M0就和其他的M一样了

G0

G0是每次启动一个M都会第一个创建的goroutine,G0仅用于负责调度G,G0不指向任何可执行的函数,每个M都会有一个自己的G0,在调度或系统调用时会使用G0的栈空间,全局变量的G0是M0的G0

一个G由于调度被中断,此后如何恢复?

中断的时候将寄存器里的栈信息,保存到自己的G对象里面。当再次轮到自己执行时,将自己保存的栈信息复制到寄存器里面,这样就接着上次之后运行了。

我这里只是根据自己的理解进行了简单的介绍,想要详细了解有关GMP的底层原理可以去看Go调度器 G-P-M 模型的设计者的文档或直接看源码

参考: ()

()

go语言框架有的不支持windows

根据资料显示,go语言框架有的不支持windows,是因为与运行的 Windows 版本不兼容。那么需要安装与windows兼容的版本,即可。

怎么样使用Go语言中函数的参数传递与调用

按值传递函数参数,是拷贝参数的实际值到函数的形式参数的方法调用。在这种情况下,参数在函数内变化对参数不会有影响。

默认情况下,Go编程语言使用调用通过值的方法来传递参数。在一般情况下,这意味着,在函数内码不能改变用来调用所述函数的参数。考虑函数swap()的定义如下。

代码如下:

/* function definition to swap the values */

func swap(int x, int y) int {

var temp int

temp = x /* save the value of x */

x = y /* put y into x */

y = temp /* put temp into y */

return temp;

}

现在,让我们通过使实际值作为在以下示例调用函数swap():

代码如下:

package main

import "fmt"

func main() {

/* local variable definition */

var a int = 100

var b int = 200

fmt.Printf("Before swap, value of a : %d\n", a )

fmt.Printf("Before swap, value of b : %d\n", b )

/* calling a function to swap the values */

swap(a, b)

fmt.Printf("After swap, value of a : %d\n", a )

fmt.Printf("After swap, value of b : %d\n", b )

}

func swap(x, y int) int {

var temp int

temp = x /* save the value of x */

x = y /* put y into x */

y = temp /* put temp into y */

return temp;

}

让我们把上面的代码放在一个C文件,编译并执行它,它会产生以下结果:

Before swap, value of a :100

Before swap, value of b :200

After swap, value of a :100

After swap, value of b :200

这表明,参数值没有被改变,虽然它们已经在函数内部改变。

通过传递函数参数,即是拷贝参数的地址到形式参数的参考方法调用。在函数内部,地址是访问调用中使用的实际参数。这意味着,对参数的更改会影响传递的参数。

要通过引用传递的值,参数的指针被传递给函数就像任何其他的值。所以,相应的,需要声明函数的参数为指针类型如下面的函数swap(),它的交换两个整型变量的值指向它的参数。

代码如下:

/* function definition to swap the values */

func swap(x *int, y *int) {

var temp int

temp = *x /* save the value at address x */

*x = *y /* put y into x */

*y = temp /* put temp into y */

}

现在,让我们调用函数swap()通过引用作为在下面的示例中传递数值:

代码如下:

package main

import "fmt"

func main() {

/* local variable definition */

var a int = 100

var b int= 200

fmt.Printf("Before swap, value of a : %d\n", a )

fmt.Printf("Before swap, value of b : %d\n", b )

/* calling a function to swap the values.

* a indicates pointer to a ie. address of variable a and

* b indicates pointer to b ie. address of variable b.

*/

swap(a, b)

fmt.Printf("After swap, value of a : %d\n", a )

fmt.Printf("After swap, value of b : %d\n", b )

}

func swap(x *int, y *int) {

var temp int

temp = *x /* save the value at address x */

*x = *y /* put y into x */

*y = temp /* put temp into y */

}

让我们把上面的代码放在一个C文件,编译并执行它,它会产生以下结果:

Before swap, value of a :100

Before swap, value of b :200

After swap, value of a :200

After swap, value of b :100

这表明变化的功能以及不同于通过值调用的外部体现的改变不能反映函数之外。


当前文章:go语言调用系统 go调用系统命令
网站链接:http://mswzjz.cn/article/docoiep.html

其他资讯