信号

信号概念

信号机制

A给B发送信号, B收到信号之前执行自己的代码, 收到信号后, 不管执行到程序的什么位置, 都要暂停运行, 去处理信号, 处理完毕再继续执行, 与硬件中断类似——异步模式. 但信号是软件层面上实现的中断, 早期常被称为“软中断”

信号的特质: 由于信号是通过软件方法实现, 其实现手段导致信号有很强的延时性. 但对于用户来说, 这个延迟时间非常短, 不易察觉

每个进程收到的所有信号,都是由内核负责发送的,内核处理。

与信号相关的事件和状态

信号的状态: (1) 产生; (2) 未决状态, 产生和递达之间的状态。主要由于阻塞(屏蔽)导致该状态; (3) 递达, 递送并且到达进程

信号处理方式: (1) 执行默认动作; (2) 忽略(丢弃); (3) 捕捉(调用户处理函数)

Linux内核的进程控制块PCB是一个结构体, task_struct除了包含进程id, 状态, 工作目录, 用户id, 组id, 文件描述符表, 还包含了信号相关的信息, 主要指阻塞信号集和未决信号集

阻塞信号集, 未决信号集

阻塞信号集(信号屏蔽字): 将某些信号加入集合, 对他们设置屏蔽, 当屏蔽x信号后, 再收到该信号, 该信号的处理将推后(解除屏蔽后)

未决信号集:

  1. 信号产生, 未决信号集中描述该信号的位立刻翻转为1, 表信号处于未决状态. 当信号被处理对应位翻转回为0. 这一时刻往往非常短暂
  2. 信号产生后由于某些原因(主要是阻塞)不能抵达. 这类信号的集合称之为未决信号集. 在屏蔽解除前, 信号一直处于未决状态

信号4要素

每个信号也有其必备4要素, 分别是: (1)编号 (2)名称 (3)事件 (4)默认处理动作

默认动作:
  Term:终止进程
  Ign: 忽略信号(默认即时对该种信号忽略操作)
  Core: 终止进程, 生成Core文件(查验进程死亡原因, 用于gdb调试)
  Stop: 停止(暂停)进程
  Cont: 继续运行进程

SIGKILL和19SIGSTOP信号, 不允许忽略和捕捉, 只能执行默认动作, 甚至不能将其设置为阻塞

另外需清楚, 只有每个信号所对应的事件发生了, 该信号才会被递送(但不一定递达), 不应乱发信号

基础API

kill

int kill(pid_t pid, int sig);
发送信号给指定进程,
参数: sig建议写宏的名字

raise

int raise(int sig);
给自己发送异常终止信号
没有参数返回值, 永远不会调用失败

abort

void abort(void);
该函数无返回

alarm

unsigned int alarm(unsigned int seconds);
设定定时器(每个进程只有一个定时器), 使用的是自然定时法, 时间运行不受当前进程的影响

参数: 秒, 当时间到达之后, 函数发出一个信号SIGALRM, alarm(0)0表示取消闹钟设置, 返回0或剩余的秒数, 无失败

返回值: 上一个定时器还有多长时间发送信号

getitimer与setitimer

int getitimer(int which, struct itimerval *curr_value);

int setitimer(int which, const struct itimerval *new_value, struct itimerval *old_value);
定时器, 实现周期性定时
参数:
  which: 使用哪种定时器, 有三种定时器, 不同定时器的信号不同
    自然定时: ITIMER_REAL: 发送4号SIGLARM; 计算自然时间
    虚拟空间计时(用户空间): ITIMER_VIRTUAL 发送26号SIGVTALRM; 只计算进程占用cpu的时间
    运行时计时(用户+内核): ITIMER_PROF, 发送27号SIGPROF; 计算占用cpu及执行系统调用的时间
  old_value: 传出参数, 上一次设置定时器信息, 一般设置为NULL

struct itimerval {
   struct timeval it_interval; // 定时周期
   struct timeval it_value;    // 第一次触发定时器的时间
};

struct timeval {			   // 两个值是相加的关系, 两个变量都需赋值, 否则是垃圾值
   time_t      tv_sec;         /* seconds */
   suseconds_t tv_usec;        /* microseconds */
};

示例程序

kill用法

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <signal.h>

int main(int argc, const char *argv[]) {
    pid_t pid = fork();
    if (pid > 0) {
        while (1) {
            printf("parent process, %d
", getpid()); 
            sleep(1);
        }
    }
    // child process
    // 杀死父进程
    else if (pid == 0) {
        sleep(2);
        kill(getppid(), SIGKILL);
    }

    return 0;
}

abort用法

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <signal.h>
#include <sys/wait.h>

int main(int argc, const char *argv[]) {
    pid_t pid = fork();
    if (pid > 0) {
        // 父进程, 回收子进程资源
        int s;
        pid_t wpid = wait(&s);
        printf("child died pid = %d
", wpid);
        if (WIFSIGNALED(s)) {
            printf("die by signal: %d 
", WTERMSIG(s));
        }
    }
    else if (pid == 0) {
        // 自己给自己发送信号
        //raise(SIGINT);
        while (1) {
            abort();
        }
    }

    return 0;
}

/*
child died pid = 12779
die by signal: 6 
*/

alarm用法

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>

int main(int argc, const char *argv[]) {
    int ret = alarm(5);
    printf("ret = %d
", ret);

    sleep(2);
    // 重新设置定时器
    ret = alarm(2);
    printf("ret = %d
", ret);

    while (1) {
        printf("Hello World
");
        sleep(1);
    }

    return 0;
}

测试一秒钟程序能数多少数字

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>

int main(int argc, const char *argv[]) {
    // 设置定时器
    alarm(1);
    int i = 0;
    while (1) {
        printf("%d
", i++);
    }
    return 0;
}
/*
timer ./a.out 查看程序执行时间
real = 用户 + 内核 + 损耗
*/

setitimer使用

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <sys/time.h>
#include <signal.h>

int main(int argc, const char *argv[]) {
    // 设置定时器
    struct itimerval new_val;
    // 第一次触发的时间
    new_val.it_value.tv_sec = 2;
    new_val.it_value.tv_usec = 0;

    // 周期性定时
    new_val.it_interval.tv_sec = 1;
    new_val.it_interval.tv_usec = 0;

    // 倒计时2s
    setitimer(ITIMER_REAL, &new_val, NULL);
    while (1) {
        printf("haha
");
    }
    
    return 0;
}
原文地址:https://www.cnblogs.com/hesper/p/10738887.html