ICode9

精准搜索请尝试: 精确搜索
首页 > 系统相关> 文章详细

Linux进程之间的通讯(IPC)-- 信号

2021-09-05 00:02:40  阅读:178  来源: 互联网

标签:IPC 函数 -- pid int 信号 Linux include sigaction


四 信号

1. 信号概述

对于 Linux来说,实际信号是软中断,许多重要的程序都需要处理信号。信号,为 Linux 提供了一种处理异步事件的方法。比如,终端用户输入了 ctrl+c 来中断程序,会通过信号机制停止一个程序。

1.1 信号的名字和编号

每个信号都有一个名字和编号,这些名字都以“SIG”开头,例如“SIGIO ”、“SIGCHLD”等等。
信号定义在signal.h头文件中,信号名都定义为正整数。
具体的信号名称可以使用kill -l来查看信号的名字以及序号,信号是从1开始编号的,不存在0号信号。kill对于信号0又特殊的应用。
在这里插入图片描述

1.2信号的处理:

信号的处理有三种方法,分别是:忽略、捕捉和默认动作
(1)忽略信号:大多数信号可以使用这个方式来处理,但是有两种信号不能被忽略(分别是 SIGKILL和SIGSTOP)。因为他们向内核和超级用户提供了进程终止和停止的可靠方法,如果忽略了,那么这个进程就变成了没人能管理的的进程,显然是内核设计者不希望看到的场景
(2)捕捉信号:需要告诉内核,用户希望如何处理某一种信号,说白了就是写一个信号处理函数,然后将这个函数告诉内核。当该信号产生时,由内核来调用用户自定义的函数,以此来实现某种信号的处理。
(3)系统默认动作:对于每个信号来说,系统都对应由默认的处理动作,当发生了该信号,系统会自动执行。不过,对系统来说,大部分的处理方式都比较粗暴,就是直接杀死该进程。

1.3 信号处理函数

信号处理函数的注册
信号处理函数的注册不只一种方法,分为入门版和高级版

1、入门版:函数signal
2、高级版:函数sigaction

信号处理发送函数
信号发送函数也不止一个,同样分为入门版和高级版

1.入门版:kill
2.高级版:sigqueue

1.4 信号编程

1.4.1 signal

函数的原型:

 typedef void (*sighandler_t)(int)

函数指针,返回值为无

sighandler_t signal(int signum, sighandler_t handler)

第一个参数就是信号可以用kill -l查看哪个(shell指令),第二个参数就是上面的函数指针了。

下面是输入Ctrl + C这个信号让进程不会退出的代码signalDemo1.c

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

//typedef void (*sighandler_t)(int);
//sighandler_t signal(int signum, sighandler_t handler);

void handler(int signum)//收到信号(ctrl + c)让它执行我们希望执行的这个函数,而不是执行原有的默认动作。
{
        printf("get signum=%d\n",signum);
        printf("never quit\n");
}

int main()
{
        signal(SIGINT,handler);//注册信号

        while(1);
        return 0;
}

简单的总结一下,我们通过 signal 函数注册一个信号处理函数,分别注册了两个信号SIGIOSIGUSER1;随后主程序就一直“长眠”了。
通过 kill 命令发送信号之前,我们需要先查看到接收者,通过 ps 命令查看了之前所写的程序的 PID,通过 kill 函数来发送。
对于已注册的信号,使用 kill 发送都可以正常接收到,但是如果发送了未注册的信号,则会使得应用程序终止进程。
那么,已经可以设置信号处理函数了,信号的处理还有两种状态,分别是默认处理和忽略,这两种设置很简单,只需要将 handler 设置为 SIG_IGN(忽略信号)或 SIG_DFL(默认动作)即可。
在此还有两个问题需要说明一下:
(1)当执行一个程序时,所有信号的状态都是系统默认或者忽略状态的。除非是 调用exec进程忽略了某些信号。exec 函数将原先设置为要捕捉的信号都更改为默认动作,其他信号的状态则不会改变 。
(2)当一个进程调动了 fork 函数,那么子进程会继承父进程的信号处理方式。

1.4.2 kill

kill 的函数原型

#include <sys/types.h>
#include <signal.h>
int kill(pid_t pid, int sig);

kil的使用

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


int main(int argc, char** argv)
{
    if(3 != argc)
    {
        printf("[Arguments ERROR!]\n");
        printf("\tUsage:\n");
        printf("\t\t%s <Target_PID> <Signal_Number>\n", argv[0]);
        return -1;
    }
    int pid = atoi(argv[1]);
    int sig = atoi(argv[2]);
    //int kill(pid_t pid, int sig);
    if(pid > 0 && sig > 0)
    {
        kill(pid, sig);
    }
    else
    {
        printf("Target_PID or Signal_Number MUST bigger than 0!\n");
    }
    
    return 0;
}

总结一下:
根据以上的结果可看到,基本可以实现了信号的发送,虽然不能直接发送信号名称,但是通过信号的编号,可以正常的给程序发送信号了,也是初步实现了信号的发送流程。

关于 kill 函数,还有一点需要额外说明,上面的程序限定了 pid 必须为大于0的正整数,其实 kill 函数传入的 pid 可以是小于等于0的整数。
pid > 0:将发送个该 pid 的进程
pid == 0:将会把信号发送给与发送进程属于同一进程组的所有进程,并且发送进程具有权限想这些进程发送信号。
pid < 0:将信号发送给进程组ID 为 pid 的绝对值得,并且发送进程具有权限向其发送信号的所有进程
pid == -1:将该信号发送给发送进程的有权限向他发送信号的所有进程。(不包括系统进程集中的进程)

1.4.3 sigaction

sigaction 的函数原型

#include <signal.h>
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);

struct sigaction {
   void       (*sa_handler)(int); //信号处理程序,不接受额外数据,SIG_IGN 为忽略,SIG_DFL 为默认动作
   void       (*sa_sigaction)(int, siginfo_t *, void *); //信号处理程序,能够接受额外数据和sigqueue配合使用
   sigset_t   sa_mask;//阻塞关键字的信号集,可以再调用捕捉函数之前,把信号添加到信号阻塞字,信号捕捉函数返回之前恢复为原先的值。
   int        sa_flags;//影响信号的行为SA_SIGINFO表示能够接受数据
 };
//回调函数句柄sa_handler、sa_sigaction只能任选其一

sigaction 是一个系统调用,根据这个函数原型,我们不难看出,在函数原型中,第一个参数signum应该就是注册的信号的编号,可以为除 SIGKILLSIGSTOP 外的任何一个特定有效的信号;第二个参数act如果不为空说明需要对该信号有新的配置;第三个参数oldact如果不为空,那么可以对之前的信号配置进行备份,以方便之后进行恢复。

在这里额外说一下struct sigaction结构体中的 sa_mask 成员,设置在其的信号集中的信号,会在捕捉函数调用前设置为阻塞,并在捕捉函数返回时恢复默认原有设置。这样的目的是,在调用信号处理函数时,就可以阻塞默写信号了。在信号处理函数被调用时,操作系统会建立新的信号阻塞字,包括正在被递送的信号。因此,可以保证在处理一个给定信号时,如果这个种信号再次发生,那么他会被阻塞到对之前一个信号的处理结束为止。

sigaction 的时效性:当对某一个信号设置了指定的动作的时候,那么,直到再次显式调用 sigaction并改变动作之前都会一直有效。

关于结构体中的 flag 属性的详细配置,在此不做详细的说明了,只说明其中一点。如果设置为 SA_SIGINFO 属性时,说明了信号处理程序带有附加信息,也就是会调用 sa_sigaction 这个函数指针所指向的信号处理函数。否则,系统会默认使用 sa_handler 所指向的信号处理函数。在此,还要特别说明一下,sa_sigactionsa_handler 使用的是同一块内存空间,相当于 union,所以只能设置其中的一个,不能两个都同时设置。

关于void (*sa_sigaction)(int, siginfo_t *, void *);处理函数来说还需要有一些说明。void* 是接收到信号所携带的额外数据;而struct siginfo这个结构体主要适用于记录接收信号的一些相关信息。

 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 */
               int      si_band;     /* Band event */
               int      si_fd;       /* File descriptor */
}
sigaction的使用
```c
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>


/** 信号处理函数 */
void signal_handler(int sig)
{
    printf("\nthis signal number is %d \n",sig);

    if (sig == SIGINT) {
        printf("I have get SIGINT!\n\n");
        printf("The signal is automatically restored to the default handler!\n\n");
        /** 信号自动恢复为默认处理函数 */
    }

}

int main(void)
{
    struct sigaction act;

    printf("this is sigaction function test demo!\n\n");

    /** 设置信号处理的回调函数 */
    act.sa_handler = signal_handler;
    
    /* 清空屏蔽信号集 */
    sigemptyset(&act.sa_mask); 

    /** 在处理完信号后恢复默认信号处理 */
    act.sa_flags = SA_RESETHAND;

    sigaction(SIGINT, &act, NULL);

    while (1)
    {
        printf("waiting for the SIGINT signal , please enter \"ctrl + c\"...\n\n");
        sleep(1);
    }
    
    exit(0);
}

#### **1.4.4  sigqueue**
**信号发送函数——高级版**
发送信号函数`sigqueue`原型:

```c
#include <signal.h>
int sigqueue(pid_t pid, int sig, const union sigval value);//第一个参数是发给谁pid;第二个参数是什么信号;第三个参数要发送的消息
union sigval {
   int   sival_int;//整形
   void *sival_ptr;//字符串
 };

信号携带消息编程
(1)接收端代码

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

//int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact);
void handler(int signum, siginfo_t *info, void *context)//对收到的信号处理程序,能够接受额外数据和sigqueue配合使用
{
        printf("get signum %d\n",signum);

        if(context != NULL){//用context是否为空判断有没有信息传过来
                printf("get data=%d\n",info->si_int);//打印发送端的信号携带信息。
                printf("get data=%d\n",info->si_value.sival_int);//info结构体里面的value也是一个结构体,value里面的int型和info的结构体的int是一样的。
                printf("from:%d\n",info->si_pid);//打印info结构体里面的发送端pid值
        }
}

int main()
{
        struct sigaction act;//定义一个信号处理的结构体。
        printf("pid = %d\n",getpid());

        act.sa_sigaction = handler;//配置信号处理函数
        act.sa_flags = SA_SIGINFO;//flags配置这个宏是能够接收到信号携带的信息(be able to get message)

        sigaction(SIGUSR1,&act,NULL);//接收(注册)信号的函数
        while(1);

        return 0;
}

(2)发送端代码

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

//int sigqueue(pid_t pid, int sig, const union sigval value);

int main(int argc,char **argv)
{
        int signum;
        int pid;

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

        union sigval value;//发送信号时携带的信息放在这个结合体里,和信号接收端的siginfo_t结构体中的结构体sigval_t si_value对应的。
        value.sival_int = 100;//发送信号时携带的信息配置

        sigqueue(pid,signum,value);//发送信号及携带的信息。
        printf("%d done\n",getpid());

        return 0;
}

标签:IPC,函数,--,pid,int,信号,Linux,include,sigaction
来源: https://blog.csdn.net/baidu_39594043/article/details/120107335

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有