[APUE] 再读之信号

1 . 每个信号都是以SIG 开头的正整数
2. 信号发生条件 用户敲击终端,比如按下ctrl+c (SIGINT) 发给所有前台进程组, ctrl+z 发 SIGSTOP 挂起前台进程,ctrl+\ 发SIGQUIT 给进程。而 ctrl+D 只是发EOF给终端
  • 硬件异常。 比如遇到除以0, 或者内存访问越界的情况。
  • kill 函数发相应的信号
  • 软件条件发生。 比如向一个已经关闭的管道写数据会产生SIGPIPE,以及闹钟信号SIGALRM。或者网络上传来一个非规定波特率的数据,SIGURG

      #include <stdio.h>
      #include <signal.h>
      
      static void sig_alrm(int signo)
      {
          printf("Caught alarm signal\n");
      }
      
      
      int main()
      {
          if (signal(SIGALRM,sig_alrm)==SIG_ERR)
          {
              printf("Register SIGALRM failed\n");
              return -1;
          }
          //register one second alarm
          alarm(1);
          pause();
          printf("Exit main\n");
      }
      //输出
      //Caught alarm signal
      //Exit main
      SIGPIPE的例子

      #include <stdio.h>
      #include <stdlib.h>
      #include <signal.h>
      
      
      static void sig_pipe(int signo)
      {
          printf("Caught signal pipe signal\n");
      }
      int main()
      {
          int fd[2];
          if(pipe(fd)<0)
          {
              printf("Create pipe failed\n");
              exit(-1);
          }
          pid_t pid;
          pid = fork();
          if (pid<0)
          {
              printf("fork error\n");
          }
          else if (pid>0)
          {
              close(fd[0]);
              // register pipe signal
              if(signal(SIGPIPE,sig_pipe)==SIG_ERR)
              {
                  printf("register SIGPIPE error\n");
                  sleep(1);
                  exit(-1);
              }
              sleep(1);
              printf("Parent Prcess begin\n");
              //try to write a closed pipe
              if(write(fd[1],'c',1)!=1)
                  printf("write pipe error\n");
              close(fd[1]);
              printf("Parent Prcess end\n");
          }
          else
          {
              printf("Child Prcess begin\n");
              //here close read and write channel
              close(fd[0]);
              close(fd[1]);
          }
      }
      
      输出

      Child Prcess begin
      Parent Prcess begin
      Caught signal pipe signal
      write pipe error
      Parent Prcess end

      Kill 函数的例子

      #include <stdio.h>
      #include <signal.h>
      
      static void sig_usr1(int signo)
      {
          printf("Get signal user1 \n");
      }
      int main()
      {
          if (signal(SIGUSR1, sig_usr1)==SIG_ERR)
          {
              printf("Register sig user1 failed\n ");
          }
      
          kill(getpid(),SIGUSR1);
          printf("End of main\n");
      }
      3. 对待信号的三种方式: 捕抓,忽视或者系统默认。 SIGKILL, SIGSTOP不可忽略。

      4. Signal 函数 void(* signal(int signo, void(*func)(int)  )  ) (int), 好复杂。

      typedef  void Sigfunc (int) 

      然后再定义 Sigfunc* signal(int signo, Sigfunc*)

      5. 程序后台启动时候shell 自动对SIGINT 和SIGQUIT 选择忽略

      6. 进程创建时候子进程复制父进程的信号处理方式

      #include <stdio.h>
      #include <stdlib.h>
      #include <signal.h>
      
      void sig_quit(int signo)
      {
          printf("caught SIGQUIT \n");
      }
      
      int main()
      {
      
          if (signal(SIGQUIT,sig_quit)==SIG_ERR)
          {
              printf("Register sigquit error\n");
          }
      
          pid_t pid ;
          pid = fork();
      
          if (pid< 0)
          {
              printf("fork error\n");
              exit(-1);
          }
          else if(pid>0)
          {
              while(1)
                  pause();
          }
          else
          {
              while(1)
                  pause();
          }
      
      }
      输入ctrl+\  时候会得到两行,分别为子进程和父进程产生的

      caught SIGQUIT 
      caught SIGQUIT 

      7. 中断的系统调用。

      系统调用分为低速系统调用和其他系统调用。低速系统调用,信号发生时, 其不再执行。

      低速系统调用包括:

      a. pause 函数

      b. 读某些文件,数据不存在;如果某进程为读打开FIFO,且此时没有为写的进程打开该FIFO

      #include <sys/types.h>
      #include <sys/stat.h>
      #include <fcntl.h>
      #include <stdio.h>
      #include <errno.h>
      #include <string.h>
      
      #define FIFO_NAME "/tmp/fifotest"
      #define BUF_LEN  512
      
      int main()
      {
          int res;
          extern int errno;
          res = mkfifo(FIFO_NAME, O_CREAT| O_EXCL);
          if ((res<0) && (errno!= EEXIST))
          {
              printf("We get an error %d, reason %s\n", errno, strerror(errno));
          }
      
          int fd = open(FIFO_NAME,O_RDONLY,0);
          char buf[BUF_LEN];
          int n;
      
          if ((n=read(fd, buf,BUF_LEN))<0)
          {
              printf("read error %d, reason %s\n", errno, strerror(errno));
          }
          buf[n]= '\0';
          printf("read data from fifo: %s\n", buf);
      
      }
      上面代码将一直阻塞,一直等到有其他程序写fifo为止。

      下面代码将写相应的fifo,运行下面代码可使得上面读fifo的进程激活,并输出内容。

      #include <stdio.h>
      #include <stdlib.h>
      #include <fcntl.h>
      #include <errno.h>
      #include <string.h>
      
      #define FIFO_NAME "/tmp/fifotest"
      
      int main()
      {
          int fd;
          fd = open(FIFO_NAME, O_WRONLY,0 );
      
          char* buf = "Greeting from fifo message\n";
      
          if(write(fd, buf, strlen(buf))!= strlen(buf))
          {
              printf("Write error. errno= %d, reason is : %s\n", errno, strerror(errno));
          }
      
      }



      c. 写某些文件,不能立即接受这些数据;打开文件时; 某种ioctl函数;某些进程间通信函数。

      8. 可再入函数

      典型的不可再入函数包括 a)malloc, free b) 操作静态区内容的函数, 比如getpwnam c)标准IO 函数

      9. 信号未决

      信号未决指的是信号产生,却还没有传递到进程的状态。

      如果一个信号发生多次,那最终进程接收信号时,只会被传递一次。

      10. Kill ,raise函数

      Kill函数参数: a) pid>0, 则发送给对应pid进程。 b) pid == 0 , 发送信号给所在进程的进程组所有进程 c) pid< 0, 发送给绝对值等于PID的进程组中所有进程

      #include <stdio.h>
      #include <signal.h>
      
      static void sig_usr1(int signo)
      {
          printf("Get signal user1 \n");
      }
      
      int main()
      {
          if (signal(SIGUSR1, sig_usr1)==SIG_ERR)
          {
              printf("Register sig user1 failed\n ");
          }
      
          kill(getpid(),SIGUSR1);
          printf("Test kill pid > 0 senario done\n");
      
          pid_t pid;
          if ((pid=fork())<0)
          {
              printf("Fork error\n ");
          }
      
          else if (pid> 0)
          {
              printf("pid = %d, group id = %d\n", getpid(), getpgrp());
              while(1)
                  pause();
          }
          else
          {
              printf("pid = %d, group id = %d\n", getpid(), getpgrp());
              kill(0,SIGUSR1);
          }
      }
      测试了kill pid>0 和pid=0的情况

      输出为:

      Get signal user1 
      Test kill pid > 0 senario done
      pid = 28220, group id = 28220
      pid = 28221, group id = 28220
      Get signal user1 
      Get signal user1 

      11. alarm 和pause 函数

      alarm 提示系统多长时间向进程发送SIGALRM信号. alarm 函数后注册的闹钟会把前面注册的给覆盖掉。比方说下面的例子,printf两次之间应该只是相隔一秒钟时间。

      time now is 1440655538
      Caught alarm signal
      time now is 1440655539

      #include <stdio.h>
      #include <signal.h>
      #include <time.h>
      
      static void sig_alrm(int signo)
      {
          printf("Caught alarm signal\n");
      }
      
      
      int main()
      {
          if (signal(SIGALRM,sig_alrm)==SIG_ERR)
          {
              printf("Register SIGALRM failed\n");
              return -1;
          }
          //register one second alarm
          printf("time now is %d\n", time(NULL));
          alarm(2);
          alarm(1);
          pause();
          printf("time now is %d\n", time(NULL));
      
      

      pause函数则阻塞进程,直到进程得到信号。

      用alarm, pause 和setjmp 设置的sleep 函数。缺点是当和其他信号发生冲突时,会有些问题。

      #include <stdio.h>
      #include <setjmp.h>
      #include <signal.h>
      static jmp_buf buf;
      static void sig_alrm(int signo)
      {
          longjmp(buf,1);
      }
      
      int sleep2(unsigned int seconds)
      {
          if (signal(SIGALRM, sig_alrm)==SIG_ERR)
          {
              printf("Register SIGALRM failed\n");
          }
      
          alarm(seconds);
          if(!setjmp(buf))
          {
              printf("set jump first return\n");
              pause();
          }
          else
          {
              printf("Set jump returned\n");
          }
      
          return seconds;
      }
      int main()
      {
          sleep2(1);
      }

      下面代码,在5秒总之内发生SIGINT 信号,将导致sig_int后面的语句无法执行完成。

      #include <stdio.h>
      #include <setjmp.h>
      #include <signal.h>
      static jmp_buf buf;
      static void sig_alrm(int signo)
      {
          longjmp(buf,1);
      }
      
      int sleep2(unsigned int seconds)
      {
          if (signal(SIGALRM, sig_alrm)==SIG_ERR)
          {
              printf("Register SIGALRM failed\n");
          }
      
          alarm(seconds);
          if(!setjmp(buf))
          {
              printf("set jump first return\n");
              pause();
          }
          else
          {
              printf("Set jump returned\n");
          }
      
          return seconds;
      }
      static void sig_int(int signo)
      {
          printf("Sum is 0\n");
          volatile int sum;
          int i;
          for(i=0;i<2000000000;i++)
              sum += i*i;
          printf("Sum is %d\n", sum);
      }
      int main()
      {
          if(signal(SIGINT,sig_int)==SIG_ERR)
          {
              printf("Register SIGINT failed\n");
          }
          sleep2(5);
      }

      12 信号集sigset_t

      int sigemptyset(sigset_t * )

      int sigfillset(sigset_t *)

      int sigdelset(sigset_t* ,int signo)

      int sigaddset(sigset_t* ,int signo)

      int sigismember(const sigset_t *, int signo)

      int sigprocmask(int how, const sig_set* new, sig_set* old)

      sigprocmask是个好函数,一个函数既可以获取当前信号屏蔽字,也可以设置信号屏蔽。

      how 等于SIG_BLOCK, SIG_UNBLOCK,SIG_SETMASK。前面为取并,第二个为取交,最后一个直接设置。 打印当前屏蔽字的demo

      #include <stdio.h>
      #include <signal.h>
      #include <errno.h>
      #include <string.h>
      
      void printmask()
      {
          int result;
          sigset_t new,old;
      
          if (sigprocmask(SIG_BLOCK,NULL, &old)<0)
          {
              printf("Sigprocmast failed. errno = %d, error is: %s\n", errno, strerror(errno));
              return;
          }
      
          if (sigismember( &old,SIGALRM))
              printf("sig_alrm has been in signal mask\n");
          printf("End of print mask\n");
      }
      
      int main()
      {
          sigset_t new,old;
          if(sigemptyset(&new)<0 || sigemptyset(&old)<0 || sigaddset(&new, SIGALRM)<-1)
          {
              printf("sigemtpy set error. errno = %d, error is : %s\n",errno,strerror(errno));
              return -1;
          }
      
          if (sigprocmask(SIG_BLOCK,&new, &old)<-1)
          {
              printf("sigprocmask set error. errno = %d, error is : %s\n",errno,strerror(errno));
              return -1;
          }
          printmask();
      }

      int sigpending(sigset_t*) 正确返回0,未决信号存储在参数中。下面代码执行后,中间按两次ctrl + c,得到结果

      SIGINT is in pending status
      Caught sig int 

      #include <stdio.h>
      #include <signal.h>
      #include <errno.h>
      #include <string.h>
      
      static void sig_int(int signo)
      {
          printf("Caught sig int \n");
      }
      
      int main()
      {
          sigset_t new,old;
          sigset_t pending;
          if (signal(SIGINT,sig_int) ==SIG_ERR)
          {
              printf("signal error\n");
              return;
          }
          if(sigemptyset(&new)<0 || sigemptyset(&old)<0 || sigemptyset(&pending)<0 || sigaddset(&new, SIGINT)<-1)
          {
              printf("sigemtpy or  sigemptyset error. errno = %d, error is : %s\n",errno,strerror(errno));
              return -1;
          }
      
          if (sigprocmask(SIG_BLOCK,&new, &old)<-1)
          {
              printf("sigprocmask set error. errno = %d, error is : %s\n",errno,strerror(errno));
              return -1;
          }
          sleep(5);
      
          if (sigpending(&pending)<-1)
          {
              printf("sigpending error. errno = %d, error is : %s\n",errno,strerror(errno));
              return -1;
          }
          if (sigismember(&pending, SIGINT))
              printf("SIGINT is in pending status\n");
      
          if (sigprocmask(SIG_SETMASK,&old, NULL)<-1)
          {
              printf("sigprocmask set error. errno = %d, error is : %s\n",errno,strerror(errno));
              return -1;
          }
      }

      13. sigaction 函数

      int sigaction(int signo, const sigaction* act, sigaction* oact)

      struct sigaction

      {

      void (*sighandler)(); //signal process function

      sigset_t mask; //mask 为信号屏蔽字,在sighandler 函数返回之前,会屏蔽信号。返回后再解除屏蔽,这样做的目的是保证sighandler的执行不会受到影响。

      int sa_flags;

      };

      14. sigsetjmp, setjmp, setlongjmp 和longjmp

      siglongjmp 从信号函数跳出后会恢复信号屏蔽字,而longjmp从信号函数跳出后则不会恢复信号。

      下面代码,当用sig开头函数时,每次向进程发送SIGUSR1信号时都会输出一次:

      Get signal usr1
      Returned from sigsetjmp

      而不用sig开头函数时,只有第一次向进程发送SIGUSR1才会输出:

      Get signal usr1
      Returned from sigsetjmp

      #include <stdio.h>
      #include <setjmp.h>
      #include <signal.h>
      #include <string.h>
      #include <errno.h>
      #include <stdlib.h>
      static jmp_buf buf;
      
      static void sig_usr(int signo)
      {
          printf("Get signal usr1\n");
          //siglongjmp(buf,1);
          longjmp(buf,1);
      }
      
      int main()
      {
          if (signal(SIGUSR1,sig_usr)==SIG_ERR)
          {
              printf("Register singal error %d. reason:%s\n", errno, strerror(errno));
              exit(-1);
          }
      
          //if(!sigsetjmp(buf,1))
          if(!setjmp(buf))
          {
              printf("sigsetjmp buf\n");
          }
          else
          {
              printf("Returned from sigsetjmp\n");
          }
          while(1)
              pause();
      }

      15. sigsuspend 函数

       解决pause会丢失信号,从而造成进程永远阻塞问题。sigsuspend相当于unblock 信号mask 和pause()的原子操作。如下程序kill 信号SIGINT将立即唤醒进程。
      #include <stdio.h>
      #include <stdlib.h>
      #include <signal.h>
      #include <errno.h>
      #include <string.h>
      static void sig_usr1(int signo)
      {
          printf("Get signal\n");
      }
      int main()
      {
          printf("Begin main()\n");
          if (signal(SIGUSR1,sig_usr1)==SIG_ERR ||signal(SIGINT,sig_usr1)==SIG_ERR   )
          {
              printf("Register sig_usr1 failed. errno=%d, error = %s", errno,strerror(errno));
              return -1;
          }
          sigset_t oldset, newset, pendmask;
          if(sigemptyset(&oldset)<0 || sigemptyset(&newset)<0 ||sigemptyset(&pendmask)<0  ||  \
              sigaddset(&newset,SIGUSR1)<0 ||sigaddset(&newset,SIGINT)<0 || sigaddset(&pendmask,SIGUSR1)<0)
          {
              printf("sigemtpyset or sigaddset error. errno=%d, error = %s", errno,strerror(errno));
              return -1;
          }
          if (sigprocmask(SIG_BLOCK,&newset,&oldset)<0)
          {
              printf("sigprocmask error. errno=%d, error = %s", errno,strerror(errno));
              return -1;
          }
          printf("Critical section\n");
          sleep(5);
          sigsuspend(&pendmask);
          printf("Awake by signal");
          if (sigprocmask(SIG_SETMASK,&oldset,NULL)<0)
          {
              printf("sigprocmask error. errno=%d, error = %s", errno,strerror(errno));
              return -1;
          }
      }

      16. abort函数

      书上的abort函数有些啰嗦,不懂后面哪一段代码有什么必要。懂的同学可以评论下,让我知道。

      #include <stdio.h>
      #include <signal.h>
      #include <errno.h>
      #include <string.h>
      #include <unistd.h>
      #include <stdlib.h>
      void mabort(void)
      {
          struct sigaction action;
          sigset_t set,oldset;
          if(sigaction(SIGABRT,NULL,&action)<0)
          {
              printf("sigaction error:%d, reason: %s\n", errno,strerror(errno));
              exit(-1);
          }
          if(action.sa_handler==SIG_IGN)
          {
              action.sa_handler == SIG_DFL;
              sigaction(SIGABRT,&action,NULL);
          }
      
          if(action.sa_handler==SIG_DFL)
              fflush(NULL);
      
          if(sigfillset(&set)<0 || sigdelset(&set,SIGABRT)<0)
          {
              printf("sigfillset error:%d, reason: %s\n", errno,strerror(errno));
              exit(-1);
          }
      
          if(sigprocmask(SIG_SETMASK,&set,NULL)<0)
          {
              printf("sigaction error:%d, reason: %s\n", errno,strerror(errno));
              exit(-1);
          }
          kill(getpid(),SIGABRT);
          printf("we are here\n");
      	    printf("we are here\n");                                                                                                                                  
          fflush(NULL);
      }
      int main()        
      {                                                                                                                                                             
          printf("we are in main\n");
          mabort();
          printf("we are end main\n");
          return;
      }
              

      17 system 函数

      POSIX 2.0 要求system 函数忽略SIGINT , SIGQUIT,并且阻塞SIGCHLD。测试代码如下:

      #include <signal.h>
      #include <stdio.h>
      #include <errno.h>
      #include <string.h>
      #include <unistd.h>
      int mysystem(const char* cmd)
      {
          struct sigaction ignore,saveintptr, savequitptr;
          ignore.sa_handler = SIG_IGN;
          sigemptyset(&ignore.sa_mask);
          pid_t pid;
          int status;
          //ignore sigint and sigquit
          if (sigaction(SIGINT,&ignore,&saveintptr)<0 || sigaction(SIGQUIT,&ignore, &savequitptr)< 0 )
          {
              printf("sigaction failed. errno: %d, reason: %s",errno, strerror(errno));
              return -1;
          }
          sigset_t childmask,savemask;
      
          if (sigemptyset(&childmask)<0 || sigaddset(&childmask,SIGCHLD)<0)
          {
              printf("sigemtpyset failed. errno: %d, reason: %s",errno, strerror(errno));
              return -1;
          }
      
          if (sigprocmask(SIG_BLOCK,&childmask,&savemask)<0)
          {
              //printf("sigaction failed. errno: %d, reason: %s",errno, strerror(errno));
      
              return -1;
          }
          if((pid=fork())<0)
          {
              printf("fork error. errno: %d, reason: %s",errno, strerror(errno));
              return -1;
          }
          else if(pid==0)
          {
              //child
              sleep(5);
              sigaction(SIGINT,&saveintptr,NULL);
              sigaction(SIGQUIT,&savequitptr,NULL);
              sigprocmask(SIG_SETMASK,&savemask,NULL);
              execl("/bin/sh","sh","-c", cmd, (char*)0);
              //why not print this, because, execl with no return if succeed
              printf("returning child %d\n", pid);
              fflush(NULL);
              _exit(127);
          }
          else
          {
              //here should be blocked, just while in case of error
              while(waitpid(pid, &status,0) <0)
              {
                  printf("wait pid is %d\n", pid);
                  //why we use EINTR, EINTR is for slow system, which get signal, here in case of execl for slow function
                  if(errno!=EINTR)
                  {
                      status =-1;
                      break;
                  }
              }
              //waitpid(pid, &status,0) ;
              printf("waiting %d\n", pid);
          }
          sigaction(SIGINT,&saveintptr,NULL);
          sigaction(SIGQUIT,&savequitptr,NULL);
          sigprocmask(SIG_SETMASK,&savemask,NULL);
          return status;
      }
      
      int main()
      {
          int status = mysystem("ls -lt");
          printf("status is %d\n", status);
      }
      18 sleep 函数实现

      #include <stdio.h>
      #include <signal.h>
      
      static void sig_alrm()
      {
          return;
      }
      unsigned int msleep(unsigned int nsecs)
      {
      
          sigset_t pendmask,oldmask,susmask;
          struct sigaction action,oldaction;
          action.sa_handler = sig_alrm;
          if(sigemptyset(&action.sa_mask)<0)
              return -1;
          if(sigaction(SIGALRM,&action,&oldaction)<0)
              return -1;
      
          sigemptyset(&pendmask);
          sigaddset(&pendmask, SIGALRM);
          sigprocmask(SIG_BLOCK,&pendmask,&oldmask);
          alarm(nsecs);
      
          susmask = oldmask;
          sigdelset(&susmask,SIGALRM);
          sigsuspend(&susmask);
          int uslept = alarm(0);
          sigaction(SIGALRM,&oldaction,NULL);
          sigprocmask(SIG_BLOCK,&oldmask,NULL);
      
          return uslept;
      }
      
      int main()
      {
          msleep(5);
      }




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

    “相关推荐”对你有帮助么?

    • 非常没帮助
    • 没帮助
    • 一般
    • 有帮助
    • 非常有帮助
    提交
    评论
    添加红包

    请填写红包祝福语或标题

    红包个数最小为10个

    红包金额最低5元

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

    抵扣说明:

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

    余额充值