《UNIX环境高级编程》笔记——信号

信号概念

    信号是软件终端,它提供了一种异步处理事件的方法。每个信号都有一个名字,这些名字都是以三个字符SIG开头。

产生终端信号的条件:

1. 当用户按某些终端键时,产生信号。

2. 硬件异常产生信号:除数为0、无效的存储访问等等。

3. 进程用kill函数可将信号发送给另一个进程或进程组用户可用kill命令将信号发送给其他进程。

4. 当检测到某种软件条件已经发生,并将其通知有关进程时也产生信号。

进程对信号的响应

进程可以通过三种方式来响应一个信号:

1. 忽略信号,即对信号不做任何处理,其中,有两个信号不能忽略:SIGKILLSIGSTOP

2. 捕捉信号。定义信号处理函数,当信号发生时,执行相应的处理函数;

3. 执行缺省操作

信号的分类

非可靠信号

   早期unix下的不可靠信号主要指的是进程可能对信号做出错误的反应以及信号可能丢失。
可靠信号

   信号值位于SIGRTMINSIGRTMAX之间的信号都是可靠信号,可靠信号克服了信号可能丢失的问题。
   信号的可靠与不可靠只与信号值有关,与信号的发送及安装函数无关。当然也可以称为实时信号或者非实时信号,非实时信号都不支持排队,都是不可靠信号;实时信号都支持排队,都是可靠信号。

信号的生命周期

   从信号发送到信号处理函数的执行完毕。对于一个完整的信号生命周期(从信号发送到相应的处理函数执行完毕)来说,可以分为三个重要的阶段,这三个阶段由四个重要事件来刻画:1)信号产生;2)信号在进程中注册完毕;3)信号在进程中的注销完毕;4)信号处理函数执行完毕。相邻两个事件的时间间隔构成信号生命周期的一个阶段。
    当一个实时信号发送给一个进程时,不管该信号是否已经在进程中注册,都会被再注册一次,因此,信号不会丢失,因此,实时信号又叫做"可靠信号"。这意味着同一个实时信号可以在同一个进程的未决信号信息链中占有多个sigqueue结构(进程每收到一个实时信号,都会为它分配一个结构来登记该信号信息,并把该结构添加在未决信号链尾,即所有诞生的实时信号都会在目标进程中注册);
    当一个非实时信号发送给一个进程时,如果该信号已经在进程中注册,则该信号将被丢弃,造成信号丢失。因此,非实时信号又叫做"不可靠信号"。这意味着同一个非实时信号在进程的未决信号信息链中,至多占有一个sigqueue结构(一个非实时信号产生后,1如果发现相同的信号已经在目标结构中注册,则不再注册,对于进程来说,相当于不知道本次信号发生,信号丢失;2如果进程的未决信号中没有相同信号,则在进程中注册自己)。
需要注意的要点是:
1)信号注册与否,与发送信号的函数(如kill()sigqueue()等)以及信号安装函数(signal()sigaction())无关,只与信号值有关(信号值小于SIGRTMIN的信号最多只注册一次,信号值在SIGRTMINSIGRTMAX之间的信号,只要被进程接收到就被注册)。
2)在信号被注销到相应的信号处理函数执行完毕这段时间内,如果进程又收到同一信号多次,则对实时信号来说,每一次都会在进程中注册;而对于非实时信号来说,无论收到多少次信号,都会视为只收到一个信号,只在进程中注册一次。

函数详细介绍

 信号的发送
发送信号的主要函数有:kill(),raise(),sigqueue(),alarm(),setitimer()以及abort()
1int kill(pid_t pid,int signo)
参数pid的值 信号的接收进程 
pid>0        进程IDpid的进程 
pid=0        同一个进程组的进程 
pid<0 pid!=-1  进程组ID为 -pid的所有进程 
pid=-1        除发送进程自身外,所有进程ID大于1的进程 
Sinno是信号值,当为0时(即空信号),实际不发送任何信号,但照常进行错误检查,因此,可用于检查目标进程是否存在,
以及当前进程是否具有向目标发送信号的权限(root权限的进程可以向任何进程发送信号,非root权限的进程只能向属于同一个session或者同一个用户的进程发送信号)。
kill最常用于pid>0时的信号发送,调用成功返回 0; 否则,返回 -1

2int raise(int signo)
允许向进程自身发送信号。
3int sigqueue(pid_t pid, int sig, const union sigval val)
   调用成功返回 0;否则,返回 -1
   sigqueue()是比较新的发送信号系统调用,主要是针对实时信号提出的(当然也支持前32种),支持信号带有参数,与函数sigaction()配合使用。
   sigqueue的第一个参数是指定接收信号的进程ID,第二个参数确定即将发送的信号,第三个参数是一个联合数据结构union sigval,指定了信号传递的参数,即通常所说的4字节值。

typedef union sigval 
{
  int sival_int;
  void *sival_ptr;
}sigval_t;
    sigqueue() kill() 传递了更多的附加信息,但 sigqueue() 只能向一个进程发送信号,而不能发送信号给一个进程组。如果 sig 0 ,将会执行错误检查,但实际上不发送任何信号, 0 值信号可用于检查 pid 的有效性以及当前进程是否有权限向目标进程发送信号。
在调用 sigqueue 时, sigval_t 指定的信息会拷贝到 3 参数信号处理函数( 3 参数信号处理函数指的是信号处理函数由 sigaction 安装,并设定了 sa_sigaction 指针,稍后将阐述)的 siginfo_t 结构中,这样信号处理函数就可以处理这些信息了。由于 sigqueue 系统调用支持发送带参数信号,所以比 kill() 系统调用的功能要灵活和强大得多。
    注: sigqueue() 发送非实时信号时,第三个参数包含的信息仍然能够传递给信号处理函数;  sigqueue() 发送非实时信号时,仍然不支持排队,即在信号处理函数执行过程中到来的所有相同信号,都被合并为一个信号。
4 unsigned int alarm(unsigned int seconds)
   专门为 SIGALRM 信号而设,在指定的时间 seconds 秒后,将向进程本身发送 SIGALRM 信号,又称为闹钟时间。进程调用 alarm 后,任何以前的 alarm() 调用都将无效。如果参数 seconds 为零,那么进程内将不再包含任何闹钟时间。
   函数返回是这样的,如果调用 alarm ()前,进程中已经设置了闹钟时间,则返回上一个闹钟时间的剩余时间,否则返回 0
5 int setitimer(int which, const struct itimerval *value, struct itimerval *ovalue));
setitimer() alarm 功能强大,支持 3 种类型的定时器:
ITIMER_REAL : 设定绝对时间;经过指定的时间后,内核将发送 SIGALRM 信号给本进程;  
ITIMER_VIRTUAL  设定程序执行时间;经过指定的时间后,内核将发送 SIGVTALRM 信号给本进程;  
ITIMER_PROF  设定进程执行以及内核因本进程而消耗的时间和,经过指定的时间后,内核将发送 ITIMER_VIRTUAL 信号给本进程;  
Setitimer() 第一个参数 which 指定定时器类型(上面三种之一);第二个参数是结构 itimerval 的一个实例,结构 itimerval
结构 itimerval
struct itimerval 
{
struct timeval it_interval; /* next value */
struct timeval it_value; /* current value */
};

struct timeval 
{
long tv_sec; /* seconds */
long tv_usec; /* microseconds */
};
第三个参数可不做处理。
Setitimer() 调用成功返回 0 ,否则返回 -1
6、   void abort(void);
    向进程发送 SIGABORT 信号,默认情况下进程会异常退出,当然可定义自己的信号处理函数。
   即使 SIGABORT 被进程设置为阻塞信号,调用 abort() 后, SIGABORT 仍然能被进程接收。该函数无返回值。

 信号的捕获与安装(设置信号关联动作)
   如果进程要处理某一信号,那么就要在进程中安装该信号。安装信号主要用来确定信号值及进程针对该信号值的动作之间的映射关系,即进程将要处理哪个信号;该信号被传递给进程时,将执行何种操作。
   主要有两个函数实现信号的安装:signal()sigaction()。其中signal()在可靠信号系统调用的基础上实现是库函数。它只有两个参数,不支持信号传递信息,主要是用于前32种非实时信号的安装;而sigaction()是较新的函数(由两个系统调用实现:sys_signal以及sys_rt_sigaction),有三个参数,支持信号传递信息,主要用来与sigqueue() 系统调用配合使用,当然,sigaction()同样支持非实时信号的安装。sigaction()优于signal()主要体现在支持信号带有参数。

1. void (*signal(int signum, void (*handler))(int)))(int);
如果该函数原型不容易理解的话,可以参考下面的分解方式来理解:
   typedef void (*sighandler_t)(int)
   sighandler_t signal(int signum, sighandler_t handler));
第一个参数指定信号的值,第二个参数指定针对前面信号值的处理,
   a,可以忽略该信号(参数设为SIG_IGN);
   b,可以采用系统默认方式处理信号(参数设为SIG_DFL)
   c,也可以自己实现处理方式(参数指定一个函数地址)
如果signal()调用成功,返回最后一次为安装信号signum而调用signal()时的handler值;失败则返回SIG_ERR

2. int sigaction(int signum,const struct sigaction *act,struct sigaction *oldact));

    sigaction函数用于改变进程接收到特定信号后的行为。该函数的第一个参数为信号的值,可以为除SIGKILLSIGSTOP外的任何一个特定有效的信号(为这两个信号定义自己的处理函数,将导致信号安装错误)。第二个参数是指向结构sigaction的一个实例的指针,在结构sigaction的实例中,指定了对特定信号的处理,可以为空,进程会以缺省方式对信号处理;第三个参数oldact指向的对象用来保存原来对相应信号的处理,可指定oldactNULL。如果把第二、第三个参数都设为NULL,那么该函数可用于检查信号的有效性。
    第二个参数最为重要,其中包含了对指定信号的处理、信号所传递的信息、信号处理函数执行过程中应屏蔽掉哪些函数等等。


可重入函数

   重入一般可以理解为一个函数同时被多个程序调用。可重入函数在任何时候都可以被中断,而一段时间之后又可以恢复运行,而相应的数据不会破坏或者丢失。

   一般可重入函数有三个条件:1)不适用静态数据结构;2)不调用mallocfree3)不是标准IO函数。

   在信号的处理过程中调用一个不可重入的函数,其结果是不可预测的。

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值