20191323王予涵第六章学习笔记

20191323王予涵第六章学习笔记

信号和信号处理

一、知识点归纳

信号和信号中断

中断:发送给"进程"的事件,它将"进程"从正常活动转移到其他活动

中断的种类:

  • 硬件中断
  • 其他进程的中断
  • 自身造成的中断

其中,每个中断都有唯一的向量号,动作函数是中断表中的中断处理程序。

Unix/Linux中的信号处理

信号类型

Unix/Linux支持的31种信号,在signal.h文件中均有定义

#define  	SIGHUP  	1
#define  	SIGINT  	2
#define  	SIGQUIT  	3
#define  	SIGILL  	4
#define  	SIGTRAP  	5
#define  	SIGABRT  	6
#define  	SIGIOT  	6
#define  	SIGBUS  	7
#define  	SIGFPE  	8
#define  	SIGKILL  	9
#define  	SIGUSR1  	10
#define  	SIGSEGV  	11
#define  	SIGUSR2  	12
#define  	SIGPIPE  	13
#define  	SIGALRM  	14
#define  	SIGTERM	        15
#define  	SIGSTKFLT	16
#define  	SIGCHLD    	17
#define  	SIGCONT	        18
#define  	SIGSTOP      	19
#define  	SIGTSTP	        20
#dpfine  	STGTTTN	        21
#define  	SIGTTOU	        22
#define  	SIGURG	        23
#define  	SIGXCPU	        24
#define  	SIGXFSZ    	25
#define  	SIGVTALRM	26
#define  	SIGPROF  	27
#define  	SIGWINCH	28
#define  	SIGPOLL  	29
#define  	SIGPWR	        30
#define  	SIGSYS	        31

信号处理数组

signal bits:

0 = no signal

1= signal

31 30 ··· I ··· 1 0
··· 1 ···

若向量位中I为1,则会产生信号I,并发送给相应进程

signal mask:

0 = no block

1 = block

··· 0 ···

若屏蔽位I为1,则信号会被阻塞或屏蔽

signal handler:

0 = default

1 = ignore

other = catcher address

··· 0 ···

若发现为阻塞信号时,则将信号位清除为0,并尝试通过信号处理数组中的处理函数来处理该信号

信号捕捉函数

#include <signal.h> 
int sigaction(int signum,const struct sigaction *act,struct sigaction *oldact));

 struct sigaction {
          void (*sa_handler)(int);
     	  void (*sa_sigaction)(int sig, siginfo_t *siginfo, void *context)
          sigset_t sa_mask;
          int sa_flags;
     	  void (*sa_restorer)(void);
           };
  • sa_handler:指向默认处理函数的指针
  • sa_sigaction:用户定义的处理函数
  • sa_mask:执行期间需要阻塞的信号
  • sa_flags:修改信号处理进程的行为,若要使用sa_sigaction函数,必须将sa_flags设置为SA_SIGINFO

信号处理步骤

  1. 当某进程处于内核模式时,会检查信号并处理未完成的信号。如果某信号有用户安装的捕捉函数,该进程会先清除信号,获取捕捉函数地址,对于大多数陷阱信号,则将已安装的捕捉函数重置为DEFault。然后,它会在用户模式下返回,以执行捕捉函数,以这种方式篡改返回路径。当捕捉函数结束时,它会返回到最初的中断点,即它最后进入内核模式的地方。因此,该进程会先迁回执行捕捉函数,然后再恢复正常执行。
  2. 重置用户安装的信号捕捉函数:用户安装的陷阱相关信号捕捉函数用于处理用户代码中的陷阱错误。由于捕捉函数也在用户模式下执行,因此可能会再次出现同样的错误。如果是这样,该进程最终会陷入无限循环,一直在用户模式和内核模式之间跳跃。为了防止这种情况,Unix内核通常会在允许进程执行捕捉函数之前先将处理函数重置为 DEFault。这意味着用户安装的捕捉函数只对首次出现的信号有效。若要捕捉再次出现的同一信号,则必须重新安装捕捉函数。但是,用户安装的信号捕捉函数的处理方法并不都一样,在不同Unix版本中会有所不同。例如,在 BSD Unix中,信号处理函数不会被重置,但是该信号在执行信号捕捉函数时会被阻塞。
  3. 信号号和唤醒:在Unix/Lintx内核中有两种SLEEP进程;深度休眠进程和浅度休眠进程。前一种进程不可中断,而后一种进程可由信号中断。如果某进程处于不可中断的SLEEP状态,到达的信号(必须来自硬件中断或其他进程)不会唤醒进程。如果它处于可中断的SLEEP状态,到达的信号将会唤醒它。例如,当某进程等待终端输入时,它会以低优先级休眠,这种休眠是可中断的,SIGINT这类信号即可唤醒它。

信号用作IPC

缺点:

● 每个信号由位向量中的一个位表示,只能记录一个信号的一次出现。如果某个进程向另一个进程发送两个或多个相同的信号,它们可能只在接收PROC中出现一次。

● 在执行信号捕捉函数时,虽然可以通过阻塞同一信号来防止竞态条件,但是无法防止丢失信号。

● 大多数信号都有预定义的含义。不加区别地任意使用信号不仅不能达到通信的目的,反而会造成混乱。

二、实践捕捉除0异常信号

"head.h"

#ifndef HEAD_H_INCLUDED
#define HEAD_H_INCLUDED
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <setjmp.h>
jmp_buf env;
int count = 0;
#endif // HEAD_H_INCLUDED

mian.c

#include "head.h"
void handler(int sig, siginfo_t *siginfo, void *context)
{
    printf("handler: sig=%d from PID=%d UID=%d count=%d\n", sig, siginfo -> si_pid,siginfo -> si_uid,++count);
    if(count >= 4)
    {
        longjmp(env,20191323);
    }
}

int BAD()
{
    int i = 3;
    printf("in BAD(): try to divide zero\n");
    i /= 0;
    printf("could not see this line");
}
int main(void)
{
    int  r;
    struct sigaction act;
    memset(&act, 0, sizeof(act));
    act.sa_sigaction = &handler;
    act.sa_flags = SA_SIGINFO;
    sigaction(SIGFPE, &act,NULL);
    if((r = setjmp(env)) == 0)
    {
        BAD();
    }
    else
    {
        printf("proc %d survived SEGMENTATION FAULT : r = %d\n", getpid(),r);
    }
    printf("proc %d looping\n",getpid());
    while(1);

}

运行结果截图:

posted @ 2021-11-14 18:07  WangYuHan20191323  阅读(15)  评论(0编辑  收藏  举报