今天我们就要来一起学习信号啦!!!还记得小编在之前的文章中说过的ctrl+c吗?之前小编没有详细介绍过,现在我们就要来学习啦!!!

一、信号的基本介绍
        首先,小编带领大家先一起学习一下什么是信号吧。

        信号是系统响应某个条件而产生的事件,进程接收到信号会执行相应的操作;

        大家要注意,我们在使用信号时,是需要添加头文件的。与信号有关的系统调用在<signal.h>头文件中。

1、信号的存储位置
旧版

vim /usr/include/x86_64-linux-gnu/bits/signum.h 

新版(23版)

vim /usr/include/x86_64-linux-gnu/bits/signum-arch.h

vim /usr/include/x86_64-linux-gnu/bits/signum-generic.h

2、常见信号对应的功能
SIGBORT      *进程异常终止

SIGALRM      超时警告

SIGFPE         *浮点运算异常

SIGHUP         连接挂断

SIGILL            *非法指令

SIGINT            终端中断

SIGKILL           终止进程(此信号不能被捕获或忽略)

SIGPIPE          向无读进程的管道写数据

SIGQUIT          终端退出

SIGSEGV         *无效内存段访问

SIGTERM        终止

SIGUSR1         用户定义信号1

SIGUSR2         用户定义信号2

(在这里,重点的信号用了加粗提醒大家一定要记住,在这篇文章里,小编还不会向大家介绍SIGPIPE,在后面小编介绍管道时,会结合前边的内容和新的内容全面介绍管道)

3、信号的值
            信号名称  信号代号

#define SIGHUP 1

#define SIGINT 2     //键盘按下 Ctrl+c 时,会产生终端中断信号

#define SIGQUIT 3  //键盘按下 Ctrl+\时,会产生终端退出信号

#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    //系统 kill 命令默认发送的信号

#define SIGSTKFLT 16

#define SIGCHLD 17     //子进程结束后,内核会默认给父进程发送该信号

#define SIGCONT 18

#define SIGSTOP 19

#define SIGSTP 20

#define SIGTTIN 21

#define SIGTTOU 22

#define SIGURG 23

(通过这个,小编是想告诉大家,这些信号其实对应的就是数字)

二、信号的响应方式
信号有三种响应方式:默认、忽略、自定义;

1、信号处理函数
        在Linux系统中,我们想要了解一个新的知识必不可少的就是帮助手册啦!!!大家还记得怎么使用吗?

        答案就是:man signal

2、 三种响应方式

(1)默认

        如果signal函数的参数为 SIG_DFL,则系统将使用默认的信号处理动作。

        大家可以输入命令“man 7 signal”查看默认处理方式,当然啦,小编也会为大家展示出来。

(2)忽略
        如果signal函数的参数为 SIG_IGN,则系统将忽略该信号。

(3)自定义
        信号自定义处理,其实是对信号进行捕捉,然后让信号执行自定义的方法。

下面,小编向大家演示一下默认的处理方式(也就是收到信号后,进程按照信号默认的方式去处理)
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<assert.h>
#include<signal.h>
int main()
{
    while(1)
    {
        printf("main run\n");
        sleep(1);
    }   
    exit(0);
}

3、改变型号的响应方式

(1)将默认改为自定义
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<assert.h>
#include<signal.h>
#include<string.h>
void sig_fun(int sig)
{
    printf("sig=%d\n",sig);
}
int main()
{
    signal(SIGINT,sig_fun);//这里不是调用,这里是作约定
    while(1)
    {
        printf("main run\n");
        sleep(1);
    }   
    exit(0);
}

(2)将默认改为忽略

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<assert.h>
#include<signal.h>
#include<string.h>
int main()
{
    signal(SIGINT,SIG_IGN);//这里不是调用,这里是作约定
    while(1)
    {   
        printf("main run\n");
        sleep(1);
    }   
    exit(0);
}

4、SIGCHLD信号

(1)子进程结束,父进程会收到内核发送的SIGCHLD信号(注意:内核发送)

大家还记得我们在学习fork复制进程中的父子进程时用到的代码吗?

小编把代码放到这里帮助大家回顾昂

#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
 
int main(){
    char *s=NULL;
    int n=0;
    pid_t id=fork();
    if(pid==-1){
        printf("fork err\n");
        exit(1);
    }
    if(id == 0){
        s="child";
        n=3;
    }//子进程
    else{
        s="parent";
        n=7;
    }//父进程
    int i=0;
    for(;i<n;i++){
        printf("s=%s\n",s);
        sleep(1);
    }
    exit(0);
}

在学习僵死进程时,小编说过父进程没有获取退出码是会产生僵死进程的;

在上面这段代码里,其实子进程结束了,已经给父进程发送了一个信号.只不过父进程忽略了;那么,我们修改一下代码,让父进程收到子进程的代码,打印一下收到的信号代号,不要忽略掉;

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<signal.h>
 
void sig_fun(int sig)
{
    printf("sig = %d\n",sig);
    printf("child over!\n");
}
int main()
{
    int n=0;
    char *s = NULL;
    pid_t pid=fork();
    if(pid == -1) 
    {   
        printf("fork err\n");
        exit(1);
    }   
    if(pid==0)
    {   
        n=3;
        s="child";
    }   
    else
    {   
        signal(SIGCHLD,sig_fun);//子进程结束,内核会默认给父进程发送信号
        n=7;
        s="parent";
    }
    for(int i =0;i<n;i++)
    {
        printf("s=%s\n",s);
        sleep(1);
    }
    exit(0);
}

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<signal.h>
#include<wait.h>//注意,wait的头文件不要忘记
 
void sig_fun(int sig)
{
    printf("sig = %d\n",sig);
    printf("child over!\n");
    int val;
    wait(&val);
    //我们也可以简单写,就是不获取退出码,只要不变成僵死进程就可以
    //wait(NULL);
}
int main()
{
    int n=0;
    char *s = NULL;
    pid_t pid=fork();
    if(pid == -1) 
    {   
        printf("fork err\n");
        exit(1);
    }   
    if(pid==0)
    {   
        n=3;
        s="child";
    }
    else
    {
        signal(SIGCHLD,sig_fun);//子进程结束,内核会默认给父进程发送信号
        n=7;
        s="parent";
    }
    for(int i =0;i<n;i++)
    {
        printf("s=%s\n",s);
        sleep(1);
    }
    exit(0);
}
Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐