Linux信号的基本概念

1,256 阅读6分钟

一、Linux信号的基本概念

  1. 信号的介绍

    • Linux的消息机制是基于信号的

    • 携带的信息量少

    • 优先级很高

  2. 信号的本质

    ​ 操作系统给进程发送信号,本质上是往进程的PCB(进程控制块)中写入数据,修改相应的PCB字段,进程在合适的时间去处理所接受的信号。例如:

    ​ (1)启动一个前台进程

    ​ (2)按下Ctrl+c,产生一个硬件中断

    ​ (3)CPU从用户态切换至内核态处理中断,进程的用户区的代码将被暂停执行

    ​ (4)终端驱动程序将Ctrl+c解释为一个SIGINT信号,写入该进程的PCB中

    ​ (5)内核处理完中断,准备返回内核区前先处理当前进程的未决信号

    ​ (6)SIGINT信号的默认处理方式是终止进程,所以不返回用户区

  3. 系统定义的信号列表

    $kill -l
    	1) SIGHUP	 		2) SIGINT		3) SIGQUIT	 	4) SIGILL	
    	5) SIGTRAP			6) SIGABRT	 	7) SIGBUS		8) SIGFPE	 	
    	9) SIGKILL			10) SIGUSR1		11) SIGSEGV		12) SIGUSR2		
    	13) SIGPIPE			14) SIGALRM		15) SIGTERM		16) SIGSTKFLT		
    	17) SIGCHLD			18) SIGCONT		19) SIGSTOP		20) SIGTSTP
    	21) SIGTTIN			22) SIGTTOU		23) SIGURG		24) SIGXCPU	
    	25) SIGXFSZ			26) SIGVTALRM	27) SIGPROF		28) SIGWINCH	
    	29) SIGIO			30) SIGPWR		31) SIGSYS		34) SIGRTMIN	
    	35) SIGRTMIN+1		36) SIGRTMIN+2	37) SIGRTMIN+3	38) SIGRTMIN+4	
    	39) SIGRTMIN+5		40) SIGRTMIN+6	41) SIGRTMIN+7	42) SIGRTMIN+8
    	43) SIGRTMIN+9		44) SIGRTMIN+10	45) SIGRTMIN+11	46) SIGRTMIN+12	
    	47) SIGRTMIN+13		48) SIGRTMIN+14	49) SIGRTMIN+15	50) SIGRTMAX-14	
    	51) SIGRTMAX-13		52) SIGRTMAX-12	53) SIGRTMAX-11	54) SIGRTMAX-10	
    	55) SIGRTMAX-9		56) SIGRTMAX-8	57) SIGRTMAX-7	58) SIGRTMAX-6	
    	59) SIGRTMAX-5		60) SIGRTMAX-4	61) SIGRTMAX-3	62) SIGRTMAX-2
    	63) SIGRTMAX-1		64) SIGRTMAX	
    
  4. 信号的详细信息

    $man 7 signal
    
    	   Signal     Value     Action   Comment
      ──────────────────────────────────────────────────────────────────────
           SIGHUP        1       Term    Hangup detected on controlling terminal
                                         or death of controlling process
           SIGINT        2       Term    Interrupt from keyboard
           SIGQUIT       3       Core    Quit from keyboard
           SIGILL        4       Core    Illegal Instruction
    
           SIGABRT       6       Core    Abort signal from abort(3)
           SIGFPE        8       Core    Floating-point exception
           SIGKILL       9       Term    Kill signal
           SIGSEGV      11       Core    Invalid memory reference
           SIGPIPE      13       Term    Broken pipe: write to pipe with no
                                         readers; see pipe(7)
           SIGALRM      14       Term    Timer signal from alarm(2)
           SIGTERM      15       Term    Termination signal
           SIGUSR1   30,10,16    Term    User-defined signal 1
           SIGUSR2   31,12,17    Term    User-defined signal 2
           SIGCHLD   20,17,18    Ign     Child stopped or terminated
           SIGCONT   19,18,25    Cont    Continue if stopped
           SIGSTOP   17,19,23    Stop    Stop process
           SIGTSTP   18,20,24    Stop    Stop typed at terminal
           SIGTTIN   21,21,26    Stop    Terminal input for background process
           SIGTTOU   22,22,27    Stop    Terminal output for background process
           ........
    
    • Term:终止进程
    • Ign:当前进程忽略该信号
    • Core:终止进程,并生成一个core文件。默认情况下core文件的大小为0,需要使用ulimit -c 文件大小指定生成core文件的大小
    • Stop:暂停当前进程
    • Cont:继续执行当前进程
  5. 信号的状态

    • 产生
    • 未决(没有被处理)
    • 递达(被处理了)
    • 阻塞(阻塞是未决的一种特殊状态)
  6. 可选的信号的处理动作

    • 系统默认的处理动作

    • 用户自定义的处理动作

    • 忽略

      如果不执行系统默认处理动作,需要对信号进行捕捉,但是9号信号(SIGKILL)和19号信号(SIGSTOP)不允许被捕捉,阻塞和忽略。

二、信号相关的函数

  1. raise/abort

    #include <signal.h>
    //给当前进程发送信号
    int raise(int sig);		
    	参数:
            - sig:要发送的信号
        返回值:
            - 0:成功
            - 非0:失败
    
    #include <stdlib.h>
    //发送SIGABRT信号给当前进程,杀死当前进程
    void abort(void);
    
  2. kill

    #include <sys/types.h>
    #include <signal.h>
    //发送信号给指定的进程
    int kill(pid_t pid, int sig);
    	参数:
            - pid:进程ID
            - sig:要发送的信号
        返回值:
            - 0:成功
            - -1:失败
    
  3. alarm

    #include <unistd.h>
    //倒计时函数,当倒计时结束,函数给当前进程发送一个信号:SIGALRM,终止当前进程
    unsigned int alarm(unsigned int seconds);
    	参数:
            - seconds:倒计时时长,单位:秒,参数值大于0,参数为0取消倒计时
        返回值:
            返回倒计时剩余时间
    
  4. setitimer

    #include <sys/time.h>
    //获取定时器当前计时器的值
    int getitimer(int which, struct itimerval *curr_value);
    	参数:
            - whitch:定时器以什么时间计时
    			ITIMER_REAL:真实时间,时间到达发送信号:SIGALRM
    			ITIMER_VIRTUAL:用户时间,时间到达发送信号:SIGVALRM
    			ITIMER_PROF:内核时间,时间到达,发送信号:SIGPROF
            - curr_value:传出参数,记录当前定时器的值
            
    //设置定时器的值
    int setitimer(int which, const struct itimerval *new_value,struct itimerval *old_value);
    	参数:
            - whitch:定时器以什么时间计时
    			ITIMER_REAL:真实时间,时间到达发送信号:SIGALRM
    			ITIMER_VIRTUAL:用户时间,时间到达发送信号:SIGVALRM
    			ITIMER_PROF:内核时间,时间到达,发送信号:SIGPROF
            - new_value:设置定时器
            - old_value:记录了上次定时器设置的值
            
    struct itimerval {
           struct timeval it_interval; /* 隔多久触发一次 */
           struct timeval it_value;    /* 第一次触发定时器的时长 */
    };
    
    struct timeval {
    	   time_t      tv_sec;         /* 秒 */
    	   suseconds_t tv_usec;        /* 微秒 */
    };
    
    
    

    三、信号捕捉

    1. 信号捕捉的介绍

      ​ 进程运行时,PCB中默认有一个阻塞信号集,它决定了进程自动屏蔽哪些信号。当注册了某个信号捕捉函数,捕捉到该信号后,会调用该函数,函数执行期间的阻塞信号集与默认阻塞信号集不同。

    2. 信号捕捉的过程

      • 进程由于中断、异常或系统调用进入内核
      • 内核处理完毕,先处理当前进程的未决信号
      • 如果信号有自定义动作,则返回到用户区的信号处理函数
      • 信号处理函数执行完毕,返回到内核
      • 内核返回到第一步用户区执行的位置
    3. 信号捕捉函数

      • signal

        #include <signal.h>
        typedef void (*sighandler_t)(int);
        
        sighandler_t signal(int signum, sighandler_t handler);
        	参数:
                - signum:要捕捉的信号
                - handler:接收参数为int,返回值类型为void*的回调函数
                	handler也可以是特殊的值:
        				SIG_IGN:用于设置信号忽略
        				SIG_DFL:用于恢复信号的默认处理动作
            返回值:
                - 成功:返回前一次的信号捕捉函数指针,若没有则返回NULL
                - 失败:返回SIG_ERR
        
      • sigaction

        #include <signal.h>
         int sigaction(int signum, const struct sigaction *act, struct 		  sigaction *oldact);
        		参数:
                    - signum:要捕捉的信号
                    - act:捕捉到信号后的处理动作
                    - oldact:上一次的处理动作
                返回值:
                    - 0:成功
                    - -1:失败
         
        struct sigaction {
            void     (*sa_handler)(int);	//回调函数
            void     (*sa_sigaction)(int, siginfo_t *, void *);	//回调函数
            sigset_t   sa_mask;	//阻塞信号集
            int        sa_flags;	//选用哪个回调函数
            			sa_flags = 0,使用sa_handler
                        sa_flags = SA_SIGINFO,使用sa_sigaction
            void     (*sa_restorer)(void);	//废弃了
        };
        

    四、信号集

    1. 信号集介绍

      ​ 在PCB中有两个非常重要的信号集。一个称之为“阻塞信号集”,另一个称之为“未决信号集”。这两个信号集都是内核使用位图机制来实现的。但操作系统不允许我们直接对这两个信号集进行位操作。需自定义另外一个集合,借助信号集操作函数来对PCB中的这两个信号集进行修改。

    2. 未决信号/阻塞信号

      • 未决信号,指的是从信号产生到被处理前的状态。
      • 阻塞信号,指的是信号处理动作被阻止,但是不影响信号的产生。
    3. 信号集相关操作函数

      #include <signal.h>
      
      //将自定义信号集添加到内核中
      int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
      	参数:
      		- how:决定如何调用该函数
              	SIG_BLOCK:将用户设置的阻塞信号集添加到内核中,内核原来数据不变
              	SIG_UNBLOCK:清除用户在内核中设置的数据
              	SIG_SETMASK:覆盖内核中原来的值
          	- set:自定义的阻塞信号集
          	- oldset:上次内核中的阻塞信号集
           返回值
            	- 0:成功
              - -1:失败
      //将信号集中的数据设置为0
      int sigemptyset(sigset_t *set);
      //将信号集中的数据设置为1
      int sigfillset(sigset_t *set);
      //向信号集中添加阻塞信号
      int sigaddset(sigset_t *set, int signum);
      //将信号从设置好的信号集中删除
      int sigdelset(sigset_t *set, int signum);
      //判断信号是否在信号集中
      int sigismember(const sigset_t *set, int signum);