首页 > 系统相关 >进程通信3-信号

进程通信3-信号

时间:2023-03-01 18:33:06浏览次数:64  
标签:set int 通信 阻塞 内核 信号 进程

1. 信号的概念

信号是 Linux 进程间通信的最古老的方式之一,是事件发生时对进程的通知机制,有时也
称之为软件中断,它是在软件层次上对中断机制的一种模拟,是一种异步通信的方式。信号
可以导致一个正在运行的进程被另一个正在运行的异步进程中断,转而处理某一个突发事件。

发往进程的诸多信号,通常都是源于内核。引发内核为进程产生信号的各类事件如下:
1. 对于前台进程,用户可以通过输入特殊的终端字符来给它发送信号。比如输入Ctrl+C
通常会给进程发送一个中断信号。
2. 硬件发生异常,即硬件检测到一个错误条件并通知内核,随即再由内核发送相应信号给
相关进程。比如执行一条异常的机器语言指令,诸如被 0 除,或者引用了无法访问的
内存区域。
3. 系统状态变化,比如 alarm 定时器到期将引起 SIGALRM 信号,进程执行的 CPU
时间超限,或者该进程的某个子进程退出。
4. 运行 kill 命令或调用 kill 函数。

使用信号的两个主要目的是:

  1. 让进程知道已经发生了一个特定的事情。
  2. 强迫进程执行它自己代码中的信号处理程序。

信号的特点:

  1. 简单
  2. 不能携带大量信息
  3. 满足某个特定条件才发送
  4. 优先级比较高

查看系统定义的信号列表:kill –l
前 31 个信号为常规信号,其余为实时信号。

查看信号的详细信息:man 7 signal
◼ 信号的 5 中默认处理动作

  1. Term 终止进程
  2. Ign 当前进程忽略掉这个信号
  3. Core 终止进程,并生成一个Core文件
  4. Stop 暂停当前进程
  5. Cont 继续执行当前被暂停的进程

信号的几种状态:产生、未决、递达
SIGKILL 和 SIGSTOP 信号不能被捕捉、阻塞或者忽略,只能执行默认动作。

2. 信号的相关函数

2.1 kill raise abort

int kill(pid_t pid, int sig);
功能: 给任何的进程或者进程组, 发送某个信号 sig
  参数:
  -pid: 需要发送给的进程的id
    >0: 将信号发送给指定的进程
      =0:将信号发送给当前的进程组
      =-1: 将信号发送给每一个有权限接受这个信号的进程
      <-1:  这个pid就等于某个进程组的ID取反
  -sig: 需要发送的信号的编号或者是宏值, 建议使用宏值,因为不同的架构下的编号可能不一样
      =0: 表示不发送信号

   kill(getppid(), 9);

int raise(int sig);
功能: 给当前进程发送信号
  参数: 
  -sig 要发送的信号

  返回值: 成功0 失败非0
  相当于 kill(getpid(), sig);

void abort(void);
功能: 发送SIGABRT信号给当前进程,杀死当前的进程
相当于 kill(getpid(), SIGABRT);

2.2 alarm setitimer

#include <unistd.h>
unsigned int alarm(unsigned int seconds);
功能: 设置定时器。函数调用开始倒计时,当倒计时为0的时候,函数会给当前的进程发送一个信号 SIGALARM
参数:
    seconds  倒计时的时长,单位秒; 如果参数为0,定时器无效
   	若取消定时器,也是将参数设置为0
返回值:之前没有定时器,返回0
  之前有定时器返回之前的定时器剩余的时间

  -SIGALARM: 默认终止当前的进程,每一个进程都有且只有唯一的一个定时器(调用一个alarm后前面的alarm就失效了,剩余的时间会更新)

alarm(100)   该函数是不阻塞的
    
    
    
#include <sys/time.h>
int setitimer(int which, const struct itimerval *new_value,
                  struct itimerval *old_value);

功能:设置定时器,可以替代alarm函数。精度微妙, 可以实现周期性的定时
参数:
  -which: 定时器以什么时间计时
    ITIMER_REAL:真实时间,时间到达,发送SIGALRM 常用
    ITIMER_VIRTUAL: 用户时间,时间达到,发送SIGVTALRM
    ITIMER_PROF: 以该进程在用户态和内核态下所消耗的时间来计算,时间到达,发送SIGPROF   【和alarm共享同一个定时器】

  -*new_value: 设置定时器的属性
    struct itimerval {   //定时器的结构体
        struct timeval it_interval; // Interval for periodic timer每个阶段的时间,多长时间触发1次
        struct timeval it_value;    // Time until next expiration    延迟多长时间执行定时器
     };
     struct timeval {   // 时间的结构体
         time_t      tv_sec;         // seconds   秒
         suseconds_t tv_usec;        // microseconds   微妙 
      };

过10秒后,每隔2秒定时一次  it_value=10  it_interval=2
-old_value: 记录上一次定时的时间参数, 一般不使用 NULL

返回值: 成功 失败-1

2.3 signal sigaction

#include <signal.h>
typedef void (*sighandler_t)(int);

sighandler_t signal(int signum, sighandler_t handler);
功能: 设置某个信号的捕捉行为, 捕捉一个信号
  参数:
  -signum: 要捕捉的信号
  -handler: 捕捉到信号要如何处理
      SIG_IGN: 忽略信号
      SIG_DFL:使用信号默认的行为
      回调函数:这个函数是内核调用,程序员只负责写,捕捉到信号后如何去处理信号
      回调函数: 【一般也用在框架当中】
        - 需要程序员实现,提前准备好的,函数的类型根据实际需求,看函数指针的定义
        - 不是程序员调用,而是当信号产生,由内核调用
        - 函数指针是实现回调的手段,函数实现之后,将函数名放到函数指针的位置就可以了

返回值: 成功返回上一次注册的信号处理函数的地址,第一次调用返回NULL
        失败 返回SIG_ERR,设置错误号

SIGKILL和SIGSTOP不能被捕捉,不能被忽略
        
        
#include <signal.h>
int sigaction(int signum, const struct sigaction *act,
                     struct sigaction *oldact);
功能: 检查或改变信号的处理,信号捕捉
参数:
 	- signum:需要捕捉的信号的编号或宏值(信号的名称)
 	- act: 捕捉到信号之后的处理动作
 	- oldact: 上一次对信号捕捉相关的设置,一般不使用,传递NULL
返回值: 成功0 失败-1
    
    struct sigaction {
      // 函数指针,指向的函数就是信号捕捉到之后的处理函数
      void     (*sa_handler)(int);
      // 不常用   
      void     (*sa_sigaction)(int, siginfo_t *, void *);
      // 临时阻塞信号集,在信号捕捉函数执行过程中,临时阻塞某些信号
      // sa_mask是一个信号集,当接收到某个信号,并且调用sa_handler函数对信号处理之前,把该信号集里面的信号加入到进程的信号屏蔽字当中,当sa_handler函数执行完之后,这个信号集中的信号又会从进程的信号屏蔽字中移除
      sigset_t   sa_mask;
      // 使用哪一个信号处理函数对捕捉到的信号进行处理
      // 这个值可以是0,表示sa_handler,也可以是SA_SIGINFO,表示使用第二个sa_sigaction
      int        sa_flags;
      // 被废弃掉 设置成NULL就可以
      void     (*sa_restorer)(void);
    };   
        

3. 信号集

许多信号相关的系统调用都需要能表示一组不同的信号,多个信号可使用一个称之为
信号集的数据结构来表示,其系统数据类型为 sigset_t。

在 PCB 中有两个非常重要的信号集。一个称之为 “阻塞信号集” ,另一个称之为
“未决信号集” 。这两个信号集都是内核使用位图机制来实现的。但操作系统不允许我
们直接对这两个信号集进行位操作。而需自定义另外一个集合,借助信号集操作函数
来对 PCB 中的这两个信号集进行修改。

信号的 “未决” 是一种状态,指的是从信号的产生到信号被处理前的这一段时间。

信号的 “阻塞” 是一个开关动作,指的是阻止信号被处理,但不是阻止信号产生。

信号的阻塞就是让系统暂时保留信号留待以后发送。由于另外有办法让系统忽略信号,所以一般情况下信号的阻塞只是暂时的,只是为了防止信号打断敏感的操作。

image-20230228153447964

信号集相关函数

信号集相关函数
以下信号集相关函数都是对自定义的信号集进行操作
介绍了怎么去定义自己的信号集,这个信号集是没有实际用处的,因为还没有交给内核。交给内核的函数在下一节课程。交给内核后才会起作用

    int sigemptyset(sigset_t *set);
    功能: 清空信号集中的数据,将信号集中所有的标志位置为0
    参数: set 传出参数 需要操作的信号集
    返回值: 成功0 失败-1

    int sigfillset(sigset_t *set);
    功能: 将信号集中所有的标志位置为1
    参数: set 传出参数 需要操作的信号集
    返回值: 成功0 失败-1

    int sigaddset(sigset_t *set, int signum);
    功能:设置信号集中的某一个信号的对应标志位为1,表示阻塞这个信号
    参数: set 传出参数 需要操作的信号集
            signum: 需要设置阻塞的那个信号
    返回值: 成功0 失败-1

    int sigdelset(sigset_t *set, int signum);
     功能:设置信号集中的某一个信号的对应标志位为0,表示不阻塞这个信号
    参数: set 传出参数 需要操作的信号集
            signum: 需要设置阻塞的那个信号
    返回值: 成功0 失败-1

    int sigismember(const sigset_t *set, int signum);
    功能: 判断某个信号是否阻塞
    参数: 
        -set: 需要操作的信号集
        signum: 需要判断的信号
    返回值: 1表示signum被阻塞, 0表示不阻塞   -1调用失败

sigset以及上一讲中的那些函数是泛化的信号集的概念,既可以表示阻塞集也可以表示未决集,
sigaddset、sigdelset、sigismember等这些函数只是去操作某个信号集,
而这个信号集的含义是人为设定的。这一讲提到的sigprocmask和sigpending才是分别针对阻塞集和未决集的。
	  int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
    功能: 将自定义信号集中的数据设置到内核中(设置阻塞,解除阻塞,替换)          设置阻塞信号集
    参数:
        -how: 如何对内核阻塞信号集进行处理
            SIG_BLOCK:将用户设置的阻塞信号集添加到内核中,内核中原来的数据不变
                假设内核中默认的阻塞信号集是mask   mask|set
            SIG_UNBLOCK 根据用户设置的数据,对内核中的数据进行解除阻塞        (自定义的信号集也是1)
                mask &= ~set
            SIG_SETMASK: 覆盖内核中原来的值

        -set: 已经初始化好的用户自定义的信号集
        -oldset: 保存设置之前的内核中的阻塞信号集的状态,可以设置成NULL
    返回值: 成功0 失败-1并设置错误号(EFAULT, EINVAL)

    int sigpending(sigset_t *set);
    功能: 用来获取内核中的未决信号集
    参数: set传出参数,保存内核中的未决信号集中的信息
    返回值: 成功0 失败-1并设置错误号
sigset以及上一讲中的那些函数是泛化的信号集的概念,既可以表示阻塞集也可以表示未决集,
sigaddset、sigdelset、sigismember等这些函数只是去操作某个信号集,
而这个信号集的含义是人为设定的。这一讲提到的sigprocmask和sigpending才是分别针对阻塞集和未决集的。

4.SIGCHLD

SIGCHLD信号产生的条件

  1. 子进程终止时
  2. 子进程接收到 SIGSTOP 信号停止时
  3. 子进程处在停止态,接受到SIGCONT后唤醒时

以上三种条件都会给父进程发送 SIGCHLD 信号,父进程默认会忽略该信号

标签:set,int,通信,阻塞,内核,信号,进程
From: https://www.cnblogs.com/Yuqi0/p/17169308.html

相关文章

  • Linux系统的进程管理
    (Linux系统的进程管理)一、系统进程介绍1.进程概念进程process是指正在执行的程序;是程序正在运行的一个实例。它由程序指令,和从文件、其它程序中读取的数据或系统用户的......
  • 多线程和多进程的区别
    一个线程从属于一个进程;一个进程可以包含多个线程。一个线程挂掉,对应的进程挂掉,多线程也挂掉;一个进程挂掉,不影响其它进程,多进程稳定。进程系统开销显著大于线程开销;线程......
  • 创建进程的流程及进程切换的情况
    创建进程为新进程分配一个唯一的进程标识符(pid)。为新进程分配资源,如内存空间、文件描述符、信号处理函数等。初始化新进程的进程控制块(PCB),包括设置初始状态、优先级、......
  • 【Linux】孤儿进程 | 命令行参数 | 进程优先级
    1.孤儿进程如果父进程一直存在,子进程先退出了,父进程对子进程退出不管不顾,会出现什么样问题么?创建makefile并输入以下内容mytest:test.c2gcc-o$@$^3.PHONY:clea......
  • 网络通信基本概念
    通信人与人、人与物、物与物之间通过某种媒介和行为进行信息传递与交流网络通信指终端设备之间通过计算机网络进行的通信数据通信网络由路由器、交换机、防火墙、无线......
  • modbus通信协议
     一、Modbus通信协议概述   Modbus是一种串行通信协议,是Modicon公司(现在的施耐德电气SchneiderElectric)于1979年为使用可编程逻辑控制器(PLC)通信而发表。Modbus已......
  • 进程
    1.调用atexit()函数注册一个进程在正常终止时需要调用的函数。2.getpid获取本程序的进程号,getppid获取父进程的进程号。3.env查看环境变量,添加环境变量:exportLINUX_APP=......
  • 信号与系统(拓展二):无限集
    无限集分为可数无限集和不可数无限集。可数集例如:自然数集、有理数集、证书集;不可数集例如:实数集。无限集比大小方法:如果两个无限集A、B的元素之间存在一一对应关系,则认为......
  • 信号与系统(拓展一):勒贝格积分
    1.勒贝格关于函数相等的定义如何定义两个函数f1(t)和f2(t)相等(即何时f1(t)=f2(t))?我们在高中所学的定义是这样的:f1(t)和f2(t)定义域相同,且对于定义域上任意的t0,有f1(t0)......
  • shell-awk-打印进程ID
    dockerps|grepcita|awk-F""'{print$1}'awk-F""按空格分割e.g.[root@host-10-0-169-67~]#sudodockerpsCONTAINERIDIMAGE......