Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1068861
  • 博文数量: 135
  • 博客积分: 10182
  • 博客等级: 上将
  • 技术积分: 1565
  • 用 户 组: 普通用户
  • 注册时间: 2006-08-07 16:05
文章分类

全部博文(135)

文章存档

2011年(5)

2010年(20)

2009年(3)

2008年(16)

2007年(91)

分类: LINUX

2007-04-22 16:56:09

1 概念

1.1 产生条件

- 用户按下了某些终端键(如Ctrl-c)时

- 硬件异常产生信号

- 进程用kill(2)函数给另一个进程或进程组发送信号

- 用户用kill(1)命令发送信号给进程

- 某种软件条件发生(如SIGPIPE等信号)

1.2 动作

- 忽略此信号

- 捕捉信号

- 执行系统默认动作

1.3 信号分类

- 不可靠信号,即普通信号。如果一个进程接收到一个普通信号,而进程的未决信号集(pending signals)中存在相同的信号,则这个新发送的信号丢失。即同一时间进程的未决信号集中只可能有一个普通信号。文件 /usr/include/bits/signum.h中定义了所有普通信号。

- 可靠信号,即实时信号。信号值在SIGRTMIN(32)和SIGRTMAX(64)之间的信号。实时信号每次都会被加入到未决信号集中。

2 signal函数

2.1 函数原形

    typedef void (*sighandler_t)(int);

    sighandler_t signal(int signum, sighandler_t handler);

2.2 说明

handler的值可以是SIG_IGN(忽略信号),SIG_DFL(默认行为)或者是用户自定义的一个信号处理函数。返回的是之前的信号处理函数指针或者SIG_ERR。

3 中断的系统调用

如果进程执行一个低速系统调用而阻塞期间捕捉到一个信号,该系统调用会被中断返回出错,errno设为EINTR。4.2BSD引入了自动再启动的系统调用包括:read、readv、write、writev、ioctl、wait和waitpid。

4 可再入函数

不可再入函数一般是指:a)使用了静态数据结构,b)调用了malloc或free或c)标准I/O函数。如果在信号处理函数种调用不可再入函数,则可能会引起错误。

即使是可载入函数,因为errno每个进程只有一个,也有可能引起错误。因此一般调用使用errno的系统调用之前都要保存errno,在调用后再恢复。

5 发送等待信号

5.1 相关函数

    int kill(pid_t pid, int sig);

    int raise(int sig);

    unsigned int alarm(unsigned int seconds);

    int pause(void);

5.2 说明

kill可以给人以进程发送信号,raise则给当前进程发送信号。

alarm则设置一个闹钟值,所设置的时间超过后,产生一个SIGALARM信号,其默认动作时终止该进程。

pause是当前进程挂起直到接收到一个信号。

实例:

- sleep的不完整的实现:signals/sleep1.c

6 信号集

6.1 相关函数

    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);

6.2 说明

sigset是表示多个信号的数据类型。sigemptyset函数使信号集set不包含任何信号,sigfillset使set包含所有的信号。sigaddset和sigdelset添加和删除一个信号。sigismember检测信号集是否包含特定的信号。

7 POSIX信号处理函数

7.1 相关函数

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

    int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);

    int sigpending(sigset_t *set);

    int sigsuspend(const sigset_t *mask);

7.2 说明

用sigprocmask设定进程信号屏蔽字,参数how有三个可选项:SIG_BLOCK,SIG_UNBLOCK和SIG_SETMASK。

sigpending返回对于调用进程被阻塞的和当前未决的信号集。

sigaction取代了早期的signal函数,用于设定与信号相关联的处理动作。参数act的类型为:

    struct sigaction {

        void (*sa_handler)(int);

        void (*sa_sigaction)(int, siginfo_t *, void *);

        sigset_t sa_mask;

        int sa_flags;

    }

因为可能会用union实现,只能给两个信号处理函数指针sa_handler和 sa_sigaction中的一个赋值。sa_mask是信号处理函数执行时应该被屏蔽的信号。flag则是一些标识,要使用sa_sigaction则 必须设定SA_SIGINFO,这样信号的一些信息及传递的变量才会被设置到sa_sigaction的第二个参数中。关于sa_sigaction第二 个参数的使用,参看man文档。

sigsuspend以参数mask为屏蔽字是进程挂起直到接收到一个信号。相当于以原子操作实现:

    sigprocmask(SIG_SETMASK, &mask, &oldmask);

    pause();

    sigprocmask(SIG_SETMASK, &oldmask, NULL);

实例:

- 父子进程同步的实现:lib.rhlin /tellwait.c

- 用sigsuspend保护临界区:signals/suspend1.c

- 用sigsuspend等待一个全局变量被设置:signals/suspend2.c

- abort的实现:signals/abort.c

- system的POSIX.2实现:signals/system.c

- 处理SIGTSTP信号(Ctrl-z):signals/sigtstp.c

8 非局部跳转

8.1 相关函数

    int sigsetjmp(sigjmp_buf env, int savesigs);

    void siglongjmp(sigjmp_buf env, int val);

8.2 说明

这两个函数和setjmp、longjmp之间的唯一区别是sigsetjmp增加了一个参数。savesigs为非0,则sigsetjmp在env中保存进程的当前屏蔽字。

实例:

- 信号屏蔽及siglongjmp的使用:signals/mask.c

阅读(2334) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~