曲径通幽论坛

 找回密码
 立即注册
搜索
查看: 8010|回复: 2
打印 上一主题 下一主题

[信号] sigaction() -- 信号处理

[复制链接]

4918

主题

5880

帖子

3万

积分

GROAD

曲径通幽,安觅芳踪。

Rank: 6Rank: 6

积分
34397
跳转到指定楼层
楼主
发表于 2009-7-4 15:25:35 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
sigaction() 函数可以用来检查或设置进程在接收到信号时的动作。它是 signal()调用的替代函数,比 signal() 更为健壮。原型为:
#include <signal.h>
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);

sigaction() 可以根据参数 signum 指定的信号编号来设置该信号的处理函数。参数 signum 可以是 SIGKILLSIGSTOP 以外的任何信号。
act 参数如果不是空指针,则为 signum 设置新的信号处理函数;如果 oldact 不是空指针,则旧的信号处理函数将被存储在 oldact 中。

struct sigaction 结构体定义如下:
struct sigaction {
            void (*sa_handler) (int);
            void (*sa_sigaction)(int, siginof_t *, void *);
            int sa_flags;
            void (*sa_restorer) (void);
}
sa_hanldersa_sigaction 在某些体系结构上被定义为共用体,即这两个值在某一时刻只有一个有效。

sa_restorer 已经作废!

sa_handler 可以是常数 SIG_DEFSIG_IGN,或者是一个信号处理函数的函数名。

sa_sigaction 也是用来指定信号 signum 的处理函数,它有三个参数,第一个是信号编号;第二个是一个指向 siginfo_t 结构的指针;第三个是一个指向任何类型的指针,一般不使用。

sa_mask 声明了一个信号集,在调用信号捕捉函数之前,该信号集会增加到进程的信号屏蔽码中,新的信号屏蔽码会自动包括正在处理的信号( sa_flags 未指定 SA_NODEFER 或 SA_NOMASK )。当信号捕捉函数返回时,进程的信号屏蔽码会恢复回原来的值。因此,当处理一个给定的信号时,如果这种信号再次发生,那么它会被阻塞,直到本次信号处理结束为止。若这种信号发生了多次,则对于不可靠信号,它只会被阻塞一次,即本次信号处理结束后只会再处理一次( 相当于丢失了信号 );对于可靠信号( 实时信号 ),则会被阻塞多次,即信号不丢失,信号发生了多少次就会调用信号处理函数多少次。

sa_flags 成员用来说明信号处理的一些其他相关操作,它可以取以下值或它们的组合( 用 | 运算符 ):
SA_NOCLDSTOP : 如果参数 signum 为 SIGCHLD ,则当子进程暂停时,并不会通知父进程。
SA_ONESHOT 或 SA_RESETHAND : 在调用新的信号处理函数前,将此信号的处理方式改为系统默认的方式。
SA_ONSTACK : 以预先定义好的堆栈调用信号处理函数。
SA_RESTART : 被信号中断的系统调用,在信号处理函数执行完毕后会自动重新开始执行( BSD 操作系统默认使用该值 )。
SA_NOMASK 或 SA_NODEFER : 在信号处理函数前允许此信号再次递送,相当于中断嵌套。
SA_SIGINFO : 如果设置了该标志,则信号处理函数由三参数的 sa_sigaction() 指定,而不是 sa_handler() 指定。

当使用三参数的 sa_sigaction() 来指定信号处理函数时,它的第二个参数可以用来传递数据,其定义如下:
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 ,发送信号的进程 ID*/
      uid_t    si_uid;      /* Real user ID of sending process ,发送信号进程真实用户 ID*/
      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 ,时间 ID*/
      void    *si_addr;     /* Memory location which caused fault ,产生内存访问错误的内存地址*/
      int      si_band;     /* Band event ,band 事件*/
      int      si_fd;       /* File descriptor 文件描述符*/
  }

4918

主题

5880

帖子

3万

积分

GROAD

曲径通幽,安觅芳踪。

Rank: 6Rank: 6

积分
34397
沙发
 楼主| 发表于 2009-7-5 02:07:10 | 只看该作者

测试代码

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

int temp = 0;
struct sigaction act;

/*信号处理函数*/
void handler_sigint(int signo)
{
     printf("recv SIGINT\\n");

     sleep(5);
     temp += 1;
     printf("the value of temp is: %d\\n", temp);
     sleep(5);

     temp = temp + 2 ;
     printf("再加一次temp :%d\\n",temp);
     printf("in handler_sigint, after sleep\\n");

}

int main()
{
//    struct sigaction act;
     act.sa_handler = handler_sigint;
     act.sa_flags = SA_NOMASK;

     /*安装信号处理函数*/
     sigaction(SIGINT, &act, NULL);

     while(1)
     ;

     return 0;
}
程序执行与输出
beyes@linux-beyes:~/C/base>beyes@linux-beyes:~/C/base> ./sigaction.exe
^Crecv SIGINT
^Crecv SIGINT
the value of temp is: 1
再加一次temp :3
in handler_sigint, after sleep
the value of temp is: 4
再加一次temp :6
in handler_sigint, after sleep
^\\退出
说明
act 结构体定义在 main() 中或者定义为全局变量都可以。
指定了 SA_NOMASK , 意思就是在处理此信号结束前允许此信号再次递送,相当于中断嵌套
所以,由以上输出可以看到,在第一次输入 ctrl+c 组合键发送 SIGINT 信号,程序打印输出 recv SIGINT ,表明信号处理程序处理了信号 SIGINT,然后睡眠 5秒。接着,在此睡眠期间,又快速的键入 ctrl+c 组合键一次,由于程序中设定了 sa_flags的值为 SA_NOMASK,因此程序又一次响应 SIGINT信号 ,程序从 sleep()处嵌套调用信号处理函数 handler_sigint,再一次打印出 recv SIGINT ,并睡眠 5秒。
在程序中,   
printf("the value of temp is: %d\\n", temp);
     sleep(5);
这两条语句的用意是测试一下程序进入中断嵌套后的切换情况。在第一次输出:the value of temp is: 1时,还无法确定程序是在嵌套层还是在首层中。做一下试验,如果我们第二次按下 ctrl+c 是在第一次按下后的2秒进行。那么当在第二次按下组合键时,在发生中断嵌套并输出处理信息程序recv SIGINT 后进入睡眠 5 秒。在嵌套层睡眠期间,首层程序的会存在两种情况:要么醒来,要么根本没有被系统调度而仍然处于挂起状态--也就是 sleep()的时间也不再计时,而是停止了。假设其已经醒来并且系统已经切换到这层去执行,那么 temp值加 1后继续睡眠 5秒。这时,已经轮到嵌套层醒来,temp值会再 +1变成 2后输出。但是,从输出结果来看,输出结果显然和推测的相悖!
所以,可以推断,在嵌套层睡眠时,首层信号处理函数被挂起了,直到嵌套层执行完毕后,再会返回到首层中去,执行信号处理任务完毕后再返回主函数!
这一点,也可以在程序的输出时间间隔中感觉得到。

现在把原程序中的 act.sa_flags = SA_NOMASK 这一行注释掉,再适当修改一下程序,然后重新编译并执行( 执行时,快速按下 ctrl + c 组合键 3 次或 3 次以上)。修改后的程序如下:
#include <stdio.h>
#include <unistd.h>
#include <signal.h>

int temp = 0;
struct sigaction act;

/*信号处理函数*/
void handler_sigint(int signo)
{
     printf("recv SIGINT\\n");

     sleep(5);
     temp += 1;
     printf("the value of temp is: %d\\n", temp);
     printf("in handler_sigint, after sleep\\n");

}

int main()
{
//    struct sigaction act;
     act.sa_handler = handler_sigint;
//    act.sa_flags = SA_NOMASK;

     /*安装信号处理函数*/
     sigaction(SIGINT, &act, NULL);

     while(1)
     ;

     return 0;
}
执行时,连续按下 ctrl +c 6 次
beyes@linux-beyes:~/C/base> ./sigaction.exe
^Crecv SIGINT
^C^C^C^C^Cthe value of temp is: 1
in handler_sigint, after sleep
recv SIGINT
the value of temp is: 2
in handler_sigint, after sleep
^\\退出
由输出可见,发送了这么多次的信号,程序只处理了 2 次!
这是因为,在去掉了 SA_NOMASK 后,sigaction() 按照默认的阻塞方式来处理信号。由于 SIGINT 是不可靠信号,不可靠信号不支持排队。对于这种信号的多次发生,它也只会被阻塞一次,即本次信号处理结束后只会再处理一次( 相当于丢失了信号 ),从输出的结果来看,4 个信号确实被丢失掉了。!

4918

主题

5880

帖子

3万

积分

GROAD

曲径通幽,安觅芳踪。

Rank: 6Rank: 6

积分
34397
板凳
 楼主| 发表于 2009-7-7 00:58:13 | 只看该作者

小结

从上面程序可以看到,temp 的值随着信号处理函数的调用次数的增加而递增。但是在实际应用中,信号总是随机发生的,这样 temp 的值也会随机变化。如果 main 函数或者其他地方还用到了这个全局变量,则程序产生不可预料的结果。我们称这种数据会被破坏的函数为不可重入函数。编写程序信号处理程序时,要注意不要使用不可重入函数。一般说来,满足下列条件之一的函数是不可重入的。
( 1 )使用了静态的数据结构,如 getgrpid(),全局变量等。
( 2 )函数实现时,调用了 malloc() 或者 free() 函数
( 3 )函数实现时,使用了标准 I/O 函数。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|曲径通幽 ( 琼ICP备11001422号-1|公安备案:46900502000207 )

GMT+8, 2025-7-2 09:26 , Processed in 0.083370 second(s), 22 queries .

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表