1. 程式人生 > >go語言快速入門 IPC之管道通訊 8

go語言快速入門 IPC之管道通訊 8

熟悉Unix/C程式設計的應該對IPC也非常的熟悉,多程序之間的通訊主要的手段有管道/訊號量/共享記憶體/Socket等,而管道作為父子程序間進行少量資料傳遞的有效手段也得到了廣泛的應用,在這篇文章中我們來看一下go語言中如何使用管道進行程序進行通訊。

管道的使用

在linux下,管道被非常廣泛地使用,一般在程式設計中我們實現了popen等的應用即可提供管道功能。而在命令列中使用地也非常多,|就是最為典型的管道的應用例子。shell會為|符號兩側的命令各建立一個指令碼,將左側的輸出管道與右側的輸入管道進行連線,可以進行單向管道通訊。
比如我們使用go env來確認go語言的環境變數,然後使用grep從中確認出GOROOT環境變數的值一般會如下這樣做

[root@liumiaocn goprj]# go env |grep GOROOT
GOROOT="/usr/local/go"
[root@liumiaocn goprj]#
  • 1
  • 2
  • 3

實現的過程其實go env會啟動一個程序, 而grep命令也會產生一個程序,grep的程序會在go env的標準輸出中進行檢索GOROOT的行的資訊然後顯示出來,而負責這兩個程序間的通訊的正是管道。
在c語言中,我們需要父程序中進行fork以及對父程序的基本資訊進行處理,同時初期化連線的管道資訊從而實現管道通訊。接下來,我們來看一下在go語言中是如何實現的

呼叫作業系統命令

為了方便演示,我們使用標準庫os中的api以呼叫作業系統的命令並在此基礎上建立用於通訊的管道

例子程式碼

[[email protected] goprj]# cat basic-ipc-pipe.go
package main

import "fmt"
import "os/exec"

func main() {
        //create cmd
        cmd_go_env := exec.Command("go", "env")
        //cmd_grep:=exec.Command("grep","GOROOT")

        stdout_env, env_error := cmd_go_env.StdoutPipe()
        if
env_error != nil { fmt.Println("Error happened about standard output pipe ", env_error) return } //env_error := cmd_go_env.Start() if env_error := cmd_go_env.Start(); env_error != nil { fmt.Println("Error happened in execution ", env_error) return } a1 := make([]byte, 1024) n, err := stdout_env.Read(a1) if err != nil { fmt.Println("Error happened in reading from stdout", err) } fmt.Printf("Standard output of go env command: %s", a1[:n]) } [[email protected] goprj]#
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

執行結果

[root@liumiaocn goprj]# go run basic-ipc-pipe.go
Standard output of go env command: GOARCH="amd64"
GOBIN=""
GOEXE=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOOS="linux"
GOPATH=""
GORACE=""
GOROOT="/usr/local/go"
GOTOOLDIR="/usr/local/go/pkg/tool/linux_amd64"
CC="gcc"
GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build142715013=/tmp/go-build -gno-record-gcc-switches"
CXX="g++"
CGO_ENABLED="1"
[root@liumiaocn goprj]#
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

管道連線

通過呼叫exec.Start啟動一個程序,通過StdoutPipe將此呼叫的輸出管道也建立了出來,在這裡,我們讀取了此輸出的資訊,確實是go env命令的標準輸出,接下來要做的事情就是將此輸出的管道與grep命令的程序進行連線了。我們將上面的程式碼進一步充實:

例子程式碼

[[email protected] goprj]# cat basic-ipc-pipe.go
package main

import "fmt"
import "os/exec"
import "bufio"
import "bytes"

func main() {
        //create cmd
        cmd_go_env := exec.Command("go", "env")
        cmd_grep := exec.Command("grep", "GOROOT")

        stdout_env, env_error := cmd_go_env.StdoutPipe()
        if env_error != nil {
                fmt.Println("Error happened about standard output pipe ", env_error)
                return
        }

        //env_error := cmd_go_env.Start()
        if env_error := cmd_go_env.Start(); env_error != nil {
                fmt.Println("Error happened in execution ", env_error)
                return
        }
        /*
                a1 := make([]byte, 1024)
                n, err := stdout_env.Read(a1)
                if err != nil {
                        fmt.Println("Error happened in reading from stdout", err)
                        return
                }

                fmt.Printf("Standard output of go env command: %s", a1[:n])
        */
        //get the output of go env
        stdout_buf_grep := bufio.NewReader(stdout_env)

        //create input pipe for grep command
        stdin_grep, grep_error := cmd_grep.StdinPipe()
        if grep_error != nil {
                fmt.Println("Error happened about standard input pipe ", grep_error)
                return
        }

        //connect the two pipes together
        stdout_buf_grep.WriteTo(stdin_grep)

        //set buffer for reading
        var buf_result bytes.Buffer
        cmd_grep.Stdout = &buf_result

        //grep_error := cmd_grep.Start()
        if grep_error := cmd_grep.Start(); grep_error != nil {
                fmt.Println("Error happened in execution ", grep_error)
                return
        }

        err := stdin_grep.Close()
        if err != nil {
                fmt.Println("Error happened in closing pipe", err)
                return
        }

        //make sure all the infor in the buffer could be read
        if err := cmd_grep.Wait(); err != nil {
                fmt.Println("Error happened in Wait process")
                return
        }
        fmt.Println(buf_result.String())

}
[[email protected] goprj]#
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72

內容非常簡單,無需過多解釋,唯一需要注意的就是上一個例子中為了確認中間資訊,讀出管道的資訊,但是此處讀完了,輸入管道就讀不到了,所以註釋掉了才能正常執行

執行結果

[root@liumiaocn goprj]# go run basic-ipc-pipe.go
GOROOT="/usr/local/go"

[root@liumiaocn goprj]#
  • 1
  • 2
  • 3
  • 4

從這裡可以看出與go env |grep GOROOT是一樣的結果。

總結

本文通過模擬非常簡單的管道在go中實現的例子,解釋了go語言中匿名管道的使用方式,當然和unix/c一樣,go也支援命名管道,通過os.Pipe()即可輕鬆實現,基本原理均類似,在此不再贅述。

再分享一下我老師大神的人工智慧教程吧。零基礎!通俗易懂!風趣幽默!希望你也加入到我們人工智慧的隊伍中來!https://www.cnblogs.com/captainbed