信号

函数signle:
    #icnlude <signal.h>
    void (*signal(int signo,void (*func)(int)))(int);
        返回值:成功,返回以前的信号处理配置,出错,返回SIG_ERR
    signo参数是信号名。func的值是常量SIG_IGN、常量SIG_DEL或当接到此信号后调用的函数的地址。如果指定SIG_IGN,则向内核表示忽略此信号。
    (记住两个信号不能忽略SIGKILL和SIGSTOP)。如果指定SIG_DFL,则表示接到信号后的动作是系统默认动作。当指定函数地址时,信号发生时,
    调用该函数,我们称这种处理为捕捉该信号。

    例子:
        #include <stdio.h>
        #include <signal.h>

        static void sig_usr(int);

        int main()
        {
            if(signal(SIGUSR1,sig_usr)==SIG_ERR)
                printf("can't catch SIGUSR1\n");
            if(signal(SIGUSR2,sig_usr)==SIG_ERR)
                printf("can't catch SIGUSR2\n");
            for(;;)
                pause();
        }

        static void sig_usr(int signo)
        {
            if(signo==SIGUSR1)
                printf("received SIGUSR1\n");
            else if(signo==SIGUSR2)
                printf("received SIGUSR2\n");
            else
                printf("received signal %d\n",signo);
        }
    执行:
    $ ./signal &
    [1] 5904
    $ kill -USR2 5904
    received SIGUSR2
    $ kill -USR1 5904
    received SIGUSR1
    $ kill  5904
    [1]+  Terminated              ./signal

    因为执行的进程不捕捉SIGTERM的信号,而对该信号的系统默认动作是终止,所以当向该进程发送SIGTERM信号后,该进程就终止。

中断系统的调用:
    被中断的系统调用相关的问题是必须显式地处理出错返回。典型的代码序列如下(假定一个读操作,它被中断,我们希望重启它):
    again:
        if((n=read(fd,buf,BUFFSIZE))<0)
        {
            if(errno==EINTR)
                goto agin;
        }
    按系统而分,有的系统提供了sigaction默认方式是重启动被中断的系统调用。


可重入函数:
    进程捕获到信号并对其进行处理时,进程正在执行的正常指令序列就被信号处理程序临时中断,它首先执行该信号处理程序中的
    指令。

SIGCLD语义:
    SIGCLD和SIGCHLD这两个信号很容易混淆。SIGCLD(没有H)是一个SystemV的一个信号名,其语义名为SIGCHLD的BSD信号不同。
    BSD的SIGCHLD信号语义与其他信号的语义向类似。子进程状态改变产生此信号,父进程需要调用一个wait函数以检测发生了什么。

    SystemV处理SIGCLD信号的方式不同于其他信号。如果用signal或者sigset设置信号配置,则基于SVR4的系统继承了这一具有问题色彩
    的传统。对于SIGCLD的早期处理方式是:

    1、如果进程明确地将信号的配置设置为SIG_IGN,则调用进程的子进程将不产生僵死进程。注意,这与其默认动作(SIG_DFL)忽略不同。
    子进程在终止时,将其状态丢弃。如果调用进程随后调用一个wait函数,那么它将阻塞直到所有子进程都终止,然后该wait会返回-1,并
    将其errno设置为ECHILD。(此信号的默认设置是忽略,但这不会使上述语义起作用。必须将其配置明确指定为SIG_IGN才可以)。

    也可以使用sigaction可设置SA_NOCLDWAIT标志以避免进程僵死。

    2、如果将SIGCLD的配置设置为捕捉,则内核立即检查是否有子进程准备好被等待,如果是这样,则调用SIGCLD处理程序。

    程序一行行地不断重复输出"SIGCLD received",最后进程用完其栈空间并异常终止。
    例子程序:
        #include <unistd.h>
        #include <stdlib.h>
        #include <stdio.h>
        #include <sys/wait.h>
        #include <signal.h>

        static void sig_cld(int);

        int main()
        {
            pid_t pid;

            if(signal(SIGCLD,sig_cld)==SIG_ERR)
                printf("signal error\n");
            if((pid=fork())<0)
                printf("fork error\n");
            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)
                printf("signale error\n");
            if(pid=wait(&status)<0)
                printf("wait error\n");
            printf("pid =%d\n",pid);
        }
    执行:
    $ ./signal1
    SIGCLD received
    pid =0
    此程序的问题是:在信号处理程序的开始处调用signal,按照上述第2种方式,内核检查是否有需要等待的子进程(因为我们正在
    处理一个SIGCLD信号,所以确实有这种子进程),所以它产生另一个信号处理程序的调用。信号处理程序再次调用signal,整个
    过程再次重复。

    解决这个问题,应当在调用wait取得子进程的终止状态后再调用signal。


可靠信号术语和语义:
    当产生一个信号时,内核通常在进程中以某种形式设置一个标志。

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

    信号递送的顺序是:递送与当前进程状态有关的信号。

    进程可以使用sigprocmask来检测和更改其当前信号屏蔽字。

函数kill和raise:
    kill函数将信号发送给进程或者进程组。raise函数则允许进程向自身发送信号。
    #include <signal.h>
    int kill(pid_t pid,int signo);
    int raise(int signo);

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

函数alarm和pause:
    使用alarm函数可以设置一个定时器(闹钟时间),在将来的某个时刻该定时器会超时。当
    定时器超时时,产生SIGALRM信号。如果忽略或不捕捉此信号,则其默认动作时终止调用该
    alarm函数的进程:
    #include <unistd.h>
    unsigned int alarm(unsigned int seconds);
        返回值:0或者以前设置的闹钟时间的余留秒数。
    参数seconds的值时产生信号SIGALRM需要经过的时钟秒数。

    pause函数使进程挂起直至捕获到一个信号:
    #include <unistd.h>
    int pause(void);
        返回值:-1,errno设置为EINTR
    只有执行了一个信号处理程序并从其返回时,pause才返回。在这种情况下,pause返回-1,errno设置为EINTR。

    程序中有一个读低速设备的可能阻塞的操作,我们希望超过一定时间量后就停止执行该操作:
    #include <unistd.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <signal.h>

    static void sig_alrm(int);

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

        if(signal(SIGALRM,sig_alrm)==SIG_ERR)
            printf("signal(SIGALRM) error\n");

        alarm(10);
        if((n=read(STDIN_FILENO,line,MAXLINE))<0)
            printf("read error\n");
        alarm(0);
        write(STDOUT_FILENO,line,n);
        exit(0);
    }
    static void sig_alrm(int signo)
    {

    }
    1、如果在第一次alarm调用和read调用之间有一个竞争条件。如果内核在这两个函数调用之间使进程阻塞,不能占用处理机运行,
    而其时间长度又超过闹钟时间,则read可能永远阻塞。大多数这种类型的操作使用较长的闹钟时间,例如1分钟或者更长一点,使
    这种问题不会发生。
    2、如果系统调用是自动重启动的,则当从SIGALRM信号处理程序返回时,read并不被中断。

    下列就不用担心一个慢速的系统调用:
        #include <unistd.h>
        #include <stdlib.h>
        #include <stdio.h>
        #include <signal.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)
                printf("signal(SIGALRM) error\n");
            if(setjmp(env_alrm)!=0)
                printf("read timeout\n");
            alarm(10);
            if((n=read(STDIN_FILENO,line,MAXLINE))<0)
                printf("read error\n");
            alarm(0);

            write(STDOUT_FILENO,line,n);
            exit(0);
        }
        static void sig_alrm(int signo)
        {
            longjmp(env_alrm,1);
        }



信号集:
    #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);
        返回值:成功,返回0,出错,返回-1
    int sigismember(const sigset_t *set,int signo);
        返回值:成功,返回1,假,返回0
    函数sigemptyset初始化由set指向的信号集,清除其中所有的信号。函数sigfillset初始化由set指向的信号集,使其包括所有信号。
    所有应用程序在使用信号集前,要对该信号集调用sigemptyset或者sigfillset一次。这是因为C编译程序将赋初值的外部变量和静态
    变量都初始化为0,而这是否与给定系统上信号集的实现相对应却不清楚。

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

    sigemptyset函数将整形设置为0,sigfillset函数将整形设置为0,sigfillset函数则将整形中的各位都设置为1.这两个函数可以
    在<signal.h>头文件中实现宏:
    #define sigemptyset(ptr) (*(ptr)=0)
    #define sigfillset(ptr) (*(ptr)=(sigset_t)0,0)



    sigaddset、sigdelset和sigismember的实现原型:
        #include <signal.h>
        #include <errno.h>

        //<signal.h> ususlly defines NSIG to include signal number 0.
        #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);
        }

函数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是或操作,而SIG_SETMASK是赋值操作。注意,不能阻塞SIGKILL和SIGSTOP信号。

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

    程序是一个函数,它打印调用进程信号屏蔽字中的信号名。
        #include <unistd.h>
        #include <stdlib.h>
        #include <stdio.h>
        #include <signal.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)
                printf("sigprocmask error\n");
            else{
                printf("%s",str);
                if(sigismember(&sigset,SIGINT))
                        printf("SIGINT\n");
                if(sigismember(&sigset,SIGQUIT))
                        printf("SIGQUIT\n");
                if(sigismember(&sigset,SIGUSR1))
                        printf("SIGUSER1\n");
                if(sigismember(&sigset,SIGALRM))
                        printf("SIGALRM\n");

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

函数sigpending:
    sigpending函数返回一信号集,对于调用进程而言,其中的各信号是阻塞不能递送的,因而也一定是当前未决的。
    信号集通过set参数返回。
    #include <signal.h>
    int sigpending(sigset_t *set);
        返回值:成功,返回0,出错,返回-1.

    信号:
        #include <unistd.h>
        #include <stdlib.h>
        #include <stdio.h>
        #include <signal.h>

        static void sig_quit(int);

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

            if(signal(SIGQUIT,sig_quit)==SIG_ERR)
                printf("can't catch SIGQUIT\n");

            sigemptyset(&newmask);
            sigaddset(&newmask,SIGQUIT);
            if(sigprocmask(SIG_BLOCK,&newmask,&oldmask)<0)
                printf("SIG_BLOCK error\n");
            sleep(5);//SIGQUIT  here will remain pending

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

            if(sigprocmask(SIG_SETMASK,&oldmask,NULL)<0)
                printf("SIG_SETMASK error\n");
            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)
                printf("can't reset SIGQUIT\n");
        }   

        $ ./sigpending
        ^\
        SIGQUIT pending
        caught SIGQUIT
        SIGQUIT unblocked
        ^\Quit (core dumped)

        $ ./sigpending
        ^\^\^\^\^\^\^\^\^\^\^\
        SIGQUIT pending
        caught SIGQUIT
        SIGQUIT unblocked
        ^\Quit (core dumped)
    在休眠期间如果产生了退出信号,那么此时信号是未决的,但是不再受阻塞,所以在sigprocmask返回之前,它被递送到调用进程。
    从程序的输出在中可以看出这一点:SIGQUIT处理程序(sig_quit)中的printf语句先执行,然后再执行sigprocmask之后的prinf语句。

    注意可以看出系统上没有将信号进行排队。

函数sigaction:
    sigaction函数的功能是检查或者修改与指定信号相关联的处理动作。此函数取代了UNIX早期版本使用signal函数。在下面用sigaction
    函数实现了signal。
    #include <signal.h>
    int sigaction(int signo,const sigaction *restrict act,struct sigaction *restrict oact);
        返回值:成功,0,出错,-1。
    其中,参数signo是要检测或者修改具体动作的信号编号。
    若act指针非空,则要修改其动作。
    如果oact指针非空,则系统经由oact指针返回该信号的上一个动作。

    struct sigaction{
    void (*sa_handler)(int);//addr的信号处理程序
    sigset_t sa_mask;//额外的信号屏蔽
    int sa_flags;//信号的选择
    void (*sa_sigaction)(int,siginfo_t *,void *);//交替处理程序
    }

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

    但是,如果设置了SA_SIGINFO标志,那么按下列方式调用信号处理程序:
    void hangler(int signo,siginfo_t *info,void *context);
    siginfo结构包含了信号产生原因的有关信息。该结构的大致样式如下所示。
    struct siginfo{
    int si_signo;//signal number
    int si_errno;//if nonzero,errno value from <error.h>
    int si_code;//additional info 
    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
    union sigval si_value;//application-specific value
    }

    sigval联合包含下列字段:
    int sival_int;
    void *sival_ptr;

函数sigsetjmp和siglongjmp:
    在信号处理程序中经常调用longjmp函数以返回到程序的主循环中,而不是从该处理程序返回。
    #include <setjmp.h>
    int sigsetjmp(sigjmp_buf env,int savemask);
        返回值:若直接调用,返回0,若从siglongjmp调用返回,则返回非0
    void siglongjmp(sigjmp_buf env,int val);

    与setjmp、longjmp之间的区别时sigsetjmp增加了一个参数。如果savemask非0,则sigsetjmp在env中
    保存进程的当前信号屏蔽字。调用siglongjmp时如果非0savemask的sigsetjmp调用已经保存了env,则
    siglongjmp从其中恢复保存的信号屏蔽字。

函数sigsuspend:
    比如在信号阻塞的时候,产生一个信号,那么信号的传递就会被推迟直到对它解除了阻塞。
    如果在解除阻塞时刻和pause之间确实产生了信号,可能永远不会见到该信号,这样pause就会永远阻塞。
    为了纠正这个问题,需要在一个原子操作中先恢复信号屏蔽字,然后使进程休眠。sigsuspend正好:
    #include <signal.h>
    int sigsuspend(const sigset_t *sigmask);
    进程信号的屏蔽字设置为由sigmask指向的值。在捕捉到一个信号或者发生了一个终止该进程的信号之前,该
    进程被挂起。如果捕捉到一个信号而且从信号处理程序返回,则sigsuspend返回,并且该进程的信号屏蔽字
    设置为调用sigsuspend之前的值。
    注意,此函数没有成功返回值。如果它返回到调用这,则总是返回-1.并将errno设置为EINTR(表示一个被
    中断的系统调用)。

    保护临界区不被信号中断:
        #include <unistd.h>
        #include <stdlib.h>
        #include <stdio.h>
        #include <signal.h>

        static void sig_int(int);

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

            printf("program start:\n");

            if(signal(SIGINT,sig_int)==SIG_ERR)
                printf("signal(SIGINT) error\n");
            sigemptyset(&waitmask);
            sigaddset(&waitmask,SIGUSR1);
            sigemptyset(&newmask);
            sigaddset(&newmask,SIGINT);

            if(sigprocmask(SIG_BLOCK,&newmask,&oldmask)<0)
                printf("SIG_BLOCK error\n");
            printf("int critical refion:");

            if(sigsuspend(&waitmask)!=-1)
                printf("sigsuspend error\n");
            printf("after return from sigsuspend:");
            if(sigprocmask(SIG_SETMASK,&oldmask,NULL)<0)
                printf("SIG_SETMASK error\n");
            printf("program exit:");
            exit(0);
        }

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

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

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

        //volatile的作用是作为指令关键字,确保本条指令不会因编译器的优化而省略,且要求每次直接读值
        //sig_atomic_t会保证该变量在使用或赋值时, 无论是在32位还是64位的机器上都能保证操作是原子的, 
        //它会根据机器的类型自动适应。
        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)
                printf("signal(SIGINT) error\n");
            if(signal(SIGQUIT,sig_int)==SIG_ERR)
                printf("signal(SIGQUIT) error\n");

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

            if(sigprocmask(SIG_BLOCK,&newmask,&oldmask)<0)
                printf("SIG_BLOCK error\n");

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

            quitflag=0;

            if(sigprocmask(SIG_SETMASK,&oldmask,NULL)<0)
                printf("SIG_SETMASK error\n");

            exit(0);
        }


函数abort:
    abort函数的功能是使程序异常终止。
    #include <stdlib.h>
    void abort(void);
    此函数将SIGABRT信号发送给调用进程(进程不应该忽略此信号)。

函数sleep、nanosleep和clock_nanosleep:
    程序中的两个sleep的实现,但是它们是缺陷的:
    #include <unistd.h>
    unsigned int sleep(unsigned int seconds);
        返回值:0或者未休眠完的秒数
    此函数使调用进程被挂起直到满足下面两个条件之一:
    1、已经过了seconds所指定的强上时钟时间
    2、调用进程捕获到一个信号并从信号处理程序中返回。


    nanosleep函数与sleep函数类似,但提供了纳秒级的精度:
    #include <time.h>
    int nanosleep(const struct timespec *reqtp,struct timespec *remtp);
        返回值:若休眠到要求的时间,返回0,出错,返回-1
    这个函数挂起调用进程,直到要求的时间已经超时或者某个信号中断了该函数。reqtp参数用秒和纳秒指定了需要休眠的时间长度。
    如果某个信号中断了休眠间隔,进程并没有终止,remtp参数指向timespec结构就会被设置为未休眠完的时间长度。如果对未休眠
    完的时间并不感兴趣,可以把该参数设置为NULL。


    使用相当于特定时钟的延迟时间来挂起调用线程。clock_nanosleep函数提供了这种功能:
    #include <time.h>
    int clock_nanosleep(clockid_t clock_id,int flags,const struct timespec *reqtp,struct timespec *remtp);
        返回值:若休眠要求的时间,返回0;出错,返回错误码
    clock_id参数指定了计算延迟时间基于的时钟。
    flags参数用于控制延迟时相对还是绝对的。为0时表示的是相对的,为TIMER_ABSTIME,表示的是绝对的。
    reqtp参数用秒和纳秒指定了需要休眠的时间长度
    remtp参数指向timespec结构就会被设置为未休眠完的时间长度。如果对未休眠完的时间并不感兴趣,可以把该参数设置为NULL。

    除了出错返回,调用:  
    //CLOCK_REALTIME : 这种时钟表示的是绝对时间, 指的是从1970年1月1月0:00到目前经过多少秒, 相当于你的linux系统中显示的时间,
    // 所以这个时间是可以更改的, 当系统的时钟源被改变,或者系统管理员重置了系统时间之后,这种类型的时钟可以得到相应的调整, 对设定
    //为此类型的timer是有影响的.
    clock_nanosleep(CLOCK_REALTIME,0,reqtp,remtp);
    和调用
    nanosleep(reqtp,remtp);
    效果是相同的。


函数sigqueue:
    我们介绍了大部分UNIX系统不对信号排队。
    使用排队信号必须做以下几个操作:
    1、使用sigaction函数安装信号处理程序时指定SA_SIGINFO标志。如果没有给出这个标志,信号会被延迟,但信号
    是否进入队列要取决于具体实现。
    2、在sigaction结构的sa_sigaction成员中(而不是通常的sa_handler字段)提供信号处理程序。实现可能允许用户
    使用sa_handler字段,但不能后取sigqueue函数发送出来的额外信息。
    3、使用sigqueue函数发送信号:
    #include <signal.h>
    int sigqueue(pid_t pid,int signo,const union sigval value);
        返回值:成功,返回0,出错,返回-1
    sigqueue只能把信号发送给单个进程,可以使用value参数向信号处理程序传递整数和指针值,除此之外,sigqueue函数与
    kill函数类似。
    信号不能被无限排队。对于SIGQUEUE_MAX限制。到达相应的限制以后,sigqueue就会失败,将errno设为EAGAIN。
    随着实时信号的增强,引入了用于应用程序的独立信号集。这些信号的编号在SIGRTMIN~SIGRTMAX之间,包括着两个限制值。
    注意,这些信号的默认行为时终止进程。

    行为                  linux3.2.0
    支持sigqueue              *
    对在SIGRTMIN和SIGREMAX之外的信号排队  
    即使调用者没使用SA_SIGINFO标志,也对信号排队 *

作业控制信号:
    POSIX.1认为有以下6个作业控制有关:
    SIGCHLD     子进程已停止或者终止
    SIGCONT     如果进程已停止,则使其继续运行
    SIGSTOP     停止信号(不能被捕捉或者忽略)
    SIGTSTP     交互式停止信号
    SIGTTIN     后台进程组成员读控制终端
    SIGTTOU     后台进程组成员写控制终端        

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

        #define BUFFSIZE 1024

        static void sig_tstp(int signo)//SIGTSTP 信号处理程序
        {
            sigset_t mask;

            //将光标移到左下角,重置tty模式。
            //打开SIGTSTP,因为它在我们处理的时候被阻塞了。
            sigemptyset(&mask);
            sigaddset(&mask,SIGTSTP);
            sigprocmask(SIG_UNBLOCK,&mask,NULL);

            signal(SIGTSTP,SIG_DFL);//重置配置默认

            kill(getpid(),SIGTSTP);//把信号发送给我们自己。
            //在我们继续之前,我们不会从kill中返回。
            signal(SIGTSTP,sig_tstp);//重建信号处理器
            //重置tty模式,屏幕重绘
        }

        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)
                        printf("write error\n");

            if(n<0)
                printf("read error\n");

            exit(0);
        }


信号名和编号:
    信号编号和信号名之间进行映射。某些系统提供数组:
    extern char *sys_siglist[];
    数组下标时信号编号,数组中的元素时指向信号名符串的指针。

    可以使用psignal函数可移植地打印与信号编号对应的字符串:
    #include <signal.h>
    void psignal(int signo,const char *msg);
    字符串msg(通常是程序名)输出到标准错误文件,后面跟随一个冒号和一个空格,再后面对该信号的说明,
    最后一个是换行符。如果msg为NULL,只有信号说明部分输出到标准错误文件,该函数类似与perror。

    如果在sigaction信号处理程序中有sigingo结构,可以使用psiginfo函数打印信号信息。
    #include <signal.h>
    void psiginfo(const siginfo_t *info,const char *msg);

    如果需要信号的字符描述部分,也不需要把它写到标准错误文件中(如可以写到日志文件中),可以使用strsignal
    函数,它类似于strerror:
    #include <string.h>
    char *strsignal(int signo);
        返回值:指向描述该信号的字符串的指针
    程序可以用来打印关于接受到信号的出错信息。

    一个函数将信号编号映射为信号名,另一个反之:
    #include <signal.h>
    int sig2str(int signo,char *str);
    int str2sig(const char *str,int *signo);
        返回值;成功,0,失败-1.
    当它们失败时,并不舍值errno。
posted @ 2018-03-26 22:30  带头大哥小白  阅读(138)  评论(0编辑  收藏  举报