linux信号之基本概念


信号概念:


信号是软件中断。

首先每个信号都有一个名字,这些名字都以3个字符SIG开头。例如,SIGARBT是夭折信号,当进程调用abort函数时产生这种信号。SIGALRM是闹钟信号,由alarm函数设定的定时器超时后产生此信号。Linux3.2.0支持31种信号(不同的系统支持的信号数不一样)。


产生信号的条件:

1.当用户按某些终端键时,引发终端产生的信号,比如(Ctrl+C)产生中断信号(SIGINT)。

2.硬件异常产生信号:除数为0、无效的内存引用等。这些条件通常由硬件检测到,并通知内核。然后内核为该条件发生时正在运行的进程产生适当的信号。例如,对执行一个无效内存引用的进程产生SIGSEGV信号。

3.进程调用kill(2)函数可将任意信号发生给另一个进程或进程组。自然,对此有所限制:接收信号进程和发送信号进程的所有者必须相同,或者发送信号进程的所有者必须是超级用户。

4.用户可用kill(1)命令将信号发送给其他进程。此命令只是kill(2)函数的接口。常用此命令终止一个失控的后台进程。

5.当检测到某种软件条件已经发生,并应将其通知有关进程时也产生信号。 这里指的不是硬件产生条件(如除以0),而是软件条件。例如前面提到的SIGALRM闹钟信号。


在某个信号出现时,可以告诉内核按下列3种方式之一进行处理,我们称之为信号的处理或者信号相关的动作:

1.忽略此信号。大多数信号都可使用这种方式进行处理,但有两种信号却绝不能被忽略。它们是SIGKILL和SIGSTOP。这两种信号不能被忽略的原因是:它们向内核和超级用户提供了使进程终止或停止的可靠方法。

2.捕捉信号。为了做到这一点,要通知内核在某种信号发生时,调用一个用户函数。在用户函数中,可执行用户希望对这种事情进行的处理。例如捕捉到SIGCHLD信号,则表示一个子进程已经终止,所以此信号的捕捉函数可以调用waitpid以取得该子进程的进程ID以及它的终止状态。防止僵尸进程的fork编程

3.执行系统默认动作。图10-1给出了对每一种信号的系统默认动作。注意:对大多数信号的系统默认动作是终止该进程。在系统默认动作列,"终止+core"表示进程当前工作目录的core文件中复制了该进程的内存映像,在Linux中,core文件名通过/proc/sys/kernel/core_pattern进行配置,ulimit -c控制了core文件的大小。




信号机制最简单的接口是signal函数:

SIGNAL(2)                  Linux Programmer's Manual                 SIGNAL(2)

NAME
       signal - ANSI C signal handling

SYNOPSIS
       #include <signal.h>

       typedef void (*sighandler_t)(int);

       sighandler_t signal(int signum, sighandler_t handler);

返回值:若成功,返回以前的信号处理配置;若出错,返回SIG_ERR


例子1,给出了一个简单的信号处理程序,它捕捉两个用户定义的信号并打印信号编号:

#include <signal.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>

/* 信号处理函数 */
void sig_usr(int signum)
{
    if (signum == SIGUSR1)
        printf("recvived SIGUSR1\n");
    else if (signum == SIGUSR2)
        printf("recvived SIGUSR2\n");
    else {
        printf("recvived signal %d\n", signum);
        exit(1);
    }
}

int main()
{
    if (signal(SIGUSR1, sig_usr) == SIG_ERR) {
        perror("can't catch SIGUSR1");
        exit(1);
    }
    if (signal(SIGUSR2, sig_usr) == SIG_ERR) {
        perror("can't catch SIGUSR2");
        exit(1);
    }
    for ( ; ; )
        pause(); /* 调用进程接到一信号前挂起 */

    return 0;
}


编译运行结果(使用kill(1)命令向进程发送信号):


注:kill 3491产生SIGTERM信号,默认动作是终止进程。


例子2,捕获无效指针引起的段错误

#include <stdio.h>
#include <signal.h>
#include <stdlib.h>

void sig_usr(int signum)
{
    if (signum == SIGSEGV) {
        printf("recvived SIGSEGV\n");
        exit(1);
    }
}

int main()
{
    if (signal(SIGSEGV, sig_usr) == SIG_ERR) {
        perror("can't catch SIGSEGV");
        exit(1);
    }

    char *str = "";
    str[100] = 'a';

    printf("hello world\n");

    return 0;
}

编译运行:



深入:

1.程序启动

当执行一个程序时,所有信号的状态都是系统默认或忽略。通常所有信号都被设置为它们的默认动作,除非调用exec的进程忽略该信号。

2.进程创建

当一个进程调用fork时,其子进程继承父进程的信号处理方式。因为子进程在开始时复制了父进程内存映像,所以信号捕捉函数的地址在子进程是有意义的。


参考:《unix环境高级编程》·第三版


End;


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值