信号signal
信号是给进程的事件通知,可以完成进程间一异步通信,
Unix信号利用进程间通信向进程1发送信号,可使接收进程受信号影响( 终止、挂起、继续)
信号的产生
- 命令发出信号 kill
kill -信号编号 进程id
注: kill -l 可以查看系统下支持的信号
1-31 Unix 经典信号,软件开发工程师使用
34-64 实时信号,驱动开发者使用
32 33 隐藏保留信号,给NTPL线程库使用 - 函数产生信号(显示请求)
(1)kill(pid_t pid , int signo) 像任意进程发送任意信号
(2)raise(int signal) 向调回进程发送任意信号
(3)abort(void) 向调用进程发送SIGABRT信号 退出进程 - 终端按键产生信号
(1)CTL + C 发送 SIGINT(2)
(2)CTL + \ 终止退出唯一前台进程 SIGQUIT(3)
(3)CTL + Z 挂起唯一前台进程 SIGSTP(20)
注: 操作系统中前台进程永远只有一个
jobs 查看作业编号
fg 作业编号 唤醒至前台继续执行
bg 作业编号 唤醒至后台继续执行 - 硬件异常产生信号
(1)段错误:非法操作内存 SIGSEGV(11),将进程杀死
(2)浮点数例外:cpu/0 SIGFPE(8),将进程杀死
(3)总线错误: 调度异常 ,SIGBUS(7) - 软条件产生
(1)定时器,使用alarm 到时间 发送SIGALRM(14)
(2)管道读端关闭写端向管道写数据内核向写端发送SIGPIPE(13) , 杀死写进程
信号的三种行为
注:使信号失效的三种方式
(1)改变信号行为,将默认行为改为忽略行为,可以失去
(2)改变信号行为,将默认行为改为捕捉行为,可使信号失效
(3)屏蔽信号/阻塞信号,使信号无法递达,不会对进程造成影响(被屏蔽的信号没有丢失,某一刻1接触屏蔽,一样可以递达,影响进程)
高权信号(直接为内核服务,只要发出必然递达)
SIGKILL(9) 只要发出必然杀死进程,无法屏蔽、捕捉、忽略
SIGSTOP(19) 只要发出必然挂起进程,无法屏蔽、捕捉、忽略
默认行为SIG_DEF
有五种默认处理动作
- TERM 结束终止进程
- CORE 结束进程并且生成转储文件
- IGN 进程不受任何信号影响,单纯接收信号
- STOP 挂起进程
- COUNT 唤醒继续执行
忽略行为SIG_IGN
进程不受信号干涉(进程无影响)
可以让某些信号失去原有的作用
SIGCHLD 信号默认处理动作忽略:父进程使用。子进程结束变为僵尸进程,内核向父进程发送SIGCHLD通知1父进程回收
捕捉行为SIG_ACTION
捕捉特定的信号,为进程服务,信号绑定特定的任务,进程接收到该信号后,自动调用特定任务(信号触发任务)
信号处理过程
- 信号发给PCB
- PCB 交给未决信号集(未决信号集为1 信号丢弃)
- 未决信号集交给屏蔽信号集(屏蔽信号集为1 信号阻塞)
- 交给handler->选择信号处理行为->执行信号处理动作
注:未决信号集内核设置,通过但未处理置1 ,处理后置零(标志为一的都是未决态信号)
信号通过未决与屏蔽信号集,进入处理流程,未决对应为0 , 信号切换为递达态
unix 经典信号最大支持一次排队
如果需要查看进程中未处理的信号,应该1打印未决信号集(可以读该信号集,但是用户无法更改)
sigpending(sigset_t * set) 调用该函数,传出进程当前为决信号集
信号捕捉
信号捕捉流程
-
定义初始化信号行为结构体
struct sigaction act;
act.sa_sigaction = 自定义函数名
act.sa_flags = 0
sigenptyset(&act.sa_mask) // 设置屏蔽信号集 -
顶哟实现捕捉函数
-
将自定义信号行为替换为默认行为
sigaction(SIGUSR2,&act,&oact); // 信号关联
注:传出信号行为是为了恢复
特殊捕捉函数 signal
#include <sinal.h>
void ( *signal(int sig, void (*f) (int))) (int);
等价于:
typedef void (*func)(int);
func signal(int sig, func f)
注:
SIG_DFL 恢复信号默认处理机制
SIG_IGN 忽略信号处理
函数地址 调用信号捕获函数执行处理
信号捕捉函数执行过程
进程通常工作于用户层,当信号到达进程,抵达内核层,无法实时处理,只有进程切换到内核空间,才可以检测处理信号并进行处理
可以通过三种方式进行进程状态切换:系统调用、中断、异常
(cpu 权级切换,用户空间cpu 权限低限制大,内核空间权限高可以访问系统所有资源)
可不可冲入函数
不可重入函数:函数内部使用全局资源(全局或静态),如果信号捕捉函数和main主函数一起调用会出现冲突
可重入函数:函数内部没有全局变量,可以放心使用信号捕捉函数
利用信号尽心进程通信
sigqueue(pid_t pid , int signo , sigval_t val)
像任意进程发送任意信号携带任意参数
union sigval
{
int sival_int;
void * sival_ptr;
}
信号屏蔽
- 创建屏蔽字集
sigset_t newset; - 初始化屏蔽字
sigemptyset(&newset); - 设置屏蔽字
sigaddset(&newset,SIGINT); - 替换屏蔽字
sigprocmask(SIG_SETMASK ,&newset ,NULL);
定时器
在UNIX中,定时器的处理是通过信号来完成的
(1)程序使用系统调用向UNIX内核设置定时器信息
(2)定时完成,UNIX内核向进程发送定时器类信号
(3)表示进程捕获定时器类信号,并且执行相关信号处理操作
#include <unistd.h>
unsigned int alarm(unsigned int seconds);
函数alarm在UNIX内核中为调用进程设置一个定时器,进程在调用alarm后seconds秒后,接收到UNIX内核发送到一个SIGALRM信号。
更改信号SIGALRM的默认处理机制,并设置捕获响应函数。
如:signal(SIGALRM, timefunc);