在操作系统进程终止操作中,发生异常时,通常我们最多用到的是强制终止,其实系统提供了多个终止的信号,都有不同的用处。最近在写nodejs的测试程序时,因为信号的使用错误,导致出现了无法预测的问题,不得不重新学习这一部分的内容。
信号的定义
Linux系统中有许多信号,每个信号都有不同的用途。以下是常见的信号以及它们的编号和描述:
- SIGHUP(1):挂起(Hangup)
- SIGINT(2):中断(Interrupt)
- SIGQUIT(3):退出(Quit)
- SIGILL(4):非法指令(Illegal instruction)
- SIGTRAP(5):跟踪/断点(Trace/Breakpoint trap)
- SIGABRT(6):中止(Abort)
- SIGBUS(7):总线错误(Bus error)
- SIGFPE(8):浮点异常(Floating point exception)
- SIGKILL(9):杀死(Kill)
- SIGUSR1(10):用户定义信号1(User-defined signal 1)
- SIGSEGV(11):段错误(Segmentation fault)
- SIGUSR2(12):用户定义信号2(User-defined signal 2)
- SIGPIPE(13):管道破裂(Broken pipe)
- SIGALRM(14):闹钟(Alarm clock)
- SIGTERM(15):终止(Termination)
- SIGSTKFLT(16):栈故障(Stack fault)
- SIGCHLD(17):子进程状态改变(Child status has changed)
- SIGCONT(18):继续(Continue)
- SIGSTOP(19):停止(Stop)
- SIGTSTP(20):终端停止(Keyboard stop)
- SIGTTIN(21):后台读取终端(Background read from terminal)
- SIGTTOU(22):后台写入终端(Background write to terminal)
- SIGURG(23):紧急情况(Urgent condition)
- SIGXCPU(24):CPU时间限制(CPU limit exceeded)
- SIGXFSZ(25):文件大小限制(File size limit exceeded)
- SIGVTALRM(26):虚拟定时器(Virtual alarm)
- SIGPROF(27):定时器溢出(Profiling timer expired)
- SIGWINCH(28):窗口大小改变(Window size change)
- SIGIO(29):异步I/O事件(I/O now possible)
- SIGPWR(30):电源故障(Power failure)
- SIGSYS(31):非法系统调用(Bad system call)
以上信号是定义在操作系统层级上的,当系统遇到对应的错误时,会根据中断优先级提供不同的终止方式.这些信号是可编程的,也就是说用户可以借着编程语言去向cpu发送任意上述信号来终止某个进程。
# linux 系统下使用kill命令终止进程
kill -15 <pid>
# windows cmd 下
taskkill /F /PID <pid>
通常我们会用-9来kill某一个进程,-9杀死进程的效果是最好的,好比你在做某项工作,收到SIGKILL(9)信号时候,好比发生特别紧急的事情如地震、火情时,立马丢下手头工作立即终止不做任何现场保护。
而使用SIGTERM(15),它是一种请求进程正常终止的信号。当进程接收到SIGTERM信号时,它会进行清理工作并安全地退出。与SIGKILL信号不同,SIGTERM信号可以被进程捕获、处理或忽略。这允许进程在终止之前执行一些清理操作,如关闭文件、释放资源等。
SIGINT(2)它是由终端(通常是用户按下Ctrl+C组合键)发送给前台进程的中断信号。当用户在终端中按下Ctrl+C时,操作系统会发送SIGINT信号给当前正在前台运行的进程。SIGINT信号通常用于中断进程的正常执行,用户通常使用它来终止正在运行的程序。通常,接收到SIGINT信号的进程会进行清理工作,然后优雅地退出。
系统的差异性
在信号的处理上,Windows和Linux有一些不同。以下是一些常见信号以及它们在不同系统中的情况:
信号 | 数字 | Windows | Linux |
---|---|---|---|
SIGHUP | 1 | ✔ | |
SIGINT | 2 | ✔ | ✔ |
SIGQUIT | 3 | ✔ | ✔ |
SIGILL | 4 | ✔ | ✔ |
SIGTRAP | 5 | ✔ | ✔ |
SIGABRT | 6 | ✔ | ✔ |
SIGBUS | 7 | ✔ | |
SIGFPE | 8 | ✔ | ✔ |
SIGKILL | 9 | ✔ | ✔ |
SIGUSR1 | 10 | ✔ | |
SIGSEGV | 11 | ✔ | ✔ |
SIGUSR2 | 12 | ✔ | |
SIGPIPE | 13 | ✔ | ✔ |
SIGALRM | 14 | ✔ | |
SIGTERM | 15 | ✔ | ✔ |
SIGSTKFLT | 16 | ✔ | |
SIGCHLD | 17 | ✔ | |
SIGCONT | 18 | ✔ | |
SIGSTOP | 19 | ✔ | ✔ |
SIGTSTP | 20 | ✔ | ✔ |
SIGTTIN | 21 | ✔ | |
SIGTTOU | 22 | ✔ | |
SIGURG | 23 | ✔ | |
SIGXCPU | 24 | ✔ | |
SIGXFSZ | 25 | ✔ | |
SIGVTALRM | 26 | ✔ | |
SIGPROF | 27 | ✔ | |
SIGIO | 29 | ✔ | |
SIGPWR | 30 | ✔ | |
SIGSYS | 31 | ✔ | |
SIGUNUSED | 0 | ✔ |
SIGKILL信号注意问题
由于SIGKILL信号是不做后续操作,强制终止当前进程,当这个进程和某些进程有父子对应关系时,仅仅会关闭当前而不对其父或子进程做处理,有可能会出现意料之外的错误。如果要强制使用SIGKILL的话那还需要做如下的处理。
- 关闭文件和网络连接: 进程可能打开了文件、套接字或其他资源。在终止之前,需要确保这些资源被正确关闭,以防止资源泄漏或数据丢失。
- 释放内存: 如果进程分配了内存,需要确保在终止时释放所有已分配的内存,以免造成内存泄漏。
- 保存数据: 如果进程在终止时需要保存数据,例如正在进行的工作或临时文件,需要确保数据被正确保存,以防止数据丢失。
- 通知其他进程: 如果进程与其他进程通信或共享资源,可能需要向这些进程发送通知,以便它们能够处理进程终止的情况。
- 清理临时状态: 进程可能会保持一些临时状态或缓存数据,这些数据在终止时可能需要被清理。
- 关闭数据库连接或其他外部资源: 如果进程使用了外部资源,如数据库连接或其他服务,需要确保在终止时正确关闭这些连接,以避免资源泄漏或不一致的状态。
- 执行善后工作: 在一些情况下,进程可能需要执行一些额外的善后工作,例如向日志中记录终止事件或发送通知给管理员。
进程关闭操作
对于有相关进程的情况,我们需要额外操作:
- 找到所有相关的进程
- 关闭相关进程
找到所有相关的进程
找到进程最方便的方式就是使用系统的进程管理器,如果是在脚本中操作,可以尝试下面操作
-
Unix:
# 找到相关的pid,需要下载并安装pstree pstree -p pid <pid>
# 或者根据名字查找, 如node pgrep <name>
-
Windows:
# 使用tasklist + findstr查找对应名字的pid tasklist | findstr "<进程名字>"
关闭相关进程
- Unix:
# 把上一步找到的所有进程给关闭 kill -9 <pid> # 或者根据名字关闭,使用pkill pkill -9 <name>
- Windows:
# windows虽然不支持关闭信号,但可以使用/T支持直接关闭进程树 taskkill /T /F /PID <pid> # 根据名字关闭 taskkill /T /F /IM <name>
关闭端口
如果某些进程开了端口,-9信号还需要通用关闭打开的端口
- Unix:
kill -9 $(lsof -t -i:<端口号>)
- Windows:
# 使用netstat 找出要关闭的端口的pid netstat -ano | findstr :"<端口号>" # 关闭端口 taskkill /T /F /PID <pid> # 或者使用一条指令完成 for /f "tokens=5" %%i in ('netstat -ano ^| findstr :"<端口号>"') do (taskkill /T /F /PID %%i)