UNIX高级环境编程读书笔记(chapter10)

chapter 10 信号

信号就是软件中断。

信号概念

            首先,每个信号都有一个名字。这些名字都以三个字符SIG开头。在头文件<signal.h>中,这些信号都被定义为正整数(新号编号)。不存在编号为0的信号。

        信号是异步事件的经典实例。产生信号的事件对进程而言是随机出现的。进程不能简单的测试一个变量来判别是否出现了一个信号,而是必需告诉内核“在此信号出现时,请执行下列操作”。

                 (1)忽略此信号;

                 (2)捕捉信号;

                 (3)执行系统默认动作。

signal函数:

UNIX系统的信号机制最简单的接口是signal函数。

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

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

         signo参数是信号名。func的值是常量SIG_IGN,常量SIG_DFL或当接到此信号后要调用的函数的地址。如果指定SIG_IGN,则像内核表示忽略此信号。如果指定SIG_DFL,则表示接到此信号后的动作是系统默认动作。当指定函数地址时,则在信号发生时,调用该函数,我们称这种处理为“捕捉”该信号。称函数为信号处理程序(signal handler)或信号捕捉函数.

         signal函数原型说明此函数需要两个参数,返回一个函数指针,而该指针所指向的函数无返回值(void)。第一个参数signo是一个整数,它所指向的函数需要一个整形参数,无返回值。signal的返回值是一个函数地址,该函数有一个整型参数。用自然语言来描述也就是要向信号处理程序传送一个整型参数,而他却无返回值。当调用signal设置信号处理程序时,第二个参数是指向该函数的指针。signal的返回值则是指向之前的信号处理程序的指针。

Ex10.1
#include "apue.h"

static void sig_usr(int signo);


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");
    }
    else
    {
        err_dump("received signal %d\n", signo);
    }
}


执行过程和结果

[root@localhost Unix]# ./10.1 &
[3] 3347
[root@localhost Unix]# kill -USR1 3347
[root@localhost Unix]# received  SIGUSR1
kill 3347
[root@localhost Unix]# 
[3]+  Terminated              ./10.1


程序启动:

           当执行一个程序时,所有信号的状态都是系统默认或忽略。通常所有信号都被设置为它们的默认动作,除非调用exec的进程忽略该信号。确切的讲,exec函数将原先设置为要捕捉的信号都要更改为他们的默认动作,其它信号的状态则不变。

           当一个进程调用fork()时,其子进程继承父进程的信号处理方式。因为子进程在开始时复制了父进程的存储映像,所以信号捕捉函数的地址在子进程中是有意义的。


中断的系统调用:

系统调用分为两类:低速系统调用和其它系统调用。低速系统调用是可能会使进程永远阻塞的一类系统调用。

自动重启动系统调用包括:ioctl、read、readv、write、wait和waitpid。其中前五个函数只有对低速设备进行操作时才会被信号中断。而wait和waitpid在捕捉到信号时总是被中断。因为这种自动重启动的处理方式也会带来问题,所以某些应用程序并不希望这些函数被中断后重启动。


可重入函数

实例:信号处理程序my_alarm调用不可重入函数getpwnam,而my_alarm每秒钟被调用一次。在该程序中调用alarm函数使得每秒钟产生一次SIGALRM信号:


#include "apue.h"
#include <pwd.h>

static void
my_alarm(int signo)
{
    struct passwd *rootptr;

    printf("in 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("fly_lihongwei")) == NULL)
        {
            err_sys("getpwnam error");
        }

        if (strcmp(ptr->pw_name, "fly_lihongwei") != 0)
        {
            printf("return value corrupted! , pw_name = %s\n",ptr->pw_name);
        }
    }
}

运行该程序时,其结果具有随意性。若在信号处理程序中调用一个不可重入的函数,则其结果是不可预料的。


SIGCLD语义

实例:不能正常工作的系统V SIGCLD处理程序:

#include "apue.h"
#include <sys/wait.h>

static void sig_cld(int);

int main(void)
{
    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 receive\n");
    if (signal(SIGCLD, sig_cld) == SIG_ERR)
    {
        perror("sig error");
    }

    if ((pid = wait(&status)) < 0)
    {
        perror("wait error");
    }

    printf("pid = %d\n", pid);
}
此程序的问题是:在信号处理程序的开始处调用signal,内核检查是否有需要等待的子进程,所以它产生了另一个对信号处理程序的调用。信号处理程序调用signal。此时仅当其它子程序终止时,内核才会再次产生此种信号。

可靠信号术语和语义

当对信号采取了这种动作时,我们说向进程递送了一个信号。在信号产生和递送之间的时间间隔内,称信号是未决的。如果递送该信号多次,则称对这些信号进行了排队。

每个信号都有一个信号屏蔽字,它规定了当前要阻塞递送到该进程的信号集。对于某种信号,若其对应为已设置,则它当前是被阻塞的。进程可以调用sigprocmask来检测和更改其当前信号屏蔽字。


kill和raise函数

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

调用raise(signo)     <==> kill(getpid(), signo)

kill的pid有四种不同情况:

(1)pid > 0        将该信号发送给进程ID为pid的进程;

(2)pid==0        将该信号发送给与发送进程属于同一进程组的所有进程(这些进程的进程组ID等于发送进程的进程组ID).

(3)pid <  0       将该信号发送给其进程组ID等于pid的绝对值,而且发送进程具有向其发送信号的权限.

(4) pid == -1    将该信号发送给发送进程有权限向他们发送信号的系统上的所有进程.

 

alarm函数和pause函数

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

          每个进程只能有一个闹钟时钟。如果在调用alarm时,以前已为该进程设置过闹钟时钟,而且它还没有超时,则将该闹钟时钟的余留值作为本次alarm函数调用的值返回。如果有以前为进程等级的尚未超过的闹钟时钟,而且本次调用的seconds值是0,则取消以前的闹钟时钟,其余留值任作为alarm函数的返回值。

实例:sleep的简单而不完整的实现:



#include <signal.h>

#include <unistd.h>



static void 

sig_arm(int signo)

{

 /**/

}



unsigned int 

sleep1(unsigned int nsecs)

{

     if (signal(SIGALRM, sig_alarm) == SIG_ERR)

     {

          return(nsecs);

     }



     alarm(nsecs);

     pause();

     return(alarm(0));

}


三个问题:

        (1)如果在调用sleep1之前,调用者已设置了闹钟,则它会被sleep1函数中的第一次alarm调用擦除.

        (2)该程序中修改了对SIGALRM的配置.

        (3)在第一次调用alarm和调用pause之间有一个竞争条件.


         为避免第三个问题,修改后的程序如下:




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


static jmp_buf env_alrm;

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

unsigned int
sleep2(unsigned nsecs)
{
    if (signal(SIGALRM, sig_alrm) == SIG_ERR)
    {
        return(nsecs);
    }

    if (setjmp(env_alrm) == 0)
    {
        alarm(nsecs);
        pause();
    }

    return (alarm(0));
}

实例:在一个捕捉其他信号的程序中调用sleep2


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


static jmp_buf env_alrm;

static void sig_alrm(int signo);
unsigned int sleep2(unsigned int);
static void sig_int(int);


int main(void)
{
    unsigned int unslept;

    if (signal(SIGINT, sig_int) == SIG_ERR)
    {
        err_sys("signal(SIGINT) error");
    }

    unslept = sleep2(5);
    printf("sleep2 returned: %u\n", unslept);
    exit(0);
}

static void
sig_int(int signo)
{
    int i, j;
    volatile int k;

    printf("\nsig_int starting\n");

    for (i = 0; i < 300000; i++)
    {
        for (j = 0; j < 4000; j++)
        {
            k += i * j;
        }
    }

    printf("sig_int finished");
}




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

unsigned int
sleep2(unsigned nsecs)
{
    if (signal(SIGALRM, sig_alrm) == SIG_ERR)
    {
        return(nsecs);
    }

    if (setjmp(env_alrm) == 0)
    {
        alarm(nsecs);
        pause();
    }

    return (alarm(0));
}

        除了用来实现sleep函数外,alarm还常用于对可能阻塞的操作设置时间上限值。

实例:具有超时限制的read()调用




#include "apue.h"

static void sig_alrm(int);


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

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

    alarm(10);
    if ((n = 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,带超时限制调用read()

#include "apue.h"
#include <setjmp.h>

static void sig_alrm(int);
static jmp_buf env_alrm;

int main(void)
{
    int n;
    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 ((n = 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);
}

       不管系,统是否重新启动中断的系统调用,该系统都会如所预期的那样工作,但是要知道,改程序任旧与其它信号处理程序交互的问题。


信号集

POSIX.1定义了数据类型sigset_t以包含一个信号集,并且定义了五个处理信号集的函数。

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

函数sigfillset初始化由set指向的信号集,使其包括所有信号.

        所有应用程序在使用信号集前,要对该按书信号集调用sigemptyset或sigfillset一次.

一旦初始化了一个信号集,以后就可以在该信号中增、删特定的信号。函数sigaddset将一个信号添加到现有集中,sigdeleset则从信号集中删除一个信号。对所有以信号集作为参数的函数,我们总是以信号集地址作为向其传送的参数。

程序清单:sigaddset、sigdelset,sigismember的实现

#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 |= << (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(sigset_t *set, int signo)
{
    if (SIGBAD(signo)){errno = EINVAL; return(-1)};

    return ((*set & (1 << (signo - 1))) != 0);
}

sigprocmask函数

         调用函数sigprocmask可以检测或更改其信号屏蔽字,或者在一个步骤中同时执行这两个操作。

在调用sigprocmask后如果有任何未决的,不在阻塞的信号,则在sigprocmask返回前,至少会将其中一个信号递送给该进程。


实例:为进程打印信号屏蔽字

#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 error");
    }

    prinf("%s", str);

    if (sigismember(&sigset, SIGINT))   printf("SIGINT");
    if (sigismember(&sigset, SIGQUIT))  printf("SIGQUIT");
    if (sigismember(&sigset, SIGUSR1))  printf("SIGUSR1");
    if (sigismember(&sigset, SIGALRM))  printf("SIGALRM");

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


sigpending 函数

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



就在刚才,因为昨天晚上把环境变量修改错了,导致今天的linux无法启动,按alt + shift + F2 切换到终端界面后,执行/bin/vi /etc/profile 把昨天晚上修改的东西删除,然后保存退出,就可以正常启动了。、

实例:使用很多前面说过的信号功能。

 

信号设置和sigprocmask

#include "apue.h"

static void sig_quit(int);

int main(void)
{
    sigset_t newmask, oldmask, pendmask;

    if (signal(SIGQUIT, sig_quit) == SIG_ERR)
    {
        err_sys("signal(SIGQUIT) error");
    }

    sigemptyset(&newmask);
    sigaddset(&newmask, SIGQUIT);
    if(sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0)
    {
        err_sys("SIG_BLOCK error");
    }

    sleep(5);

    if (sigpending(&pendmask) < 0)
    {
        err_sys("sigpending error");
    }
    if (sigismember(&pendmask, SIGQUIT))
    {
        printf("\nSIGQUIT pending\n");
    }

    if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0)
    {
        err_sys("SIG_SETMASK error");
    }

    printf("SIGQUIT unblocked\n");

    sleep(5);
    exit(0);
}

static void
sig_quit(int signo)
{
    printf("caught SIGQUIT\n");
    if (signal(SIGQUIT, SIG_DFL) == SIG_ERR)
    {
        err_sys("can't reset SIGQUIT");
    }
}


执行结果:

[root@localhost Unix]# ./10.11
^\
SIGQUIT pending
caught SIGQUIT
SIGQUIT unblocked


sigaction函数:

sigaction函数的功能是检查或修改于指定信号相关联的处理动作。此函数取代了UNIX早期版本使用的signal函数。

实例:用sigaction实现的signal函数:



#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_RESETART
        act.sa_flags |= SA_RESTART;
#endif
    }

    if (sigaction(signo, &act, &oact) < 0)
    {
        return(SIG_ERR);
    }

    return(oact.sa_handler);
}


signal_intr函数:

是signal函数的另一种版本,它力图阻值任何被中断的系统调用重启动.

如果系统定义了SA_INTERRUPT标志,那么为了提高可移植性,我们在sa_flags中增加该标志,这样也就阻止了被中断的系统调用重启动。

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

#ifdef SA_INTERRUPT
        act.sa_flags |= SA_INTERRUPT;
#endif

    if (sigaction(signo, &act, &oact) < 0)
    {
        return(SIG_ERR);
    }

   

sigsetjmp和siglongjmp函数

在信号处理程序中进行非局部转移时应当使用这两个函数。

实例:信号屏蔽字、sigsetjmp和siglongjmp实例

#include "apue.h"
#include <setjmp.h>
#include <time.h>
#include <errno.h>

static void sig_usr1(int), sig_alrm(int);
static sigjmp_buf jmpbuf;
static volatile sig_atomic_t canjmp;

void pr_mask(const char *str);
int main(void)
{
    if (signal(SIGUSR1, sig_usr1) == SIG_ERR)
    {
        err_sys("signal(SIGUSR1) error");
    }

    if (signal(SIGALRM, sig_alrm) == SIG_ERR)
    {
        err_sys("signal(SIGALRM) error");
    }
    pr_mask("starting main: ");

    if (sigsetjmp(jmpbuf, 1))
    {
        pr_mask("ending main: ");
        exit(0);
    }

    canjmp = 1;
    for ( ; ; );

    pause();
}


static void
sig_usr1(int signo)
{
    time_t starttime;

    if (canjmp == 0)
    {
        return;
    }

    pr_mask("strating sig_usr1: ");
    alarm(3);

    starttime = time(NULL);
    for (;;)
    {
        if (time(NULL) > starttime + 5)
                break;
    }

    pr_mask("finishing sig_usr1: ");

    canjmp = 0;
    siglongjmp(jmpbuf, 1);
}


static void
sig_alrm(int signo)
{
    pr_mask("in sig_alrm: ");
}



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

    if (sigprocmask(0, NULL, &sigset) < 0)
    {
        err_sys("sigprocmask error");
    }

    printf("%s", str);

    if (sigismember(&sigset, SIGINT))   printf("SIGINT");
    if (sigismember(&sigset, SIGQUIT))  printf("SIGQUIT");
    if (sigismember(&sigset, SIGUSR1))  printf("SIGUSR1");
    if (sigismember(&sigset, SIGALRM))  printf("SIGALRM");

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



执行过程和结果:

[root@localhost Unix]# ./10.14 &
[1] 4091
[root@localhost Unix]# starting main: 
kill -USR1 4091
[root@localhost Unix]# strating sig_usr1: 
in sig_alrm: 
finishing sig_usr1: 
ending main: 


[1]+  Done                    ./10.14


sigsuspend函数

需要在一个原子操作中线回复信号屏蔽字,然后使进程休眠。这种功能是由sigsuspend函数提供的。此函数没有成功返回值。如果它返回到调用者,则总是返回-1,并将errno设置为哦EINTR(表示一个被中断的系统调用).

实例:保护临界区不被信号中断



#include "apue.h"

static void sig_int(int);

void pr_mask(const char *str);

int main(void)
{
    sigset_t newmask, oldmask, waitmask;

    pr_mask("program start: ");

    if (signal(SIGINT, sig_int) == SIG_ERR)
    {
        err_sys("signal(SIGINT) error");
    }

    sigemptyset(&waitmask);
    sigaddset(&waitmask, SIGUSR1);
    sigemptyset(&newmask);
    sigaddset(&newmask, SIGINT);

    if (sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0)
    {
        err_sys("SIG_BLOCK error");
    }

    pr_mask("in critical region: ");

    if (sigsuspend(&waitmask) != -1)
    {
        err_sys("sigsuspend error");
    }

    pr_mask("after return from sigsuspend ");

    if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0)
    {
        err_sys("SIG_SETMASK error");
    }

    pr_mask("program exit: ");

    exit(0);
}

static void sig_int(int signo)
{
    pr_mask("\nin sig_int: ");
}



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

    if (sigprocmask(0, NULL, &sigset) < 0)
    {
        err_sys("sigprocmask error");
    }

    printf("%s", str);

    if (sigismember(&sigset, SIGINT))   printf("SIGINT");
    if (sigismember(&sigset, SIGQUIT))  printf("SIGQUIT");
    if (sigismember(&sigset, SIGUSR1))  printf("SIGUSR1");
    if (sigismember(&sigset, SIGALRM))  printf("SIGALRM");

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


执行结果:[root@localhost Unix]# ./10.15
program start: 
in critical region: SIGINT
^C
in sig_int: SIGUSR1
after return from sigsuspend SIGINT
program exit:


sigsuspend的另一种应用是等待一个信号处理程序设置一个全局变量。

实例:用于捕捉中断信号和退出信号,但是希望仅当捕捉到退出信号时,才唤醒主例程。



#include "apue.h"

volatile sig_atomic_t quitflag;

static void sig_int(int signo)
{
    if (signo == SIGINT)
    {
        printf("\ninterrupt\n");
    }
    else if (signo == SIGQUIT)
    {
        quitflag = 1;
    }
}


int main(void)
{
    sigset_t newmask, oldmask, zeromask;

    if (signal(SIGINT, sig_int) == SIG_ERR)
    {
        err_sys("signal(SIGINT) error");
    }

    if (signal(SIGQUIT, sig_int) == SIG_ERR)
    {
        err_sys("SIGQUIT error");
    }

    sigemptyset(&zeromask);
    sigemptyset(&newmask);
    sigaddset(&newmask, SIGQUIT);

    if (sigprocmask(SIG_BLOCK, &newmask, &oldmask))
    {
        err_sys("SIG_BLOCK error");
    }

    while (quitflag == 0)
    {
        sigsuspend(&zeromask);
    }

    quitflag = 0;

    if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0)
    {
        err_sys("SIG_SETMASK error");
    }

    exit(0);
}

父子进程可实现同步的例程:


#include "apue.h"

static volatile sig_atomic_t sigflag;
static sigset_t newmask, oldmask, zeromask;

static void
sig_usr(int signo)
{
    sigflag = 1;
}

void
TELL_WAIT(void)
{
    if (signal(SIGUSR1, sig_usr) == SIG_ERR)
    {
        err_sys("signal(SIGUSR1) error");
    }

    if (signal(SIGUSR2, sig_usr) == SIG_ERR)
    {
        err_sys("signal(SIGUSR2) error");
    }

    sigemptyset(&zeromask);
    sigemptyset(&newmask);
    sigaddset(&newmask, SIGUSR1);
    sigaddset(&newmask, SIGUSR2);

    if (sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0)
    {
        err_sys("SIG_BLOCK error");
    }
}


void
TELL_PARENT(pid_t pid)
{
    kill(pid, SIGUSR2);
}

void
WAIT_PARENT(void)
{
    while (sigflag == 0)
    {
        sigsuspend(&zeromask);
    }

    if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0)
    {
        err_sys("SIG_SETMASK error")
    }
}

void
TELL_CHILD(pid_t pid)
{
    while (sigflag == 0)
    {
        sigsuspend(&zeromask);
    }

    sigflag = 0;


    if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0)
    {
        err_sys("SIG_MASK error");
    }
}


其中使用了两个用户定义的信号:SIGUSR1由父进程发送给子进程,SIGUSR2由子进程发送给父进程。

如果在等待信号发生时希望去休眠,则使用sigsuspend是非常适当的。如果不使用线程,那么我们能尽力做到最好的是,当信号发生时,在信号捕捉程序中对一个全局变量置1.


abort函数

此函数将SIGBRT信号发送给调用进程。调用abort将向主机环境递送一个未成功终止的通知,其方法是调用raise函数。

实例:abort的POSIX.1实现


#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)
    {
        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);
    kill(getpid(), SIGABRT);

    fflush(NULL);
    action.sa_handler = SIG_DFL;
    sigaction(SIGABRT, &action, NULL);
    sigprocmask(SIG_SETMASK, &mask, NULL);
    kill(getpid(), SIGABRT);
    exit(1);
}


system函数

POSIX.1要求system忽略SIGINT和SIGQUIT, 阻塞SIGCHLD。


#include "apue.h"

static void sig_int(int signo)
{
    printf("caught SIGINT\n");
}

static void sig_chld(int signo)
{
    printf("caught SIGCHLD");
}

int
main(void)
{
    if (signal(SIGINT, sig_int) == SIG_ERR)
    {
        err_sys("signal(SIGINT) error");
    }

    if (signal(SIGCHLD, sig_chld) == SIG_ERR)
    {
        err_sys("signal(SIGCHLD), error");
    }

    if (system("/bin/ed") < 0)
    {
        err_sys("system() error");
    }

    exit(0);
}

system函数的另一个实现:

#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 (cmdsting == NULL)
    {
        return 1;
    }

    ignore.sa_handler = SIG_IGN;
    sigemptyset(&ignore.sa_mask);
    ignore.sa_flag = 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 (status);
}

sleep函数的可靠实现:


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


static void
sig_alrm(int signo)
{
    /**********/
}

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

    newact.sa_handler = sig_alrm;
    sigemptyset(&newact.samask);
    newact.sa_flags = 0;
    sigaction(SIGALRM, &newact, &oldact);

    sigempty(&newmask);
    sigaddset(&newmask, SIGALRM);
    sigprocmask(SIG_BLOCK, &newmask, &oldmask);

    alarm(nsecs);

    suspmask = oldmask;
    sigdelset(&suspmask, SIGALRM);
    sigsuspend(&suspmask);

    unslept = alarm(0);
    sigaction(SIGALRM, &oldact, NULL);

    sigprocmask(SIG_SETMASK, &oldmask, NULL);

    return(unslept);
}


作业控制台:

POSIX.1 认为信号中6个与控制作业有关:

SIGCHLD 子进程已停止或终止

SIGCONT 如果进程已终止,则使其继续运行

SIGSTOP 停止信号

SIGTSTP 交互式停止信号

SIGTTIN 后台进程组成员读控制终端

SIGTTOU 后台进程组成员写到控制终端

除SIGCHLD以外,大多数应用程序并不处理这些信号:交互式shell则通常做处理这些信号的所有工作。


实例:

如何处理SIGTSTP


#include "apue.h"

#define BUFFSIZE 1024

static void sig_tstp(int signo);

int main(void)
{
    int n;

    char buf[BUFFSIZE];

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


static void sig_tstp(int signo)
{
    sigset_t mask;

    sigemptyset(&mask);
    sigaddset(&mask, SIGTSTP);
    sigprocmask(SIG_UNBLOCK, &mask, NULL);

    signal(SIGTSTP, SIG_DFL);
    kill(getpid(), SIGTSTP);
    signal(SIGTSTP, sig_tstp);
}


小结:

信号用于大多数复杂的应用程序中。理解信号处理的原因和方式对于高级UNIX编程极其重要。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值