Linux系统下程序异常如何优雅的退出

本文详细介绍了Linux系统中信号的生成和捕获,包括kill命令用于发送信号,如SIGINT、SIGTERM等,以及signal函数用于处理信号。当进程收到信号时,可以采取预设动作或自定义函数进行优雅退出,释放资源。信号在进程间通信和异常处理中起到关键作用。
摘要由CSDN通过智能技术生成

        当我们想强制结束一个程序的时候,我们通常会给它发送一个信号然后该进程捕捉到信号,再然后该进程执行一定操作最终被终止。信号是UNIX和Linux系统响应某些条件而产生的一个事件,接收到该信号的进程会相应地采取一些行动。通常信号是由一个错误产生的。但它们还可以作为进程间通信或修改行为的一种方式,明确地由一个进程发送给另一个进程。

        一个信号的产生叫生成,接收到一个信号叫捕获,生成信号的主要函数有:kill()、raise()、 sigqueue()、alarm()、setitimer()以及abort(),最常用的是kill;捕获信号的主要函数有:signal(),sigprocmask() ,sigpending(),sigsuspend(), sigemptyset(),最常用的是signal。下面重点讲一下kill和signal的使用。

1.信号的生成kill

        在Linux系统下想结束某个进程时,用的最多的指令就是kill或者killall,这就是常用的信号生成方式,在程序运行时ctrl+c中断进程,会产生信号SIGINT。在Linux系统对应kill命令也有kill函数,c语言引入头文件sys/types.h和signal.h之后就可以使用。kill 送出一个特定的信号 (signal) 给行程 id 为 pid 的行程根据该信号而做特定的动作,若没有指定,预设是送出终止 (SIGTERM) 的信号,例如kill -9 pid就是强制杀死进程号为pid的进程。在这里有人就会问为什么要用-9呢,-9只是kill发送信号的一个编号,可以输入kill –l把kill所有可用的信号名称列出来。

kill -9 意思是把SIGKILL信号发送给进程号为pid的进程。

其中前面31个信号为不可靠信号(非实时的,可能会出现信号的丢失),后面的信号为可靠信号(实时的real_time,对信号排队,不会丢失)。

1) SIGHUP (挂起) 当运行进程的用户注销时通知该进程,使进程终止

2) SIGINT (中断) 当用户按下时,通知前台进程组终止进程

3) SIGQUIT (退出) 用户按下或时通知进程,使进程终止

4) SIGILL (非法指令) 执行了非法指令,如可执行文件本身出现错误、试图执行数据段、堆栈溢出

5) SIGTRAP 由断点指令或其它trap指令产生. 由debugger使用

6) SIGABRT (异常中止) 调用abort函数生成的信号

7) SIGBUS 非法地址, 包括内存地址对齐(alignment)出错. eg: 访问一个四个字长的整数, 但其地址不是4的倍数.

8) SIGFPE (算术异常) 发生致命算术运算错误,包括浮点运算错误、溢出及除数为0.

9) SIGKILL (确认杀死) 当用户通过kill -9命令向进程发送信号时,可靠的终止进程

10) SIGUSR1 用户使用

11) SIGSEGV (段越界) 当进程尝试访问不属于自己的内存空间导致内存错误时,终止进程

12) SIGUSR2 用户使用

13) SIGPIPE 写至无读进程的管道, 或者Socket通信SOCT_STREAM的读进程已经终止,而再写入。

14) SIGALRM (超时) alarm函数使用该信号,时钟定时器超时响应

15) SIGTERM (软中断) 使用不带参数的kill命令时终止进程

17) SIGCHLD (子进程结束) 当子进程终止时通知父进程

18) SIGCONT (暂停进程继续) 让一个停止(stopped)的进程继续执行. 本信号不能被阻塞.

19) SIGSTOP (停止) 作业控制信号,暂停停止(stopped)进程的执行. 本信号不能被阻塞, 处理或忽略.

20) SIGTSTP (暂停/停止) 交互式停止信号, Ctrl-Z 发出这个信号

21) SIGTTIN 当后台作业要从用户终端读数据时, 终端驱动程序产生SIGTTIN信号

22) SIGTTOU 当后台作业要往用户终端写数据时, 终端驱动程序产生SIGTTOU信号

23) SIGURG 有"紧急"数据或网络上带外数据到达socket时产生.

24) SIGXCPU 超过CPU时间资源限制. 这个限制可以由getrlimit/setrlimit来读取/改变。

25) SIGXFSZ 当进程企图扩大文件以至于超过文件大小资源限制。

26) SIGVTALRM 虚拟时钟信号. 类似于SIGALRM, 但是计算的是该进程占用的CPU时间.

27) SIGPROF (梗概时间超时) setitimer(2)函数设置的梗概统计间隔计时器(profiling interval timer)

28) SIGWINCH 窗口大小改变时发出.

29) SIGIO(异步I/O) 文件描述符准备就绪, 可以开始进行输入/输出操作.

30) SIGPWR 电源失效/重启动

31) SIGSYS 非法的系统调用。

程序不可捕获、阻塞或忽略的信号有:SIGKILL,SIGSTOP。
不能恢复至默认动作的信号有:SIGILL,SIGTRAP。
默认会导致进程退出的信号有:SIGALRM,SIGHUP,SIGINT,SIGKILL,SIGPIPE,SIGPOLL,SIGPROF,SIGSYS,SIGTERM,SIGUSR1,SIGUSR2,SIGVTALRM。
默认会导致进程停止的信号有:SIGSTOP,SIGTSTP,SIGTTIN,SIGTTOU。
默认进程忽略的信号有:SIGCHLD,SIGPWR,SIGURG,SIGWINCH。

2.信号的捕获signal

signal的函数原型是:

        typedef void (*sig_t)( int );

        sig_t signal(int signum,sig_t handler);

        第一个参数signum指明了所要处理的信号类型,它可以取除了SIGKILL和SIGSTOP外的任何一种信号。

        第二个参数handler描述了与信号关联的动作,它可以取以下三种值:

(1)一个无返回值的函数地址

        此函数必须在signal()被调用前申明,handler中为这个函数的名字。当接收到一个类型为signum的信号时,就执行handler 所指定的函数。这个函数应有如下形式的定义:

void func(int sig);

(2)SIG_IGN

这个符号表示忽略该信号,执行了相应的signal()调用后,进程会忽略类型为sig的信号。

(3)SIG_DFL

        这个符号表示恢复系统对信号的默认处理。

        最常用的捕获signal场景是当signal到来时,程序运行某函数,函数由你自己指定,此时函数一般是做一些资源的释放,比如socket关闭,内存释放,文件关闭,有人会说了我不释放那些资源也可以退出程序,你说的没错,你不做任何处理一样可以退出程序,但是有时会影响其他共享或者交互进程,比如作为服务端异常退出时不主动关闭socket,可能会导致客户端不知道断开连接,导致某些数据丢失或者逻辑异常,所以在程序正常和异常退出时最好都做到优雅,这里除了SIGKILL,SIGSTOP除外,因为这些信号无法捕获想优雅退出不给机会。

代码示例:

#include <stdio.h>

#include <signal.h>

void server_uninit()

{

        //释放所有资源

        printf("close all socket and file\n");

}

void func1()

{

        printf("SIGINT \n");

        server_uninit();

        printf("可以优雅的退出程序了 \n");

        exit(0);

}

void func2()

{

        printf("SIGTERM \n");

        server_uninit();

        printf("可以优雅的退出程序了 \n");

        exit(0);

}

void func3()

{

        printf("SIGSEGV \n");

        server_uninit();

        printf("可以优雅的退出程序了 \n");

        exit(0);

}

void func0()

{

        printf("SIGKILL \n");

        server_uninit();

        printf("可以优雅的退出程序了 \n");

        exit(0);

}

int main()

{

        signal (SIGINT, func1);

        signal(SIGKILL, func0);

        signal(SIGSEGV, func3);

        signal(SIGTERM, func2);

        int i = 0;

        while(1)

        {

                printf("i %d\n",i++);

                sleep(2);

         }        

        return 0;

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值