Linux---进程间信号

信号的概念

信号就是一个中断 : 它通知一个事件的发生(打断当前的操作(选择合适的时机),去处理信号)
而进程的信号就是一个软件(应用程序)的中断

信号的种类

在Linux下,使用 kill -l 命令查看信号的种类
在这里插入图片描述

  • 一共有62种信号
  • 1~31号信号是Linux继承Unix而来的31种事件信号,当系统发生对应的时间的时候,就会发送对应的信号
  • 34~64号信号是没有指定具体事件的,当我们用户需要用到自定义事件的时候,我们可以去对这些信号进行定义

注意 :
1~31号信号为非可靠事件, 意味着当有多个相同的信号到来时, 只会对一个信号进行处理,其他的信号都会被丢弃 , 同时也称为非实时信号 .
34~64号信号为可靠信号, 意味着当有多个相同的信号到来时, 每一个都会被处理 , 同时也称为实时信号

信号的生命周期

信号的产生

信号的产生方式有软件产生于硬件产生
硬件产生 : 指直接在终端中输入一些指令产生信号
例如: ctrl+c ctrl+z ctrl+\ 等
软件产生 : 指在程序中产生的信号

  1. int kill(pid_t pid, int sig);
    在指定的pid进程发送指定的sig信号
  2. int raise(int sig);
    给调用进程发送sig信号
  3. void abort(void);
    给调用进程发送SIGABRT
  4. unsigned int alarm(unsigned int seconds);
    在指定的seconds秒之后向调用进程发送一个SIGALRM信号----定时器
    若seconds为0, 则作为为取消上一个定时器

信号在进程中的注册

信号在继承中的注册是通过位图来注册的, 这个位图在内核中所对应的就是一个 struct sigset_t
它的大小是128位,用来对信号是否到来做标记.

# define _SIGSET_NWORDS (1024/(8*sizeof(unsigned long int)))
typedef struct{
	unsigned long int _val[_SIGSET_NWORDS];
}_sigset_t;

注意 :

  • 在PCB中用来描述信号注册的结构不仅只有一个sigset_t结构体, 还有一个sigqueue链表, 他的作用是在信号到来的时候组织一个对应信号的结点,然后添加到这个链表中
  • 我们前面说了信号的种类有可靠信号与非可靠信号,而非可靠信号就是因为, 该信号的到来只能在位于中进行一次标记,当有其他相同的信号到来时,将什么也不做, (每一个信号只添加一个节点)
  • 而可靠信号不管位图是否已经标记, 都会地每一个道到来的信号组织一个结点添加到列表中

信号在进程中的注销

信号的注销指的就是在PCB中删除信号的sig’queue结点, 修改位图
可靠信号的注销
删除节点,判断链表中是否还有相同的信号, 如果没有,则将位图置0.否则,位图依然为1.
非可靠信号的注销
删除节点, 修改位图为0.

信号在进程中的处理

  1. 默认处理方式
    默认处理方式是有操作系统已将规定好了的处理法方式 例如:SIGQUIT信号的默认处理为使当前进程退出
  2. 忽略处理方式
    僵尸进程的产生原因为父进程先于子进程退出, 父进程没有关心子进程的退出返回原因, 而子进程在退出时会向父进程发送一个SIGCHLD信号,而父进程对SIGCHLD的默认处理方式就是忽略处理, 因此,父进程没有及时获取待子进程的退出原因, 所以才造成了僵尸进程
  3. 自定义处理方式
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);

自定义处理方式为一个signal接口,在这个接口中有两个参数, 第一个signum为要自定义处理方式的信号, 而第二个参数是一个函数指针, 在这个函数指针内部就是,这个信号定义的处理方式,在这个参数中有两个宏 SIG_IGN(忽略处理) 与SIG_DFL(默认处理)

int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact);
//signum : 信号的编号---指定要处理的信号
//act : 自定义的信号处理方式
//oldact : 信号的原有处理方式
struct sigaction {
               void     (*sa_handler)(int);
               void     (*sa_sigaction)(int, siginfo_t *, void *);
               sigset_t   sa_mask;
               int        sa_flags;
               void     (*sa_restorer)(void);
           };

在PCB中有一个pending位图, 这个位图标记的就是未处理的信号, 而在PCB中也同样拥有一个action结构体数组, 它里面保存着着各个信号的原始处理方式, 而未决信号是通过pending位图, 来拿到action中的未处理信号的处理方式.

在sigaction结构体中, sa_handler就是信号的处理方式, 我们的sigaction接口就是将,新的sa_gandler进行修改, sa_mask是为了避免在进行信号处理的时候, 其他的信号造成影响,因为设定sa_mask, 添加进sa_mask中的信号都将别忽略.

自定义信号的捕捉流程

默认信号的处理方式是在内核中完成的, 因此当我们的程序被信号中断时, 我们的程序就有用户态切换到内核态, 在内核态中完成信号的处理
而自定义信号的流程是:
一开始程序运行在我们的用户态中, 当发生系统调用 / 中断 / 异常的时候, 我们的程序会重用户态转到内核态, 完成内核功能从内核态返回的时候回判断是否有信号等待处理, 如果有, 并且这个信号处理方式为自定义处理方式, 则会有内核态转向用户态, 执行sigcb()函数, 在执行完信号处理之后, 会再一次返回内核态, 之后在判断是否有未处理的信号, 直到没有信号未处理, 才会返回用户态继续执行主流程.

信号的阻塞 : 阻止信号被递达

递达的意思就是信号的处理, 而阻塞就是不处理信号,信号依然可以注册.
信号的阻塞也是在PCB中添加标志, 在PCB有一个blocked位图,其中标志的就是用户需要阻塞的信号, 当有信号在pending位图中被注册时, 会将blocked取反之后与pending位图进行与操作, 之后留下俩的就是需要进行处理的信号.

//设置信号阻塞集合
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
//how : 即将处理的动作
//SIG_BLOCK : 将set集合中的信号添加到阻塞集合当中
//SIG_UNBLOCK : 将set集合当中的数据在阻塞集合当中移除
//SIG_SETMASK :  使用set集合集合替换阻塞集合

//清空set集合当中的数据
int sigemptyset(sigset_t *set);

//将所有信号添加到set集合中
int sigfillset(sigset_t *set);
//将指定的信号添加到set集合中
int sigaddset(sigset_t *set, int signum);

注意: 在所有的信号中SIGKILL(9号), SIGSTOP(19号) 这两个信号无法被自定义处理, 无法被阻塞, 无法被忽略

//获取未决信号的集合
int sigpending(sigset_t *set);
//判断一个信号是否在集合中
int sigismember(const sigset_t *set, int signum);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值