熟悉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
原文地址:https://www.cnblogs.com/firsttry/p/10159917.html