Linux信号处理

信号处理
基本概念:
1、中断
当程序接收信息后中止当前执行的程序,转而执行其它任务,等其它任务执行完成后再返回,这种执行模式叫中断,分为硬件中断和软件中断。
2、信号
是一种软件中断,由操作系统发出,程序接收到后会执行相应的操作
3、常见的信号
SIGINT(2) Ctrl+c 终止
SIGQUIT(3) Ctrl+\ 终止+core
SIGFPE(8) 除0 终止+core
SIGKILL(9) 终止信号 终止
SIGSEGV(11) 非法内存访问 终止+core

    注意:kill -l 查看Linux所有信号
4、不可靠信号和可靠信号
    建立在早期的信号处理机制上的信号(1-31)是不可靠的信号
    不支持排队,有丢失的可能,同一个信号如果产生多次,进程可能只接受到一次
    建立在新信号处理机制上的信号(34-64)是可靠的信号
    支持排队,不会丢失
5、信号来源
    硬件异常:除0、无效的内存访问、未定义的指令、总线错误
    软件异常:通过一些命令、函数产生的信号
6、信号处理方式
    1、忽略
    2、终止进程
    3、终止进程并产生core文件
    4、捕获信号并处理(当信号发送前,先向内核注册一个函数,当信号发生时让系统自动地执行该函数)

信号捕获:
typedef void (*sighandler_t)(int);
功能:定义信号处理函数的格式

sighandler_t signal(int signum, sighandler_t handler);
功能:向内核注册一个信号处理函数
signum:信号的编号
handler:函数指针
    还可以用以下参数:
        SIG_IGN 忽略
        SIG_DFL 按照默认方式
返回值:之前的信号处理方式

注意:
    有些系统通过signal注册的函数只能执行一次,如果想要持续有效,可以在信号处理函数最后再注册一次
    
    子进程会继承父进程的信号处理方式,如果通过exce系列函数来创建子进程,子进程会恢复默认的信号处理方式

    如果我们捕获并处理段错误或者算术异常信号可能产生死循环,因为信号处理完后会返回到信号产生的代码处处理,
    就可能导致死循环,正确的段错误和算术异常的处理方式应该先备份数据并结束进程

练习1:测试一下1-31中哪个信号不能被捕获处理 kill
    SIGKILL(9)  SIGSTOP(19)

信号的发送:
键盘:
Ctrl+c
Ctrl+
Ctrl+z 暂停\挂起 (fg唤醒)
错误:
除0
非法访问内存
硬件故障
命令:
kill -信号编号 进程号
killall 信号编号 进程名
函数:
int kill(pid_t pid, int sig);
功能:向指定的进程发送信号

    int raise(int sig);
    功能:给进程自己发送信号

    void abort(void);
    功能:给进程自己发送SIGABRT信号

    unsigned int alarm(unsigned int seconds);
    功能:让内核在secondes后向进程发送SIGALRM信号
    返回值:上一个alarm剩余的时间
    注意:如果再次调用alarm会覆盖之前的设置,而不会产生两个闹钟信号

进程休眠信号:
int pause(void);
功能:让调用者进入休眠状态,直到进程遇到任何信号
返回值:要么一直休眠不返回,要么结束休眠返回-1
相当于没有时间限制的sleep

unsigned int sleep(unsigned int seconds);
功能:让调用者进入指定秒数的休眠状态,当遇到信号时也会提前返回
返回值:剩余的休眠时间

信号集与信号阻塞:
信号集:是一种数据类型,可以存储多个信号
sigset_t 128位的二进制数,每一位都代表一个信号
相关函数:
int sigemptyset(sigset_t *set);
功能:清空信号集

    int sigfillset(sigset_t *set);
    功能:填满信号集

    int sigaddset(sigset_t *set, int signum);
    功能:向信号集添加信号

    int sigdelset(sigset_t *set, int signum);
    功能:从信号集中删除信号

    int sigismember(const sigset_t *set, int signum);
    功能:测试信号集中是否有某个信号
    返回值:
        0   不存在
        1   存在
        -1  非法信号

信号阻塞:
    当程序执行一些特殊操作时不适合处理信号,此时可以让内核先屏蔽信号,等这些操作结束后再发送该信号
    
    当信号产生时,内核会在其维护的信号表中为进程设置一个与信号对应的一个标记,该过程叫做递送。
    
    从信号产生到完成递送有个时间间隔,处于这个间隔的信号状态叫做未决
    信号屏蔽就是让信号先处于未决状态,暂停递送,当屏蔽解除时继续递送
    
    每个进程都有一个信号集,专门用于储存要被屏蔽的信号

int sigprocmask(int how,const sigset_t *set,sigset_t *old‐set);
功能:设置要屏蔽的信号,这些信号存储在信号集里面
how:信号屏蔽的方式
    SIG_BLOCK   把set中的信号添加到要屏蔽的信号集中
    SIG_UNBLOCK 从信号集中删除set的信号
    SIG_SETMASK 用set替换之前的信号集 
set:要设置的set信号集
old:获取旧的屏蔽信号集

带附加信息的信号处理:
int sigaction(int signum,const struct sigaction *act,struct sigaction *oldact);
功能:向内核注册一个信号的处理方式
signum:要捕获的信号
act:信号处理方式函数
oldact:获取旧的信号处理方式
struct sigaction {
void (*sa_handler)(int);//不带附加数据信号处理函数
void (*sa_sigaction)(int, siginfo_t *, void *);//带附加数据信号处理函数
sigset_t sa_mask;//信号屏蔽集
//在信号函数执行过程中,会默认屏蔽当前信号,如果想要屏蔽其他信号可以向sa_mask添加信号
int sa_flags;//信号处理标志
SA_NODEFER 在信号处理过程中不要屏蔽当前信号
SA_RESETHAND 该信号处理完成后,会还原为默认的处理方式
SA_RESTART
SA_SIGINFO 使用函数2来处理信号
void (*sa_restorer)(void);// 保留,NULL
};

siginfo_t {
int      si_signo;    /* Signal number */
int      si_errno;    /* An errno value */
int      si_code;     /* Signal code */
int      si_trapno;   /* Trap number that caused
                        hardware-generated signal
                        (unused on most architectures) */
pid_t    si_pid;      /* Sending process ID */
uid_t    si_uid;      /* Real user ID of sending process */
int      si_status;   /* Exit value or signal */
clock_t  si_utime;    /* User time consumed */
clock_t  si_stime;    /* System time consumed */
sigval_t si_value;    /* Signal value */
int      si_int;      /* POSIX.1b signal */
void    *si_ptr;      /* POSIX.1b signal */
int      si_overrun;  /* Timer overrun count; POSIX.1b timers */
int      si_timerid;  /* Timer ID; POSIX.1b timers */
void    *si_addr;     /* Memory location which caused fault */
long     si_band;     /* Band event (was int in
                        glibc 2.3.2 and earlier) */
int      si_fd;       /* File descriptor */
short    si_addr_lsb; /* Least significant bit of address
                        (since kernel 2.6.32) */

}

int sigqueue(pid_t pid, int sig, const union sigval value);
功能:向指定进程发送信号并附加信息
union sigval {
           int   sival_int; //整数
           void *sival_ptr; //指针
       };

定时器:
int getitimer(int which, struct itimerval *curr_value);
功能:获取当前的定时方案
which:
ITIMER_REAL 真实计时器 程序运行总时间
ITIMER_VIRTUAL 虚拟计时器 用户态的运行时间
ITIMER_PROF 实际计时器 用户态+内核态运行时间

    真实计时器 = 实际计时器 + 休眠时间

struct itimerval {
           struct timeval it_interval; /* next value */
           //每次时钟信号产生的时间间隔
           struct timeval it_value;    /* current value */
           //第一次产生时钟信号的时间
       };

       struct timeval {
           long tv_sec;                /* seconds */
           long tv_usec;               /* microseconds */
       };


int setitimer(int which, const struct itimerval *new_value,
                 struct itimerval *old_value);
功能:设置新的定时方案
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值