进程间通信方式
学习操作系统的原理,我们知道,进程间通信有以下几种方式:
而在应用系统开发中,我们常用的方式就是消息队列和套接字两种方式。在程序中写了一个死循环,运行时,常使用 ctrl+c
来中断进程。突然软件卡死了,我们无法关闭,这时,你知道使用kill -9 pip来结束进程。这些基本的操作常识性操作,背后就使用的“信号量"和应用程序发生通信。
信号(Signal)是Linux, 类Unix和其它POSIX兼容的操作系统中用来进程间通讯的一种方式。一个信号就是一个异步的通知,发送给某个进程,或者同进程的某个线程,告诉它们某个事件发生了。当信号发送到某个进程中时,操作系统会中断该进程的正常流程,并进入相应的信号处理函数执行操作,完成后再回到中断的地方继续执行。如果目标进程先前注册了某个信号的处理程序(signal handler),则此处理程序会被调用,否则缺省的处理程序被调用。这种方式只有事件类型,不能实现进程间数据传递。
程序中可以设置接受信号,编写处理函数对信号进行拦截处理,信号有以下处理动作。其中SIGKILL和SIGSTOP不能被程序所捕捉做拦截处理 在mac电脑下,在命令终端输入 kill-l
会列出所有的signal信息
HUP INT QUIT ILL TRAP ABRT EMT FPE KILL BUS SEGV SYS PIPE ALRM TERM URG STOP TSTP CONT CHLD TTIN TTOU IO XCPU XFSZ VTALRM PROF WINCH INFO USR1 USR2
我们要终止一个服务进程,在终止前,让取它打印让其做个倒计时的功能。那么怎么实现这个小功能呢?这就需要用到信号,通过kill发送终止信号,在程序中编写捕捉函数,在信号被触发时,执行捕捉函数。
package main
import (
"fmt"
"os"
"os/signal"
"syscall"
"time"
)
func main() {
c := make(chan os.Signal)
signal.Notify(c, syscall.SIGINT)
fmt.Println("启动服务")
sig := <-c
fmt.Println("收到信号", sig)
TipMsg()
sum := 0
for {
sum += 1
time.Sleep(1 * time.Second)
}
}
func TipMsg() {
fmt.Printf("5秒后退出程序。。。")
for i := 5; i > 0; i-- {
time.Sleep(1 * time.Second)
}
fmt.Println("退出程序")
os.Exit(0)
}
go run server.go启动服务,按下 ctrl+c
既可完成测试,也可以使用kill发送信号进行测试。方法
$go build
$./server
//另开一个终端tab
$ps -ef | grep server
$kill 2 pid(服务的进程id)
我们在写程序时,常常将一些可控的参数通过配置文件的方式进行加载。这些参数也经常需要动态的调整,那么修改了配置文件后,就需要重新的加载配置文件,就需要重启服务。借助信号量是不就可以达到配置文件重新加载的目的。基于上边的例子,我们将TipMsg的操作改为配置文件重加载的操作,然后将信号修改为SIGUSR1, 完成修改后,使用kill -USER1 pid即可完成配置的重新加载。具体的实现可以参考这篇文章https://segmentfault.com/a/1190000019436438
参考:https://colobu.com/2015/10/09/Linux-Signals/ https://www.jianshu.com/p/f445bfeea40a