Go語言也稱 Golang,兼具效率、性能、安全、健壯等特性。這套Go語言教程(Golang教程)通俗易懂,深入淺出,既適合沒有基礎(chǔ)的讀者快速入門,也適合工作多年的程序員查閱知識(shí)點(diǎn)。
創(chuàng)新互聯(lián)公司專注于婺城網(wǎng)站建設(shè)服務(wù)及定制,我們擁有豐富的企業(yè)做網(wǎng)站經(jīng)驗(yàn)。 熱誠為您提供婺城營銷型網(wǎng)站建設(shè),婺城網(wǎng)站制作、婺城網(wǎng)頁設(shè)計(jì)、婺城網(wǎng)站官網(wǎng)定制、成都小程序開發(fā)服務(wù),打造婺城網(wǎng)絡(luò)公司原創(chuàng)品牌,更為您提供婺城網(wǎng)站排名全網(wǎng)營銷落地服務(wù)。
Go 語言
這套教程在講解一些知識(shí)點(diǎn)時(shí),將 Go 語言和其他多種語言進(jìn)行對(duì)比,讓掌握其它編程語言的讀者能迅速理解 Go 語言的特性。Go語言從底層原生支持并發(fā),無須第三方庫、開發(fā)者的編程技巧和開發(fā)經(jīng)驗(yàn)就可以輕松搞定。
Go語言(或 Golang)起源于 2007 年,并在 2009 年正式對(duì)外發(fā)布。Go 是非常年輕的一門語言,它的主要目標(biāo)是“兼具 Python 等動(dòng)態(tài)語言的開發(fā)速度和 C/C++ 等編譯型語言的性能與安全性”。
Go語言是編程語言設(shè)計(jì)的又一次嘗試,是對(duì)類C語言的重大改進(jìn),它不但能讓你訪問底層操作系統(tǒng),還提供了強(qiáng)大的網(wǎng)絡(luò)編程和并發(fā)編程支持。Go語言的用途眾多,可以進(jìn)行網(wǎng)絡(luò)編程、系統(tǒng)編程、并發(fā)編程、分布式編程。
Go語言的推出,旨在不損失應(yīng)用程序性能的情況下降低代碼的復(fù)雜性,具有“部署簡單、并發(fā)性好、語言設(shè)計(jì)良好、執(zhí)行性能好”等優(yōu)勢,目前國內(nèi)諸多 IT 公司均已采用Go語言開發(fā)項(xiàng)目。Go語言有時(shí)候被描述為“C 類似語言”,或者是“21 世紀(jì)的C語言”。Go 從C語言繼承了相似的表達(dá)式語法、控制流結(jié)構(gòu)、基礎(chǔ)數(shù)據(jù)類型、調(diào)用參數(shù)傳值、指針等很多思想,還有C語言一直所看中的編譯后機(jī)器碼的運(yùn)行效率以及和現(xiàn)有操作系統(tǒng)的無縫適配。
因?yàn)镚o語言沒有類和繼承的概念,所以它和 Java 或 C++ 看起來并不相同。但是它通過接口(interface)的概念來實(shí)現(xiàn)多態(tài)性。Go語言有一個(gè)清晰易懂的輕量級(jí)類型系統(tǒng),在類型之間也沒有層級(jí)之說。因此可以說Go語言是一門混合型的語言。
此外,很多重要的開源項(xiàng)目都是使用Go語言開發(fā)的,其中包括 Docker、Go-Ethereum、Thrraform 和 Kubernetes。Go 是編譯型語言,Go 使用編譯器來編譯代碼。編譯器將源代碼編譯成二進(jìn)制(或字節(jié)碼)格式;在編譯代碼時(shí),編譯器檢查錯(cuò)誤、優(yōu)化性能并輸出可在不同平臺(tái)上運(yùn)行的二進(jìn)制文件。要?jiǎng)?chuàng)建并運(yùn)行 Go 程序,程序員必須執(zhí)行如下步驟。
使用文本編輯器創(chuàng)建 Go 程序;
保存文件;編譯程序;運(yùn)行編譯得到的可執(zhí)行文件。
這不同于 Python、Ruby 和 JavaScript 等語言,它們不包含編譯步驟。Go 自帶了編譯器,因此無須單獨(dú)安裝編譯器。
鏈喬教育在線旗下學(xué)碩創(chuàng)新區(qū)塊鏈技術(shù)工作站是中國教育部學(xué)校規(guī)劃建設(shè)發(fā)展中心開展的“智慧學(xué)習(xí)工場2020-學(xué)碩創(chuàng)新工作站 ”唯一獲準(zhǔn)的“區(qū)塊鏈技術(shù)專業(yè)”試點(diǎn)工作站。專業(yè)站立足為學(xué)生提供多樣化成長路徑,推進(jìn)專業(yè)學(xué)位研究生產(chǎn)學(xué)研結(jié)合培養(yǎng)模式改革,構(gòu)建應(yīng)用型、復(fù)合型人才培養(yǎng)體系。
在Go語言中有一些調(diào)試技巧能幫助我們快速找到問題,有時(shí)候你想盡可能多的記錄異常但仍覺得不夠,搞清楚堆棧的意義有助于定位Bug或者記錄更完整的信息。
本文將討論堆棧跟蹤信息以及如何在堆棧中識(shí)別函數(shù)所傳遞的參數(shù)。
Functions
先從這段代碼開始:
Listing 1
01 package main
02
03 func main() {
04 ? ? slice := make([]string, 2, 4)
05 ? ? Example(slice, "hello", 10)
06 }
07
08 func Example(slice []string, str string, i int) {
09 ? ? panic("Want stack trace")
10 }
Example函數(shù)定義了3個(gè)參數(shù),1個(gè)string類型的slice, 1個(gè)string和1個(gè)integer, 并且拋出了panic,運(yùn)行這段代碼可以看到這樣的結(jié)果:
Listing 2
Panic: Want stack trace
goroutine 1 [running]:
main.Example(0x2080c3f50, 0x2, 0x4, 0x425c0, 0x5, 0xa)
/Users/bill/Spaces/Go/Projects/src/github.com/goinaction/code/
temp/main.go:9 +0x64
main.main()
/Users/bill/Spaces/Go/Projects/src/github.com/goinaction/code/
temp/main.go:5 +0x85
goroutine 2 [runnable]:
runtime.forcegchelper()
/Users/bill/go/src/runtime/proc.go:90
runtime.goexit()
/Users/bill/go/src/runtime/asm_amd64.s:2232 +0x1
goroutine 3 [runnable]:
runtime.bgsweep()
/Users/bill/go/src/runtime/mgc0.go:82
runtime.goexit()
/Users/bill/go/src/runtime/asm_amd64.s:2232 +0x1
堆棧信息中顯示了在panic拋出這個(gè)時(shí)間所有的goroutines狀態(tài),發(fā)生的panic的goroutine會(huì)顯示在最上面。
Listing 3
01 goroutine 1 [running]:
02 main.Example(0x2080c3f50, 0x2, 0x4, 0x425c0, 0x5, 0xa)
/Users/bill/Spaces/Go/Projects/src/github.com/goinaction/code/
temp/main.go:9 +0x64
03 main.main()
/Users/bill/Spaces/Go/Projects/src/github.com/goinaction/code/
temp/main.go:5 +0x85
第1行顯示最先發(fā)出panic的是goroutine 1, 第二行顯示panic位于main.Example中, 并能定位到該行代碼,在本例中第9行引發(fā)了panic。
下面我們關(guān)注參數(shù)是如何傳遞的:
Listing 4
// Declaration
main.Example(slice []string, str string, i int)
// Call to Example by main.
slice := make([]string, 2, 4)
Example(slice, "hello", 10)
// Stack trace
main.Example(0x2080c3f50, 0x2, 0x4, 0x425c0, 0x5, 0xa)
這里展示了在main中帶參數(shù)調(diào)用Example函數(shù)時(shí)的堆棧信息,比較就能發(fā)現(xiàn)兩者的參數(shù)數(shù)量并不相同,Example定義了3個(gè)參數(shù),堆棧中顯示了6個(gè)參數(shù)。現(xiàn)在的關(guān)鍵問題是我們要弄清楚它們是如何匹配的。
第1個(gè)參數(shù)是string類型的slice,我們知道在Go語言中slice是引用類型,即slice變量結(jié)構(gòu)會(huì)包含三個(gè)部分:指針、長度(Lengthe)、容量(Capacity)
Listing 5
// Slice parameter value
slice := make([]string, 2, 4)
// Slice header values
Pointer: ?0x2080c3f50
Length: ? 0x2
Capacity: 0x4
// Declaration
main.Example(slice []string, str string, i int)
// Stack trace
main.Example(0x2080c3f50, 0x2, 0x4, 0x425c0, 0x5, 0xa)
因此,前面3個(gè)參數(shù)會(huì)匹配slice, 如下圖所示:
Figure 1
figure provided by Georgi Knox
我們現(xiàn)在來看第二個(gè)參數(shù),它是string類型,string類型也是引用類型,它包括兩部分:指針、長度。
Listing 6
// String parameter value
"hello"
// String header values
Pointer: 0x425c0
Length: ?0x5
// Declaration
main.Example(slice []string,?str string, i int)
// Stack trace
main.Example(0x2080c3f50, 0x2, 0x4,?0x425c0, 0x5, 0xa)
可以確定,堆棧信息中第4、5兩個(gè)參數(shù)對(duì)應(yīng)代碼中的string參數(shù),如下圖所示:
Figure 2
figure provided by Georgi Knox
最后一個(gè)參數(shù)integer是single word值。
Listing 7
// Integer parameter value
10
// Integer value
Base 16: 0xa
// Declaration
main.Example(slice []string, str string,?i int)
// Stack trace
main.Example(0x2080c3f50, 0x2, 0x4, 0x425c0, 0x5,?0xa)
現(xiàn)在我們可以匹配代碼中的參數(shù)到堆棧信息了。
Figure 3
figure provided by Georgi Knox
Methods
如果我們將Example作為結(jié)構(gòu)體的方法會(huì)怎么樣呢?
Listing 8
01 package main
02
03 import "fmt"
04
05 type trace struct{}
06
07 func main() {
08 ? ? slice := make([]string, 2, 4)
09
10 ? ? var t trace
11 ? ? t.Example(slice, "hello", 10)
12 }
13
14 func (t *trace) Example(slice []string, str string, i int) {
15 ? ? fmt.Printf("Receiver Address: %p\n", t)
16 ? ? panic("Want stack trace")
17 }
如上所示修改代碼,將Example定義為trace的方法,并通過trace的實(shí)例t來調(diào)用Example。
再次運(yùn)行程序,會(huì)發(fā)現(xiàn)堆棧信息有一點(diǎn)不同:
Listing 9
Receiver Address:?0x1553a8
panic: Want stack trace
01 goroutine 1 [running]:
02 main.(*trace).Example(0x1553a8, 0x2081b7f50, 0x2, 0x4, 0xdc1d0, 0x5, 0xa)
/Users/bill/Spaces/Go/Projects/src/github.com/goinaction/code/
temp/main.go:16 +0x116
03 main.main()
/Users/bill/Spaces/Go/Projects/src/github.com/goinaction/code/
temp/main.go:11 +0xae
首先注意第2行的方法調(diào)用使用了pointer receiver,在package名字和方法名之間多出了"*trace"字樣。另外,參數(shù)列表的第1個(gè)參數(shù)標(biāo)明了結(jié)構(gòu)體(t)地址。我們從堆棧信息中看到了內(nèi)部實(shí)現(xiàn)細(xì)節(jié)。
Packing
如果有多個(gè)參數(shù)可以填充到一個(gè)single word, 則這些參數(shù)值會(huì)合并打包:
Listing 10
01 package main
02
03 func main() {
04 ? ? Example(true, false, true, 25)
05 }
06?
07 func Example(b1, b2, b3 bool, i uint8) {
08 ? ? panic("Want stack trace")
09 }
這個(gè)例子修改Example函數(shù)為4個(gè)參數(shù):3個(gè)bool型和1個(gè)八位無符號(hào)整型。bool值也是用8個(gè)bit表示,所以在32位和64位架構(gòu)下,4個(gè)參數(shù)可以合并為一個(gè)single word。
Listing 11
01 goroutine 1 [running]:
02 main.Example(0x19010001)
/Users/bill/Spaces/Go/Projects/src/github.com/goinaction/code/
temp/main.go:8 +0x64
03 main.main()
/Users/bill/Spaces/Go/Projects/src/github.com/goinaction/code/
temp/main.go:4 +0x32
這是本例的堆棧信息,看下圖的具體分析:
Listing 12
// Parameter values
true, false, true, 25
// Word value
Bits ? ?Binary ? ? ?Hex ? Value
00-07 ? 0000 0001 ??01? ??true
08-15 ? 0000 0000 ??00? ? false
16-23 ? 0000 0001 ??01? ? true
24-31 ? 0001 1001 ??19? ? 25
// Declaration
main.Example(b1, b2, b3 bool, i uint8)
// Stack trace
main.Example(0x19010001)
以上展示了參數(shù)值是如何匹配到4個(gè)參數(shù)的。當(dāng)我們看到堆棧信息中包括十六進(jìn)制值,需要知道這些值是如何傳遞的。
在前一小節(jié)中介紹了點(diǎn)亮第一個(gè)LED燈,這里我們準(zhǔn)備進(jìn)階嘗試下,輸出第一段PWM波形。(PWM也就是脈寬調(diào)制,一種可調(diào)占空比的技術(shù),得到的效果就是:如果用示波器測量引腳會(huì)發(fā)現(xiàn)有方波輸出,而且高電平、低電平的時(shí)間是可調(diào)的。)
這里爪爪熊準(zhǔn)備寫成一個(gè)golang的庫,并開源到github上,后續(xù)更新將直接更新到github中,如果你有興趣可以和我聯(lián)系。 github.com/dpawsbear/bear_rpi_go
我在很多的教程中都看到說樹莓派的PWM(硬件)只有一個(gè)GPIO能夠輸出,就是 GPIO1 。這可是不小的打擊,因?yàn)槲蚁胧褂弥辽偎膫€(gè) PWM ,還是不死心,想通過硬件手冊(cè)上找尋蛛絲馬跡,看看究竟怎么回事。
手冊(cè)上找尋東西稍等下講述,這里先提供一種方法測試 樹莓派3B 的 PWM 方法:用指令控制硬件PWM。
這里通過指令的方式掌握了基本的pwm設(shè)置技巧,決定去翻一下手冊(cè)看看到底PWM怎么回事,這里因?yàn)闆]有 BCM2837 的手冊(cè),根據(jù)之前文章引用官網(wǎng)所說, BCM2835 和 BCM2837 應(yīng)該是一樣的。這里我們直接翻閱 BCM2835 的手冊(cè),直接找到 PWM 章節(jié)。找到了如下圖:
圖中可以看到在博通的命名規(guī)則中 GPIO 12、13、18、19、40、41、45、52、53 均可以作為PWM輸出。但是只有兩路PWM0 PWM1。根據(jù)我之前所學(xué)知識(shí),不出意外應(yīng)該是PWM0 和 PWM1可以輸出不一樣的占空比,但是頻率應(yīng)該是一樣的。因?yàn)闆]有示波器,暫時(shí)不好測試。先找到下面對(duì)應(yīng)圖:
根據(jù)以上兩個(gè)圖對(duì)比可以發(fā)現(xiàn)如下規(guī)律:
對(duì)照上面的表可以看出從 BCM2837 中印出來的能夠使用在PWM上的就這幾個(gè)了。
為了驗(yàn)證個(gè)人猜想是否正確,這里先直接使用指令的模式,模擬配置下是否能夠正常輸出。
通過上面一系列指令模擬發(fā)現(xiàn),(GPIO1、GPIO26)、(GPIO23、GPIO24)是綁定在一起的,調(diào)節(jié)任意一個(gè),另外一個(gè)也會(huì)發(fā)生變化。也即是PWM0、PWM1雖然輸出了兩路,可以理解成兩路其實(shí)都是連在一個(gè)輸出口上。這里由于沒有示波器或者邏輯分析儀這類設(shè)備(僅有一個(gè)LED燈),所以測試很簡陋,下一步是使用示波器這類東西對(duì)頻率以及信號(hào)穩(wěn)定性進(jìn)行下測試。
小節(jié):樹莓派具有四路硬件輸出PWM能力,但是四路中只能輸出兩個(gè)獨(dú)立(占空比獨(dú)立)的PWM,同時(shí)四路輸出的頻率均是恒定的。
上面大概了解清楚了樹莓派3B的PWM結(jié)構(gòu),接下來就是探究如何使用Go語言進(jìn)行設(shè)置。
因?yàn)槟玫搅耸謨?cè),這里我想直接操作寄存器的方式進(jìn)行設(shè)置,也是順便學(xué)習(xí)下Go語言處理寄存器的過程。首先需要拿到pwm 系列寄存器的基地址,但是翻了一圈手冊(cè),發(fā)現(xiàn)只有偏移,沒有找到基地址。
經(jīng)過了一段時(shí)間的努力后,決定寫一個(gè) 樹莓派3B golang包開源放在github上,只需要寫相關(guān)程序進(jìn)行調(diào)用就可以了,以下是相關(guān)demo(pwm)(在GPIO.12 上輸出PWM波,放上LED燈會(huì)有呼吸燈的效果,具體多少頻率還沒有進(jìn)行測試)
以下是demo(pwm) 源碼
文章題目:go語言精髓技巧 go語言入門
本文URL:http://chinadenli.net/article6/dodeeog.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供定制開發(fā)、網(wǎng)站內(nèi)鏈、網(wǎng)站維護(hù)、網(wǎng)站設(shè)計(jì)公司、用戶體驗(yàn)、網(wǎng)頁設(shè)計(jì)公司
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)