Linux学习笔记:信号基础

信号简介:

信号 (signal) 是一种软件中断,它提供了一种处理异步事件的方法,也是进程间唯一的异步通信方式。在Linux系统中,根据POSIX标准扩展后的信号机制,不仅可以用来通知某进程发生了什么事件,还可以给进程传递数据。


1>  信号的来源:

信号的来源有很多种方式,根据其产生条件的不同可分为硬件与软件两种方式:

硬件方式:

  • 用户在终端上按下某些键时,将产生信号。 如按下 <Ctrl+c> 组合键将产生一个 SIGINT 信号。
  • 硬件异常产生的信号:除数为0、无效的存储访问等。这些事件通常由硬件 (如  CPU) 检测到并将其通知给Linux系统内核,然后内核生成相应信号并把信号发送给该事件发生时正在运行的程序。

软件方式:

  • 用户在终端下调用 kill 命令向进程发送的任意信号。
  • 进程调用 kill() 或 sigqueue() 函数发送的信号。
  • 当检测到某种软件条件已经具备时发出的信号。如由 alarm() 和 setitimer() 函数设置的定时器超时生成的SIGALRM信号。


2>  信号的种类:

在 Shell 下输入 kill -l 命令可显示Linux系统支持的所有信号。如下图:

信号的值在 signal.h 中定义。下面介绍几个比较重要的信号:

  1. SIGHUP:当用户退出 Shell 的时后,由该 Shell 启动的所有进程将接收到这个信号。默认动作为终止进程。
  2. SIGINT:当用户按下 <Ctrl+c> 组合键时,用户终端向正在运行中的由该终端启动的程序发出磁信号。默认动作为终止程序。
  3. SIGQUIT:当与用户按下 <Ctrl+\> 组合键时产生该信号,用户终端向正在运行中的由该终端启动的程序发出此信号。默认动作为终止进程并生成 core 文件。

34) SIGMIN 到 64) SIGMAX 这些信号都是Linux中的可靠信号 (也称实时信号) ,它们没有固定的含义,可以由用户自由使用。所有的可靠信号的默认动作都是终止进程。(实时信号的前3个被Linux的线程机制使用)


可靠信号与不可靠信号:

在Linux系统中,信号的可靠性是指信号是否会丢失,即该信号是否支持排队。

简单来说,就是当多个相同的信号同时到达时,进程只能对其中一个进行响应,而将此种信号加入进程的信号屏蔽集中,用以阻塞在同一时间到来的其他信号,如果这种信号是不可靠信号,则只能被阻塞一个,其他的将被丢弃,如果这种信号是可靠信号,则将到来的所有相同信号全部阻塞并形成队列。

当第一个信号处理完时,进程将阻塞的信号移出进程的信号屏蔽集,并从阻塞的信号中取出一个,重复前面的处理信号操作,此时不可靠信号只阻塞了一个,其他的信号将被丢弃,而可靠信号则排成队列依次等待处理,所以不会丢失信号。

Linux中 1)SIGHUP 到 31)SIGSYS 之间的信号都是继承自 unix 系统,是不可靠信号。


信号未决:

当引发信号的事件发生时,内核将产生一个信号。信号产生后,内核通常会在其维护的进程表中设置某种形式的标志。当内核设置了这种标志,就称之为内核向一个进程递送了一个信号。在信号产生与信号被进程处理之间的时间间隔,就称之为信号未决 (pending) 。


信号操作:

1>  进程对信号的响应:

当信号产生时,用户可以设置进程以以下3种方式之一对信号作出响应:

  • 捕捉信号:对于要捕捉的信号,可以为其指定信号处理函数,信号产生时该函数自动被调用,在信号处理函数内部实现对该信号的处理。
  • 忽略信号:大多数信号都可以按此方式进行处理,但需要注意的是,SIGKILL 和 SIGSTOP 这两个信号不能被忽略,同时这两个信号也不能被捕捉和阻塞。如果忽略某些由硬件异常导致的信号,那么进程的行为是不可预测的。
  • 按照系统默认方式处理:大多数信号的默认操作是终止进程,且所有可靠信号的默认操作都是终止进程。

2>  信号的捕捉与处理:

Linux系统中对信号的捕捉主要由 signal() 和 sigaction() 函数来完成,其中 sigaction() 函数功能更多,所以此处只介绍sigaction() 函数。

函数原型:

#include<signal.h>
int  sigaction (int signum,  const  struct  sigaction  *act,  struct sigaction  *oldact)  ;
函数的第一个参数是信号编号,第二、三参数是一个结构体型指针

struct  sigaction {
                 void  (*sa_handler)  (int) ;
                 void  (*sa_sigaction)  (int,  siginfo_t *,  void *) ;
                 sigset_t  sa_mask ;
                 int  sa_flags ;
                 void  (*sa_restorer) (void)  ;
}
其中数据成员 sa_reastorer 已经作废不再使用。

sa_handler 和 sa_sigaction 在某些体系结构中被定义为共用体,即某一时刻只有一个值有效。其中 sa_handler 可以是常数 SIG_DFL、SIG_ING、或者是一个信号处理函数的函数名,此信号处理函数只有一个参数,即信号编号,无返回值。sa_sigaction 也是用来指定信号 signum 的处理函数,不过它带有三个参数,第一个是信号编号,第二个是指向siginfo_t 结构的指针,此参数可以用来传递数据,第三个参数是一个指向任何类型的指针,一般不使用。

sa_mask 声明了一个信号集,在调用信号捕捉函数之前,该信号集会增加到进程的信号屏蔽集中,新的信号屏蔽集将自动包括正在处理的信号 (sa_flags 未指定 SA_NODEFER 或SA_NOMASK),当从信号捕捉函数返回时,进程的信号屏蔽集将恢复为原来的值。

sa_falgs 用来说信号处理的其他相关操作,其中当 sa_flags 设置了 SA_SIGINFO 标志时,信号处理函数将由三参数的 sa_sigaction 指定而不是 sa_handler 。


信号捕捉函数用于对指定信号安装处理函数。

3>  信号的发送:

信号的发送主要由函数 kill()、 raise()、 sigqueue()、 alarm()、 setitimer()、 abort() 来完成。

这些函数的原型如下:

#include <sys/types.h>
#include <signal.h>
int kill (pid_t, int sig) ;
#include <signal.h>
int sigqueue (pid_t pid, int sig, const union sigval value) ;
#include <unistd.h>
unsigned int alarm (unsigned int seconds) ;
#include <sys/time.h> 
int  getitimer (int which, struct itimerval *value) ;
int  setitimer (int which, const struct itimerval *value, struct itimerval *ovalue) ;
kill() 函数根据进程号与信号编号来完成信号的发送, sigqueue() 函数支持信号携带参数,alarm() 函数可以用来设置定时器,定时器超时将产生 SIGALRM 信号给调用者程序,setitimer() 函数也是用来设置定时器的,且它与alarm() 函数使用同一个定时器,二者会互相影响,setitimer() 比 alarm()具有更多功能,且定时精度更高。getitimer() 函数用来获取which参数指定类型的定时器初始值。

4>  信号的屏蔽:

信号屏蔽又称为信号阻塞,它规定了当前阻塞而不能第送给该进程的信号集,此信号集称为信号屏蔽集,又称信号掩码。

POSIX标准定义了数据类型 sigset_t 来表示信号集,并定义了一系列函数来操作信号集,函数原型如下:

#include <signal.h>
int sigemptyset (sigse_t *set) ;  //初始化一个信号集,使其为空集
int sigfillset (sigset_t *set) ;  //初始化一个信号集,使其包括所有信号
int sigaddset (sigset_t *set, int signum) ; //向set参数指定的信号集中加入signum指定的信号
int sigdelset (sigset_t *set, int signum) ; //向set参数指定的信号集中删去signum指定的信号
int sigsmember (const sigset_t *set, int signum) ;//用来测试signum信号是否被包括在set信号集中
关于信号阻塞的函数:

#include <signal.h>
int sigprocmask (int how, const sigset_t *set, sigset_t *oldset) ;
int sigpending (sigset_t *set) ;
int sigsuspend (const sigset_t *mask) ;
sigprocmask() 函数可以检测或更改进程的信号屏蔽码。

sigpending() 函数可以获取进程因被阻塞而不能递送和当前未决的信号集。

sigsuspend() 函数将进程的信号屏蔽码设为 mask ,然后与 pause 函数一样等待信号的发生并执行完信号处理函数,信号处理函数执行完之后再把进程的信号屏蔽码设置为原来的,然后sigsuspend() 函数才返回。









  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值