PIPE
: 通常有两种限制, 一是单工, 只能单向传输; 二是只能在父子或者兄弟进程间使用.s_pipe
: 去除了第一种限制, 为半双工,只能在父子或兄弟进程间使用,可以双向传输.name_pipe
:去除了第二种限制, 可以在许多并不相关的进程之间进行通讯.syscall
包,一个用于底层操作系统原语的接口,其中定义了所有的系统调用看这里。
os/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) |
sugoi@sugoi:~$ kill --help
kill: kill [-s 信号声明 | -n 信号编号 | -信号声明] 进程号 | 任务声明 ... 或 kill -l [信号声明]
向一个任务发送一个信号。
向以 PID 进程号或者 JOBSPEC 任务声明指定的进程发送一个以
SIGSPEC 信号声明或 SIGNUM 信号编号命名的信号。如果没有指定
SIGSPEC 或 SIGNUM,那么假定发送 SIGTERM 信号。
选项:
-s sig SIG 是信号名称
-n sig SIG 是信号编号
-l 列出信号名称;如果参数后跟 `-l'则被假设为信号编号,
而相应的信号名称会被列出
Kill 成为 shell 内建有两个理由:它允许使用任务编号而不是进程号,
并且在可以创建的进程数上限达到是允许进程被杀死。
退出状态:
返回成功,除非使用了无效的选项或者有错误发生。
sugoi@sugoi:~$ kill -l
1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP
6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1
11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM
16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP
21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ
26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR
31) SIGSYS 34) SIGRTMIN 35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3
38) SIGRTMIN+4 39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8
43) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7
58) SIGRTMAX-6 59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2
63) SIGRTMAX-1 64) SIGRTMAX
kill
与 kill -9
获取PID的方式,执行这些命令
ps/ps/pidof/pstree/top
。
kill pid
的作用是向进程号为pid
的进程发送SIGTERM
(这是kill
默认发送的信号),该信号是一个结束进程的信号且可以被应用程序捕获。若应用程序没有捕获并响应该信号的逻辑代码,则该信号的默认动作是kill
掉进程。这是终止指定进程的推荐做法。
kill -9 pid
则是向进程号为pid
的进程发送 SIGKILL
(该信号的编号为9
),SIGKILL
信号既不能被应用程序捕获,也不能被阻塞或忽略,其动作是立即结束指定进程。所以,应用程序根本无法“感知”SIGKILL
信号,在完全无准备的情况下,被收到SIGKILL
信号的操作系统给终止,在这种“暴力”情况下,应用程序完全没有释放当前占用资源的机会。
事实上,SIGKILL
信号是直接发给init
进程的,它收到该信号后,负责终止pid
指定的进程。
在某些情况下(如进程已经hang死,无法响应正常信号),就可以使用 kill -9
来结束进程。
Linux Server端的应用程序经常会长时间运行,在运行过程中,可能申请了很多系统资源,也可能保存了很多状态,在这些场景下,我们希望进程在退出前,可以释放资源或将当前状态dump到磁盘上或打印一些重要的日志,也就是希望进程优雅退出(exit gracefully)。
Go中对信号的处理主要使用os/signal
包中的两个方法:
func Notify(c chan<- os.Signal, sig ...os.Signal)
func Stop(c chan<- os.Signal)
os
包中对syscall
包中的几个常用信号进行了封装,如下:
var (
Interrupt Signal = syscall.SIGINT
Kill Signal = syscall.SIGKILL
)
package main
import (
"fmt"
"os"
"os/signal"
)
func main() {
s := &http.Server{
Addr: ":8080",
Handler: myHandler,
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
MaxHeaderBytes: 1 << 20,
}
log.Fatal(s.ListenAndServe())
c := make(chan os.Signal) // 设置发送信号通知的通道。如果在发送信号时还没有准备好接收信号,则必 使用缓冲通道,否则可能会丢失信号
signal.Notify(c) // 不将任何信号传递给通知意味着将所有信号发送到通道
signal.Notify(c, os.Interrupt) // 监听Interrupt即syscall.SIGINT信号,用户发送INTR字 (Ctrl+C)触发
fmt.Println("启动了程序")
s := <-c // 阻塞直到收到信号
fmt.Println("收到信号:", s)
ctx := context.Background()
log.Println("shut:",s.Shutdown(ctx))
}
优雅的退出一般就是根据需要监听多个信号。
在服务器不停机状态下,对正常访问流程不造成干扰和影响的程序升级方式。主要指的是服务程序的热更新,并不是 APP 和操作系统的 HotFix 技术。
互联网服务都追求服务高可用,即能提供 7x24 不间断服务,升级代码影响用户是不可以接受的。一般都追求 SLA 达标 99.99%,即一年故障时间不能超过 1 小时,而几乎每天都会进行代码升级,因此必须采用热更新。后端服务往往都是用集群承担大规模流量,代码升级往往涉及很多台机器,人工升级肯定是不现实的,需要自动化的升级方式。
Nginx进程及控制信息:
热更新流程:
func (srv *Server) Shutdown(ctx context.Context) error