• 信号


    函数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
    ");
                if(signal(SIGUSR2,sig_usr)==SIG_ERR)
                    printf("can't catch SIGUSR2
    ");
                for(;;)
                    pause();
            }
    
            static void sig_usr(int signo)
            {
                if(signo==SIGUSR1)
                    printf("received SIGUSR1
    ");
                else if(signo==SIGUSR2)
                    printf("received SIGUSR2
    ");
                else
                    printf("received signal %d
    ",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
    ");
                if((pid=fork())<0)
                    printf("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
    ");
    
                if(signal(SIGCLD,sig_cld)==SIG_ERR)
                    printf("signale error
    ");
                if(pid=wait(&status)<0)
                    printf("wait error
    ");
                printf("pid =%d
    ",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
    ");
    
            alarm(10);
            if((n=read(STDIN_FILENO,line,MAXLINE))<0)
                printf("read error
    ");
            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
    ");
                if(setjmp(env_alrm)!=0)
                    printf("read timeout
    ");
                alarm(10);
                if((n=read(STDIN_FILENO,line,MAXLINE))<0)
                    printf("read error
    ");
                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
    ");
                else{
                    printf("%s",str);
                    if(sigismember(&sigset,SIGINT))
                            printf("SIGINT
    ");
                    if(sigismember(&sigset,SIGQUIT))
                            printf("SIGQUIT
    ");
                    if(sigismember(&sigset,SIGUSR1))
                            printf("SIGUSER1
    ");
                    if(sigismember(&sigset,SIGALRM))
                            printf("SIGALRM
    ");
    
                    printf("
    ");
                }
                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
    ");
    
                sigemptyset(&newmask);
                sigaddset(&newmask,SIGQUIT);
                if(sigprocmask(SIG_BLOCK,&newmask,&oldmask)<0)
                    printf("SIG_BLOCK error
    ");
                sleep(5);//SIGQUIT  here will remain pending
    
                if(sigpending(&pendmask)<0)
                    printf("sigpending error
    ");
                if(sigismember(&pendmask,SIGQUIT))
                    printf("
    SIGQUIT pending
    ");
    
                if(sigprocmask(SIG_SETMASK,&oldmask,NULL)<0)
                    printf("SIG_SETMASK error
    ");
                printf("SIGQUIT unblocked
    ");
    
                sleep(5);
                exit(0);
            }
    
            static void sig_quit(int signo)
            {
                printf("caught SIGQUIT
    ");
                if(signal(SIGQUIT,SIG_DFL)==SIG_ERR)
                    printf("can't reset SIGQUIT
    ");
            }   
    
            $ ./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:
    ");
    
                if(signal(SIGINT,sig_int)==SIG_ERR)
                    printf("signal(SIGINT) error
    ");
                sigemptyset(&waitmask);
                sigaddset(&waitmask,SIGUSR1);
                sigemptyset(&newmask);
                sigaddset(&newmask,SIGINT);
    
                if(sigprocmask(SIG_BLOCK,&newmask,&oldmask)<0)
                    printf("SIG_BLOCK error
    ");
                printf("int critical refion:");
    
                if(sigsuspend(&waitmask)!=-1)
                    printf("sigsuspend error
    ");
                printf("after return from sigsuspend:");
                if(sigprocmask(SIG_SETMASK,&oldmask,NULL)<0)
                    printf("SIG_SETMASK error
    ");
                printf("program exit:");
                exit(0);
            }
    
            static void sig_int(int signo)
            {
                printf("
    in 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("
    interrupt
    ");
                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
    ");
                if(signal(SIGQUIT,sig_int)==SIG_ERR)
                    printf("signal(SIGQUIT) error
    ");
    
                sigemptyset(&zeromask);
                sigemptyset(&newmask);
                sigaddset(&newmask,SIGQUIT);
    
                if(sigprocmask(SIG_BLOCK,&newmask,&oldmask)<0)
                    printf("SIG_BLOCK error
    ");
    
                while(quitflag==0)
                    sigsuspend(&zeromask);
    
                quitflag=0;
    
                if(sigprocmask(SIG_SETMASK,&oldmask,NULL)<0)
                    printf("SIG_SETMASK error
    ");
    
                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
    ");
    
                if(n<0)
                    printf("read error
    ");
    
                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。
    
    技术不分国界
  • 相关阅读:
    【一篇文章就够了】Apollo
    【一篇文章就够了】Redis入门
    使用IDEA学习廖雪峰官方Git教程
    SourceTree的使用介绍
    【黑马旅游网】总结
    Entity Framework数据迁移命令使用总结
    leetcode editor
    IDEA个性化配置
    mysql5.7驱动(tableau,excel)
    正则表达式(1)
  • 原文地址:https://www.cnblogs.com/angels-yaoyao/p/12443613.html
Copyright © 2020-2023  润新知