信号相关的函数

函数 signal

#include <signal.h>

void (*signal(int signo, void (*func)(int))) (int);
//返回值: 若成功,返回以前的信号处理配置;若出错;返回 SIG_ERR

signo 参数是信号名。func 的值是常量 SIG_IGN、常量 SIG_DEL 或当接到此信号后要调用的函数的地址。

  • 如果指定 SIG_TGN,则向内核表示忽略此信号(有两个信号 SIGKILL 和 SIGSTOP 不能忽略)。
  • 如果指定 SIG_DFL,则表示街道此信号后的动作是系统默认动作。
  • 当指定函数地址时,则在信号发生时,调用此函数,我们称这种处理为捕捉该信号,称此函数为信号处理程序或信号捕捉函数。

signal 函数原型说明此函数要求两个参数,返回一个函数指针,而该指针所指向的函数无返回值(void)。第一个参数 signo 时一个整型值,第二个参数是函数指针,它所指向的函数需要一个整型参数,无返回值。signal 的返回值是一个函数地址,该函数有一个整型参数(即最后的(int))。当调用 signal 设置信号处理程序时,第二个参数是指向该函数(也就是信号处理程序)的指针。signal 的返回值则是指向在此之前的信号处理程序的指针。

例如:

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

void sig_self(int signo)
{
    printf("use signo: %d \n", signo);
}

int main()
{
    if(signal(SIGSELF1, sig_self) == SIG_ERR)
        perror("can`t catch SIGSELF1");

    if(signal(SIGSELF2, sig_self) == SIG_ERR)
        perror("can`t catch SIGSELF2");

    while(0)
        {}
    return 0;
}

信号屏蔽字和未决信号

在信号产生和递送之间的时间间隔内,称信号时未决的(pending)。

进程可以选用”阻塞信号递送“,如果为进程产生了一个阻塞的信号,而且对该信号的动作是系统默认动作或捕捉该信号,则为该进程将此信号保存未决状态,直到该进程对此信号解除了阻塞,或者将对此信号的动作更改为忽略。

内核在递送一个原来被阻塞的信号给进程时,才决定对它的处理方式。

每个进程都有一个信号屏蔽字(signal mask),它规定了当前要阻塞递送到该进程的信号集。对于每种可能的信号,该屏蔽字中都有一位与之对应。对应某种信号,若其对应位已设置,则它当前是被阻塞的。

信号编号可能会超过一个整型所包含的二进制位数,因此定义了一个新数据类型 sigset_t,它可以容纳一个信号集。

函数 kill 和 raise

kill 函数将信号发送给进程或进程组。raise 函数允许进程向自身发送信号。

#include<signal.h>

int kill(pid_t pid, int signo);

int raise(int signo);

//返回值:若成功,返回 0;若出错,返回 -1

调用 raise(signo);   等价于调用 kill(getpid(), signo);

kill 的 pid 参数有以下 4 种不同的情况:

  1. pid > 0,将该信号发送给进程 ID 位 pid 的进程;
  2. pid == 0,将该信号发送给与发送进程属于同一进程组的所有进程(这些进程的进程组 ID 等于发送进程的进程组 ID),而且发送进程具有权限向这些进程发送信号。
  3. pid < 0,将该信号发送给其他进程组 ID 等于 pid 绝对值,而且发送进程具有权限向其发送信号的所有进程。
  4. pid == -1,将该信号发送给发送进程有权限向它们发送信号的所有进程。

进程将信号发送给其他进程需要权限。超级用户可将信号发送给任意进程。对于非超级用户,其基本规则是发送者的实际用户 ID 或有效用户 ID 必须等于接收者的实际用户 ID 或有效 ID。在对权限进行测试时有一个特例:如果被发送的信号是 SIGCONT,则进程可将它发送给属于同一会话的任一其他进程。

POSIX.1 将信号编号 0 定义位空信号。如果 signo 参数是 0,则 kill 仍执行正常的错误检查,但不发送信号,这常被用来确定一个特定进程是否仍然存在。如果向一个并不存在的进程发送空信号,则 kill 返回 -1,errno 被置为 ESRCH。

函数 sigaction 检测或修改与指定信号相关联的处理动作

此函数取代了 UNIX 早期版本使用的 signal 函数

#include <signal.h>

int sigaction(int signo, const struch sigaction *restrict act, struct sigaction *restrict oact);
//返回值:若成功,返回0;若出错,返回-1

其中,参数 signo 是要检测或修改其具体动作的信号编号。若 act 指针为非空,则要修改其动作。如果 oact 指针非空,则系统经由 oact 指针返回该信号的上一个动作。此函数使用下列结果:

struct sigaction{
    void (*sa_handler)(int);
    sigset_ sa_mask;
    int sa_flags;
    void (*sa_sigaction)(int, siginfo_t *, void *);
};

当更改信号动作时,如果 sa_handler 字段包含一个信号捕捉函数的地址(不是常量 SIG_IGN 或 SIG_DFL),则 sa_mask 字段说明了一个信号集,在调用该信号捕捉函数之前,这一信号集要加到几次呢的信号屏蔽字中。仅当从信号捕捉函数返回时,再将进程的信号屏蔽字恢复为原先值。

act 结构的 sa_flags 字段指定对信号进行处理的各个选项,下面中详细列出了这些选项的意义。

选项说明
SA_INTERRUPT由此信号中断的系统调用不自动重启 动(XS1对于sigaction的默认处理方式).详见10.5节
SA_NOCLDSTOP

若signo是 SIGCHLD,当子进程停止(作 业控制),不产生此信号。当子进程终止 时,仍旧产生此信号(但请参阅下面说 明的SA_NOCLDWAIT选项)。若已设置此标志,则当停止的进程继续运行时, 作为XSI扩展,不产生SIGCHLD信号        

SA_NOCLDWAIT若signo是SIGCHLD,则当调用进程 的子进程终止时,不创建僵死进程。若调用进程随后调用wai七,则阻塞到它所有子进程都终止?此时返回司,errno 设置为 ECHILD
SA_NODEFER当捕捉到此信号时,在执行其信号捕捉函数时,系统不自动阻塞此信号(除非sa_mask包括了此信号)。注意,此种类型的操作对应于早期的不可靠信号
SA_ONSTACK若用 sigaltstack(2) 已声明了个替换栈,则此信号递送给替换栈上的进程
SA_RESETHAND在此信号捕捉函数的入口处,将此信号的处理方式重置为 SIG_DEL,并消除SA_SIGINFO标志。注意,此种类型的信号对应于早期的不可靠信号。但是不能自动重置 SIGILL 和 SIGTRAP 这两个信号的配置。设置此标志;使 sigaction 的行为如同设置了SA_NODEFER标志
SA_RESTART由此信号中断的系统调用自动重启动
SA_SIGINFO此选项对信号处理程序提供了附加信息:一个指向 siginfo 结构的指针以及一个指向进程上下文标识符的指针。

sa_sigaction 字段时一个替代的信号处理程序,再 sigaction 结构中使用 SA_SIGINFO 标志时,使用该信号处理程序,对于 sa_sigcation 字段和 sa_handler 字段两者,实现可能使用同一存储区,所有应用只能使用这两个字段中的一个。

通常,按下列方式调用信号处理程序:

void handler(int signo);

但是,如果配置了 SA_SIGINFO 标志,那么按下列方式调用信号处理程序

void handler(int signo, siginfo_t *info, void *context);

siginfo 结构包含了信号产生原因的有关信息。

函数 alarm 和 pause

使用 alarm 函数可以设置一个定时器(闹钟时间),在将来的某个时刻该定时器会超时。当定时器超时时,产生 SIGALRM 信号。如果忽略或不捕捉此信号,则其默认动作是终止调用该 alarm 函数的进程。

#include <unistd.h>

unsigned int alarm(unsigned int seconds);
// 返回值:0 或以前设置的闹钟时间的余留秒数

参数 seconds 的值是产生信号 SIGALRM 需要经过的时钟秒数,当这一时刻到达时,信号由内核产生,由于进程调用的延迟,所有进程得到控制从而能够处理该信号还需要一个时间间隔。

每个进程只能有一个闹钟时间,如果在调用 alarm 时,之前已为该进程注册的闹钟时间还没有超时,则该闹钟时间的余留值作为本次 alarm 函数调用的值返回。以前注册的闹钟时间则被新值代替。

pause 函数使调用进程挂起直至捕捉到一个信号

#include <unistd.h>

int pause(void);
//返回值:-1,errno 设置为 EINTR

只有执行了一个信号处理程序并从其返回时,pause 才返回。在这种情况下,pause 返回 -1, errno 设置为 EINTR。

信号集处理函数

#include <signal.h>

int sigemptyset(sigset_t *set);

int sigfillset(sigset_t *set);

int sigaddset(sigset_t *set, int signo);

int sigdelset(sigset_t *set, int signo);

//4个函数返回值:若成功,返回 0, 若出错,返回 -1

int sigismember(const sigset_t *set, int signo);
//返回值:若真,返回1;若假,返回0

函数 sigemptyset 初始化由 set 指向的信号集,清除其中所有信号。

函数 sigfillset 初始化由 set 指向的信号集,使其包括所有信号。所有应用程序在使用信号集前,要对该信号集调用 sigemptyset 或sigfillset 一次。

函数 sigaddset 将一个信号添加到已有的信号集种。

函数 sigdelset 则从信号集种删除一个信号。

sigismember 函数测试一个指定的位。

函数 sigprocmask 检测和更改 信号屏蔽字

一个进程的信号屏蔽字规定了当前阻塞而不能递送给该进程的信号集。调用函数 sigprocmask 可以检测或更改,或同时检测和更改进程的信号屏蔽字。

#include <signal.h>

int sigprocmask(int how, const sigset_t *restrict set, sigset_t *restrict oset);
//返回值:若成功,返回 0;若出错 返回 -1

首先,若 oset 时非空指针,那么进程的当前信号屏蔽字通过 oset 返回。

其次,若 set 是一个非空指针,则参数 how 指示如何修改当前信号屏蔽字。how 的可选值为:

  • SIG_BLOCK         该进程新的信号屏蔽字是其当前信号屏蔽字和 set 指向信号集的并集。set 包含了希望阻塞的附加信号
  • SIG_UNBLOCK    该进程新的信号屏蔽字是其当前信号屏蔽字和 set 所指向信号集补集的交集。set 包含了希望解除阻塞的信号。
  • SIG_SETMASK     该进程新的信号屏蔽是 set 指向的值。

如果 set 是个空指针,则不改变该进程的信号屏蔽字, how 的值无意义。

函数 sigpending 

sigpending 函数返回已信号集,对于调用进程而言,其中的各信号是阻塞不能递送的,因而页一定是当前未决的。该信号集通过 set 参数返回。

#include <signal.h>

int sigpending(sigset_t *set);
// 返回值:若成功,返回0,若出错,返回-1

函数 sigsuspend

#include <signal.h>

int sigsuspend(const sigset_t *sigmask);
//返回值:-1,errno 设置为 EINTR

进程的信号屏蔽字设置为 由 sigmask 指向的值。在捕捉到一个信号或发送了一个会终止该进程的信号之前,该进程被挂起。如果捕捉到一个信号而且从该信号处理程序返回,则 sigsuspend 返回,并且该进程的信号屏蔽字设置为调用 sigsuspend 之前的值。

注:此函数没有成功返回值。如果它返回到调用者,则总是返回 -1,并将 errno 设置为 EINTR。

函数 abort

abort 函数的功能是使程序异常终止

#include <stdlib.h>

void abort(void);

此函数将 SIGABRT 信号发送给调用进程(进程不应忽略此信号)。

函数 sleep、nanosleep 和 clock_nanosleep

#include <unistd.h>

unsigned int sleep(unsigned int seconds);
//返回值:0或为休眠完秒数

此函数使调用进程被挂起直到满足以下两个条件之一:

  1. 已经过滤 seconds 所指定的墙上的时钟时间
  2. 调用进程捕捉到一个信号并从信号处理程序返回

如同 alarm 信号意义,由于其它系统活动,实际返回时间比所要求的会迟一些。

nonosleep 函数与 sleep 函数类似,但提供了纳秒级的精度

#include <time.h>

int nanosleep(const struct timespec *reqtp, struct timespec *remtp);
//返回值:若休眠到要求的时间,返回0;若出错,返回-1

这个函数挂起调用进程,直到要求的时间已经超过或者某个信号中断了该函数。reqtp 参数用秒和纳秒指定了需要休眠的时间长度。如果某个信号中断了休眠间隔,进程并没有终止,remtp 参数指向的 timespec 结构就会被设置为未休眠完的时间长度。如果对未休眠完的时间并不感兴趣,可以把该参数置为 NULL。

如果系统并不支持纳秒这一精度,要求的时间就会取整。因为 nanosleep 函数并不涉及产生任何信号,所有不需要担心与其他函数的交互。

随着多个系统时钟的引入,需要使用相对特定时钟的延迟时间来挂起调用线程。clock_nonoslepp 函数提供了这种功能:

#include <time.h>

int clock_nanosleep(clockid_t clock_id, int flags, const struch timespec *reqtp, struct timespec *remtp);
//返回值:若休眠要求的时间,返回0;若出错,返回错误码

clock_id 参数指定了计算延迟时间基于的时钟。flags 参数用于控制延迟是相对的(0)还是绝对的(TIMER_ABSTIME)。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值