信号

信号:通知进程产生了某种事件

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

与信号有关的文件调用在“signal.h”头文件中有声明。

信号其实就是一个软件中断。

可以通过signal()修改对信号的响应方式。

信号一般有三种响应方式:1)默认响应;2)忽略响应;3)自定义响应。 

通过 kill 发送响应。

int kill(pid_t pid, int sig);

命令kill 就是对系统调用kill 的封装,命令kill就是通过系统调用kill给程序发送一个信号,进程收到信号后,自己结束自己。

一、改变信号的默认方式

信号已经提前在系统中定义好了,每个信号都有自己对应的代号。

1.1常见的信号的值:

1.2信号的值在系统源码中的定义如下: 

1. #define SIGHUP 1
2. #define SIGINT 2 //键盘按下 Ctrl+c 时,会产生该信号
3. #define SIGQUIT 3
4. #define SIGILL 4
5. #define SIGTRAP 5
6. #define SIGABRT 6
7. #define SIGIOT 6
8. #define SIGBUS 7
9. #define SIGFPE 8
10. #define SIGKILL 9 //该信号的响应方式不允许改变
11. #define SIGUSR1 10
12. #define SIGSEGV 11
13. #define SIGUSR2 12
14. #define SIGPIPE 13 //读端关闭的描述符,写端写入时产生,该信号会终止程序
15. #define SIGALRM 14
16. #define SIGTERM 15 //系统 kill 命令默认发送的信号
17. #define SIGSTKFLT 16
18. #define SIGCHLD 17 //子进程结束后,会默认给父进程发送该信号
19. #define SIGCONT 18
20. #define SIGSTOP 19
21. #define SIGTSTP 20
22. #define SIGTTIN 21
23. #define SIGTTOU 22
24. #define SIGURG 23

1.3改变信号的默认响应方式

通过signal()改变

#include <signal.h>

typedef void (*sighandler_t)(int);//sighandler_t 是函数指针类型

sighandler_t signal(int signum, sighandler_t handler);//int signum 信号代号;sighandler_t handler 函数指针,指向参数为int,返回值为void的函数

需要传入一个信号代号以及响应方式
eg:#define SIGINT 2 //键盘按下 Ctrl+c 时,会产生该信号

实现改变其信号,使其不中断程序,只打印信号的值

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

int main()
{
    while(1)
    {
        printf("main run\n");
        sleep(1);
    }

    exit(0);
}                                                        
                                                              
                                                          

 1.3.1自定义响应方式

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

void fun_a(int sig)
{
    printf("sig =%d\n",sig);//打印信号代号
}

int main()
{
    signal(2,fun_a);//signal(SIGINT,fun_a);改变信号代号2的响应方式,使其接收到信号2时,调用fun_a函数作出响应,此处不是由我们调用函数,是程序接收到信号后,内核调用fun_a(SIGINT)
    while(1)
    {
        printf("main run\n");
        sleep(1);
    }

    exit(0);
}                                                                                                                             
                                                                                                                                
                                                                                                                                
                                                                                                                                
                                                                                                                                

进程接收到信号之后,会短暂的暂停,处理了信号之后又会进行执行自己的

1.3.2重新变为默认响应方式

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

void fun_a(int sig)
{
    printf("sig =%d\n",sig);
    signal(sig,SIG_DFL);//重新约定响应方式,按默认的响应方式,后期按最新的信号响应方式处理
}

int main()
{   
    signal(2,fun_a);
    while(1)
    {   
        printf("main run\n");
        sleep(1);
    }
    
    exit(0);
}
                                                              
                                                              
                                                              
                                                              

 

 1.3.3忽略响应方式

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

int main()
{
    signal(2,SIG_IGN);//忽略响应方式
    while(1)
    {
        printf("main run\n");
        sleep(1);
    }
    
    exit(0);
}
                                                                 
                                                                 
                                                                 
                                                                 

 二、不允许改变响应方式的信号 SIGKILL 9

9号信号不允许被改变被忽略

命令中的 kill -9 pid  就是传的此信号

三、实现自己的kill

 kill 可以给进程发送信号,其实就是系统调用的一个封装。

int kill(pid_t pid, int sig);//进程id,信号代码

sscanf(),将输入的数据转化为整型

#include <stdio.h>

int scanf(const char *format, ...);
int fscanf(FILE *stream, const char *format, ...);
int sscanf(const char *str, const char *format, ...);

固定调用2号信号,结束进程

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


int main(int argc,int *argv[])
{
    if(argc !=2)//第一个参数函数名称./mykill,第二个参数进程名
    {
        printf("argc erro\n");
        exit(1);
    }
    int pid = 0;
    sscanf(argv[1],"%d",&pid);
    if(kill(pid,SIGINT)==-1)//固定调用2号信号
    {
        printf("kill err\n");
    }
    exit(0);

}
                                                                     
                                                                     

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


int main(int argc,int *argv[])
{
    if(argc !=3)//第一个参数函数名称./mykill,第二个参数进程名,第三个参数
    {
        printf("argc erro\n");
        exit(1);
    }

    int pid = 0;
    int sig =0;
    sscanf(argv[1],"%d",&pid);
    sig = atoi(argv[2]);
    if( kill(pid,sig) == -1 )//固定调用2号信号
    {
        printf("kill err\n");
    }
    exit(0);

}
                                                                              
                                                                              
                                                                              
                                                                              

 kill 默认发送15号信号

 三、#define SIGCHLD 17 

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

子进程结束后,父进程会受到此信号量,父进程对此信号量默认响应是忽略

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>//常用的库函数,包含系统调用
#include<signal.h>

void fun(int sig)
{
    printf("sig =%d\n",sig);
}

int main()
{
    signal(SIGCHLD,fun);//约定接收到子进程结束的信号调用函数fun
     int n=0;
     char *s = NULL;

     pid_t id = fork();
     if(id == -1)
     {
         exit(1);//0代表成功,1,2,3,,都是失败
     }
     if(id == 0)//在子进程中执行
     {
         n=3;
         s = "child";
     }
     else
     {
         n = 7;
         s = "parent";
     }

     for(int i = 0;i<n;i++)//父子进程都可以执行
     {
         printf("s =%s\n",s);
         sleep(1);
     }
     exit(0);
}

 信号是软件层次的中断,进程接收到信号后,产生中断,处理信号后继续运行。

 子进程结束后,父进程还未获取它的退出码,子进程就会变成僵死进程,

3.1如何利用信号处理僵死进程:

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>//常用的库函数,包含系统调用
#include<signal.h>
#include<sys/wait.h>

int main()
{
     int n=0;
     char *s = NULL;

     pid_t id = fork();
     if(id == -1)
     {
         exit(1);//0代表成功,1,2,3,,都是失败
     }
     if(id == 0)//在子进程中执行
     {
         n=3;
         s = "child";
     }
     else
     {
         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<sys/wait.h>

void fun(int sig)
{
    printf("sig =%d\n",sig);
    int val =0;
    int id =wait(&val);//获取退出码
    printf("id =%d,exit code:%d\n",id,WEXITSTATUS(val));//打印子进程id以及退出码

}

int main()
{
    signal(SIGCHLD,fun);
     int n=0;
     char *s = NULL;

     pid_t id = fork();
     if(id == -1)
     {
         exit(1);//0代表成功,1,2,3,,都是失败
     }
     if(id == 0)//在子进程中执行
     {
         n=3;
         s = "child";
     }
     else
     {
         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<sys/wait.h>

void fun(int sig)
{
    printf("sig =%d\n",sig);
    wait(NULL);
//也可以直接用wait处理了僵死进程,父进程收到子进程结束的信号短暂的中断,调用wait,因为子进程已经结束,wait可以直接获取子进程的退出码,父进程接收后就又去执行自己的进程了,整个过程父进程不会阻塞住
    //int val =0;
    //int id =wait(&val);//获取退出码
    //printf("id =%d,exit code:%d\n",id,WEXITSTATUS(val));//打印子进程id以及退出码

}

int main()
{
    signal(SIGCHLD,fun);
     int n=0;
     char *s = NULL;

     pid_t id = fork();
     if(id == -1)
     {
         exit(1);//0代表成功,1,2,3,,都是失败
     }
     if(id == 0)//在子进程中执行
     {
         n=3;
         s = "child";
     }
     else
     {
         n = 7;
         s = "parent";
     }

     for(int i = 0;i<n;i++)//父子进程都可以执行
     {
         printf("s =%s\n",s);
         sleep(1);
     }
     exit(0);
}
                                                                      

四、进程如何接收信号

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值