信号是用于通知发生某些事件,然后打断进程当前操作,改去处理该发生事件的进程间通信方式之一
信号实质是进程PCB(task_struct)中的一个位图表,其中包含两个位图和一个函数指针:阻塞位图(blocked),未决位图(pending),信号递达函数指针(handler)
信号也是软件中断的方式之一
查看信号列表命令:kill -l
信号格式:编号 + 宏定义名称
信号的总数:62个
信号分类:
1.非可靠信号(非实时信号):继承于unix,每一个信号都对应一个固定的事件;总共有31个,编号由1–31;当某个信号已经被注册了之后,再次出现该信号,则直接丢弃该信号,不对其进行注册。
2.可靠信号(实时信号):总共31个,编号由34–64;不管该信号有没有被注册过,都不会丢弃信号。
中断
概念:打断当前操作,并且去处理该打断事件的过程
分类: 硬件中断、软件中断
信号生命周期:
信号产生–>信号注册–>(信号阻塞/屏蔽)–>信号注销–>信号处理(递达)
产生方式:
1.硬件中断:如CTRL+C,表示对该进程发送2号信号SIGINT…
2.硬件异常:如非法内存访问…
3.命令产生:kill + 信号编号g + 进程id,表示对id进程发送信号g
4.系统函数产生:
(a):kill函数:int kill(int pid, int signo);
对任意进程发送任意信号
signo:表示信号编号,一般有信号的宏定义名称
成功返回0,失败-1。
(b):raise函数:int raise(int signo);
进程对自己发送任意信号
( c):sigqueue函数:int sigqueue(int pid, int signo, union sigval value);
比kill函数多带一个参数
(d):abort函数:void abort(void);
对自己发送一个abort信号
该函数一定成功,所以没有返回值
(e):alarm接口调用:int alarm(unsigned int second);
对自己设置一个闹钟,即second秒之后,对自己发送一个alarm信号;第二个闹钟会 覆盖 第一个闹钟。
若second参数为0,则表示取消闹钟设置
返回值:返回上一个闹钟还剩余的秒数或0
信号产生代码测试:https://gitee.com/free_xin/codes/jeougyq13al7phkz89tsd14
概念:在进程PCB(task_struct)中的pending表中记录收到的信号
pending表(未决)
概念:信号在内核中注册的位置;
非可靠信号: 当进程收到一个信号时,在进程PCB中的pending表中记录该信号结点,并且对应该信号的位图置1,当再次收到该信号时,直接丢弃。
可靠信号: 和非可靠信号不同的是,再次收到该信号时,会将该结点链接到原来的信号结点之后
注:该信号结点采用的是sigset_t数据结构(信号集)
blocked表(阻塞)
概念:默认该表中对应的信号都是非阻塞,当收到要阻塞信号的命令时,会将对应该信号的比特位置1。
采用的是sigset_t数据结构(信号集)
设置/解除阻塞信号:
1.步骤:
定义阻塞信号集(屏蔽字)–>初始化(清空)信号集–>添加要阻塞/解除阻塞的信号–>设置屏蔽/解除阻塞
清空信号集函数: int sigemptyset(sigset_t set);
表示将所有比特位清零,表示该信号集不包含任何有效信号。
添加信号函数:int sigadddset(sigset_t set, int signo);
设置屏蔽函数:int sigprocmask(int how, const sigset_t set, const sigset_t oset);
how:要对新的信号集执行的操作;SIG_BLOCK:设置屏蔽/阻塞;SIG_UNBLOCK:设置非阻塞/解除阻塞
set:要设置的新的信号集
oset:将老的信号集拿出保存
其他信号集函数:
填充信号集函数:int sigfillset(sigset_t *set);
表示将用系统中支持的所有信号填充该信号集。 也可以用于初始化
删除信号集中某个信号函数:int sigdelset(sigset_t *set, int signo);
判定某个信号是否在该信号集中:int sigismember(const sigset_t *set, int signo);
只有该函数为布尔函数,存在返回1,不存在返回0,出错返回-1;其他函数均成功返回0,失败返回-1。
获取当前进程的未决信号集:int sigpending(sigset_t set);
set: 作为输出型参数,用于获取该进程的未决信号集
成功返回0,失败返回-1。
信号阻塞测试代码:https://gitee.com/free_xin/codes/p9uwxdjz8f2im5e3yhl6s69
概念:在pending表中将已经递达的信号移除
对于非可靠信号:将信号结点移除,将对应位图置0
对于可靠信号:将该信号结点移除,查看是否还存在与该信号相同的信号结点;若有,则不处理位图;若无,则将对应位图置0
条件:当进程从内核态回到用户态时,检查pending表中是否有未递达的信号,然后在blocked表中查看该信号是否被阻塞,若存在没有被阻塞的未递达的信号,则对该信号进行递达处理。
信号处理方式:
1.忽略:直接将该信号注销
2.默认处理方式:执行系统定义好的对应该信号的处理方式
3.自定义处理方式:自己定义处理方式
信号处理(递达)函数:
1.sighandler_t signal(int signo, sighandler_t handler);
signo:要处理的信号的宏定义名称
handler:要对该信号进行的处理方式
a).SIG_IGN:忽略处理
b).SIG_DFL:默认处理
c).自己定义的处理方式的函数名
成功返回0,失败返回-1
2.int sigaction(int signo, const struct sigaction act, struct sigaction oact);
signo:要处理的信号的宏定义名称
act:是sigaction结构体,表示对该信号的要执行什么处理操作;
oact:sigaction结构体,记录原来对该信号的处理操作
成功返回0,失败返回-1.
sigaction结构体解析:
3.int pause(void);
作用:使进程挂起等待,直到有信号递达
返回值:若信号递达动作是进程终止,则该进程终止,无返回值;若信号递达动作是忽略,则该进程继续挂起等待,无返回值;若信号递达动作为自定义函数,则执行自定义函数之后返回-1,;失败也返回-1;
信号是我认为Linux中很难的部分,关于信号集(sigset_t)结构体、以及三张位图表的内容一直没搞懂,希望以后能搞懂一下…