Linux进程间的通信-信号(signal、kill、sigaction、sigqueue)

 一、软中断信号
软中断信号(s i g n a l signalsignal,又简称为信号)用来通知进程发生了事件进程之间可以通过调用kill函数发送软中断信号。L i n u x LinuxLinux 内核也可能给进程发送信号,通知进程发生了某个事件(例如内存越界)。
注意:信号只是用来通知某进程发生了什么事件,无法给进程传递任何数据,进程对信号的处理方法有三种:
① 忽略信号(SIG_IGN:大多数信号可以使用这个方式来处理,但是有两种信号不能被忽略(分别是 SIGKILL和SIGSTOP)。因为他们向内核和超级用户提供了进程终止和停止的可靠方法,如果忽略了,那么这个进程就变成了没人能管理的的进程,显然是内核设计者不希望看到的场景
② 捕捉信号:需要告诉内核,用户希望如何处理某一种信号,说白了就是写一个信号处理函数,然后将这个函数告诉内核。当该信号产生时,由内核来调用用户自定义的函数,以此来实现某种信号的处理
③ 系统默认动作(SIG_DFL,对于每个信号来说,系统都对应由默认的处理动作,当发生了该信号,系统会自动执行。不过,对系统来说,大部分的处理方式都比较粗暴,就是直接杀死该进程。具体的信号默认动作可以使用man 7 signal来查看系统的具体定义。在此,我就不详细展开了,需要查看的,可以自行查看。也可以参考 《UNIX 环境高级编程(第三部)》的 P251——P256中间对于每个信号有详细的说明。

下面我们来介绍信号相关的API:

  1. signal 函数

    void (*signal(int signum, void (*handler)(int))) (int);
    //给 void (*)(int)函数指针换个标签
    typedef void (*sighandler_t)(int);
    //替换第一行。
    sighandler_t signal(int signum, sighandler_t handler)
    • signum:要处理的信号编号。可以是预定义的信号常量(如 SIGINTSIGTERM 等),也可以是自定义的信号编号。

    • handler:信号处理函数的指针。当接收到指定信号时,系统将调用该信号处理函数来处理该信号。

  2. kill 函数

    int kill(pid_t pid, int signum);
    • pid目标进程的进程 ID。可以使用 getpid() 获取当前进程的进程 ID,或者使用其他方式获取指定进程的进程 ID。
    • signum:要发送的信号编号。

  3. 信号处理函数的参数

    void handler(int signum);
    • signum:触发信号处理函数的信号编号。

 4.下面是demo实现信号通知进程的(软件中断)

signal:

  1 #include <stdio.h>
  2 #include <stdlib.h>
  3 #include <string.h>
  4 #include <signal.h>
  5 
  6 
  7 void handler(int signum)
  8 {
  9         switch(signum)
 10         {
 11                 case 2:
 12                         printf("get signum:%d\n",signum);
 13                         printf("SIGINT\n");
 14                         break;
 15 
 16                 case 9:
 17                         printf("get signum:%d\n",signum);
 18                         printf("SIGKILL!\n");
 19                         break;
 20 
 21                 case 10:
 22                         printf("get signum:%d\n",signum);
 23                         printf("SIGUSER1\n");
 24                         break;
 25                 default :
 26                         break;
 27 
 28         }
 29 
 30 }
 31 int main()
 32 {
 33 
 34 //      typedef void (*sighandler_t)(int);
 35 //      sighandler_t signal(int signum, sighandler_t handler);
 36         signal(SIGINT,handler);
 37         signal(SIGKILL,handler);
 38         signal(SIGUSR1,handler);
 39 
 40         while(1);
 41         return 0;
 42 }
 43 

kill:

  1 #include <stdio.h>
  2 #include <stdlib.h>
  3 #include <string.h>
  4 #include <signal.h>
  5 
  6 int main(int argc, char *argv[])
  7 {
  8     int pid; // 进程ID
  9     int signum; // 信号编号
 10     char cmd[128] = {0}; // 用于存储执行的命令
 11 
 12     // 从命令行参数中获取进程ID和信号编号
 13     pid = atoi(argv[1]); // 将字符串转换为整数作为进程ID
 14     signum = atoi(argv[2]); // 将字符串转换为整数作为信号编号
 15     printf("pid=%d, signum=%d\n", pid, signum);
 16     kill(pid,signum);
 17     // 构造发送信号的命令
 18 //    sprintf(cmd, "kill -%d %d", signum, pid);也可以用sysem函数发送指令
 20     // 使用 system 函数执行发送信号的命令
 21 //    system(cmd);
 22 
 23     return 0;
 24 }
 25 
~                                                                                                                                                                                                           
~                                                                                                                                                                                                           
~                                                                                                                                                                                                           
~                                                                                                                                                                                                           
~                                                                                                                                                                                                           
~                                                                                                                                                                                                           
~                                                                                                                                                                                                           
~                                                                                                         
~                           

结果:

二、sigaction 函数用于设置信号的处理方式,它提供了更为灵活和可靠的信号处理机制,相较于较旧的 signal 函数,更推荐使用 sigaction。

sigaction 函数的原型如下:

int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);

signum:要设置处理方式的信号编号。

act:指向一个结构体 struct sigaction,用于设置新的信号处理方式。

oldact:如果不为 NULL,则会将原来的信号处理方式存储在这个结构体中。

struct sigaction 结构体定义如下:

struct sigaction 结构体的成员包括:

struct sigaction {

    void (*sa_handler)(int);  // 指向信号处理函数的函数指针

    void (*sa_sigaction)(int, siginfo_t *, void *);  // 用于扩展的信号处理函数

    sigset_t sa_mask;  // 在信号处理函数执行期间要阻塞的信号集合

    int sa_flags;  // 控制信号处理的各种选项

    void (*sa_restorer)(void);  // 用于兼容旧式信号处理函数的恢复函数

};

sa_handler:指向信号处理函数的函数指针,类似于旧的 signal 函数。当信号处理函数返回时,程序会恢复到正常执行。通过设置该指针为 SIG_IGN 可以忽略信号,设置为 SIG_DFL 可以使用系统默认的处理方式。

sa_sigaction:用于扩展的信号处理函数,与 sa_handler 不能同时使用。在某些情况下,可以利用该函数获取更多关于信号的信息。

sa_mask:在信号处理函数执行期间要阻塞的信号集合。当处理某个信号时,可以屏蔽其他特定的信号,以避免信号处理函数被其他信号中断。

sa_flags:控制信号处理的各种选项,例如:

SA_RESTART:在信号处理函数被中断后自动重启被打断的系统调用。

SA_NOCLDSTOP:不接收子进程停止或继续的信号。

SA_SIGINFO:使用扩展的信号处理函数 sa_sigaction。

SA_RESETHAND:当调用信号处理函数时,将信号的处理函数重置为缺省值SIG_DFL

SA_RESTART:如果信号中断了进程的某个系统调用,则系统自动启动该系统调用

SA_NODEFER :一般情况下, 当信号处理函数运行时,内核将阻塞该给定信号。但是如果设置了 SA_NODEFER标记, 那么在该信号处理函数运行时,内核将不会阻塞该信号

sa_restorer:仅用于兼容旧式信号处理函数的恢复函数。

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

void handler(int signum, siginfo_t *siginfo, void *context);

int main()
{
    struct sigaction act;
    act.sa_sigaction = handler;     // 设置信号处理函数
    act.sa_flags = SA_SIGINFO;      // 使用扩展模式,传递附加信息
    printf("pid=%d\n", getpid());
    sigaction(SIGUSR1, &act, NULL); // 注册信号处理函数
    while(1);
    return 0;
}

void handler(int signum, siginfo_t *siginfo, void *context)
{
    printf("signum=%d\n", signum);
    printf("si_pid=%d\n", siginfo->si_pid);    // 发送信号的进程 ID
    printf("sigval_t si_value=%d\n", siginfo->si_value.sival_int);   // 传递的整型值
    printf("si_int=%d\n", siginfo->si_int);    // 废弃字段,不再使用
}

在主函数中,首先创建了一个 struct sigaction 结构体对象 act,并设置其成员 sa_sigaction 为处理函数 handlersa_flags 设置为 SA_SIGINFO,表示使用扩展模式。

接下来,打印当前进程的 PID,并使用 sigaction() 函数注册了 SIGUSR1 信号的处理函数。

处理函数 handler 接收三个参数,分别是接收到的信号编号、信号附加信息的结构体指针和上下文指针。在该处理函数中,打印出信号编号、发送信号的进程 ID、传递的整型值等信息。

最后,在主函数的无限循环中,程序会一直处于运行状态,等待接收到信号。

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

int main(int argc, char *argv[])
{
    pid_t pid = atoi(argv[1]);           // 获取指定进程的 PID
    int signum = atoi(argv[2]);          // 获取要发送的信号编号

    printf("pid=%d\n", getpid());        // 打印当前进程的 PID

    union sigval value;
    value.sival_int = 666;               // 设置传递的整型值

    int ret = sigqueue(pid, signum, value);   // 向指定进程发送信号
    if (ret == -1) {
        perror("发送信号失败");             // 发送信号失败时打印错误信息
        exit(-1);
    }

    return 0;
}

在主函数中,首先通过 atoi() 函数将命令行参数转换为整型,获取了要发送信号的进程的 PID 和要发送的信号编号。

接下来,打印当前进程的 PID。

然后,创建了一个 union sigval 类型的变量 value,并将其成员 sival_int 设置为666,即要传递的整型值。

之后,使用 sigqueue() 函数向指定的进程发送信号,传递了进程的 PID、信号编号和附加的值。发送成功时,函数返回 0;发送失败时,函数返回 -1,并打印错误信息。

最后,程序正常退出。

结果:

20230821

  • 27
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值