• 进程管理
    • 执行Shell命令
    • 主进程与子进程
    • 多进程管理
  • 进程通信 - Experimental feature!

    进程管理以及进程间的通信是通过gproc模块实现的,其中进程间通信采用的是本地socket通信机制。

    使用方式

    1. import "github.com/gogf/gf/g/os/gproc"

    接口文档

    https://godoc.org/github.com/gogf/gf/g/os/gproc

    简要说明:

    1. Manager对象为进程管理对象,可以同时管理多个子进程(当前执行进程为父进程);
    2. Process为进程对象,表示特定执行或者获取的一个进程资源;
    3. 我们可以通过ShellShellExecShellRun来执行Shell指令:
      • Shell表示一个原生的Shell指令执行方式,带自定义的输入和输出控制;
      • ShellExec执行命令后将会返回输出的结果内容;
      • ShellRun执行命令后将会直接将返回内容输出到标准输出;
      • 我们可以使用goroutine来实现异步的执行,如:go ShellRun(...)等等;

    由于进程管理及通信的内容比较多,以下对常用的几种使用做简单介绍。

    进程管理

    执行Shell命令

    1. package main
    2. import (
    3. "github.com/gogf/gf/g/os/gproc"
    4. "fmt"
    5. )
    6. func main () {
    7. r, err := gproc.ShellExec(`sleep 3s; echo "hello gf!";`)
    8. fmt.Println("result:", r)
    9. fmt.Println(err)
    10. }

    执行后,可以看到程序等待了3秒之后,输出结果为:

    1. result: hello gf!
    2. <nil>

    主进程与子进程

    gproc.Manager对象创建的进程都默认带子进程标识,在子进程程序中可以通过gproc.IsChild()方法来判断自身是否为子进程。

    1. package main
    2. import (
    3. "os"
    4. "time"
    5. "github.com/gogf/gf/g/os/glog"
    6. "github.com/gogf/gf/g/os/gproc"
    7. )
    8. func main () {
    9. if gproc.IsChild() {
    10. glog.Printfln("%d: Hi, I am child, waiting 3 seconds to die", gproc.Pid())
    11. time.Sleep(time.Second)
    12. glog.Printfln("%d: 1", gproc.Pid())
    13. time.Sleep(time.Second)
    14. glog.Printfln("%d: 2", gproc.Pid())
    15. time.Sleep(time.Second)
    16. glog.Printfln("%d: 3", gproc.Pid())
    17. } else {
    18. m := gproc.NewManager()
    19. p := m.NewProcess(os.Args[0], os.Args, os.Environ())
    20. p.Start()
    21. p.Wait()
    22. glog.Printfln("%d: child died", gproc.Pid())
    23. }
    24. }

    执行后,终端打印结果如下:

    1. 2018-05-18 14:35:41.360 28285: Hi, I am child, waiting 3 seconds to die
    2. 2018-05-18 14:35:42.361 28285: 1
    3. 2018-05-18 14:35:43.361 28285: 2
    4. 2018-05-18 14:35:44.361 28285: 3
    5. 2018-05-18 14:35:44.362 28278: child died

    多进程管理

    gproc除了能够创建子进程,管理子进程之外,也能管理非自身创建的其他进程。gproc可以同时管理多个进程,这里以单个进程为例来演示对进程的管理功能。

    1. 我们使用gedit软件(Linux下常用的文本编辑器)随意打开一个文件,在进程当中我们看到该gedit的进程ID为28536
      1. $ ps aux | grep gedit
      2. john 28536 3.6 0.6 946208 56412 ? Sl 14:39 0:00 gedit /home/john/Documents/text
    2. 我们的程序如下:

      1. package main
      2. import (
      3. "fmt"
      4. "github.com/gogf/gf/g/os/gproc"
      5. )
      6. func main () {
      7. pid := 28536
      8. m := gproc.NewManager()
      9. m.AddProcess(pid)
      10. m.KillAll()
      11. m.WaitAll()
      12. fmt.Printf("%d was killed\n", pid)
      13. }

      执行后,gedit被关闭,终端输出信息为:

      1. 28536 was killed

    进程通信 - Experimental feature!

    这个是实验性特性!

    不要通过共享内存来通信,而应该通过通信来共享内存。

    常见的进程通信方式有5种:管道/信号量/共享内存/共享文件/Socket。按照常见的并发架构的设计来讲,我们尽可能地少用锁机制,包括共享内存/共享文件其实都是需要依靠锁机制才能保证数据流的正确性,因为锁机制带来的维护复杂度往往会比其带来的好处更多。信号量常用在*nix系统中,跨平台性比较差。管道虽然实现起来比较简单,但是在稳定性上并没有Socket机制好。因此,gproc实现的进程通信采用的是Socket机制。但是需要注意的是,通信的两个进程都需要使用gproc模块来实现发送&接收数据

    gproc的进程通信API非常简便,只需通过以下两个方法实现:

    1. func Send(pid int, data []byte) error
    2. func Receive() *Msg

    我们通过Send方法向指定的进程发送数据(每调用一次相当于发送一条消息),在指定的进程中可以通过Receive方法获得数据。其中,Receive方法提供了类似消息队列的形式来收取其他进程传递的数据,当队列为空时,该方法将会阻塞等待。

    我们来看一个进程间通信的基本使用示例:

    1. package main
    2. import (
    3. "os"
    4. "fmt"
    5. "time"
    6. "github.com/gogf/gf/g/os/gproc"
    7. "github.com/gogf/gf/g/os/gtime"
    8. )
    9. func main () {
    10. fmt.Printf("%d: I am child? %v\n", gproc.Pid(), gproc.IsChild())
    11. if gproc.IsChild() {
    12. gtime.SetInterval(time.Second, func() bool {
    13. gproc.Send(gproc.PPid(), []byte(gtime.Datetime()))
    14. return true
    15. })
    16. select { }
    17. } else {
    18. m := gproc.NewManager()
    19. p := m.NewProcess(os.Args[0], os.Args, os.Environ())
    20. p.Start()
    21. for {
    22. msg := gproc.Receive()
    23. fmt.Printf("receive from %d, data: %s\n", msg.Pid, string(msg.Data))
    24. }
    25. }
    26. }

    该示例中,我们的主进程启动时创建了一个子进程,该子进程每隔1秒钟向主进程发送当前的时间,主进程收取到子进程发送的参数后输出到终端上。执行后,终端输出的内容如下:

    1. 29978: I am child? false
    2. 29984: I am child? true
    3. receive from 29984, data: 2018-05-18 15:01:00
    4. receive from 29984, data: 2018-05-18 15:01:01
    5. receive from 29984, data: 2018-05-18 15:01:02
    6. receive from 29984, data: 2018-05-18 15:01:03
    7. receive from 29984, data: 2018-05-18 15:01:04
    8. ...