【Linux系统编程】26.信号、kill、alarm、setitimer

发布于:2024-04-28 ⋅ 阅读:(21) ⋅ 点赞:(0)

目录

信号

信号共性

信号特质

产生信号

信号相关概念

默认处理动作

信号4要素

常规信号  ​编辑

注意

kill

参数pid

测试代码1

测试结果

测试代码2

测试结果

alarm

参数seconds

返回值

取消闹钟

测试代码3

测试结果1

测试结果2  ​编辑

setitimer

参数which

参数new_value

参数old_value

测试代码4

测试结果

信号集操作函数

信号

信号共性

  • 简单

  • 不能携带大量信息

  • 满足条件才能发送

信号特质

       信号是软件层面上的“中断”,一旦信号产生,无论程序执行到什么位置,都必须立即停止运行,处理信号,处理完成后,再继续执行后续程序。所有信号的产生及处理都是由内核完成。

产生信号

  1. 按键产生

  2. 系统调用产生

  3. 软件条件产生

  4. 硬件异常产生

  5. 命令产生

信号相关概念

未决:产生与递达之间的状态。

递达:产生并且送达到进程,直接被内核处理掉。

信号处理方式

  1. 执行默认处理动作。

  2. 忽略。

  3. 捕捉。

阻塞信号集(信号屏蔽字):本质是位图。用来记录信号的屏蔽状态。一旦被屏蔽的信号,在解除屏蔽之前,一直处于未决态。

未决信号集:本质是位图。用来记录信号的处理状态。该信号集中的信号表示已经产生,但未被处理。

默认处理动作

Term:终止进程。

lgn:忽略信号。

Core:终止进程,生成Core文件。可以查验进程死亡原因,用gdb进行调试。

Stop:停止进程或暂停进程。

Cont:继续运行进程。

信号4要素

信号使用之前,必须先确定其4要素,再进行使用。

  1. 信号编号

  2. 信号名称

  3. 信号对应时间

  4. 信号默认处理动作

man 7 signal

       在标准信号中,有一些信号是有三个“Value”,第一个值通常对alpha和sparc架构有效,中间值针对x86、arm和其他架构,最后一个应用于mips架构。一个‘-’表示在对应架构上尚未定义该信号。

       不同的操作系统定义了不同的系统信号。因此有些信号出现在Unix系统内,也出现在Linux 中,而有的信号出现在FreeBSD或Mac OS中却没有出现在Linux下。

常规信号  

  1. SIGHUP:当用户退出shell时,由该shell启动的所有进程将收到这个信号,默认动作为终止进程。

  2. SIGIN:当用户按下了<Ctrl+C>组合键时,用户终端向正在运行中的由该终端启动的程序发出此信号。默认动作为终止进程。

  3. SIGQUIT:当用户按下<ctrl+\>组合键时产生该信号,用户终端向正在运行中的由该终端启动的程序发出些信号。默认动作为终止进程。

  4. SIGILL:CPU检测到某进程执行了非法指令。默认动作为终止进程并产生core文件。

  5. SIGTRAP:该信号由断点指令或其他trap指令产生。默认动作为终止进程并产生core文件。

  6. SIGABRT:调用abort函数时产生该信号。默认动作为终止进程并产生core文件。

  7. SIGBUS:非法访问内存地址,包括内存对齐出错,默认动作为终止进程并产生core文件。

  8. SIGFPE:在发生致命的运算错误时发出。不仅包括浮点运算错误,还包括溢出及除数为0等所有的算法错误。 默认动作为终止进程并产生core文件。

  9. SIGKILL:无条件终止进程。本信号不能被忽略,处理和阻塞。默认动作为终止进程。它向系统管理员提供了可以杀死任何进程的方法。

  10. SIGUSE1:用户定义的信号。即程序员可以在程序中定义并使用该信号。默认动作为终止进程。

  11. SIGSEGV:指示进程进行了无效内存访问。默认动作为终止进程并产生core文件。

  12. SIGUSR2:另外一个用户自定义信号,程序员可以在程序中定义并使用该信号。默认动作为终止进程。

  13. SIGPIPE:Broken pipe向一个没有读端的管道写数据。默认动作为终止进程。

  14. SIGALRM:定时器超时,超时的时间由系统调用alarm设置。默认动作为终止进程。

  15. SIGTERM:程序结束信号,与SIGKILL不同的是,该信号可以被阻塞和终止。通常用来要示程序正常退出。执行 shell命令Kill时,缺省产生这个信号。默认动作为终止进程。

  16. SIGSTKFLT:Linux早期版本出现的信号,现仍保留向后兼容。默认动作为终止进程。

  17. SIGCHLD:子进程状态发生变化时,父进程会收到这个信号。默认动作为忽略这个信号。

  18. SIGCONT:如果进程已停止,则使其继续运行。默认动作为继续/忽略。

  19. SIGSTOP:停止进程的执行。信号不能被忽略,处理和阻塞。默认动作为暂停进程。

  20. SIGTSTP:停止终端交互进程的运行。按下<ctrl+z>组合键时发出这个信号。默认动作为暂停进程。

  21. SIGTTIN:后台进程读终端控制台。默认动作为暂停进程。

  22. SIGTTOU:该信号类似于SIGTTIN,在后台进程要向终端输出数据时发生。默认动作为暂停进程。

  23. SIGURG:套接字上有紧急数据时,向当前正在运行的进程发出些信号,报告有紧急数据到达。如网络带外数据到达,默认动作为忽略该信号。

  24. SIGXCPU:进程执行时间超过了分配给该进程的CPU时间 ,系统产生该信号并发送给该进程。默认动作为终止进程。

  25. SIGXFSZ:超过文件的最大长度设置。默认动作为终止进程。

  26. SIGVTALRM:虚拟时钟超时时产生该信号。类似于SIGALRM,但是该信号只计算该进程占用CPU的使用时间。默认动作为终止进程。

  27. SGIPROF:类似于SIGVTALRM,它不公包括该进程占用CPU时间还包括执行系统调用时间。默认动作为终止进程。

  28. SIGWINCH:窗口变化大小时发出。默认动作为忽略该信号。

  29. SIGIO:此信号向进程指示发出了一个异步IO事件。默认动作为忽略。

  30. SIGPWR:关机。默认动作为终止进程。

  31. SIGSYS:无效的系统调用。默认动作为终止进程并产生core文件。

34~64. LINUX 的实时信号,它们没有固定的含义,可以由用户自定义。所有的实时信号的默认动作都为终止进程。

注意

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

        只有每个信号所对应的事件发生了,该信号才会被递送,但不一定递达,不应乱发信号!

kill

man 2 kill

参数pid

大于0:发送信号给指定进程。

等于0:发送信号给跟调用kill函数的那个进程处于同一进程组的进程。

小于-1:取绝对值,发送信号给该绝对值所对应的进程组的所有进程。

等于-1:发送信号给有权限发送的所有进程,普通用户基本规则是,发送者实际或有效用户ID==接收者实际或有效用户ID。

测试代码1

用子进程杀了父进程。

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <signal.h>

int main(int argc, char *argv[])
{
    pid_t JinCheng_ID;
    JinCheng_ID = fork();
    if (JinCheng_ID > 0) //父进程
    {
        printf("这是父进程,该进程ID是%d。开始进入死循环。\n", getpid());
        while (1)
        {
            printf("这是父进程,已在死循环。\n");
            sleep(1);
        }
    }
    else if (JinCheng_ID == 0) //子进程
    {
        printf("这是子进程,该进程ID是%d。先睡一会再杀父进程。\n", getpid());
        sleep(3);
        printf("这是子进程,该进程ID是%d。睡醒了,我杀。\n", getpid());
        kill(getppid(),SIGKILL);
        printf("这是子进程,该进程ID是%d。杀完了,拜拜。\n", getpid());
    }
    return 0;
}

测试结果

 

测试代码2

杀进程组

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <signal.h>

int main(int argc, char *argv[])
{
    pid_t JinCheng_ID;
    JinCheng_ID = fork();
    if (JinCheng_ID > 0) //父进程
    {
        printf("这是父进程,该进程ID是%d。开始进入死循环。\n", getpid());
        while (1)
        {
            printf("这是父进程,已在死循环。\n");
            sleep(1);
        }
    }
    else if (JinCheng_ID == 0) //子进程
    {
        printf("这是子进程,该进程ID是%d。先睡一会再杀进程组。\n", getpid());
        sleep(3);
        printf("这是子进程,该进程ID是%d。睡醒了,我杀。\n", getpid());
        kill(0, SIGKILL);
        printf("这是子进程,该进程ID是%d。杀完了,拜拜。\n", getpid());
    }
    return 0;
}

测试结果

进程组被杀后,子进程最后的输出不再进行输出。

 

alarm

定时发送SIGALRM给当前进程。

实际时间=用户时间+内核时间+等待时间,代码优化,优先从IO下手。

man 2 alarm

参数seconds

定时秒数。

返回值

上次剩余的时间。

取消闹钟

alarm(0);

测试代码3

查看程序1秒钟的计数次数。

#include <stdio.h>
#include <unistd.h>

int main(int argc, char *argv[])
{
    int i = 0;
    alarm(1); //定时1秒
    while (1)
    {
        printf("%d\n", i++);
    }
    return 0;
}

测试结果1

测试结果2  

setitimer

设置定时器(闹钟)。 可代替alarm函数。精度微秒us,可以实现周期定时。

man 2 setitimer

参数which

指定定时方式。

  • ITIMER_REAL:自然定时,计算自然时间,信号是14)SIGLARM。

  • ITIMER_VIRTUAL:虚拟空间计时(用户空间),只计算进程占用cpu的时间,信号是26)SIGVTALRM。

  • ITIMER_PROF:运行时计时(用户+内核),计算占用cpu及执行系统调用的时间,信号是27)SIGPROF。

参数new_value

定时秒数。

struct itimerval {
    struct timeval it_interval; //后续每个中断的间隔执行时间
    struct timeval it_value;    //第一次执行中断的时间
};

struct timeval {
    time_t      tv_sec;         //秒
    suseconds_t tv_usec;        //微秒
};
struct itimerval {
    struct timeval {	//后续每个中断的间隔执行时间
        time_t      tv_sec;         //秒
        suseconds_t tv_usec;        //微秒
    }it_interval;
    struct timeval {	//第一次执行中断的时间
        time_t      tv_sec;         //秒
        suseconds_t tv_usec;        //微秒
    }it_value;
};

参数old_value

传出参数,上次定时剩余的时间。

测试代码4

周期性输出内容。

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

void ChuLi_HanShu()
{
    printf("你好,世界!\n");
}

int main(int argc, char *argv[])
{
    int i = 0;
    struct itimerval new_value, old_value;
    signal(SIGALRM, ChuLi_HanShu); //注册SIGALRM信号的捕捉处理函数

    //2秒后开始第1次中断
    new_value.it_value.tv_sec=2;
    new_value.it_value.tv_usec=0;

    //每5秒来一次中断
    new_value.it_interval.tv_sec=5;
    new_value.it_interval.tv_usec=0;

    if(setitimer(ITIMER_REAL,&new_value,&old_value)==-1){
        perror("设置中断错误");
        exit(1);
    }

    while (1)
    {
        printf("%d秒\n", i++);
        sleep(1);
    }
    return 0;
}static

测试结果

信号集操作函数

sigset_t set;	//自定义信号集
sigemptyset(sigset_t *set);		//清空信号集
sigfillset(sigset_t *set);		//全部置1
sigaddset(sigset_t *set,int signum);	//每一个信号添加到集合中
sigdelset(sigset_t *set,int signum);	//将一个信号从集合中移除
sigismember(const sigset_t *set,int signum);	//判断一个信号是否在集合中。返回值:1:在;0:不在。

网站公告

今日签到

点亮在社区的每一天
去签到