为了支持系统调用被实信号过不去而不再试行的本性,用法如下

  早期的UNIX系统的二个特色:假如经过在推行一个低速系统调用而堵塞时期,捕捉到二个时限信号,则该体系调用就被搁浅而不再继续实践。该系统调用再次回到出错,其error棉被服装置为EINTHaval。即实信号中断系统调用的实施
如此处理的理由是:因为二个时限信号发生了,进度捕捉到了它,那么意味着已经产生了某种事情,所以是个提醒被堵塞的体系调用 的火候。
为了协助系统调用被非随机信号过不去而不再实践的风味,将系统调用分为两类:低速系统调用和任何系统调用。
低速系统调用是只怕会使进度永世阻塞的壹类系统调用,他们包括:
一.
read()、readv(),在读壹些品种的公文(管道、终端设备以及互连网设施)时,如若数量并不设有则只怕会使调用者长久阻塞;
二.
write()、writev(),在写这么些品种的文本时,假如不可能马上接受那一个数量,则也恐怕会使调用者长久阻塞;
三.
打开某个种类的文书,在某种条件发出此前也大概会使调用者阻塞(例如,张开终端设备,他要等待直到所连接的调制解调器应答了电话)—>那种类型还尚未实际例子测试

第十章 信号


  1. pause()和wait();
  2. 某些ioctl;
  3. 或多或少进程间通讯函数;

拾.二 能量信号概念

产生非确定性信号的尺度:

  • 用户按下1些终端键时,引发终端产生的信号
  • 硬件极度发生信号(除数为0,无效的内部存款和储蓄器引用等)
  • kill函数/kill命令
  • 软件条件

时域信号的处理:忽略,捕捉,试行系统暗中认可动作
SIGKILL和SIGSTOP无法被忽略或者捕捉,只能执行系统默认动作

信号名称 说明 默认动作
SIGABRT 异常终止 终止+core
SIGALRM 定时器超时(alarm) 终止
SIGBUS 硬件故障 终止+core
SIGCHLD 子进程终止/停止 忽略
SIGCONT 使暂停进程继续 继续/忽略
SIGEMT 硬件故障 终止+core
SIGFPE 算术运算异常 终止+core
SIGHUP 连接断开 终止
SIGILL 表示进程已执行一条非法硬件指令 终止+core
SIGINT 中断(Delete/Ctrl+C) 终止
SIGIO(等效于SIGPOLL) 终止
SIGIOT(等效于SIGABRT) 终止+core
SIGKILL 杀掉进程 终止
SIGPIPE 写至无读进程的管道 终止
SIGPOLL 可轮询事件 终止
SIGPWR 蓄电池也不能支持工作时候,发送该信号 终止
SIGQUIT Ctrl+\类似于SIGINT,但是同时产生core文件 终止+core
SIGSEGV 无效的内存引用 终止+core
SIGSTOP 停止 停止进程
SIGSYS 指示一个无效的系统调用 终止+core
SIGTERM 终止 终止
SIGTRAP 硬件故障 终止+core
SIGTSTP Ctrl+Z 停止进程
SIGTTIN 后台读控制tty 停止进程
SIGTTOU 后台写向控制tty 停止进程
SIGURG 紧急情况(套接字) 忽略
SIGUSR1/SIGUSR2 用户定义信号 终止
SIGWINCH 终端窗口大小改变 忽略
SIGXCPU 超过CPU限制 终止+core
SIGXFSZ 超过软文件长度限制 终止+core

 

10.3 函数signal

函数原型:

#include <signal.h>
void (*sinal(int signo, void (*fun)(int)))(int);
返回值:成功返回以前的信号处理配置;失败返回SIG_ERR

抑或写成:

typedef void sigfunc(int);
sigfunc *signal(int,sigfunc *);

用法如下:
首先个参数为随机信号
其次个参数为SIG_DFL/SIG_IGN恐怕函数地址:
SIG_IGN表示忽略该时限信号
SIG_DFL表示系统默许动作
当为函数地址时,调用该函数处理连续信号

<signal.h>中:
#define SIG_ERR (void(*)())-1
#define SIG_DFL (void(*)())0
#define SIG_IGN (void(*)())1

先后运转时:
exec函数将原来安装为要捕获的能量信号都改成为私下认可动作,其余非能量信号的状态不改变

进度创立时:
子进度继续父进度的时限信号处理情势

怎么着是系统调用的机动重运维?
  当系统调用被连续信号中断时,并不回去,而是继续实施。假如read()阻塞等待,当进度接受到数字信号时,并不将read重回,而是继续阻塞等待。
缘何要引进自动重运行的?
  有时用户并不知道所选拔的输入、输出设备是还是不是是低速设备。假如编写的先后能够用交互情势运转,则他大概读、写低速终端设备。
  假使在先后中捕捉到确定性信号,而系统调用并不提供重运行功用,则对每一遍读、写系统调用都要拓展是还是不是出错再次回到的测试,假如是被功率信号中断,则再调用读、写系统调用。
怎么时候引进系统调用的电动重运转?
  四.贰BSD匡助少数被中断系统调用的全自动重运转。
  4.3BSD同意进程基于每一个实信号禁止使用自动重运营效用(因为也存在1些应用程序并不希望系统调用被中止后活动重启)

拾.四 不保证的连续信号

最初版本的UNIX系统中,时限信号是不可靠的。

  1. 在经过每趟接到时域信号对其举行拍卖时,随就要该非实信号动作复位为暗许值

int sig_int();
...
signal(SIGINT,sig_int);
...
sig_int()
{
    signal(SIGINT,sig_int);
    ...
}

留存的标题:在频限信号爆发现在到功率信号处理程序调用signal(SIGINT,sig_int)从前有八个时日窗口,假如在那段时日内爆发中断非确定性信号,则第一个非时域信号会实践私下认可动作,即甘休该进度。

  1. 在经过不期望某种复信号爆发时,它不能够关闭该实信号。进度能做的正是忽视该确定性信号。

int sig_int();
int sig_int_flag;
...
int main()
{
    signal(SIGINT,sig_int);
    ...
    while(sig_int_flag == 0)
        pause();
    ...
}
sig_int()
{
    signal(SIGINT,sig_int);
    sig_int_flag = 1;
}

留存的标题:假使在测试sig_int_flag之后,调用pause以前发生时域信号,则此进程在调用pause时大概将永生恒久休眠。

默许自动重运行的系统调用包蕴:ioctl(),read(),readv(),write(),writev(),wait(),waitpid();在那之中前多少个函数唯有在对低速设备开始展览操作时才会被随机信号中断。而wait和waitpid在捕捉到实信号时老是被中止。

拾.五 中断的系统调用

早期UNIX系统的多少个特色是:
假如经过在举行一个低速系统调用而围堵时期捕获3个信号,则该系统调用就被中断而不再奉行。该种类调用再次回到出错,其errno设置为EINT卡宴。
低速系统调用 VS 其他系统调用
低速系统调用(只怕会使得进度永恒阻塞的①类系统调用)包蕴:

  • 借使某个项目文件的数码不存在,则读操作恐怕会使调用者永久阻塞
  • 假定那一个多少不嗯给你被同样的档次文件马上接受,则写操作大概会使得调用者永恒阻塞
  • 在某种条件发出在此以前展开某个系列文件,可能会发生堵塞
  • pause函数和wait函数
  • 某些ioctl函数
  • 一点进程间通讯函数

与被中止的种类调用相关的标题是必须显式地拍卖失误再次回到
卓绝的代码类别(假定进行一个读操作,它被暂停,大家期待再次开动它)

again:
    if(n = read(fd,buf,BUFFSIZE) < 0){
        if(errno == EINTR)
        goto again;
    }

四.二BSD引入自动重运维的系统调用:ioctl、read、readv、write、writev、wait、waitpid
前多个函数唯有对较低速设备开始展览操作时才会被功率信号中断
wait和waitpid在抓获非复信号时老是被搁浅
当功率信号处理程序是用signal函数时,被中止的体系调用会重运转。

函数 信号处理程序仍被安装 阻塞信号的能力 被中断系统调用的自动重启动
signal . . 默认
sigaction . . 可选

 

10.陆 可重入函数

可重入函数是在复信号处理程序中保险调用安全的函数
正是时域信号处理程序调用的是可重入函数,可是对于errno变量要拓展拍卖(调用前保存errno,调用后复苏errno)。

自家的测试环境是四.3BSD,上边以read()为例来注解机关心器重运营和被暂停。

10.7 SIGCLD语义

SIGCLD
对SIGCLD的早期处理情势如下:

  1. 当将该频域信号的陈设安装为SIG_DFL时,即不会理会那二个实信号,然而并未有调用wait/waitpid,发生了僵死进程
  2. 当将该时域信号的安插安装为SIG_IGN时,子进度在终止时的气象被撇下,即不会产生僵死进度。但是即使父进度调用wait/waitpid,那么它将封堵到全体子进度都终止,然后wait/waitpid重回-一,并且将errno设置为ECHILD
  3. 当将该能量信号的布局安装为时域信号处理函数的地点,则根本立刻检查是或不是有子进度准备好被等候。注意会产生循环,所以在拍卖SIGCLD时,应该先wait处理掉了功率信号音讯后,再调用signal。

在Linux中,SIGCLD == SIGCHLD
SIGCHLD在配置数字信号处理格局时,是不会即刻检查是还是不是有子进程准备好被扽带,也不会在此刻调用时限信号处理函数。

 

10.捌 可信功率信号术语和语义

  • 当形成信号的事件(硬件万分、软件条件、终端发生的尺度、调用kill函数)发生时,为经过发生二个信号
  • 投递:当3个随机信号发生时,内核在进程表中以某种情势设置2个标明
  • 信号未决:实信号处于发生和投递之间的光阴距离内
  • 闭塞时限信号递送:若进度发生八个绿灯的功率信号,而且对该实信号的动作是系统默许动作/捕获该非非确定性信号,则该进程将此复信号保持为未决状态,直到该进度对此实信号解除阻塞/对此实信号的动作改造为忽略。sigpending函数能够剖断随机信号是或不是被设置为堵塞并处在未决状态
  • 确定性信号屏蔽字:规定了眼下要阻塞递送到该过程的能量信号集(sigset_t)

机关心重视启动:

10.9 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参数有多样景况:

  1. pid>0 将功率信号发送给进度ID为pid的经过
  2. pid=0
    将时域信号发送给与发送进度属于同一进程组的具有进程,而且发送进程要具有权限向那几个经过发送确定性信号。那里的全数进程不包含系统进程集(内核进度和init(pid==壹))
  3. pid<0
    将复信号发送给进度组ID等于|pid|,而且发送进度要具备权限向这么些进程发送频限信号。那里的装有进度不包蕴系统进程集(内核进度和init(pid==一))
  4. pid==-壹 将连续信号发送给发送进度有权力向他们发送功率信号的享有进度

关于权限

  • 极品用户可将连续信号发送给任一进度
  • 非最棒用户:发送者的莫过于用户ID/有作用户ID ==
    接收者的其实用户ID/有效能户ID。EXCEPT:若兑现协助_POSIX_SAVED_IDS,则检查接受者的保留设置用户ID(而不是有功能户ID)
  • 若被发送的能量信号是SIGCONT,则经过可将它发送给属于同1对话的任1其余进度

频域信号编号0定义为空时域信号。
若signo参数是0,则kill仍执行常规的荒谬检查,但不发送功率信号。(该用途常被看做分明1个一定进度是还是不是存在。若检查的进度并不设有,则kill重返-一,errno棉被服装置为ES智跑CH。尽管kill重回0,也不可能担保一定期存款在该进程。

restart.c

10.10 alarm 和 pause 函数

#include <unistd.h>
unsigned int alarm(unsigned int seconds);

#include <unistd.h>
int pause(void);
返回值:-1,error设置为EINTR

用alarm()和pause()模拟sleep()函数

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

static void sig_alarm(int signo)
{
    //什么事都不做,只是返回来唤醒pause()函数
}

unsigned int sleep1(unsigned int seconds)
{
    if(signal(SIGALRM,sig_alarm) == SIG_ERR)
        return(seconds);
    alarm(seconds);
    pause();
    return(alarm(0));
}
存在的问题:
1. 如果在调用sleep1之前已经调用了alarm函数,则它被sleep1函数中的第一次alarm调用擦除
2. sleep1()修改了对SIGALRM的配置。
3. 在一个繁忙的系统中,可能alarm在调用pause之前超时。

用setjmp和longjmp创新后的sleep:

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

static jmp_buf env_alrm;

static void sig_alarm(int signo)
{
    longjmp(env_alrm,1);
}

unsigned int sleep2(unsigned int seconds)
{
    if(signal(SIGALRM,sig_alarm) == SIG_ERR)
        return(seconds);
    if(setjmp(env_alrm) == 0){
        alarm(seconds);
        pause();
    }
    return(alarm(0));
}
存在的问题:
如果SIGALRM中断了某个其他信号,则调用longjmp会提早终止该信号处理程序

alarm()还足以常用于只怕阻塞的操作设置时间上限值

#include "apue.h"
static void sig_alrm(int);

int main(void)
{
    char line[MAXLINE];

    if(signal(SIGALRM,sig_alrm) == SIG_ERR)
        err_sys("signal(SIGALRM) error");

    alarm(10);
    if(read(STDIN_FILENO,line,MAXLINE) < 0)
        err_sys("read error");
    alarm(0);

    write(STDOUT_FILENO,line,n);
    exit(0);
}
static void sig_alrm(int signo)
{
    //什么都不做,只是返回中断read操作
}
存在的问题:
1. 第一次alarm调用和read调用之间有一个竞争条件,进程如果在这期间被切换至其他进程,则read会阻塞;
2. 如果系统调用是自动重启动,则当从SIGALRM信号处理程序返回时,read并不中断。

防护系统调用重运维

#include "apue.h"
static void sig_alrm(int);
static jmp_buf env_alrm;
int main(void)
{
    char line[MAXLINE];

    if(signal(SIGALRM,sig_alrm) == SIG_ERR)
        err_sys("signal(SIGALRM) error");
    if(setjmp(env_alrm) != 0)
        err_quit("read timeout");
    alarm(10);
    if(read(STDIN_FILENO,line,MAXLINE) < 0)
        err_sys("read error");
    alarm(0);

    write(STDOUT_FILENO,line,n);
    exit(0);
}
static void sig_alrm(int signo)
{
    longjmp(env_alrm,1);
}
 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <unistd.h>
 4 #include <string.h>
 5 #include <signal.h>
 6 #include <errno.h>
 7 
 8 #define BUFSIZE (1024)
 9 
10 static void sig_alrm(int signo)
11 {
12     printf("caught alarm...\n");
13 }
14 
15 void sig_usr(int signo)
16 {
17     if (signo == SIGUSR1)
18         printf("received SIGUSR1\n");
19     else if (signo == SIGUSR2)
20         printf("received SIGUSR2\n");
21     else
22         printf("received signal %d\n", signo);
23 }
24 
25 int main(int argc, char** argv)
26 {
27     int nSize = 0;
28     char acBuf[BUFSIZE] = {0};
29 
30     signal(SIGUSR1, sig_usr);
31     signal(SIGALRM, sig_alrm);
32 
33     alarm(5);
34 
35 //    while(1)
36     {
37         
38         memset(acBuf, '\0', BUFSIZE);
39         nSize = read(STDIN_FILENO, acBuf, BUFSIZE); 
40         if (errno == EINTR)
41             printf("interrupt, size=%d\n", nSize);
42 
43         if (1 == nSize && acBuf[0] == 10)
44             ;
45         else if (nSize != -1)
46         {
47             printf("nSize=%d, acBuf=%s", nSize, acBuf);        
48         }
49     }
50 
51     return 0;
52 }

10.11 信号集

#include <signal.h>
int sigemptyset(sigset_t *set);
int sigfillset(siget_t *set);
int sigaddset(siget_t *set,int signo);
int sigdelset(siget_t *set,int signo);
若成功返回0;出错返回-1

int sigismember(const siget_t *set,int signo);
真返回1,假返回0

假如用int类型来实现
#define sigemptyset(ptr) (*(ptr) = 0)
#define sigfillset(ptr)  (*(ptr) = ~(sigset_t)0,0)

#include <signal.h>
#include <errno.h>

#define SIGBAD(signo) ((signo) <= 0 || (signo) >= NSIG)

int sigaddset(sigset_t *set, int signo)
{
    if(SIGBAD(signo)){
        errno = EINVAL;
        return(-1);
    }
    *set |= 1 << (signo - 1);
    return(0);
}

运作结果是:

10.12 sigprocmask函数

#include <signal.h>
int sigprocmask(int how,const sigset_t * restrict set,sigset_t *restrict oset);
成功返回0;错误返回-1
  1. 先是,oset假设是非空指针,那么进度的此时此刻功率信号屏蔽字通过oset重返
  2. 附带,倘若set是3个非空指针,则参数how提示怎么着修改当前随机信号屏蔽字。
    |how|说明|
    |:—:|:—:|
    |SIG_BLOCK|该进度新的功率信号屏蔽字是当下功率信号屏蔽字和set指向的频限信号集的并集。于是set包括了期待阻塞的附加随机信号|
    |SIG_UNBLOCK|该进程新的实信号屏蔽字是眼前数字信号屏蔽字和set所指向的时域信号集补集的插花。于是set包蕴了梦想解决阻塞的功率信号|
    |SIG_SETMASK|该过程新的时限信号屏蔽字是set所针对的值|
  3. 若是set是空指针,则不转移该进度的数字信号屏蔽字,how也随后没有趣
    在调用sigprocmask后一旦有其余未决的、不再阻塞的非随机信号,则在sigprocmask再次来到前,至少校内部之1递送给该进程

图片 1

10.13 sigpending函数

#include <signal.h>
int sigpending(sigset_t *set);//set返回被阻塞不能递送的信号集
成功返回0;出错返回-1

定期器计数到伍秒后,会发送alarm数字信号,从打字与印刷能够看到确实收到到了alarm非功率信号,且read()并从未回到而是继续阻塞接受正规输入;然后在手动发送三个SIGUS猎豹CS六一信号,同样,read()并不曾被中止,当输入jfdk后,read能正常读出。表明signal()暗中同意是将时域信号设置为全自动重运行

10.14 sigaction函数

#include <signal.h>
int sigaction(int signo,const struct sigaction *restrict act,struct sigaction *restrict oact);
返回值:成功返回0,出错返回-1
struct sigaction{
    void (*sa_handler)(int);
    sigset_t sa_mask;
    int sa_flags;
    void (*sa_sigaction)(int, siginfo_t *, void *);
}
struct siginfo{
    int si_signo;
    int si_errno;
    int si_code;
    pid_t si_pid;
    uid_t si_uid;
    void *si_addr;
    int si_status;
    union sigval si_value;
}
struct sigval{
    int sival_int;
    void *sigval_int;
}

sa_mask:在调用该复信号捕捉函数从前,那一复信号集要加到进度的时域信号屏蔽字中。仅当从时限信号捕捉函数再次回到时再将经过的功率信号屏蔽字苏醒为原先值
附:在信号处理程序被调用时,操作系统建立的新信号屏蔽字包括正被递送的信号。所以,如果在处理一个信号的信号处理函数中,会阻塞这一个信号。

选项 说明
SA_INTERRUPT 由此中断的系统调用不再重启动(默认的处理方式)
SA_RESTART 由此中断的系统调用不再重新启动
SA_NOCLDSTOP 如果signo是SIGCHLD,子进程停止时,不产生此信号,子进程终止时才产生此信号
SA_NOCLDWAIT 如果signo是SIGCHLD,当调用进程的子进程终止时,不创建僵死进程。如果调用进程随后调用wait,则阻塞到它所有子进程都终止,此时wait返回-1,errno设置为ECHILD
SA_ONSTACK 如果用sigaltstack已声明一个替换栈,则此信号递送给替换栈上的进程
SA_NODEFER 当捕捉到此信号时,在执行其信号捕捉函数时,系统不自动阻塞此信号
SA_RESETHAND 在此信号捕捉函数开始时,将此信号的处理方式重置为SIG_DFL,并且清除SA_SIGINFO标志(但是,不能重置SIGILL和SIGTRAP这两个信号的配置)
SA_SIGINFO 若被设置,则使用sa_sigaction函数

用sigaction()实现的signal函数

#include "apue.h"
#include <signal.h>
typedef void Sigfunc(int);

Sigfunc* signal(int signo, Sigfunc* func)
{
    struct sigaction act, oact;
    act.sa_handler = func;
    sigemptyset(&act.sa_mask);
    act.sa_flags = 0;
    if(signo == SIGALRM){
#ifdef SA_INTERRUPT
        act.sa_flags |= SA_INTERRUPT;
#endif
    }
    else{
        act.sa_flags |= SA_RESTART;
    }
    if(sigaction(signo,&act,&oact) == -1)
        return(SIG_ERR);
    return(oact.sa_handler);
}

 

10.15 sigsetjmp和siglongjmp函数

#include <setjmp.h>
int sigsetjmp(sigjmp_buf env, int savemask);//savemask为非0值
直接调用返回0;若从siglongjmp调用返回,则返回非0;
void siglongjmp(sigjmp_buf env, int val);

被非时限信号中断停止实践:

10.17 abort函数

#include <stdlib.h>
void abort(void);

abort()实现

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

void abort(void)
{
    sigset_t mask;
    struct sigaction action;

    sigaction(SIGABRT,NULL,&action);
    if(action.sa_handler == SIG_IGN){//对SIGABRT的信号设置方式中的忽略和默认统一成默认方式,信号捕捉函数方式的不予改变
        action.sa_handler = SIG_DFL;
        sigaction(SIGABRT,&action,NULL);
    }
    if(action.sa_handler == SIG_DFL)
        fflush(NULL);

    sigfillset(&mask);
    sigdelset(&mask,SIGABRT);
    sigprocmask(SIG_SETMASK,&mask,NULL);//除了SIGABRT信号,其它信号都屏蔽掉
    kill(getpid(), SIGABRT);//在kill返回前SIGABRT信号传递到了该进程

    //以下是信号处理函数中并未exit(),而是退出之后产生的。
    fflush(NULL);
    action.sa_handler = SIG_DFL;
    sigaction(SIGABRT,&action,NULL);
    sigprocmask(SIG_SETMASK,&mask,NULL);
    kill(getpid(), SIGABRT);
    exit(1);
}

no_restart.c

10.18 system函数

POSIX要求:

  1. 在施行system时,应当阻塞对父进度递送SIGCHLD功率信号。
  2. system的调用者在等候命令完成时应有忽略SIGINT和SIGQUIT确定性信号

#include <sys/wait.h>
#include <errno.h>
#include <signal.h>
#include <unistd.h>

int system(const char *cmdstring)
{
    pid_t pid;
    int status;
    struct sigaction ignore, saveintr, savequit;
    sigset_t chldmask, savemask;

    if(cmdstring == NULL)
    {
        return(1)l
    }

    ignore.sa_handler = SIG_IGN;
    sigemptyset(&ignore.sa_mask);
    ignore.sa_flags = 0;
    if(sigaction(SIGINT, &ignore, &saveintr) < 0)
        return(-1);
    if(sigaction(SIGQUIT, &ignore, &savequit) < 0)
        return(-1);
    sigemptyset(&chldmask);
    sigaddset(&chldmask,SIGCHLD);
    if(sigprocmask(SIG_BLOCK, &chldmask, &savemask) < 0)
        return(-1);

    if((pid = fork()) < 0)
        status = -1;
    else if(pid == 0){
        sigaction(SIGINT,&saveintr, NULL);
        sigaction(SIGQUIT,&savequit,NULL);
        sigprocmask(SIG_SETMASK, &savemask, NULL);

        execl("/bin/sh", "sh", "-c", cmdstring, (char *)0);
        _exit(127);
    } else{
        while(waitpid(pid,&status,0) < 0)
            if(errno != EINTR){
                status = -1;
                break;
            }
    }

    if(sigaction(SIGINT,&saveintr,NULL) < 0)
        return(-1);
    if(sigaction(SIGQUIT,&savequit,NULL) < 0)
        return(-1);
    if(sigprocmask(SIG_SETMASK, &savemask, NULL) < 0)
        return(-1);

    return(-1);
}
 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <unistd.h>
 4 #include <string.h>
 5 #include <signal.h>
 6 #include <errno.h>
 7 
 8 #define BUFSIZE (1024)
 9 
10 static void sig_alrm(int signo)
11 {
12     printf("caught alarm...\n");
13 }
14 
15 void sig_usr(int signo)
16 {
17     if (signo == SIGUSR1)
18         printf("received SIGUSR1\n");
19     else if (signo == SIGUSR2)
20         printf("received SIGUSR2\n");
21     else
22         printf("received signal %d\n", signo);
23 }
24 
25 int main(int argc, char** argv)
26 {
27     int nSize = 0;
28     char acBuf[BUFSIZE] = {0};
29     struct sigaction act, oact;
30 
31     act.sa_handler = sig_usr;
32     sigemptyset(&act.sa_mask);
33     act.sa_flags = 0|SA_INTERRUPT;
34 
35     sigaction(SIGUSR1, &act, &oact);
36     signal(SIGALRM, sig_alrm);
37 
38     alarm(5);
39 
40     //while(1)
41     {
42         
43         memset(acBuf, '\0', BUFSIZE);
44         nSize = read(STDIN_FILENO, acBuf, BUFSIZE); 
45         if (errno == EINTR)
46             printf("interrupt, size=%d\n", nSize);
47 
48         if (1 == nSize && acBuf[0] == 10)
49             ;
50         else if (nSize != -1)
51         {
52             printf("nSize=%d, acBuf=%s", nSize, acBuf);
53         }
54     }
55 
56     return 0;
57 }

10.19 sleep,nanosleep,clock_nanosleep函数

#include <unistd.h>
unsigned int sleep(unsigned int seconds);
返回0或者未休眠完的秒数

sleep函数供给,此函数使调用进程被挂起直到知足上面八个尺码之壹:

  1. 已透过了seconds所钦命的墙上时钟时间
  2. 调用进度捕捉到一个确定性信号并从实信号处理程序重临

#include "apue.h"

static void sig_alrm(int signo)
{
    //什么都没做,只是返回从而唤醒sigsuspend()
}

unsigned int sleep(unsigned int seconds)
{
    struct sigaction newact, oldact;
    sigset_t newmask,oldmask,suspmask;
    unsigned int unslept;

    //设置sa_handler,保存之前SIGALRM的信号处理设置
    newact.sa_handler = sig_alrm;
    sigemptyset(&newact.sa_mask);
    newact.sa_flags = 0;
    sigaction(SIGALRM, &newact, &oldact);


    //阻塞SIGABRM信号,保存当前信号屏蔽字
    sigemptyset(&newmask);
    sigaddset(&newmask, SIGALRM);
    sigprocmask(SIG_BLOCK, &newmask, &oldmask);

    alarm(seconds);
    suspmask = oldmask;

    //让SIGALRM不被阻塞
    sigdelset(&suspmask, SIGALRM);

    //等待任何信号被捕捉
    sigsuspend(&suspmask);

    unslept = alarm(0);

    sigaction(SIGALRM,&oldact, NULL);

    sigprocmask(SIG_SETMASK, &oldmask, NULL);
    return(unslept);
}

#include <time.h>
int nanosleep(const struct timespec *reqtp, struct timespec *remtp);
若休眠到要求的时间,返回0,,出错返回-1

#include <time.h>
int clock_nanosleep(clockid_t clock_id, int flags, const struct timespec *reqtp, struct timespec *remtp);
若休眠到要求的时间,返回0,,出错返回-1

clock_nanosleep(CLOCK_REALTIME, 0, reqtp, remtp) == nanosleep(reqtp,remtp)

运作结果为:

10.20 sigqueue函数

应用排队实信号必须做的操作:

  1. 运用sigaction函数安装实信号处理程序时钦命SA_SIGINFO标识。然而在Linux中,无需使用SA_SIGINFO,也对非实信号排队。
  2. 选用sigaction结构中的sa_sigaction成员。
  3. 行使sigqueue()函数发送实信号

#include <signal.h>
int sigqueue(pid_t pid, int signo, const union sigval value);
成功返回0,出错返回-1

Linux扶助sigqueue()函数,然而不对SIGRTMIN和SIGRTMAX之外的时域信号排队。同时,无需使用SA_SIGINFO,也对时限信号排队。

图片 2

10.贰一 作业调节时域信号

SIGCHLD(大很多景色下处理该时限信号)
SIGCONT
停下复信号:SIGSTOP(不能够被捕捉和忽视),SIGTSTP,SIGTTIN,SIGTTOU
作业调整复信号间某些交互。当对三个历程发生甘休功率信号中的任意1种,对该进程的任壹未决SIGCONT复信号被裁撤。当对3个进程发生SIGCONT复信号时,对同样进度的任壹未决结束时域信号被丢掉。

#include "apue.h"

#define BUFFSIZE    1024

static void
sig_tstp(int signo) /* signal handler for SIGTSTP */
{
    sigset_t    mask;

    /* ... move cursor to lower left corner, reset tty mode ... */

    /*
     * Unblock SIGTSTP, since it's blocked while we're handling it.
     */
    sigemptyset(&mask);
    sigaddset(&mask, SIGTSTP);
    sigprocmask(SIG_UNBLOCK, &mask, NULL);

    signal(SIGTSTP, SIG_DFL);   /* reset disposition to default */

    kill(getpid(), SIGTSTP);    /* and send the signal to ourself */

    /* we won't return from the kill until we're continued */

    signal(SIGTSTP, sig_tstp);  /* reestablish signal handler */

    /* ... reset tty mode, redraw screen ... */
}

int
main(void)
{
    int     n;
    char    buf[BUFFSIZE];

    /*
     * Only catch SIGTSTP if we're running with a job-control shell.
     */
    if (signal(SIGTSTP, SIG_IGN) == SIG_DFL)
        signal(SIGTSTP, sig_tstp);

    while ((n = read(STDIN_FILENO, buf, BUFFSIZE)) > 0)
        if (write(STDOUT_FILENO, buf, n) != n)
            err_sys("write error");

    if (n < 0)
        err_sys("read error");

    exit(0);
}

此例子中,alarm数字信号照旧机关心爱慕运营,手动发送八个SIGUSHighlander一非确定性信号后,SIGUSQashqai1实信号处理程序试行完后,read()立马就回去了。

10.22 时限信号名和编号

#include <signal.h>
void psignal(int signo, const char *msg);

void psiginfo(const siginfo_t *info, const char *msg);

#include <string.h>
char *strsignal(int signo);

从地点两个例证中还足以看看signal()和sigaction()多少个函数的距离,明显sigaction对功率信号的的处理越来越灵活。

相关文章