首页 > 系统相关 >Linux进程通信——信号

Linux进程通信——信号

时间:2024-03-19 23:35:45浏览次数:25  
标签:int signal Linux pid 通信 si 信号 signum 进程

一. 信号的名字和编号

1. 每个信号都有一个编号和名称,这些名字都以“SIG”开头。

    (kill -l命令可显示出当前系统支持的信号的编号和名称 )

二. 信号的处理

1. 信号的处理有三种方法:  忽略,捕捉,默认动作

忽略:有俩种信号不可被忽略(SIGKILL和SIGSTOP),其向 内核 和 超级用户提供了进程终止的方法 (SIG_ICN可忽略信号,但不能忽略上述俩种信号)

捕捉:当捕捉到信号时,由内核来调用用户自定义的函数

默认动作:当捕捉到信号时,由内核来执行默认操作,如Ctrl + C 终止进程

三. 低级信号通信

1. kill命令

        kill -l 显示出当前系统支持的信号的编号和名称        

        kill -9 8066 发送编号为9的信号到PID为8066的进程当中

2.kill函数 

        int kill(pid_t pid, int sig); //用于向指定的进程发送信号

3. Signal函数

        功能: 用于捕获到某一信号然后产生对应用户自定义的动作
        函数原型:typedef void (*sighandler_t)(int);     //函数指针,别名为sighandler_t
                          sighandler_t signal(int signum, sighandler_t handler);
signum信号的编号,如SIGKILL的编号是9
handler

指向中断函数的指针,接收到信号时,调用自定义的handler函数执行相关操作

低级信号通信实例:

源程序 p1.c:

a.通过signal函数捕获信号,并调用hander指针指向的函数执行相应操作

b.建立副终端输入ps -aux|grep p1.out查询当前进程PID,输入kill -9 8199结束当前终端

#include <stdio.h>
#include <signal.h>
 
void handler(int signum)
{
	printf("get signum is %d\n",signum);
	printf("not quit\n");
        switch(signum)
        {
                case 2:
                        printf("SIGINT\n");
                        break;
                case 9:
                        printf("SIGKILL\n");
                        break;
	    }
}
 
int main()
{
	signal(SIGINT,handler);
        //signal(SIGINT,SIG_ICN);//该宏可以达到忽略信号的目的
	signal(SIGKILL,handler);
        //signal(SIGKILL,SIG_ICN);//此处不起作用,无法忽略终止信号
	while(1);
	
	return 0;
}
运行结果 p1.out:

源程序p2.c:

通过kill函数发送信号向p1.out进程中

/**
 * 直接调用kill函数向p1进程发送信号
*/
#include <stdio.h>
#include <signal.h>
 
int main(int argc,char **argv)
{
	int signum;
        int pid;

        //将命令行参数中的信号值和进程ID转换为整数。
        signum =  atoi(argv[1]);
        pid = atoi(argv[2]); 

        printf("num=%d,pid=%d\n",signum,pid);
        //向指定进程ID发送指定信号
	    kill(pid,signum); 

        printf("send signal ok!\n");
	return 0;
}
运行结果 p2.out:

源程序p3.c:

相比p2.c区别在于调用system函数执行kill命令向p1.out中发送信号,而不是kill函数发送

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

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

        char cmd[128] = {0};

        //将命令行参数中的信号值和进程ID转换为整数。
        signum =  atoi(argv[1]);
        pid = atoi(argv[2]); 

        printf("num=%d,pid=%d\n",signum,pid);
        sprintf(cmd,"kill -%d %d",signum,pid);

        //系统会调用命令解释器来执行这条命令
        system(cmd);
        printf("send signal ok!\n");
	return 0;
}

运行结果 p3.out:

五. 高级信号通信

低级信号通信存在一个问题就是,只能发送和接收到了信号无法携带一些数据,因此引入了高级信号通信。

接受函数:

#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表示能够接受数据
 };
第一个参数:要捕捉的信号的编号
第二个参数:是一个指向结构体的指针
函数指针1,不接受额外数据,和原来的signal函数一样
函数指针2,  能够额外接受数据,如它指向hander函数时,hander函数的参数如下
第一个参数是信号的编号
第二个参数是一个结构体,能获取到是谁发送的,能获取到整形数据,char型数据等等
第三个参数是一个指针,当指针为空时,无数据,非空时,有数据

 mask结构体,可以不配置,默认起到阻塞的作用
标记(如果要接受数据必须要配置宏)

第三个参数:如果不为NULL,那么可以对之前的信号配置进行备份,以方便之后进行恢复。

sigaction函数中的第二个参数是一个指向结构体的指针,这个结构体的第二个参数是一个函数指针,函数指针指向hander函数时,hander函数的第二个参数又是一个指向struct siginfo这个结构体的指针。这时,我们可以打印出携带的信息。

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 */
}

发送函数:

#include <signal.h>
int sigqueue(pid_t pid, int sig, const union sigval value);
union sigval {
   int   sival_int;
   void *sival_ptr;
 };
第一个参数:接收端PID
第二个参数:要发送的命令编号signum
第三个参数:结构体

实例:

源程序:

接受程序recive.c
#include <stdio.h>
#include <signal.h>

void handler(int signum, siginfo_t *info, void *context) {
    printf("Received signal: %d\n", signum); // 打印接收到的信号编号
    if (context != NULL) {
        printf("Received data: %d\n", info->si_int); // 打印额外信息中的整数值
        printf("get data=%d\n",info->si_value.sival_int);
    }
}

int main() {
    struct sigaction act;

    //无需配置第一个参数,因为第一个参数是无需接受额外信息
    //无需配置第三个参数,因为默认为阻塞
    act.sa_sigaction = handler; // 第二个参数,设置信号处理函数为handler
    act.sa_flags = SA_SIGINFO;  // 第四个参数,设置标志为允许接收额外信息

    //int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
    sigaction(SIGUSR1, &act, NULL);

    printf("Waiting for signals...\n");

    while(1);

    return 0;
}
发送程序send.c
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>

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

    //将命令行参数中的信号值和进程ID转换为整数。
    signum =  atoi(argv[1]);
    pid = atoi(argv[2]); 
    //value 参数是一个 union sigval 类型的联合体,用于携带额外的数据
    union sigval value;
    value.sival_int = 12345;
    //int sigqueue(pid_t pid, int sig, const union sigval value);
    sigqueue(pid,signum,value);
    printf("done\n");
    return 0;
}

运行结果:

标签:int,signal,Linux,pid,通信,si,信号,signum,进程
From: https://blog.csdn.net/m0_73510889/article/details/136750094

相关文章

  • Linux脏牛提权漏洞复现(DirtyCow)
    #简述脏牛(DirtyCow)是Linux中的一个提权漏洞。主要产生的原因是Linux系统的内核中Copy-on-Write(COW)机制产生的竞争条件问题导致,攻击者可以破坏私有只读内存映射,并提升为本地管理员权限。#前期准备靶机:vulnhub——Lampiao192.168.230.217攻击机:Kali192.168.230.128#复现......
  • 【Linux】 生产消费者模型
    线程同步同步: 在保证数据安全的前提下,让线程能够按照某种特定的顺序访问临界资源,从而有效避免饥饿问题,这就叫做同步(饥饿问题:某些线程无法得到资源而长时间无法执行,常见的就是申请不到锁)竞态条件:因为时序问题,而导致程序异常,我们称之为竞态条件。单纯的加锁会引起问题。如......
  • 进程间通信 之 管道
    目录什么是管道通信管道通信的特点匿名管道命名管道进程间通信的本质:让不同的进程看到同一份资源!什么是管道通信管道是Unix中最古老的进程间通信的形式。它是一种基于文件的通信形式,我们把从一个进程连接到另一个进程的一个数据流称为一个“管道”。管道文件是一种......
  • [Linux]文件缓冲区
    文件fd输出重定向除了用dup2()改变数组下标外,还可以用命令来完成所有的命令执行,都必须有操作系统将其运行起来变成进程,然后根据>>,<<来判断是输入重定向,还是输出重定向。缓冲区之所以有缓冲区,是为了提高效率的。就类比快递一样,如果你送一个东西给北京的朋友,那么你自己......
  • Modbus TCP转Profinet网关实现多服务器无缝对接通信
    在工业控制领域中,ModbusTCP转Profinet网关(XD-ETHPN20)扮演着连接不同设备间通讯的重要角色。当将Modbus主站与十几台服务器进行通讯时,通过modbustcp转profinet网关(XD-ETHPN20)设备将不同协议间的数据交和通迅连接起来,为工业自动化系统的运行提供了稳定可靠的基础。为了实现Modbus......
  • 操作系统实践之路——五、初始化(2.Linux初始化)
    文章目录一、全局流程二、从BIOS到GRUB三、GRUB是如何启动的四、详解vmlinuz文件结构五、流程梳理-1六、内核初始化从_start开始七、流程梳理-2参考资料前言​本章节将讨论一下Linux如何去做初始化。一、全局流程​在机器加电后,BIOS会进行自检,然后由BIOS加载......
  • 从 Linux 内核角度探秘 JDK MappedByteBuffer
    本文涉及到的内核源码版本为:5.4,JVM源码为:OpenJDK17,RocketMQ源码版本为:5.1.1在之前的文章《一步一图带你深入剖析JDKNIOByteBuffer在不同字节序下的设计与实现》中,笔者为大家详细剖析了JDKBuffer的整个设计体系,从总体上来讲,JDKNIO为每一种Java基本类型定义了对......
  • 僵尸进程_ZombieProcess
            僵尸进程(ZombieProcess)在计算机操作系统中,特别是类Unix系统中,是指一种特殊的进程状态。当一个子进程已经完成了其生命周期并通过`exit()`系统调用正常退出或者异常终止时,它并不会立即从系统进程中消失。此时,虽然它的所有资源(如内存、打开的文件描述符等)都已经......
  • Linux TCP/UDP CS模型
    LinuxTCP/UDPCS模型目录LinuxTCP/UDPCS模型TCPServer/TCPClientUDPServer/UDPClientTCPServer/TCPClient在C语言中实现一个TCP服务器时,使用select函数可以帮助我们同时监控多个文件描述符(包括socket)的状态,从而实现非阻塞的I/O操作。以下是一个简单的TCP服务器示例,它......
  • linux命令 --简化版--快速上手
    linux命令--简化版--快速上手系统信息arch显示机器的处理器架构(1)uname-m显示机器的处理器架构(2)uname-r显示正在使用的内核版本dmidecode-q显示硬件系统部件-(SMB[IOS](https://www.2cto.com/kf/yidong/iphone/)/DMI)hdparm-i/dev/hda罗列一个磁盘的......