《unix环境高级编程》--- 信号

信号是软件中断。

#include <signal.h>

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

该函数有2各参数,第一个为信号名,第二个为信号处理程序,参数为int。
该函数返回一个参数为int的函数指针,指向func。
以下定义更清楚一些

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

捕捉SIGUSR1和SIGUSR2的简单程序

#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();
    return 0;
}

static void sig_usr(int signo)
{
    if(signo == SIGUSR1)
        printf("received SIGUSR1\n");
    else if(signo == SIGUSR2)
        printf("reveived SIGUSR2\n");
    else
        err_dump("received signal %d\n", signo);
}

这里写图片描述

在信号处理程序中调用不可重入函数
不可重入原因:
1、已知使用静态数据结构。如若级才能正在执行getpwnam,这种将其结果放在静态存储单元中的函数,旗舰插入执行信号处理程序,它又调用这样的函数,返回给正常调用者的信息可能被返回给信号处理程序的信息覆盖。
2、调用mallco或free。如果进程正在执行mallco,在其堆内分配另外的存储空间,而此时由于捕捉到信号而插入执行该信号处理程序,其中又调用malloc,可能会对进程造成破坏,因为malloc通常为它所分配的存储区维护一个链表,而插入执行信号处理程序时,进程可能正在更改此链表。
3、是标准I/O函数

#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);   /* 每秒产生一次SIGALARM信号 */
    for(;;)
    {
        if((ptr = getpwnam("yjp")) == NULL)
            err_sys("getpwnam error");
        if(strcmp(ptr->pw_name, "yjp") != 0)
            printf("return value corrupted!, pw_name = %s\n", ptr->pw_name);
    }
    return 0;
}

这里写图片描述
结果不具有随意性?

不能正常工作的系统V SIGCLD处理程序
SIGCHLD:子进程状态改变后产生此信号,父进程需调用一个wait函数已确定发生了什么。
SIGCLD:如果配置为SIG_IGN,则调用进程的子进程将不产生僵死进程,终止时,将状态丢弃。如果随后调用wait函数,将阻塞到所有子进程终止,然后wait返回-1,并将errno设置为ECHILD。如果配置为捕捉,内核立即检查是否又子进程准备好被等待,调用SIGCLD处理程序。

在linux上,SIGCLD和SIGCHLD定义为同一值。但如果定义了SIGCLD的信号处理程序,则不再调用SIGCHLD的信号处理程序。

#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();  /* parent */
    return 0;
}

static void sig_cld(int sigon)  /* interrupts pause() */
{
    pid_t pid;
    int status;

    printf("SIGCLD received\n");
    if(signal(SIGCLD, sig_cld) == SIG_ERR)  /* reestablish handler */
        perror("signal error");

    if((pid = wait(&status)) < 0)  /* fetch child status */
        perror("wait error");

    printf("pid = %d\n", pid);
}

这里写图片描述
可见在linux上运行正常。

使用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("\nread timeout");

    /*
    第一次alarm调用和read调用之间有一个竞争条件。
    如果内核在这两个函数之间使进程阻塞,且超过闹钟时间,则read可能永远阻塞。
    使用longjmp则无需担心是否被中断。
    */
    alarm(10); /* start the timer */
    if((n = read(STDIN_FILENO, line, MAXLINE)) < 0)
        err_sys("read error");
    alarm(0);  /* turn off timer, return unslept time  */

    write(STDOUT_FILENO, line, n);
    exit(0);
}

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

这里写图片描述

该程序可能于其他信号处理程序交互的问题,如使得其他信号处理程序提前退出。

为进程打印信号屏蔽字
进程阻塞SIGQUIT信号,保存了当前信号屏蔽字,然后休眠5秒。
在此期间所产生的退出信号SIGQUIT都会被阻塞。
休眠结束后,检查该信号是否为未决的,然后恢复信号屏蔽字,将SIGQUIT设置为不再阻塞。

#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("can't catch SIGQUIT");

    /*
       Block SIGQUIT and save current signal mask
    */
    sigemptyset(&newmask);
    sigaddset(&newmask, SIGQUIT);
    /*
      进程的当前信号屏蔽字通过oldmask返回
      将当前信号屏蔽字和newmask指向的信号集的并集作为进程新的信号屏蔽字
    */
    if(sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0)
        err_sys("SIG_BLOCK, error");

    sleep(5);  /* SIGQUIT here will terminate with core file */

    /*
       int sigpending(sigset_t *set);
       返回在阻塞期间接收到阻塞信号的集合
    */
    if(sigpending(&pendmask) < 0)
        err_sys("sigpending error");
    if(sigismember(&pendmask, SIGQUIT))
        printf("\nSIGQUIT pending\n");

    /* Reset signal mask which unblocks SIGQUIT */
    if(sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0)
        err_sys("SIG_SETMASK error");
    printf("SIGQUIT unblock\n");

    sleep(5);
    return 0;
}

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

结果

yjp@yjp-VirtualBox:~/apue/10signals$ ./suspend1
^\^\^\^\^\                                  休眠期间产生多次信号

SIGQUIT pending       从sleep返回后
caught SIGQUIT        信号处理程序中           只产生信号一次
SIGQUIT unblock       从sigprocmask返回后
^\Quit (core dumped)  再次输出信号

信号屏蔽字、sigsetjmp和siglongjmp实例
信号处理程序被调用时,系统所设置的信号屏蔽字自动包括刚被捕捉的信号。

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

static void sig_usr1(int), sig_alrm(int);

static sigjmp_buf jmpbuf;

/* 
写此种类型的变量时不会被中断 
该变量由两个不同的控制线程--main和异步执行的信号处理程序访问
*/
static volatile sig_atomic_t canjump;  

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

    /*
       int sigsetjmp(sigjmp_buf env, int savemask);
       如果savemask非0,则在env中保存进程的当前信号屏蔽字
       siglongjmp从其中恢复保存的信号屏蔽字
    */
    if(sigsetjmp(jmpbuf, 1))
    {
        pr_mask("ending main: ");
        exit(0);
    }
    canjump = 1;  /* now sigsetjmp() is OK */

    for(;;)
        pause();
    return 0;
}

static void sig_usr1(int signo)
{
    time_t starttime;

    if(canjump == 0)
        return;   /* unexpected signal, ignore */

    pr_mask("starting sig_usr1: ");
    alarm(3);         /* SIGALRM in 3 seconds */
    starttime = time(NULL);
    for(;;)       /* busy wait for 5 seconds */
        if(time(NULL) > starttime + 5)
            break;
    pr_mask("finishing sig_usr1: ");

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

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

这里写图片描述

start main 和 ending main后面都没有阻塞信号。

保护临界区不被信号中断
1、重置SIGINT信号处理函数。
2、保存之前的信号屏蔽字,新的信号屏蔽字为SIGINT。
3、进入临界区。
4、调用sigsuspend挂起,此时信号屏蔽字改为SIGUSR1。
5、当SIGINT信号处理函数返回时,唤醒sigsuspend,信号屏蔽字恢复为SIGINT。
6、恢复之前的信号屏蔽字。

#include "apue.h"

static void sig_int(int);

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

    /* Block SIGINT and save current signal mask */
    if(sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0)
        err_sys("SIG_BLOCK error");

    /* Critical region of code */
    pr_mask("in critical region: ");

    /* Pause, allowing all signals except SIGUSR1 */
    /*
       int sigsuspend(const sigset_t *sigmask);
       将进程的信号屏蔽字设置为由sigmask指向的值
       捕捉到信号或发生会终止该进程的信号前,该进程被挂起。
       捕捉到信号且从信号处理程序返回,则sigsuspend返回,并将
       该进程的信号屏蔽字设置为调用sigsuspend之前的值。
       返回:总是-1,并将errno设置为EINTR
    */
    if(sigsuspend(&waitmask) != -1)
        err_sys("sigsuspend error");

    pr_mask("after return from sigsuspend: ");

    /* Reset signal mask which unblocks SIGINT */
    if(sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0)
        err_sys("SIG_SETMASK error");

    /* And continue processing ... */
    pr_mask("program exit: ");
    exit(0);
}

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

这里写图片描述
调用sigsuspend时,将SIGUSR1加入了信号屏蔽字中,所以在信号处理程序中发现信号屏蔽字改变了。
sigsuspend返回时,将信号屏蔽字恢复为调用它之前的值。

用sigsuspend等待一个全局变量被设置
sigsuspend另一个应用时等待一个信号处理程序设置一个全局变量。
捕捉到中断信号后,只是打印”interrupt”,不退出。进程继续被挂起。
只有捕捉到退出信号后,主例程被唤醒,退出。

#include "apue.h"

volatile sig_atomic_t quitflag; /* set nonzero by signal handler */

static void sig_int(int signo)
{
    if(signo == SIGINT)
        printf("\ninterrupt\n");
    else if(signo == SIGQUIT)
        quitflag = 1;  /* set flag for main loop */
}

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("signal(SIGQUIT) error");

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

    /* Blcok SIGQUIT and save current signal mask */
    if(sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0)
        err_sys("SIG_BLOCK error");

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

    /* SIGQUIT has been caught and is now blocked; do whatever */
    quitflag = 0;

    /* Reset signal mask which unblocks SIGQUIT */
    if(sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0)
        err_sys("SIG_SETMASK error");

    return 0;
}

这里写图片描述

父子进程可用来实现同步的例程
调用sigsuspend可使得进程在等待信号期间休眠。
该程序对于单线程,等待信号期间无法调用其他系统函数。多线程则可专门安排一个线程处理信号。

#include "apue.h"

static volatile sig_atomic_t sigflag;  /* set nonzero by sig handler */
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);

    /*Block SIGUSR1 and SIGUSR2, and save current signal mask */
    if(sigprockmask(SIG_BLOCK, &newmask, &oldmask) < 0)
        err_sys("SIG_BLOCK error");
}

void TELL_PARRENT(pid_t pid)
{
    kill(pid, SIGUSR2);  /* tell parent we're done */
}

void WAIT_PARENT(void)
{
    while(sigflag == 0)
        sigsuspend(&zeromask);  /* and wait for parent */

    /* Reset signal mask to original value */
    if(sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0)
        err_sys("SIG_SETMASK error");
}

void TELL_CHILD(pid_t pid)
{
    kill(pid, SIGUSR1);  /* tell child we're done */
}

void WAIT_CHILD(void)
{
    while(sigflag == 0)
        sigsuspend(&zeromask);  /* and wait for child */
    sigflag = 0;

    /* Reset signal mask to original value */
    if(sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0)
        err_sys("SIG_SETMASK error");
}

abort的POSIX.1实现
1、保存之前的SIGABRT信号处理函数。当SIGABRT信号处理函数为忽略时,重置为默认。
2、保存当前信号屏蔽字,并解除对信号SIGABRT的阻塞。
3、发送SIGABRT。
4、冲洗所有标准流缓冲区。
5、恢复之前的SIGABRT信号处理函数。
6、再次发送SIGABRT,而不是简单的调用_exit,是为了说明进程的终止状态是SIGABRT造成的。

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

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

    /* Caller can't ignore SIGABORT, if so reset to default */
    /*
    int sigaction(int signo, const struct sigaction *restrict act, 
            struct sigaction * restrict oact);
    返回:成功0,失败-1
    signo: 要检测 或修改器具体动作的信号编号。
    若act非空,则修改其动作。若oact非空,则由oact返回该信号的上一个动作。
    struct sigaction
    {
        void (*sa_handler)(int);   addr of signal handler, or SIG_IGN, or SIG_DFL
        sigset_t s_mask;           additional signals to block,保证信号处理函数只调用一次
        int sa_flags;          signal options

        alternate handler,当使用SA_SIGINFO标志时,使用该信号处理程序
        与sa_handler可能使用同一存储区,所以一次只能使用这两个中的一个
        void (*sa_sigaction(int, siginfo_t *, void *);      
    }

    struct siginfo
    {
        int si_signo;     signal number
        int si_errno;     if nonzero, errno value from <errno.h>
        int si_code;      additional info (depends on signal)
        pid_t si_pid;     sending process ID
        uid_t si_uid;     sending process real user ID
        void *si_addr;    address that caused the fault
        int si_status;    exit value or signal number
        long si_band;     band number for SIGPOLL

        possibly other fields also
    };

    */
    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);  /* flush all open stdio streams */

    /* Caller can't block SIGABORT; make sure it's unblocked */
    sigfillset(&mask);
    sigdelset(&mask, SIGABRT);  /* mask has only SIGABRT turned off */
    sigprocmask(SIG_SETMASK, &mask, NULL);
    kill(getpid(), SIGABRT);    /* send the signal */

    /* If we're here, process caught SIGABRT and returned */
    fflush(NULL);  /* flush all open stdio streams */
    action.sa_handler = SIG_DFL;
    sigaction(SIGABRT, &action, NULL);  /* reset to default */

    sigprocmask(SIG_SETMASK, &mask, NULL);  /* just in case ... */
    kill(getpid(), SIGABRT);  /* and one more time */
    exit(1);  /* this should never be exected */
}

用sigaction实现的signal函数

#include "apue.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;

    /* 对SIGALRM以外的所有信号,都尝试设置SA_RESTART标志,
       于是这些信号中断的系统调用都能重启动。*/
    if(signo == SIGALRM)
    {
    #ifdef SA_INTERRUPT   /* 不重启被中断的系统调用 */
        act.sa_flags |= SA_INTERRUPT;
    #endif
    }
    else
    {
    #ifdef SA_RESTART    /* 重启被中断的系统调用 */
        act.sa_flags |= SA_RESTART;
    #endif
    }

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

用system调用ed编辑器
若从shell调用ed编辑器,键入中断字符,捕捉并输出问号,对退出符的处理方式设置为忽略。

#include "apue.h"

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

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

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

结果

yjp@yjp-VirtualBox:~/apue/10signals$ ./system
a                       将正文添加到编辑器缓冲区
hello world
.                       行首的点表示停止添加
1, $p                   观察缓冲区第一行到最后一行
hello world
w temp.foo              将缓冲区写到一个文件
12                      写了12个字符
^C                      键入中断符
?                        编辑器捕捉并打印中断符,但是父进程没有捕捉中断并打印“caught SIGINT”?
^C
?
q                       离开编辑器
caught SIGCHLD

本例与书中的不符,中断信号没有被送到所有前台进程,可能system已经被改善了。

system函数的POSIX.1正确实现
1、当键入中断或退出字符时,不向调用进程发生信号。因为在调用进程已经设置为忽略信号处理函数,在子进程中恢复信号处理,并调用execl。
2、当ed命令终止时,不向进程发送SIGCHLD信号。在调用进程waitpid取得子进程终止状态后,解除对SIGCHLD的阻塞。

#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);  /* always a command processor with UNIX */

    ignore.sa_handler = SIG_IGN;   /* ignore SIGINT and SIGQUIT */
    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);   /* now block SIGHLD */
    sigaddset(&chldmask, SIGCHLD);
    if(sigprocmask(SIG_BLOCK, &chldmask, &savemask) < 0)
        return (-1);

    if((pid = fork()) < 0)
    {
        status = -1;  /* probably out of process */
    }
    else if(pid == 0)  /* child */
    {
        /* restore previous signal actions & reset signal mask */
        sigaction(SIGINT, &saveintr, NULL);
        sigaction(SIGQUIT, &savequit, NULL);
        sigprocmask(SIG_SETMASK, &savemask, NULL);

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

    /* restore previous signal actions & reset signal mask */
    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);
}

sleep的可靠实现
1、保存之前的SIGALRM信号处理函数,并设置新的SIGALRM信号处理函数。
2、保存信号屏蔽字,并阻塞SIGALRM信号。
3、计时开始。
4、到时后,解除对信号SIGALRM信号的阻塞,信号处理程序sig_alrm返回后唤醒sigsuspend
5、清除计时器。
6、恢复SIGALRM信号处理函数。
7、恢复信号屏蔽字。

#include "apue.h"

static void sig_alrm(int signo)
{
    /* nothing to do, just returning wakes up sigsuspend() */
}

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

    /* set our handler, save previous information */
    newact.sa_handler = sig_alrm;
    sigemptyset(&newact.sa_mask);
    newact.sa_flags = 0;
    sigaction(SIGALRM, &newact, &oldact);

    /* block SIGALRM and save current signal mask */
    sigemptyset(&newmask);
    sigaddset(&newmask, SIGALRM);
    sigprocmask(SIG_BLOCK, &newmask, &oldmask);

    alarm(nsecs);

    suspmask = oldmask;   
    sigdelset(&suspmask, SIGALRM);  /* make sure SIGALRM isn't blocked */
    sigsuspend(&suspmask);  /* wait for any signal to be caught */

    unslept = alarm(0);
    sigaction(SIGALRM, &oldact, NULL);  /* reset previous action */

    /* reset signal mask, which unblocks SIGALRM */
    sigprocmask(SIG_SETMASK, &oldmask, NULL);
    return (unslept);
}

如何处理交互式停止信号SIGTSTP
当键入挂起字符,调用SIGTSTP信号处理程序。应进行与终端有关的处理:将光标移到左下角,恢复终端工作方式等。
在将SIGTSTP复位为默认值,并解除对信号的阻塞后,向自己发送SIGTSTP,系统停止该进程。仅当某个进程向该进程发送SIGCONT信号时,才继续。这时从kill返回,程序继续运行时,将SIGTSTP信号复位为捕捉,并做终端处理,如重新绘制屏幕。

#include "apue.h"

#define BUFFSIZE 1024

static void sig_tstp(int);

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

    exit(0);
}

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

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

    /* Unblcok 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 untile we're continued */
    signal(SIGTSTP, sig_tstp);  /* restablish signal handler */

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

这个例子没懂

猜你喜欢

转载自blog.csdn.net/u012319493/article/details/80426385