转载地址:https://colobu.com/2015/10/09/Linux-Signals/

信号(Signal)是Linux, 类Unix和其它POSIX兼容的操作系统中用来进程间通讯的一种方式。一个信号就是一个异步的通知,发送给某个进程,或者同进程的某个线程,告诉它们某个事件发生了。
当信号发送到某个进程中时,操作系统会中断该进程的正常流程,并进入相应的信号处理函数执行操作,完成后再回到中断的地方继续执行。
如果目标进程先前注册了某个信号的处理程序(signal handler),则此处理程序会被调用,否则缺省的处理程序被调用。

发送信号

kill 系统调用(system call)可以用来发送一个特定的信号给进程。
kill 命令允许用户发送一个特定的信号给进程。
raise 库函数可以发送特定的信号给当前进程。

在Linux下运行man kill可以查看此命令的介绍和用法。

The command kill sends the specified signal to the specified process or process group. If no signal is specified, the TERM signal is sent. The TERM signal will kill processes which do not catch this signal. For other processes, it may be necessary to use the KILL (9) signal, since this signal cannot be caught.

Most modern shells have a builtin kill function, with a usage rather similar to that of the command described here. The '-a' and '-p' options, and the possibility to specify pids by command name is a local extension.

If sig is 0, then no signal is sent, but error checking is still performed.

一些异常比如除以0或者 segmentation violation 相应的会产生SIGFPESIGSEGV信号,缺省情况下导致core dump和程序退出。
内核在某些情况下发送信号,比如在进程往一个已经关闭的管道写数据时会产生SIGPIPE信号。
在进程的终端敲入特定的组合键也会导致系统发送某个特定的信号给此进程:

  • Ctrl-C 发送 INT signal (SIGINT),通常导致进程结束
  • Ctrl-Z 发送 TSTP signal (SIGTSTP); 通常导致进程挂起(suspend)
  • Ctrl-\ 发送 QUIT signal (SIGQUIT); 通常导致进程结束 和 dump core.
  • Ctrl-T (不是所有的UNIX都支持) 发送INFO signal (SIGINFO); 导致操作系统显示此运行命令的信息

kill -9 pid 会发送 SIGKILL信号给进程。

处理信号

Signal handler可以通过signal()系统调用进行设置。如果没有设置,缺省的handler会被调用,当然进程也可以设置忽略此信号。
有两种信号不能被拦截和处理: SIGKILLSIGSTOP

当接收到信号时,进程会根据信号的响应动作执行相应的操作,信号的响应动作有以下几种:

  • 中止进程(Term)
  • 忽略信号(Ign)
  • 中止进程并保存内存信息(Core)
  • 停止进程(Stop)
  • 继续运行进程(Cont)

用户可以通过signalsigaction函数修改信号的响应动作(也就是常说的“注册信号”)。另外,在多线程中,各线程的信号响应动作都是相同的,不能对某个线程设置独立的响应动作。

信号类型

个平台的信号定义或许有些不同。下面列出了POSIX中定义的信号。
Linux 使用34-64信号用作实时系统中。
命令man 7 signal提供了官方的信号介绍。

在POSIX.1-1990标准中定义的信号列表

信号 动作 说明
SIGHUP 1 Term 终端控制进程结束(终端连接断开)
SIGINT 2 Term 用户发送INTR字符(Ctrl+C)触发
SIGQUIT 3 Core 用户发送QUIT字符(Ctrl+/)触发
SIGILL 4 Core 非法指令(程序错误、试图执行数据段、栈溢出等)
SIGABRT 6 Core 调用abort函数触发
SIGFPE 8 Core 算术运行错误(浮点运算错误、除数为零等)
SIGKILL 9 Term 无条件结束程序(不能被捕获、阻塞或忽略)
SIGSEGV 11 Core 无效内存引用(试图访问不属于自己的内存空间、对只读内存空间进行写操作)
SIGPIPE 13 Term 消息管道损坏(FIFO/Socket通信时,管道未打开而进行写操作)
SIGALRM 14 Term 时钟定时信号
SIGTERM 15 Term 结束程序(可以被捕获、阻塞或忽略)
SIGUSR1 30,10,16 Term 用户保留
SIGUSR2 31,12,17 Term 用户保留
SIGCHLD 20,17,18 Ign 子进程结束(由父进程接收)
SIGCONT 19,18,25 Cont 继续执行已经停止的进程(不能被阻塞)
SIGSTOP 17,19,23 Stop 停止进程(不能被捕获、阻塞或忽略)
SIGTSTP 18,20,24 Stop 停止进程(可以被捕获、阻塞或忽略)
SIGTTIN 21,21,26 Stop 后台程序从终端中读取数据时触发
SIGTTOU 22,22,27 Stop 后台程序向终端中写数据时触发

在SUSv2和POSIX.1-2001标准中的信号列表:

信号 动作 说明
SIGTRAP 5 Core Trap指令触发(如断点,在调试器中使用)
SIGBUS 0,7,10 Core 非法地址(内存地址对齐错误)
SIGPOLL   Term Pollable event (Sys V). Synonym for SIGIO
SIGPROF 27,27,29 Term 性能时钟信号(包含系统调用时间和进程占用CPU的时间)
SIGSYS 12,31,12 Core 无效的系统调用(SVr4)
SIGURG 16,23,21 Ign 有紧急数据到达Socket(4.2BSD)
SIGVTALRM 26,26,28 Term 虚拟时钟信号(进程占用CPU的时间)(4.2BSD)
SIGXCPU 24,24,30 Core 超过CPU时间资源限制(4.2BSD)
SIGXFSZ 25,25,31 Core 超过文件大小资源限制(4.2BSD)

Windows中没有SIGUSR1,可以用SIGBREAK或者SIGINT代替。

Go中的Signal发送和处理

有时候我们想在Go程序中处理Signal信号,比如收到SIGTERM信号后优雅的关闭程序(参看下一节的应用)。
Go信号通知机制可以通过往一个channel中发送os.Signal实现。
首先我们创建一个os.Signal channel,然后使用signal.Notify注册要接收的信号。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

package main

import "fmt"

import "os"

import "os/signal"

import "syscall"

func main() {

// Go signal notification works by sending `os.Signal`

// values on a channel. We'll create a channel to

// receive these notifications (we'll also make one to

// notify us when the program can exit).

sigs := make(chan os.Signal, 1)

done := make(chan bool, 1)

// `signal.Notify` registers the given channel to

// receive notifications of the specified signals.

signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)

// This goroutine executes a blocking receive for

// signals. When it gets one it'll print it out

// and then notify the program that it can finish.

go func() {

sig := <-sigs

fmt.Println()

fmt.Println(sig)

done <- true

}()

// The program will wait here until it gets the

// expected signal (as indicated by the goroutine

// above sending a value on `done`) and then exit.

fmt.Println("awaiting signal")

<-done

fmt.Println("exiting")

}

go run main.go执行这个程序,敲入ctrl-C会发送SIGINT信号。 此程序接收到这个信号后会打印退出。

Go网络服务器如果无缝重启

Go很适合编写服务器端的网络程序。DevOps经常会遇到的一个情况是升级系统或者重新加载配置文件,在这种情况下我们需要重启此网络程序,如果网络程序暂停的时间较长,则给客户的感觉很不好。
如何实现优雅地重启一个Go网络程序呢。主要要解决两个问题:

  1. 进程重启不需要关闭监听的端口
  2. 既有请求应当完全处理或者超时

@humblehack 在他的文章Graceful Restart in Golang中提供了一种方式,而Florian von Bock根据此思路实现了一个框架endless。
此框架使用起来超级简单:

1

err := endless.ListenAndServe("localhost:4242", mux)

只需替换 http.ListenAndServe 和 http.ListenAndServeTLS

它会监听这些信号: syscall.SIGHUPsyscall.SIGUSR1syscall.SIGUSR2syscall.SIGINTsyscall.SIGTERM, 和 syscall.SIGTSTP

此文章提到的思路是:

  1. 通过exec.Command fork一个新的进程,同时继承当前进程的打开的文件(输入输出,socket等)

1

2

3

4

5

6

7

8

9

10

11

12

13

14

file := netListener.File() // this returns a Dup()

path := "/path/to/executable"

args := []string{

"-graceful"}

cmd := exec.Command(path, args...)

cmd.Stdout = os.Stdout

cmd.Stderr = os.Stderr

cmd.ExtraFiles = []*os.File{file}

err := cmd.Start()

if err != nil {

log.Fatalf("gracefulRestart: Failed to launch, error: %v", err)

}

  1. 子进程初始化
    网络程序的启动代码

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

server := &http.Server{Addr: "0.0.0.0:8888"}

var gracefulChild bool

var l net.Listever

var err error

flag.BoolVar(&gracefulChild, "graceful", false, "listen on fd open 3 (internal use only)")

if gracefulChild {

log.Print("main: Listening to existing file descriptor 3.")

f := os.NewFile(3, "")

l, err = net.FileListener(f)

} else {

log.Print("main: Listening on a new file descriptor.")

l, err = net.Listen("tcp", server.Addr)

}

  1. 父进程停止

1

2

3

4

5

6

7

if gracefulChild {

parent := syscall.Getppid()

log.Printf("main: Killing parent pid: %v", parent)

syscall.Kill(parent, syscall.SIGTERM)

}

server.Serve(l)

同时他还提供的如何处理已经正在处理的请求。可以查看它的文章了解详细情况。

因此,处理特定的信号可以实现程序无缝的重启。

其它

graceful shutdown实现非常的简单,通过简单的信号处理就可以实现。本文介绍的是graceful restart,要求无缝重启,所以所用的技术相当的hack。

Facebook的工程师也提供了http和net的实现: facebookgo。

参考资料

  1. https://en.wikipedia.org/wiki/Unix_signal
  2. http://hutaow.com/blog/2013/10/19/linux-signal/
  3. http://www.ucs.cam.ac.uk/docs/course-notes/unix-courses/Building/files/signals.pdf
  4. https://golang.org/pkg/os/signal/
  5. https://gobyexample.com/signals
  6. http://grisha.org/blog/2014/06/03/graceful-restart-in-golang/
  7. https://fitstar.github.io/falcore/hot_restart.html
  8. https://github.com/rcrowley/goagain

Linux Signal及Golang中的信号处理相关推荐

  1. linux中的信号处理与SROP

    版权声明:本文为CSDN博主「ashimida@」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明. 原文链接:https://blog.csdn.net/lidan1 ...

  2. linux信号传递给进程,bash中的信号处理机制

    Linux 中的信号 信号(Signal)是操作系统中常用的进程通信手段, 主要用来描述特定事件的发生, 进程接收到信号时有以下几种处理方式: 捕获并自定义处理函数: 给signal系统调用传递自定义 ...

  3. linux中信号的处理,linux中关于信号处理笔记(二)

    2 等待一个全局变量被设置 这种情况是等待一个信号处理程序设置一个全局变量.下面的例子用于捕捉中断信号和退出信号,但是希望仅当退出信号处理程序时,才唤醒主进程. #include #include # ...

  4. golang中os/signal包的使用

    golang中os/signal包的使用 os/signal包实现对信号的处理 golang中对信号的处理主要使用os/signal包中的两个方法:一个是notify方法用来监听收到的信号:一个是 s ...

  5. linux signal函数用法,linux信号机制之sigaction构造体浅析,signal 函数,信号捕捉.

    来自:http://hi.baidu.com/phenix_yw/blog/item/6eb4ca391d1479f23a87ce19.html 信号安装函数sigaction(int signum, ...

  6. onclick如何调用含参函数_在 golang 中如何调用私有函数(绑定隐藏的标识符)

    名字在 golang 中的重要性和在其他任何一种语言是一样的.他们甚至含有语义的作用:在一个包的外部某个名字的可见性是由这个名字首字母是否是大写来决定的. 有时为了更好的组织代码或者在其他包使用某些隐 ...

  7. linux 线程退出 signal,Linux signal 那些事儿 (3)

    这篇博客,想集中在signal 与线程的关系上,顺带介绍内核signal相关的结构.如何组织我其实并没想好,想到哪就写到哪里吧.主题一定会落在signal之内而不跑题. 提到signal与thread ...

  8. linux signal 处理

    linux signal 处理 说明: 本文主要翻译自ULK 3rd chapter 11. 主要受 http://blog.csdn.net/yunsongice 影响,故发表在csdn. 另外,本 ...

  9. linux进程signal,Linux Signal 示例

    信号是系统响应某些条件而产生的一个事件,接收到该信的进程做出相应的处理.通常信是由错误产生的,如段错误(SIGSEGV). 但信还可以作为进程间通信的一种方式,由一个进程发送给另一个进程. 信号定义在 ...

最新文章

  1. java 集合 介绍_java集合类基本简介
  2. Android构建流程——篇一
  3. C语言中低位存放,C语言 大端小端存储解析以及判断方法
  4. ExtJs4 笔记 Ext.tab.Panel 选项卡
  5. 工作流实战_04_flowable 流程的模板的图片和xml显示
  6. 讨论用户注册有的几篇好文
  7. 小汤学编程之JavaScript学习day05——DOM、事件
  8. c语言整形数组存放字符串,用一维字符数组存放字符串
  9. delphi webservice 内存释放_2020年7月——内存天梯图
  10. VMwareESX常用命令和IP地址修改
  11. 新浪实时股票数据接口http://hq.sinajs.cn/list=code
  12. 内网环境部署zabbix5.0版本监控(一)
  13. 植物大战僵尸:无冷却分析方法
  14. 【javascript】js面试题原型和原型链
  15. 干货 | 三维点云配准:ICP 算法原理及推导
  16. 正则表达式(参考百度词条)
  17. docker 删除映像_创建自己的Docker映像(技术提示#57)
  18. 关于#include iomanip中iomanip的作用~
  19. Arduino使用水银开关
  20. wcom少彐x片_大数据实战之千万量级小说网站项目开发(存储、复杂搜索、推荐、分析)...

热门文章

  1. 【SpringCloud从0到6】 第二节:Spring Cloud 和 Dubbo
  2. 设计模式之单例模式的多重实现
  3. jQuery左右循环滚动图片特效
  4. spring cloud config的bootstrap.yml与application.proterties的区别
  5. 我的Python成长之路---第六天---Python基础(20)---2016年2月20日(晴)
  6. CentOS7 install spark+ipython-nodebook
  7. 从远程服务器获取数据
  8. Hadoop1.9安装配置
  9. 网上购物安全防范很重要
  10. C++复习总结(涵盖所有C++基本考点)!