Linux初学第十一天<进程间通信四、信号>

一、信号概述(signal)

        对于Linux系统来说,实现信号的是一种软中断,就像我们单片机的中断一样,中断的触发需要信号。许多程序都需要处理信号,来做不同的事情。

1.信号的名字和编号

        在Linux系统中,已经定义好了64个信号,每个信号都有一个名字和编号。信号名都以"SIG"为开头,信号定义在<signal.h>头文件中,信号的编号都定义为正整数。
        具体的喜好名称及其编号可以使用"kill -l"指令来查看,也可以"kill - +信号编号"来执行信号,值得一提的是:信号编号是从1开始的,不存在0号的信号,"kill "对于信号0 是特殊的应用。
64个信号的名字及编号:

 1) SIGHUP	    2) SIGINT	    3) SIGQUIT	     4) SIGILL	     5) SIGTRAP
 6) SIGABRT	    7) SIGBUS	     8) SIGFPE	     9) SIGKILL	    10) SIGUSR1
11) SIGSEGV	    12) SIGUSR2	    13) SIGPIPE	    14) SIGALRM	    15) SIGTERM
16) SIGSTKFLT   17) SIGCHLD     18) SIGCONT     19) SIGSTOP     20) SIGTSTP
21) SIGTTIN	    22) SIGTTOU     23) SIGURG      24) SIGXCPU     25) SIGXFSZ
26) SIGVTALRM   27) SIGPROF	    28) SIGWINCH	29) SIGIO	    30) SIGPWR
31) SIGSYS	    34) SIGRTMIN	35) SIGRTMIN+1	36) SIGRTMIN+2	37) SIGRTMIN+3
38) SIGRTMIN+4	39) SIGRTMIN+5	40) SIGRTMIN+6	41) SIGRTMIN+7	42) SIGRTMIN+8
43) SIGRTMIN+9	44) SIGRTMIN+10	45) SIGRTMIN+11	46) SIGRTMIN+12	47) SIGRTMIN+13
48) SIGRTMIN+14	49) SIGRTMIN+15	50) SIGRTMAX-14	51) SIGRTMAX-13	52) SIGRTMAX-12
53) SIGRTMAX-11	54) SIGRTMAX-10	55) SIGRTMAX-9	56) SIGRTMAX-8	57) SIGRTMAX-7
58) SIGRTMAX-6	59) SIGRTMAX-5	60) SIGRTMAX-4	61) SIGRTMAX-3	62) SIGRTMAX-2
63) SIGRTMAX-1	64) SIGRTMAX	

2.信号的处理

        信号的处理方式一共有三种:忽略,捕捉和默认动作;
忽略一个信号时,使用SIG_IGN来传递给flag,但是"SIGKILL""SIGSTOP"是不能被忽略的。
例如:

signal(SIGKILL,SIG_IGN);//SIGKILL是不会被忽略掉的,所以还是会执行SIGKILL。

3.信号的相关API

        信号的相关API分入门和高级两个版本:
入门版:
        信号注册函数:int signal();
        信号发送函数:int kill ();
高级版:
        信号注册函数:int sigaction();
        信号发送函数:int sigqueue ();
        两个版本的区别就是,高级发送的信号能够携带消息,而入门版只是能发送信号让进程去处理简单事情,无法携带消息。简单点就是,高级版就像光纤,能带有数据,而入门版就是敲门砖,只是提醒你去开门而已。

二、信号API的详细介绍和使用

1.入门版

(1)signal();

头文件: #inlcude <signal.h>
函数原型: int signal(int signum,sighandler_t handler);
参数说明:
              1. signum 信号的名称,例如SIGKILL
              2. handler 信号的处理方式或者要去执行的函数。
返回值: 成功返回0 ,失败返回 -1

(2)kill();

头文件: #include <signal.h>、#include <sys/types.h>;
函数原型: int kill(pid_t pid , int sig);
参数说明:
              1. pid 发送给哪个进程,pid就是那个进程的ID
              2. sig 需要发送的信号。
返回值: 成功返回0 ,失败返回 -1

(3)小实验

        编写两个程序,一个负责读取信号,暂且命名为:SIG_demo1.c;另一个负责发送信号,命名为SIG_demo2.c
SIG_demo1.c代码:

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

// typedef void (*sighandler_t)(int);

//sighandler_t signal(int signum, sighandler_t handler);
void handler(int signum)
{
        printf("get signum:%d\n",signum);
        switch(signum){
                case 2:printf("SIGINT\n");//对应Crtl+C
                        break;
                case 9:printf("SIGKILL\n");//对应 SIGKILL 可以结束进程
                        break;
                case 10:printf("SIGUSR1\n");
                        break;
        }
}
int main()
{
        signal(SIGINT,SIG_IGN);//忽略Crtl+C
        signal(SIGKILL,handler);
        signal(SIGUSR1,handler);
        while(1);
        return 0;
}

        这个程序的功能是,运行时,会卡住,摁Crtl+C 无法退出,直到摁Crtl+Z或者另一个进程向这个进程发送SIGKILL信号才会退出。
SIG_demo2.c 代码:

#include <signal.h>
#include <stdio.h>
#include <sys/types.h>
// typedef void (*sighandler_t)(int);

//sighandler_t signal(int signum, sighandler_t handler);

int main(int argc,char **argv)
{
        int signum;
        int pid;
        char cmd[128];
        signum=atoi(argv[1]);//把字符转换成ASCLL码
        pid =atoi(argv[2]); //把字符转换成ASCLL码
        printf("num=%d,pid=%d\n",signum,pid);
       // sprintf(cmd,"kill %d %d",signum,pid);
        kill(pid,signum);//直接发送
       // system(cmd);
        printf("Send signal OK!\n");
        return 0;
}

切记运行SIG_demo1.c后,要查找它的进程ID,这里我们把SIG_demo1.c编译为了pro,所以 ps -aux|grep pro 精确查找进程。
在这里插入图片描述
有个R+为后缀的,就是pro的进程ID:9427
两个程序配合的运行结果:
在这里插入图片描述
图中,我们可以看到,在 ./pro 执行之后,摁Crtl +C 只会显示 ^C ,运行 ./send之后,左边的打印Terminated 然后结束程序。这个小实验就算是成功了。

2.高级版

(1)sigaction ();

头文件: #include <signal.h>;
函数原型: int sigaction(int signum, const struct sigaction * act, struct sigaction * oldact);
参数说明:
              1. signum 信号名,也就是我们要发送的信号;
              2. *act 这是信号的结构体,具体的元素,实验时我们再细说;
              3. *oldact 这个结构体是负责备份第二个结构体的信息,NULL 为不备份;
返回值: 成功返回0 ,失败返回 -1

(2)sigqueue ();

头文件: #include <signal.h>;
函数原型: int sigqueue(pid_t pid, int sig, const union sigval value);
参数说明:
              1. pid 发送给哪个进程,pid就是那个进程的ID
              2. sig 需要发送的信号;
              3. value 为需要发送的联合体(共同体),需要发送的数据都放在这;
返回值: 成功返回0 ,失败返回 -1

(3)小实验

        编写两个程序,一个负责读取信号,暂且命名为:SIG_demo3.c;另一个负责发送信号,命名为SIG_demo4.c
        先说一下,在编写读取信号之前,我们需要了解一下sigaction 的第二个参数 *act结构体:
在这里插入图片描述
act 结构体里面我们所需要关注的只有 *sa_sigaction()flag 标志位;sa_sigaction() 的原型为:void *sa_sigaction(int signum,sigginfo_t *info,void *netxt ); 而其中的 siginfo_t *info结构体中,有大量成员:

siginfo_t {
               int      si_signo;    /* Signal number */
               int      si_errno;    /* An errno value */
               int      si_code;     /* Signal code */
               int      si_trapno;   /* Trap number that caused
                                        hardware-generated signal
                                        (unused on most architectures) */
               pid_t    si_pid;      /* Sending process ID */
               uid_t    si_uid;      /* Real user ID of sending process */
               int      si_status;   /* Exit value or signal */
               clock_t  si_utime;    /* User time consumed */
               clock_t  si_stime;    /* System time consumed */
               sigval_t si_value;    /* Signal value */
               int      si_int;      /* POSIX.1b signal */
               void    *si_ptr;      /* POSIX.1b signal */
               int      si_overrun;  /* Timer overrun count; POSIX.1b timers */
               int      si_timerid;  /* Timer ID; POSIX.1b timers */
               void    *si_addr;     /* Memory location which caused fault */
               long     si_band;     /* Band event (was int in
                                        glibc 2.3.2 and earlier) */
               int      si_fd;       /* File descriptor */
               short    si_addr_lsb; /* Least significant bit of address
                                        (since kernel 2.6.32) */
           }

而我们只要关注:pid、int si_int和sigval_t si_value结构体。
SIG_demo3.c代码:

#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
//信号处理函数
void Handler(int signum,siginfo_t *siginfo,void *context)
{
        printf("get signu=%d\n",signum);
        if(context!=NULL){  
                printf("siginfo data=%d\n",siginfo->si_int); //读取信号的数据
                printf("siginfo value=%d\n",siginfo->si_value.sival_int);//读取信号的数据,和si_int是一样的
                printf("signfo getpid=%d\n",siginfo->si_pid);//读取发送信号的进程的pid
        }
}
int main()
{
        struct sigaction *act;//定义act结构体指针
        act=(struct sigaction *)malloc(sizeof(struct sigaction));//分配控制
        act->sa_sigaction=Handler;//定义sa_sigaction信号处理函数
        act->sa_flags=SA_SIGINFO;//定义赋值flags为SA_SIGINFO

        sigaction(SIGUSR1,act,NULL);//开始获取信号
        while(1);
        return 0;
}

SIG_demo4.c代码:

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

int main(int argc,char **argv)
{
        pid_t pid;
        int signum;
        union sigval val;
        val.sival_int=102;

        signum=atoi(argv[1]);
        pid=atoi(argv[2]);

        int Send_s=sigqueue(pid,signum,val);//开始发送102
        if(Send_s==-1){
                printf("Send failer!\n");
                exit(-1);
        }
        printf("Send success!\n");
        return 0;
}

运行结果;
        把SIG_demo3.c编译为proSIG_demo4.c编译为send,pro的进程ID为:10697.在这里插入图片描述
开始给pro进程发送信号,
在这里插入图片描述
send 发送的信号编号为10 进程pro 打印出了信号的编号,和信号所携带的信息102 和发送者的pid

  • 5
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值