中断

信号是进程间通信,中断是cpu操作的,CPU对紧急事件的处理和响应, 以及进程的调度和上下文切换

信号概念

  • 信号整体递送流程:
    1. 信号生成:外部中断(如按下CTRL+C);系统调用(如kill进程函数);或者软件条件(如除零操作)。当这些事件发生时,内核生成一个相应的信号
    2. 信号的传递:内核分辨和处理信号发送到目标进程的task struct,修改对应的信号记录字段。目标进程在运行的时候,检测自己的task_struct,检测是否有信号
    3. 信号的排队:内核为信号创建一个信号队列项,并将其加入到目标进程的位图OR信号队列中。(信号也可能会覆盖旧的信号或者被丢弃)

      • 信号靠位图实现
      • 信号有掩码(位图):一个进程收到信号未必会执行,取决于对应掩码的bit位,若对应bit位是1,则信号被阻塞
      • 已经递送但是还没有执行的信号被称为挂起信号(pending signal,未决信号) ,通常是由于信号被阻塞或进程暂时不能处理信号
      • 信号未决 != 信号阻塞, 信号未决的意思是信号暂时未被执行, 而信号阻塞需要对应解除阻塞的操作。
      • 信号阻塞是由进程的信号掩码控制的。

  • 由进程的某个操作产生的信号称为同步信号(synchronous signals)(例如在代码中除0)。
  • 像用户击键这样的进程外部事件产生的信号叫做异步信号(asynchronous signals)。

signal

signal(SIGINT, SIG_IGN); // 给2号信号设置忽略
signal(2, SIG_DFL);// 给2号信号恢复默认行为 
signal(2, func);//调用func函数
  • 通过signal注册一个信号处理函数,并且处理完毕一个信号之后, 还需要重新注册吗?
     不需要重新注册,就能够捕捉下一个信号。
    

    多个信号触发

    信号处理的过程中:

  • 接受到了另一个不同类型信号,当前的信号处理流程被中断,CPU先处理新的,再处理原来的
  • 接收到相同类型信号,当前处理完再处理新的
  • 连续接收到相同的信号,只能多执行一次,后面重复的会被忽略

sigaction

int sigaction( 
    int signum, // 要操作的信号编号(除了SIGKILL和SIGSTOP) 
    const struct sigaction *act,// 指定信号的新处理动作(如果非空) 
    struct sigaction *oldact// 获取信号的上一个处理动作(如果非空) 
    ); 
// 返回值: 成功时返回0,错误时返回-1
struct sigaction {
    void (*sa_handler)(int);// 函数指针:指向一个信号处理函数 (和sa_sigaction选一个即可) 
    void (*sa_sigaction)(int, siginfo_t *, void *);// 函数指针:指向一个接受三个参数的信号处理函数 
    sigset_t sa_mask;// 信号集: 指定当前信号处理函数执行时需要阻塞的额外信号 
    int sa_flags;// 指定信号处理的选项和标志: 
    void (*sa_restorer)(void);// 过时,暂无用 
    };
struct siginfo_t { 
    pid_t si_pid; /* Sending process ID */
    sigval_t si_value; /* Signal value */ // ......
    };

sa_flags:信号处理方式掩码, 可以用来设置信号的处理模式。

  • SA_SIGINFO:使用 sa_sigaction 成员而不是 sa_handler 作为信号处理函数。
  • SA_RESETHAND: 处理完捕获的信号以后,信号处理回归到默认( 一次注册只生效一次)
  • SA_NODEFER: 在信号处理函数执行期间,同一个信号设置可以再次被触发
  • SA_RESTART:使被信号打断的系统调用自动重新调用。

    用法

    sa_handler

    struct sigaction act, old;
    memset(&act, 0, sizeof(act));
    act.sa_handler = func;//直接用sa_handler
    sigaction(SIGINT,&act,&old);
    

    sa_sigaction(此用法跟上面一样)

    struct sigaction act, old;
    memset(&act, 0, sizeof(act));
    act.sa_sigaction = func;//用sa_sigaction
    act.sa_flags = SA_SIGINFO;//表示使用sa_sigaction
    sigaction(SIGINT,&act,&old);
    

    sa_mask

    ```c sigemptyset(sigset_t *set);// 初始化信号集,清除所有信号。 sigfillset(sigset_t *set);// 添加所有信号到信号集中。 sigaddset(sigset_t *set, int signo);// 向信号集添加一个信号。 sigdelset(sigset_t *set, int signo);// 从信号集中删除一个信号。 sigismember(const sigset_t *set, int signo);// 检查一个特定信号是否在信号集中。

//example sigset_t sa_mask; sigemptyset(&sa_mask); sigaddset(&sa_mask,SIGINT); act.sa_mask = sa_mask;

### sigpending

> 用于检查当前进程的未决信号集,确定哪些信号已经被产生并等待处理,但尚未被当前进程捕获或忽略。
```c
int sigpending( sigset_t *set // 接收当前进程的未决信号集 );
			   
//example
sigset_t set;
sigemptyset(&set);
sigpending(&set);//pending的信号被加入到set中
if(sigismember(&set,1)){
...
}

sigprocmask

用于在系统中,检查和更改进程的信号掩码mask,且是全程的,sigaction设置的sa_mask是临时的 ```c int sigprocmask( int how, // 可选: //SIG_BLOCK:把set内信号添加阻塞; //SIG_UNBLOCK:解除set内信号阻塞; //SIG_SETMASK:将信号掩码替换为set指定信号; const sigset_t *set, // 信号集合 sigset_t *oldset // 当前信号掩码 );

//example sigset_t set; sigempty(&set); sigfillset(&set);

sigprocmask(SIG_BLOCK, &set, NULL); sleep(5); siprocmask(SIG_UNBLOCK,&set, NULL);


### alarm
> 用于设置一个计时器,到期时内核会向该进程发送 `SIGALRM` 信号。如果程序没有捕获或忽略该信号,则其默认行为是终止进程。
```c
unsigned int alarm(unsigned int seconds);

//example
alarm(4);//一个进程只有一个计时器,重复使用alarm会更新为新的超时时间,返回值为原来计时器的剩余秒数

setitimer

高级版alarm

//getitimer,得到当前定时器的情况
int getitimer( 
    int which, // 定时器的类型 
    struct itimerval *curr_value // 
); 

struct itimerval { 
    struct timeval it_interval;// 间隔时间: 若被设置为非零值,定时器将变为周期性的 
    struct timeval it_value; // 定时器的剩余时间 
// 当定时器的it_value达到0并触发信号后,it_value会被重新设置为 it_interval 的值,然后定时器再次开始计时 
}; 

struct timeval {
    long tv_sec; //秒
    long tv_usec; //微秒
};


//settimer
int setitimer( 
    int which, // 定时器的类型 
    const struct itimerval *new_value, // 指定的新的定时器值 
    struct itimerval *old_value // 存储定时器的前一个值 
)

which参数:指定定时器的类型。常用的类型包括:

  • ITIMER_REAL:按照真实时间, 当时间到达, 发出一个 SIGALRM 信号。
  • ITIMER_VIRTUAL:按照用户态代码执行时间计算, 当时间到达, 发出一个 SIGVTALRM信号。
  • ITIMER_PROF:按照用户态用户态和内核态代码执行时间计算, 当时间到达, 发出一个 SIGPROF 信号。

更新时间: