forked from hantmac/Mastering_Go_ZH_CN
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request hantmac#131 from foxxnuaa/master
合入第8章剩余翻译内容
- Loading branch information
Showing
17 changed files
with
832 additions
and
104 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
#**处理两种信号** | ||
在本小节中,你将学习如何在`Go`程序中处理两种信号,代码见`handleTwo.go`,分为四部分。`handleTwo.go`处理的信号是`SIGINFO`和`SIGINT`,在Golang中称为`syscall.SIGINFO`和`os.Interrupt`。 | ||
|
||
>*如果你查看`os`包文档,会发现在所有系统上只保证存在两个`siganal`,分别是`syscall.SIGKILL`和`syscall.SIGINT`,在`Go`中也定义为`os.Kill`和`os.Interrupt`。* | ||
`handleTwo.go`第一部分包含如下代码: | ||
```go | ||
package main | ||
|
||
import ( | ||
"fmt" | ||
"os" | ||
"os/signal" | ||
"syscall" | ||
"time" | ||
) | ||
``` | ||
`handleTwo.go`第二部分代码如下: | ||
```go | ||
func handleSignal(signal os.Signal) { | ||
fmt.Println("handleSignal() Caught:", signal) | ||
} | ||
``` | ||
`handleSignal`函数用于处理`syscall.SIGINFO`信号,而`os.interrupt`信号将被内联处理。 | ||
`handleTwo.go`第三部分代码如下: | ||
```go | ||
func main() { | ||
sigs := make(chan os.Signal, 1) | ||
signal.Notify(sigs, os.Interrupt, syscall.SIGINFO) | ||
|
||
go func() { | ||
for { | ||
sig := <-sigs | ||
switch sig { | ||
case os.Interrupt: | ||
fmt.Println("Caught:", sig) | ||
case syscall.SIGINFO: | ||
handleSignal(sig) | ||
return | ||
} | ||
} | ||
}() | ||
``` | ||
本技术工作原理如下:首先,你需要定义一个通道`sigs`用于传递数据。然后调用`signal.Notify()`声明你感兴趣的信号。下一步,你实现一个匿名函数,作为`goroutine`运行以便在收到关心的任何一个信号时进行操作。你需要等待`Chapter 9,Go Concurrency-Goroutines,Channels,and Pipelines`,学习`goroutine`和`channels`。 | ||
`handleTwo.go`最后一部分程序如下: | ||
```go | ||
for { | ||
fmt.Printf(".") | ||
time.Sleep(20 * time.Second) | ||
} | ||
} | ||
``` | ||
`time.Sleep()`调用用于阻止程序结束。在实际应用中,不需要使用类似代码。 | ||
|
||
在调用`kill(1)`时,我们需要程序的进程`ID`,我们首先编译`handleTwo.go`,并运行可执行文件,而不是`go run handleTwo.go`。`handleTwo`输出如下: | ||
```shell | ||
$ go build handleTwo.go | ||
$ ls -l handleTwo | ||
-rwxr-xr-x 1 mtsouk staff 2005200 Jan 18 07:49 handleTwo | ||
$ ./handleTwo | ||
.^CCaught: interrupt | ||
.Caught: interrupt | ||
handleSignal() Caught: information request | ||
.Killed:9 | ||
``` | ||
注意你需要另一个终端和`handleTwo.go`交互,并获取输出。在终端执行命令如下: | ||
```shell | ||
$ ps ax | grep ./handleTwo | grep -v grep | ||
47988 s003 S+ 0:00.00 ./handleTwo | ||
$ kill -s INT 47988 | ||
$ kill -s INFO 47988 | ||
$ kill -s USR1 47988 | ||
$ kill -9 47988 | ||
``` | ||
第一条命令用于查找`handleTwo`的进程`ID`,剩余的命令用于向进程发送信号。信号`SIGUSR1`被忽略了,在输出中没有显示。 | ||
|
||
`handleTwo.go`的问题是,如果它得到一个未被编程处理的信号,它将忽略它。因此,在下一节中,你将看到一种使用相对不同的方法以更有效的方式处理信号的技术。 | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
#**处理所有信号** | ||
在本小节,你将学习如何处理所有信号,但只对真正感兴趣的信号作出响应。和上一节相比,它技术更好,并更安全。该技术代码参考`handleAll.go`,分为四部分。 | ||
|
||
`handleAll.go`第一部分代码如下: | ||
```go | ||
package main | ||
|
||
import ( | ||
"fmt" | ||
"os" | ||
"os/signal" | ||
"syscall" | ||
"time" | ||
) | ||
|
||
func handle(signal os.Signal) { | ||
fmt.Println("Received:", signal) | ||
} | ||
``` | ||
`handleAll.go`第二部分代码如下: | ||
```go | ||
func main() { | ||
sigs := make(chan os.Signal, 1) | ||
signal.Notify(sigs) | ||
``` | ||
所以,所有的魔法都在`signal.Notify(sigs)`调用上。由于没有指定信号,所有输入信号都将被处理。 | ||
>*你可以在同一程序中使用不同的通道和相同的信号多次调用`signal.Notify`。在这种情况下,每个相关通道都将收到一份它要处理的信号副本!* | ||
`handleAll.go`第三部分代码如下: | ||
```go | ||
for { | ||
sig := <-sigs | ||
switch sig { | ||
case os.Interrupt: | ||
handle(sig) | ||
case syscall.SIGTERM: | ||
handle(sig) | ||
os.Exit(0) | ||
case syscall.SIGUSR2: | ||
fmt.Println("Handling syscall.SIGUSR2!") | ||
default: | ||
fmt.Println("Ignoring:", sig) | ||
} | ||
} | ||
}() | ||
``` | ||
|
||
使用其中一个信号退出程序非常方便。 | ||
|
||
这给了你在需要时在程序中做一些内务管理的机会。此时,`syscall.SIGERM`信号被用于这个目的。但这并不妨碍你使用`SIGKILL`来终止程序。 | ||
|
||
`handleAll.go`剩余代码如下: | ||
|
||
```go | ||
for { | ||
fmt.Printf(".") | ||
time.Sleep(30 * time.Second) | ||
} | ||
} | ||
``` | ||
|
||
你仍需要调用`time.Sleep()`以阻止程序立即退出。 | ||
|
||
|
||
|
||
同样,最好使用`go build`工具编译`handleAll.go`生成可执行文件。在新的终端中执行`handleAll`会产生如下的输出: | ||
|
||
```shell | ||
$ go build handleAll.go | ||
$ ls -l handleAll | ||
-rwxr-xr-x 1 mtsouk staff 2005216 Jan 18 08:25 handleAll | ||
$ ./handleAll | ||
.Ignoring: hangup | ||
Handling syscall.SIGUSR2! | ||
Ignoring: user defined signal 1 | ||
Received: interrupt | ||
^CReceived: interrupt | ||
Received: terminated | ||
``` | ||
|
||
另一个终端命令输出如下: | ||
|
||
```shell | ||
$ ps ax | grep ./handleAll | grep -v grep | ||
49902 s003 S+ 0:00.00 ./handleAll | ||
$ kill -s HUP 49902 | ||
$ kill -s USR2 49902 | ||
$ kill -s USR1 49902 | ||
$ kill -s INT 49902 | ||
$ kill -s TERM 49902 | ||
``` | ||
|
||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
#**处理`Unix`信号** | ||
|
||
`Go`为程序员提供了`os/signal`包来处理信号。本节将向你显示如何处理`Unix`信号。 | ||
|
||
首先,让我们介绍`Unix`信号的一些有用信息。你是否使用`Ctrl+C`停止正在运行的程序?如果是,则它们已经比较相似,因为`Ctrl+C`向程序发送`SIGINT signal`。严格来讲,`Unix`信号是可以通过名称或数字访问的软件中断,它们提供了在`Unix`系统上处理异步事件的方法。有两种方式发送信号:通过名字或者通过数字。通常来说,通过名字发送信号比较安全,因为你不太可能不小心发出错误的信号。 | ||
|
||
一个程序是不可能处理所有的信号的:一些信号既不能被捕获,也不能被忽略。`SIGKILL`和`SIGSTOP`信号不能被捕获、阻塞或忽略。这样做的原因是,它们为内核和根用户提供了一种停止任何进程的方法。`SIGKILL`信号,即数字`9`,通常在需要响应迅速的极端条件下调用。因此,它是唯一一个通常由数字调用的信号,这仅仅是因为这样做更快。 | ||
|
||
> *`signal.SIGINFO`在`Linux`机器上不可用。这意味着,如果你要在一个`Linux`机器上运行包含它的`Go`程序,则需要用另一个信号替换它,否则`Go`程序将无法编译和执行。* | ||
给进程发送信号的最常用方式是通过`kill(1)`方法。默认情况下,`kill(1)`发送`SIGTERM`信号。如果你想查看`Unix`机器上支持的所有信号,可以执行`kill -l`命令。 | ||
|
||
如果你在无权限的情况下给一个进程发送信号,`kill(1)`并不会执行,且会返回类似如下的错误提示: | ||
```shell | ||
$ kill 1210 | ||
-bash: kill: (1210) - Operation not permitted | ||
|
||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
#**用`Go`实现`cat(1)`程序** | ||
|
||
在本节中,你将看到`Go`版本的`cat(1)`程序。你可能会对程序的长度感到惊讶! | ||
|
||
`cat.go`源码分为三部分。第一部分如下: | ||
|
||
```go | ||
package main | ||
|
||
import ( | ||
"bufio" | ||
"fmt" | ||
"io" | ||
"os" | ||
) | ||
``` | ||
|
||
`cat.go`第二部分代码如下: | ||
|
||
```go | ||
func printFile(filename string) error { | ||
f, err := os.Open(filename) | ||
if err != nil { | ||
return err | ||
} | ||
defer f.Close() | ||
scanner := bufio.NewScanner(f) | ||
for scanner.Scan() { | ||
io.WriteString(os.Stdout, scanner.Text()) | ||
io.WriteString(os.Stdout, "\n") | ||
} | ||
|
||
return nil | ||
} | ||
``` | ||
|
||
在本部分,你会看到如何实现将文件内容打印到标准输出。 | ||
|
||
`cat.go`最后一部分代码如下: | ||
|
||
```go | ||
func main() { | ||
filename := "" | ||
arguments := os.Args | ||
if len(arguments) == 1 { | ||
io.Copy(os.Stdout, os.Stdin) | ||
return | ||
} | ||
|
||
for i := 1; i < len(arguments); i++ { | ||
filename = arguments[i] | ||
err := printFile(filename) | ||
if err != nil { | ||
fmt.Println(err) | ||
} | ||
} | ||
} | ||
``` | ||
|
||
前面的代码包含了`cat.go`的所有魔力,因为这是定义程序行为的地方。首先,如果在没有任何命令行参数的情况下执行`cat.go`,那么程序仅仅是通过`io.Copy(os.Stdout,os.Stdin)`将标准输入复制到标准输出。但是,如果有命令行参数,那么程序将按照给定的顺序处理它们。 | ||
|
||
执行`cat.go`将创建如下的输出: | ||
|
||
```shell | ||
$go run cat.go | ||
Mastering Go | ||
Mastering Go | ||
1 2 3 4 | ||
1 2 3 4 | ||
``` | ||
|
||
但是,如果使用`Unix`管道执行`cat.go`,结果会变得非常有趣: | ||
|
||
```shell | ||
$ go run cat.go /tmp/1.log /tmp/2.log | wc | ||
2367 44464 279292 | ||
$ go run cat.go /tmp/1.log /tmp/2.log | go run cat.go | wc | ||
2367 44464 279292 | ||
``` | ||
|
||
`cat.go`还可以在屏幕上打印多个文件。 | ||
|
||
```shell | ||
$ go run cat.go 1.txt 1 1.txt | ||
2367 44464 279292 | ||
2367 44464 279292 | ||
open 1: no such file or dictory | ||
2367 44464 279292 | ||
2367 44464 279292 | ||
``` | ||
|
||
请注意,如果你试图通过`go run cat.go cat.go`来执行`cat.go`,并期望在屏幕上得到`cat.go`的内容,它会执行失败,并输出如下的错误信息: | ||
|
||
```shell | ||
package main: case-insensitive file name collision: "cat.go" and "cat.go" | ||
``` | ||
|
||
原因是`Go`不理解第二个`cat.go`应该作为命令行参数来运行`cat.go`。相反,`go run`试图编译`cat.go`两次,从而导致错误消息。解决方案是先执行`go build cat.go`,然后使用`cat.go`或任何其他`go`源文件作为生成的可执行文件的参数。 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
#**`Unix`管道编程** | ||
|
||
根据`Unix`原理,`Unix`命令行实用程序应该仅且仅执行一个任务!在实践中,这意味着,与其开发执行大量任务的大型实用程序,不如开发多个较小的程序,这些程序组合在一起执行所需的工作。两个或多个`Unix`命令行实用程序通信的最常见方式是使用管道。在`Unix`管道中,一个命令行实用程序的输出是另一个命令行实用程序的输入。此过程可能涉及两个以上的程序!`Unix`管道的标识是字符`|`。 | ||
|
||
管道有两个严重的限制:第一,它们通常是单向通信;第二,它们只能在具有相同祖先的进程之间使用。`Unix`管道实现背后的理念是,如果没有要处理的文件,那么应该等待从标准输入中获取输入。同样,如果不要求将输出保存到文件中,则应将输出写入标准输出,以便用户查看或由其他程序处理。 | ||
|
||
在第一节`Go与操作系统`中,你学习了如何从标准输入中读取,以及如何写入到标准输出。如果你对这两个操作仍有疑问,最好花点时间复习下代码`stdOUT.go`和`stdIN.go`。 | ||
|
||
|
||
|
Oops, something went wrong.