Skip to content

Commit

Permalink
Merge pull request hantmac#131 from foxxnuaa/master
Browse files Browse the repository at this point in the history
合入第8章剩余翻译内容
  • Loading branch information
hantmac committed Jun 24, 2019
2 parents 726872e + eec1893 commit fd9c809
Show file tree
Hide file tree
Showing 17 changed files with 832 additions and 104 deletions.
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,18 @@
- [08.11 再看strings包](https://github.com/hantmac/Mastering_Go_ZH_CN/tree/master/eBook/chapter8/08.11.md)
- [08.12 关于bytes包](https://github.com/hantmac/Mastering_Go_ZH_CN/tree/master/eBook/chapter8/08.12.md)
- [08.13 文件权限](https://github.com/hantmac/Mastering_Go_ZH_CN/tree/master/eBook/chapter8/08.13.md)
- [08.14 处理Unix信号](https://github.com/hantmac/Mastering_Go_ZH_CN/tree/master/eBook/chapter8/08.14.md)
- [08.14.1 处理两种信号](https://github.com/hantmac/Mastering_Go_ZH_CN/tree/master/eBook/chapter8/08.14.1.md)
- [08.14.2 处理所有信号](https://github.com/hantmac/Mastering_Go_ZH_CN/tree/master/eBook/chapter8/08.14.2.md)
- [08.15 Unix管道编程](https://github.com/hantmac/Mastering_Go_ZH_CN/tree/master/eBook/chapter8/08.15.md)
- [08.16 遍历目录树](https://github.com/hantmac/Mastering_Go_ZH_CN/tree/master/eBook/chapter8/08.16.md)
- [08.17 使用ePBF](https://github.com/hantmac/Mastering_Go_ZH_CN/tree/master/eBook/chapter8/08.17.md)
- [08.18 关于syscall.PtraceRegs](https://github.com/hantmac/Mastering_Go_ZH_CN/tree/master/eBook/chapter8/08.18.md)
- [08.19 跟踪系统调用](https://github.com/hantmac/Mastering_Go_ZH_CN/tree/master/eBook/chapter8/08.19.md)
- [08.20 User ID和group ID](https://github.com/hantmac/Mastering_Go_ZH_CN/tree/master/eBook/chapter8/08.20.md)
- [08.21 其他资源](https://github.com/hantmac/Mastering_Go_ZH_CN/tree/master/eBook/chapter8/08.21.md)
- [08.22 练习](https://github.com/hantmac/Mastering_Go_ZH_CN/tree/master/eBook/chapter8/08.22.md)
- [08.23 总结](https://github.com/hantmac/Mastering_Go_ZH_CN/tree/master/eBook/chapter8/08.23.md)
- [chapter 9 并发-Goroutines,Channel和Pipeline](https://github.com/hantmac/Mastering_Go_ZH_CN/tree/master/eBook/chapter9/09.0.md)
- [09.1 关于进程,线程和Go协程](https://github.com/hantmac/Mastering_Go_ZH_CN/tree/master/eBook/chapter9/09.1.md)
- [09.1.1 Go调度器](https://github.com/hantmac/Mastering_Go_ZH_CN/tree/master/eBook/chapter9/09.1.1.md)
Expand Down
Empty file modified eBook/chapter8/08.0.md
100755 → 100644
Empty file.
79 changes: 79 additions & 0 deletions eBook/chapter8/08.14.1.md
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`的问题是,如果它得到一个未被编程处理的信号,它将忽略它。因此,在下一节中,你将看到一种使用相对不同的方法以更有效的方式处理信号的技术。

95 changes: 95 additions & 0 deletions eBook/chapter8/08.14.2.md
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
```



18 changes: 18 additions & 0 deletions eBook/chapter8/08.14.md
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

```
98 changes: 98 additions & 0 deletions eBook/chapter8/08.15.1.md
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`源文件作为生成的可执行文件的参数。
10 changes: 10 additions & 0 deletions eBook/chapter8/08.15.md
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`



Loading

0 comments on commit fd9c809

Please sign in to comment.