Searching...

[TOC]

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

使用方式:

import "gitee.com/johng/gf/g/os/gproc"

方法列表:godoc.org/github.com/johng-cn/gf/g/os/gproc

func IsChild() bool
func PPid() int
func PPidOS() int
func Pid() int
func Send(pid int, data []byte, group ...string) error
func SetPPid(ppid int)
func Shell(cmd string, out io.Writer, in io.Reader) error
func ShellExec(cmd string) (string, error)
func ShellRun(cmd string) error
func StartTime() time.Time
func Uptime() int
type Manager
    func NewManager() *Manager
    func (m *Manager) AddProcess(pid int)
    func (m *Manager) Clear()
    func (m *Manager) GetProcess(pid int) *Process
    func (m *Manager) KillAll() error
    func (m *Manager) NewProcess(path string, args []string, environment []string) *Process
    func (m *Manager) Pids() []int
    func (m *Manager) Processes() []*Process
    func (m *Manager) RemoveProcess(pid int)
    func (m *Manager) Send(data []byte)
    func (m *Manager) SendTo(pid int, data []byte) error
    func (m *Manager) SignalAll(sig os.Signal) error
    func (m *Manager) Size() int
    func (m *Manager) WaitAll()
type Msg
    func Receive(group ...string) *Msg
type Process
    func NewProcess(path string, args []string, environment ...[]string) *Process
    func (p *Process) Kill() error
    func (p *Process) Pid() int
    func (p *Process) Release() error
    func (p *Process) Run() error
    func (p *Process) Send(data []byte) error
    func (p *Process) Signal(sig os.Signal) error
    func (p *Process) Start() (int, error)

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

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

进程管理

执行Shell命令

package main

import (
    "gitee.com/johng/gf/g/os/gproc"
    "fmt"
)

func main () {
    r, err := gproc.ShellExec(`sleep 3s; echo "hello gf!";`)
    fmt.Println("result:", r)
    fmt.Println(err)
}

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

result: hello gf!

<nil>

主进程与子进程

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

package main

import (
    "os"
    "time"
    "gitee.com/johng/gf/g/os/glog"
    "gitee.com/johng/gf/g/os/gproc"
)

func main () {
    if gproc.IsChild() {
        glog.Printfln("%d: Hi, I am child, waiting 3 seconds to die", gproc.Pid())
        time.Sleep(time.Second)
        glog.Printfln("%d: 1", gproc.Pid())
        time.Sleep(time.Second)
        glog.Printfln("%d: 2", gproc.Pid())
        time.Sleep(time.Second)
        glog.Printfln("%d: 3", gproc.Pid())
    } else {
        m := gproc.NewManager()
        p := m.NewProcess(os.Args[0], os.Args, os.Environ())
        p.Start()
        p.Wait()
        glog.Printfln("%d: child died", gproc.Pid())
    }
}

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

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

多进程管理

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

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

  2. 我们的程序如下:

    package main
    
    
    import (
        "fmt"
        "gitee.com/johng/gf/g/os/gproc"
    )
    
    
    func main () {
        pid := 28536
        m   := gproc.NewManager()
        m.AddProcess(pid)
        m.KillAll()
        m.WaitAll()
        fmt.Printf("%d was killed\n", pid)
    }
    

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

    28536 was killed
    

进程通信

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

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

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

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

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

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

package main

import (
    "os"
    "fmt"
    "time"
    "gitee.com/johng/gf/g/os/gproc"
    "gitee.com/johng/gf/g/os/gtime"
)

func main () {
    fmt.Printf("%d: I am child? %v\n", gproc.Pid(), gproc.IsChild())
    if gproc.IsChild() {
        gtime.SetInterval(time.Second, func() bool {
            gproc.Send(gproc.PPid(), []byte(gtime.Datetime()))
            return true
        })
        select { }
    } else {
        m := gproc.NewManager()
        p := m.NewProcess(os.Args[0], os.Args, os.Environ())
        p.Start()
        for {
            msg := gproc.Receive()
            fmt.Printf("receive from %d, data: %s\n", msg.Pid, string(msg.Data))
        }
    }
}

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

29978: I am child? false
29984: I am child? true
receive from 29984, data: 2018-05-18 15:01:00
receive from 29984, data: 2018-05-18 15:01:01
receive from 29984, data: 2018-05-18 15:01:02
receive from 29984, data: 2018-05-18 15:01:03
receive from 29984, data: 2018-05-18 15:01:04
...
GREETINGS
从今天开始GoFrame框架启用全新域名gfer.me!