上卷 程序设计
中卷 标准库
- bufio 1.18
- bytes 1.18
- io 1.18
- container 1.18
- encoding 1.18
- crypto 1.18
- hash 1.18
- index 1.18
- sort 1.18
- context 1.18
- database 1.18
- connection
- query
- queryrow
- exec
- prepare
- transaction
- scan & null
- context
- tcp
- udp
- http
- server
- handler
- client
- h2、tls
- url
- rpc
- exec
- signal
- embed 1.18
- plugin 1.18
- reflect 1.18
- runtime 1.18
- KeepAlived
- ReadMemStats
- SetFinalizer
- Stack
- sync 1.18
- atomic
- mutex
- rwmutex
- waitgroup
- cond
- once
- map
- pool
- copycheck
- nocopy
- unsafe 1.18
- fmt 1.18
- log 1.18
- math 1.18
- time 1.18
- timer
下卷 运行时
源码剖析
附录
文章来源于网络收集而来,版权归原创者所有,如有侵权请及时联系!
exec
使用 os.Process
可以创建进程以执行其他程序,不过我们常用的是其封装版本。
cmd
创建 Cmd
,然后选择相关执行方法。
Start
+Wait
Run
=Start
+Wait
Output
=Run
+stdout
CombinedOutput
=Run
+ (stdout
+stderr
)
基本输入输出,最直接的做法是 pipe
方法。
StdinPipe
StdoutPipe
StderrPipe
当然,也可以直接设置字段。
type Cmd struct { Stdin io.Reader Stdout io.Writer Stderr io.Writer }
需要注意, Wait
方法会清理进程资源。所以获取输出结果,须在此之前。
package main import ( "fmt" "io" "log" "os/exec" ) func main() { cmd := exec.Command("ls", "-l", "/usr/local/go/src") out, _ := cmd.StdoutPipe() if err := cmd.Start(); err != nil { log.Fatalln(err) } b, _ := io.ReadAll(out) fmt.Println(string(b)) cmd.Wait() fmt.Println(cmd.ProcessState) }
func main() { out, _ := exec.Command("ls", "-l", "/usr/local/go/src").Output() fmt.Println(string(out)) }
func main() { cmd := exec.Command("ls", "-l", "/usr/local/go/src") cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr if err := cmd.Run(); err != nil { log.Fatalln(err) } fmt.Println(cmd.ProcessState) }
僵尸进程
主进程须直接或间接调用 Wait
获取子进程的退出状态,否则会导致僵尸(zombie)进程。
除非主进程先终止,由系统 init
进程完成状态检查工作。
package main import ( "fmt" "os/exec" ) func main() { cmd := exec.Command("ls", "-lh", "/usr/local/go/bin") cmd.Start() fmt.Println(cmd.Process.Pid) fmt.Scanln() } /* 主进程退出前,可以用 ps aux 或 top 查看。 子进程 [ls] 状态为 Z,表示僵尸进程。 */
传递信息
和 fork
调用不同,默认会执行 exec
操作,重置相关信息。
因此,要向子进程传递信息,须借助 Env
和 ExtraFiles
属性。
子进程从
ExtraFiles
继承的文件描述符总是从3 + i
开始。
如果有多个文件,可用Env
传递列表。
package main import ( "fmt" "io" "log" "os" "os/exec" "strconv" ) func parent() { // 通过 Args 区分父子进程。 cmd := exec.Command(os.Args[0], "-child") cmd.Stdout = os.Stdout // 文件总数。 files := []string{"main.go", "main_test.go"} cmd.Env = append(cmd.Env, fmt.Sprintf("count=%d", len(files))) for i := 0; i < len(files); i++ { name := files[i] // 不能提前关闭。 file, _ := os.Open(name) defer file.Close() // 文件名和文件描述。 cmd.Env = append(cmd.Env, fmt.Sprintf("%d=%s", 3+i, name)) cmd.ExtraFiles = append(cmd.ExtraFiles, file) } // 启动并等待子进程结束。 if err := cmd.Run(); err != nil { log.Fatalln(err) } } func child() { // 获取文件总数。 count, _ := strconv.Atoi(os.Getenv("count")) // 从 3+i 开始,读取文件名和内容。 for i := 0; i < count; i++ { name := os.Getenv(strconv.Itoa(3 + i)) file := os.NewFile(uintptr(3+i), name) defer file.Close() b, _ := io.ReadAll(file) fmt.Println(name, len(b)) } } func main() { if len(os.Args) > 1 { fmt.Println("child:", os.Getpid(), os.Getppid()) child() return } fmt.Println("parent:", os.Getpid()) parent() } /* parent: 2042 child: 2046 2042 main.go 1162 main_test.go 67 */
process
很少直接 Process
创建子进程,而是 FindProcess
获取某个已运行进程。
进而向其发送信号,或终止。
Kill
: 发送SIGKILL
强制终止信号。Signal
: 发送信号。Release
: 释放进程资源。Wait
: 等待进程终止,释放资源,返回相关状态信息。
如果不是子进程,
Wait
可能会返回错误,ProcessState == nil
。
package main import ( "fmt" "log" "os" "os/exec" "strconv" "strings" ) func main() { log.SetFlags(log.Lshortfile) b, err := exec.Command("pidof", "top").CombinedOutput() if err != nil { log.Fatalln(err) } pid, _ := strconv.Atoi(strings.TrimSpace(string(b))) p, err := os.FindProcess(pid) if err != nil { log.Fatalln(err) } p.Kill() fmt.Println(p.Wait()) }
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论