zoukankan      html  css  js  c++  java
  • linux学习之信号篇(二)

    信号(二)

    1.信号捕捉设定

    #include <signal.h>
    int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact);
    
    struct sigaction 定义:
    struct sigaction 
    {
       void (*sa_handler)(int);//函数指针
       void (*sa_sigaction)(int, siginfo_t *, void *);//和上边的函数时互斥的,两个只能选一个
        sigset_t sa_mask;//调用捕捉函数时,临时使用的阻塞函数集,避免重复嵌套,调用完成后,又恢复为之前的 
        int sa_flags;//指定调用上边哪个函数
        void (*sa_restorer)(void);//保留,已经过时了
    };
    sa_handler : 早期的捕捉函数

      当执行捕捉函数时,默认执行的是,当前信号的屏蔽字自动置1,当执行完后,会自动恢复,响应之前在捕捉函数执行时,发生的信号。信号产生后并不一定是第一时间被响应。

    信号捕捉

    练习:

    #include<stdio.h>
    #include<signal.h>
    
    void do_sig(int num)
    
    {
    
      printf("I am do_sig
    ");
    
      printf("num=%d
    ",num);
    
    }
    
    int main(void)
    
    {
    
       struct sigaction act;
    
       act.sa_handler=do_sig;
       //act.sa_handler=SIG_DFL;
       //act.sa_handler=SIG_IGN;
    sigemptyset(&act.sa_mask); act.sa_flags=0; sigaction(SIGINT,&act,NULL); while(1) { printf("************ ") sleep(1); } return 0; } 运行结果: ************ ************ ************ ^c I am do_sig num=2//信号的编号

     2.利用SIGUSR1和SIGUSR2实现父子进程同步输出

    注意:子进程继承了父进程的信号屏蔽字和信号处理动作

    例子:设计一个父子进程,交替数数,并打印身份。

    3.C标准库信号处理函数(在window和linux,unix中都可以用)

    typedef void (*sighandler_t)(int)
    sighandler_t signal(int signum, sighandler_t handler)
    int system(const char *command)
    集合fork,exec,wait一体,子进程执行通过exec执行command  

    signal接口简单,可跨平台,缺点没有sigaction功能强大。

    4.可重入函数(和同步异步有关,多线程有关)

    不可重入函数

    * 不含全局变量和静态变量是可重入函数的一个要素
    * 可重入函数见man 7 signal
    * 在信号捕捉函数里应使用可重入函数

    * 在信号捕捉函数里禁止调用不可重入函数

    例如:上述的链表的插入函数就是一个不可重入函数。

      strtok就是一个不可重入函数,因为strtok内部维护了一个内部静态指针,保存上一次切割到的位置,如果信号的捕捉函数中也去调用strtok函数,则会造成切割字符串混乱,应用strtok_r版本,r表示可重入。

    #include<stdio.h>
    #include<string.h>
    int main(void)
    {
         char buf[]="hello world itcast xwp";
         char *save=buf,*p;
        
          while((p=strtok_r(save," ",&save))!=NULL); 
                 printf("%s
    ",p);
          return 0;
    }
    
    输出:
    hello
    world
    itcast
    xwp

    5.信号引起的竞态和异步I/O

    时序竞态

    int pause(void)
    使调用进程挂起,直到有信号递达,如果递达信号是忽略,则继续挂起
    int sigsuspend(const sigset_t *mask)
    1.以通过指定mask来临时解除对某个信号的屏蔽,
    2.然后挂起等待,
    3.当被信号唤醒sigsuspend返回时,进程的信号屏蔽字恢复为原来的值
    #include <unistd.h>
    #include <signal.h>
    #include <stdio.h>
    void sig_alrm(int signo)
    {
    /* nothing to do */
    }
    unsigned int mysleep(unsigned int nsecs)
    {
        struct sigaction newact, oldact;
        unsigned int unslept;
        newact.sa_handler = sig_alrm;
        sigemptyset(&newact.sa_mask);
        newact.sa_flags = 0;
        sigaction(SIGALRM, &newact, &oldact);
        alarm(nsecs);
        pause();//若在某一个时刻,有一个进程使用了内核,内核使用的时间是alarm(nsecs)时间,会导致pause永远不会被执行,也称之为时序竞态。
        unslept = alarm(0);
        sigaction(SIGALRM, &oldact, NULL);
        return unslept;
    }
    int main(void)
    {
        while(1)
        {
            mysleep(2);
            printf("Two seconds passed
    ");
         }
        return 0;
    }               

    mysleep的改进版

    unsigned int mysleep(unsigned int nsecs)//返回值时未睡够的时间,正常返回0
    {
        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 */ //阻塞SIGALRM
        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挂起等待,当定时器到时,产生SIGALRM信号,唤起*/
    /* some signal has been caught, SIGALRM is now blocked */
        unslept = alarm(0);//返回未睡够的时间
        sigaction(SIGALRM, &oldact, NULL); /* reset previous action */
    /* reset signal mask, which unblocks SIGALRM */
        sigprocmask(SIG_SETMASK, &oldmask, NULL);
        return(unslept);
    }        

    避免异步I/O的类型

    sig_atomic_t
    平台下的原子类型,32位是一个int型,64位是一个long型
    volatile
    防止编译器开启优化选项时,优化对内存的读写,改进异步I/O的信号传递不便的情况

    6.SIGCHLD信号处理

    SIGCHLD的产生条件(向父进程发送,若子进程死的话父进程负责回收子进程资源)

    子进程终止时
    子进程接收到SIGSTOP信号停止时
    子进程处在停止态,接受到SIGCONT后唤醒时

    status处理方式

    pid_t waitpid(pid_t pid, int *status, int options)
    
        options
            WNOHANG
                没有子进程结束,立即返回
            WUNTRACED
                如果子进程由于被停止产生的SIGCHLD, waitpid则立即返回
             WCONTINUED
                如果子进程由于被SIGCONT唤醒而产生的SIGCHLD, waitpid则立即返回
        获取status
            WIFEXITED(status)
                   子进程正常exit终止,返回真
                    WEXITSTATUS(status)返回子进程正常退出值
            WIFSIGNALED(status)
                子进程被信号终止,返回真
                    WTERMSIG(status)返回终止子进程的信号值
            WIFSTOPPED(status)
                   子进程被停止,返回真
                    WSTOPSIG(status)返回停止子进程的信号值
            WIFCONTINUED(status)
                      子进程由停止态转为就绪态,返回真                                                       
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <errno.h>
    #include <sys/types.h>
    #include <sys/wait.h>
    #include <signal.h>
    void sys_err(char *str)
    {
        perror(str);
        exit(1);
    }
    void do_sig_child(int signo)
    {
        int status;
        pid_t pid;
        //回收了子进程的资源
        while ((pid = waitpid(0, &status, WNOHANG)) > 0) 
        {
    
            if (WIFEXITED(status))
                printf("child %d exit %d
    ", pid, WEXITSTATUS(status));//打印进程的退出值时多少
            else if (WIFSIGNALED(status))//判断是否是被信号终止的
                printf("child %d cancel signal %d
    ", pid,         WTERMSIG(status));
        }
    }
    
    int main(void)
    {
        pid_t pid;
        int i;
        //阻塞SIGCHLD
        for (i = 0; i < 10; i++) 
       {
            if ((pid = fork()) == 0)
                break;
            else if (pid < 0)
                sys_err("fork");
       }
    
          if (pid == 0) 
          {
             int n = 18;
             while (n--)
             {
                    printf("child ID %d
    ", getpid());
                    sleep(1);
              }
              return i;
         }
         else if (pid > 0)
         {
            //先设置捕捉
            //再解除对SIGCHLD的阻塞
            struct sigaction act;
            act.sa_handler = do_sig_child;
            sigemptyset(&act.sa_mask);
            act.sa_flags = 0;
            sigaction(SIGCHLD, &act, NULL);
            while (1)
            {
               printf("Parent ID %d
    ", getpid());
               sleep(1);
            }
         }
            return 0;
    }                                      

    7.向信号捕捉函数传参

    sigqueue

    int sigqueue(pid_t pid, int sig, const union sigval value)
    union sigval {
       int sival_int;
       void *sival_ptr;
    };

    sigaction

    void (*sa_sigaction)(int, siginfo_t *, void *)
    siginfo_t {
        int si_int; /* POSIX.1b signal */
        void *si_ptr; /* POSIX.1b signal */
        sigval_t si_value; /* Signal value */
        ...
    }
    sa_flags = SA_SIGINFO

    8.信号中断系统调用

    read阻塞时,信号中断系统调用(要习惯返回read和write的值):
    1.返回部分读到的数据
    2.read调用失败,errno设成EINTER



  • 相关阅读:
    从 Objective-C 里的 Alloc 和 AllocWithZone 谈起
    设计模式(26)
    iOS目录结构
    IOS didReceiveMemoryWarning 的那些事
    iOS多线程编程:线程同步总结
    我要进大厂之大数据Hadoop HDFS知识点(2)
    我要进大厂之大数据Hadoop HDFS知识点(1)
    我要进大厂之大数据ZooKeeper知识点(2)
    我要进大厂之大数据ZooKeeper知识点(1)
    rabbitmq使用延迟时报异常
  • 原文地址:https://www.cnblogs.com/rainbow1122/p/7834928.html
Copyright © 2011-2022 走看看