AUPE学习第十章------信号

信号时软件中断,基本上所有应用程序都处理信号,信号是处理异步事件的方法。

10.2信号概念

每个信号都有一个名字,以SIG开头,在头文件<signal.h>中,信号被定义为正整数。

编号为0的信号称为空信号。

对信号的处理方式:忽略此信号、捕捉此信号、执行系统默认动作。

一些信号详细说明:

SIGABRT    调用abort函数产生此信号,进程异常终止。

SIGCHLD    在一个信号终止或者停止时,这个信号发送给父进程。

SIGCONT    此信号发送给当前需要继续运行,而且处于停止状态的进程。

SIGEMT      指示一个现实定义的硬件故障。

SIGHUP      如果 终端接口检测到一个连接断开,发送到与终端相关的控制进程。

SIGKILL     这是两个不能被捕捉或者忽略的信号之一,向系统管理员提供杀死一个进程的可靠方法。

10.3signal函数

linux信号机制最简单的接口是signal函数。

#include <signal.h>

void  (*signal(int  signo,  void  (*func) (int))) (int);

若成功返回信号以前的处理配置,出错则返回SIG_ERR。

func的值是常量SIG_IGN、常量SIG_DFL、或者接到此信号后调用的函数的地址。

huangcd@localhost apue]$ ./a.out &
[1] 25254
[huangcd@localhost apue]$ kill -USR1 25254
received SIGUSR1
[huangcd@localhost apue]$ kill -USR2 25254
[huangcd@localhost apue]$ received SIGUSR2

[huangcd@localhost apue]$ kill 25254
[huangcd@localhost apue]$ cat page241.c 
#include "apue.h"

static void sig_usr(int);

int main(void)
{
        if(signal(SIGUSR1, sig_usr) == SIG_ERR)
                err_sys("can't catch SIGUSR1");
        if(signal(SIGUSR2, sig_usr) == SIG_ERR)
                err_sys("can't catch SIGUSR2");
        for(;;)
                pause();
}
static void
sig_usr(int signo)
{
        if (signo == SIGUSR1)
                printf("received SIGUSR1\n");
        else if (signo == SIGUSR2)
                printf("received SIGUSR2\n");
        else
                err_dump("received signal %d\n",signo);
}

当创建一个子进程时,其继承父进程的信号处理方式。

10.6可重入函数

不可重入函数:他们使用静态数据结构、他们调用malloc或fre、他们是标准I/O函数。

每个线程只有一个errno变量。

[root@localhost apue]# ./a.out 
int signal handler

[root@localhost apue]# cat page247.c 
#include "apue.h"
#include <pwd.h>

static void my_alarm(int signo)
{
        struct passwd *rootptr;
        printf("int signal handler\n");
        if ((rootptr = getpwnam("root")) == NULL)
                err_sys("getpwnam(root) error");
        alarm(1);
}

int main(void)
{
        struct passwd *ptr;
        signal(SIGALRM, my_alarm);
        alarm(1);
        for(;;)
        {
                if ((ptr = getpwnam("huangcd")) == NULL)
                        err_sys("getpwnam error");
                if (strcmp(ptr->pw_name, "huangcd") != 0)
                        printf("return value corrupted!, pw_name = %s\n", ptr->pw_name);
        }
}

10.7SIGCLD语义

子进程状态改变以后产生此信号,父进程需要调用一个wait函数以确定发生什么。

这个信号可以配置为SIG_IGN或者捕捉。处理方式不同。

[root@localhost apue]# gcc page249.c 
[root@localhost apue]# ./a.out 
SIGCLD received
pid = 26486
[root@localhost apue]# ./a.out 
SIGCLD received
pid = 26489
[root@localhost apue]# cat page249.c 
#include "apue.h"
#include <sys/wait.h>

static void sig_cld(int);

int main()
{
        pid_t pid;
        if (signal(SIGCLD, sig_cld) == SIG_ERR)
                perror("signal error");
        if((pid = fork()) < 0)
                perror("fork error");
        else if (pid == 0)
        {
                sleep(2);
                _exit(0);
        }
        pause();
        exit(0);
}

static void sig_cld(int signo)
{
        pid_t pid;
        int status;
        printf("SIGCLD received\n");
        if(signal(SIGCLD, sig_cld) == SIG_ERR)
                perror("signal error");
        if ((pid = wait(&status)) < 0)
                perror("wait error");
        printf("pid = %d\n", pid);
}


10.8可靠信号术语和语义

当对信号采取某种动作时,我们说进程递送了一个信号,在产生信号和递送之间的时间间隔内,称信号是未决的。

进程调用sigpending函数来决定哪些信号是设置为阻塞并处于未决状态的。

进程可以调用sigprocmask来检测盒改变其当前信号屏蔽字。

10.9kill和raise函数

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

#include <signal.h>

int kill (pid_t  pid, int  signo)

int  raise(int  signo)

kill函数的pid分为四种不同的情况。具体看书。

10.10 alarm和pause函数

使用alarm函数可以设置一个计时器。当超过此计时器时,产生SIGALRM信号,如果吧忽略或不捕捉此信号,默认动作时终止调用此函数的进程。

#include  <unistd.h>

unsigned  int   alarm (unsigned   int   seconds)

返回0或者以前设置的闹钟时间的剩余秒数。

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

#include <unistd.h>

int   pause(void);


下面的实例让进程休眠一段时间:

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

static void sig_alrm(int signo)
{}

unsigned int sleep1(unsigned int nsecs)
{
        if (signal(SIGALRM, sig_alrm) == SIG_ERR)
                return(nsecs);
        alarm(nsecs);
        pause();
        return (alarm(0));
}

setjmp和longjmp.为了让你实现复杂的流控制,程序在系统里面运行完全依靠内存(代码段,全局段,堆存储器,栈存储器)和寄存器的内容(栈指针,基地址,计数器),

setjmp保存当前的寄存器里面的内容,longjmp是恢复这些内容.longjmp返回setjmp程序当前的状态.

#include < setjmp.h >
int setjmp(jmp_buf env);

保存当前寄存器的状态到env这个结构体里面.

非局部跳转语句---setjmp和longjmp函数。非局部指的是,这不是由普通C语言goto,语句在一个函数内实施的跳转,而是在栈上跳过若干调用帧,返回到当前函数调用路径上的某一个函数中。
#include <setjmp.h>
Int setjmp(jmp_buf  env);
   返回值:若直接调用则返回0,若从longjmp调用返回则返回非0值
Void longjmp(jmp_buf env,int val);
    在希望返回到的位置调用setjmp,此位置在main函数中,因为直接调用该函数,所以其返回值为0.setjmp参数evn的类型是一个特殊的类型jmp_buf,这一数据类型是某种形式的数组,其中存放在调用longjmp时能用来恢复栈状态的所有信息。因为需要在另一个函数中引用env变量,所以规范的处理方式是将env变量定义为全局变量。
   当检查到一个错误时,则以两个参数调用longjmp函数,第一个就是在调用setjmp时所用的env,第二个参数是具有非0值的val,它将成为从setjmp处返回的值。使用第二个参数的原因是对于一个setjmp可以有多个longjmp。

[root@localhost apue]# cat page253_1.c 
#include <setjmp.h>
#include <signal.h>
#include <unistd.h>

static jmp_buf env_alrm;

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

unsigned int sleep2(unsigned int nsecs)
{
        if (signal(SIGALRM,sig_alrm) == SIG_ERR)
                return(nsecs);
        if (setjmp(env_alrm) == 0)
        {
                alarm(nsecs);
                pause();
        }
        return(alarm(0));
}

10.11信号集

信号集类型是能表示多个信号的类型。可以用sigset_t以包含一个信号集。

下面五个函数用于处理信号集:

#include   <signal.h>

int   sigemptyset(  sigset_t   *set)

int   sigfillset(   sigset_t   *set)

int   sigaddset(   sigset_t  *set,  int   signo)

int   sieismember(  const sigset_t   *set,   int   signo)

int  sigdelset(  sigset_t  *set,  int  signo)

任何一个信号集都需要前两个函数来处理了才能用。

[root@localhost apue]# cat page257.c 
#include <signal.h>
#include <error.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);
}
int sigdelset(sigset_t *set,int signo)
{
        if (SIGBAD(signo)) {errno = EINVAL; return (-1);}
        *set &= ~(1 << (signo - 1));
        return (0);
}
int sigismember(const sigset_t *set,int signo)
{
        if (SIGBAD(signo)) {errno = EINVAL; return (-1);}
        return ((*set & (1 << (signo - 1))) != 0);

10.12sigprocmask函数

调用此函数可以检测或者修改一个进程的信号屏蔽字。

#include  <signal.h>

int   sigprocmask(int  how,   const   sigset_t   *restrict   set,   sigset_t  *restrict   oset)

如果oset是非空指针,那么进程的当前信号屏蔽字通过oset返回。

[root@localhost apue]# cat page259.c 
#include "apue.h"
#include <errno.h>

void pr_mask(const char *str)
{
        sigset_t sigset;
        int errno_save;

        errno_save = errno;
        if (sigprocmask(0, NULL, &sigset) < 0)
                err_sys("sigprocmask errno");

        printf("%s",str);
        if(sigismember(&sigset, SIGINT)) printf("SIGINT");
        if(sigismember(&sigset, SIGQUIT)) printf("QUIT");
        if(sigismember(&sigset, SIGUSR1)) printf("SIGUSR1");
        if(sigismember(&sigset, SIGALRM)) printf("SIGALRM");

        printf("\n");
        errno = errno_save;
}


10.13 sigpending函数

这个函数返回信号集,通过参数中的set返回。

#include   <signal.h>

int   sigpending ( sigset_t   *set)

10.14  sigaction函数

此函数检查或者修改与指定信号关联的处理动作。

#include <signal.h>

int  sigaction(int  signo,  const  struct  sigaction  *restrict  act,  struct  sigaction  *restrict  oact)

第一个参数是要查询或者修改的具体动作的信号编号。

结构体sigaction用于指定信号处理程序地址或者新的处理程序。

struct sigaction{

    void  (*sa_handler) (int);   //信号处理程序的地址

    sigset_t   sa_mask;     /、保存进入信号处理程序以前的屏蔽字。

    int   sa_flags;         //指定对信号的处理选项。

    void   (*sa_sigaction) (  int,   siginfo_t   *,void  *)   //如果修改信号处理程序,用这个方法保存。

}

现在都是用sigaction实现signal函数的功能。本书中signal函数的定义如下;

[root@localhost apue]# cat page265.c 
#include "apue.h"
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 {
        #ifdef SA_RESTATRT
                act.sa_flags |=SA_RESTATRT;
        #endif
        }
        if (sigaction(signo, &act, &oact) < 0)
                return(SIG_ERR);

        return (oact.sa_handler);
}


10.15sigsetjmp和siglongjmp函数

这两个函数用于处理longjmp函数的不足之处。

#include  <setjmp.h>

int   sigsetjmp(sigjmp_buf   env,  int   savemask)

void   siglongjmp(sigjmp_buf   env,  int  val)

对于第一个函数,如果savemask不等于0,则第二个函数调用结束以后,信号屏蔽字从savemask恢复。












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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值