进程通信:信号

215 阅读8分钟

信号

基本概念:

  • 信号:是事件发生时对进程的通知机制,有时也称之为软件中断。信号 可以导致一个正在运行的进程被另一个正在运行的异步进程中断,转而处理某一个突发事件。

  • 信号集:多个信号可使用一个称之为信号集的数据结构来表示,其系统数据类型为 sigset_t

    • 未决信号集:从信号的产生到信号被处理前的这一段时间。
    • 阻塞信号集:阻止信号被处理,但不是阻止信号产生。
      • 操作系统不允许我们直接对这两个信号集进行位操作。而需自定义另外一个集合,借助信号集操作函数来对 PCB 中的这两个信号集进行修改。
      • 若想处理未决信号集,需要先查看阻塞信号集对应信号是否被阻塞:
        • 如果没有阻塞,这个信号就被处理
        • 如果阻塞了,这个信号就继续处于未决状态,直到阻塞解除,这个信号就被处理

重要的信号:

  • SIGINT

    • 编号:2
    • 当用户按下了<Ctrl+C>组合键时,用户终端向正 在运行中的由该终端启动的程序发出此信号
    • 默认动作:终止进程
  • SIGQUIT

    • 编号:3
    • 用户按下<Ctrl+>组合键时产生该信号,用户终 端向正在运行中的由该终端启动的程序发出些信号
    • 默认动作:终止进程
  • SIGKILL

    • 编号:9
    • 无条件终止进程,可以杀死任何进程
    • 默认动作:该信号不能被忽略,处理和阻塞
  • SIGSEGV

    • 编号:11
    • 指示进程进行了无效内存访问(段错误)
    • 默认动作:终止进程并产生core文件
  • SIGPIPE

    • 编号:13
    • Broken pipe向一个没有读端的管道写数据
    • 默认动作:终止进程
  • SIGCHLD

    • 编号:17
    • 子进程结束时,父进程会收到这个信号
    • 默认动作:忽略这个信号
  • SIGCONT

    • 编号:18
    • 如果进程已停止,则使其继续运行
    • 默认动作:继续/忽略
  • SIGSTOP

    • 编号:19
    • 停止进程的执行。信号不能被忽略,处理和阻塞
    • 默认动作:终止进程

信号系统调用

  • 给任何的进程或者进程组pid, 发送任何的信号 sig
    • int kill(pid_t pid, int sig);
  • 给当前进程发送信号
    • int raise(int sig);
  • 发送SIGABRT信号给当前的进程,杀死当前进程
    • void abort(void);
  • 设置定时器
    • unsigned int alarm(unsigned int seconds);
  • 设置定时器
    • int setitimer(int which, const struct itimerval *new_value, struct itimerval *old_value)
  • 信号捕捉
    • sighandler_t signal(int signum, sighandler_t handler)
  • 将信号集设置不阻塞
    • int sigemptyset(sigset_t *set)
  • 将信号集设置阻塞
    • int sigfillset(sigset_t *set)
  • 将信号集某个信号设置阻塞
    • int sigaddset(sigset_t *set, int signum)
  • 将信号集某个信号设置不阻塞
    • int sigdelset(sigset_t *set, int signum)
  • 判断信号集某个信号是否阻塞
    • int sigismember(const sigset_t *set, int signum)
  • 将自定义信号集中的数据设置到内核中
    • int sigprocmask(int how, const sigset_t *set, sigset_t *oldset)
  • 获取内核中的未决信号集
    • int sigpending(sigset_t *set)
  • 信号捕捉
    • int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact)



int kill(pid_t pid, int sig)

头文件

#include <sys/types.h>

#include <signal.h>

功能:

给任何的进程或者进程组pid, 发送任何的信号 sig

参数:

  • pid_t pid

    .> 0 : 将信号发送给指定的进程

    = 0 : 将信号发送给当前的进程组

    = -1 : 将信号发送给每一个有权限接收这个信号的进程

    < -1 : 这个pid=某个进程组的ID取反 (-12345)

  • int sig : 需要发送的信号的编号或者是宏值,0表示不发送任何信号

返回值:

  • 成功:0
  • 失败:-1

int raise(int sig)

头文件

#include <sys/types.h>

#include <signal.h>

功能:

给当前进程发送信号

参数:

  • int sig : 要发送的信号

返回值:

  • 成功:0

  • 失败:非0

void abort(void)

头文件

#include <sys/types.h>

#include <signal.h>

功能:

发送SIGABRT信号给当前的进程,杀死当前进程

unsigned int alarm(unsigned int seconds)

头文件

#include <unistd.h>

功能:

设置定时器(闹钟)。函数调用,开始倒计时,当倒计时为0的时候,函数会给当前的进程发送一个信号:SIGALARM

  • SIGALARM :默认终止当前的进程,每一个进程都有且只有唯一的一个定时器。
  • 该函数不阻塞。

参数:

  • unsigned int seconds

    seconds: 倒计时的时长,单位:秒。如果参数为0,定时器无效(不进行倒计时,不发信号)。 取消一个定时器,通过alarm(0)。

返回值:

  • 之前没有定时器,返回0

  • 之前有定时器,返回之前的定时器剩余的时间

int setitimer(int which, const struct itimerval *new_value, struct itimerval *old_value)

头文件

#include <sys/time.h>

功能

设置定时器(闹钟)。可以替代alarm函数。精度微秒,可以实现周期性定时

  • 非阻塞。

参数:

  • int which : 定时器以什么时间计时

    • ITIMER_REAL: 真实时间,时间到达,发送 SIGALRM 常用

    • ITIMER_VIRTUAL: 用户时间,时间到达,发送 SIGVTALRM

    • ITIMER_PROF: 以该进程在用户态和内核态下所消耗的时间来计算,时间到达,发送 SIGPROF

    • 真实时间 = 内核时间 + 用户时间 + 消耗的时间

  • const struct itimerval *new_value: 设置定时器的属性

struct itimerval { // 定时器的结构体

    struct timeval it_interval; // 间隔时间:每隔多久定时

    struct timeval it_value; // 程序开始后,延迟多长时间执行定时器

};


struct timeval { // 时间的结构体

    time_t tv_sec; // 秒数

    suseconds_t tv_usec; // 微秒

};

举例:过10秒后,每隔2秒定时一次:10s:it_value, 2s:it_interval

  • struct itimerval *old_value :记录上一次的定时的时间参数,一般不使用,指定NULL

返回值:

  • 成功 : 0

  • 失败 :-1 ,并设置错误号

sighandler_t signal(int signum, sighandler_t handler)

头文件

#include <signal.h>

功能:

设置某个信号的捕捉行为

参数:

  • int signum: 要捕捉的信号

    • 注意:SIGKILLSIGSTOP不能被捕捉,不能被忽略。
  • sighandler_t handler: 捕捉到信号要如何处理

    • SIG_IGN : 忽略信号

    • SIG_DFL : 使用信号默认的行为,相当于没有捕捉。

    • 回调函数 : 这个函数是内核调用,程序员只负责写,捕捉到信号后如何去处理信号。

      • void (*sighandler_t)(int)函数指针,int类型的参数表示捕捉到的信号的值。

      • 需要程序员实现,提前准备好的,函数的类型根据实际需求,看函数指针的定义

      • 不是程序员调用,而是当信号产生,由内核调用

      • 函数指针是实现回调的手段,函数实现之后,将函数名放到函数指针的位置就可以了。

返回值:

  • 成功:返回上一次注册的信号处理函数的地址。第一次调用返回NULL

  • 失败:返回SIG_ERR,设置错误号

int sigemptyset(sigset_t *set)

头文件

#include <signal.h>

功能:

清空信号集中的数据,将信号集中的所有的标志位置为0

参数:

  • sigset_t *set:传出参数,需要操作的信号集

返回值:

  • 成功:0
  • 失败:-1

int sigfillset(sigset_t *set)

头文件

#include <signal.h>

功能:

将信号集中的所有的标志位置为1

参数:

  • sigset_t *set:传出参数,需要操作的信号集

返回值:

  • 成功:0
  • 失败:-1

int sigaddset(sigset_t *set, int signum)

头文件

#include <signal.h>

功能:

设置信号集中的某一个信号对应的标志位为1,表示阻塞这个信号

参数:

  • sigset_t *set:传出参数,需要操作的信号集

  • int signum:需要设置阻塞的那个信号

返回值:

  • 成功:0
  • 失败:-1

int sigdelset(sigset_t *set, int signum)

头文件

#include <signal.h>

功能:

设置信号集中的某一个信号对应的标志位为0,表示不阻塞这个信号

参数:

  • sigset_t *set:传出参数,需要操作的信号集

  • int signum:需要设置不阻塞的那个信号

返回值:

  • 成功:0
  • 失败:-1

int sigismember(const sigset_t *set, int signum)

头文件

#include <signal.h>

功能:

判断某个信号是否阻塞

参数:

  • const sigset_t *set:需要判断的信号集

  • int signum:需要判断的信号

返回值:

  • 1 : signum被阻塞

  • 0 : signum不阻塞

  • -1 : 失败

int sigprocmask(int how, const sigset_t *set, sigset_t *oldset)

头文件

#include <signal.h>

功能:

将自定义信号集中的数据设置到内核中,可设置

  • 阻塞
  • 解除阻塞
  • 替换

参数:

  • int how : 如何对内核阻塞信号集进行处理

    SIG_BLOCK: 将用户设置的阻塞信号集添加到内核中,内核中原来的数据不变

    SIG_UNBLOCK: 根据用户设置的数据,对内核中的数据进行解除阻塞

    SIG_SETMASK:覆盖内核中原来的值

  • const sigset_t *set :已经初始化好的用户自定义的信号集

  • sigset_t *oldset : 保存设置之前的内核中的阻塞信号集的状态,可以是 NULL

返回值:

  • 成功:0

  • 失败:-1,设置错误号:EFAULTEINVAL

int sigpending(sigset_t *set)

头文件

#include <signal.h>

功能:

获取内核中的未决信号集

参数:

  • sigset_t *set:传出参数,保存的是内核中的未决信号集中的信息。

返回值:

  • 成功:0

  • 失败:-1

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

头文件

#include <signal.h>

功能:

信号捕捉:检查或者改变信号的处理。

参数:

  • int signum : 需要捕捉的信号的编号或者宏值(推荐)

  • const struct sigaction *act :捕捉到信号之后的处理动作

struct sigaction {

    // 函数指针,指向的函数就是信号捕捉到之后的处理函数

    void (*sa_handler)(int);

    // 不常用
    
    void (*sa_sigaction)(int, siginfo_t *, void *);

    // 临时阻塞信号集,在信号捕捉函数执行过程中,临时阻塞某些信号。

    sigset_t sa_mask;

    // 使用哪一个信号处理对捕捉到的信号进行处理

    // 这个值可以是0,表示使用sa_handler,也可以是SA_SIGINFO表示使用sa_sigaction

    int sa_flags;

    // 被废弃掉了

    void (*sa_restorer)(void);

};
  • struct sigaction *oldact : 上一次对信号捕捉相关的设置,一般不使用,传递NULL

返回值:

  • 成功:0

  • 失败:-1