1~31为普通信号,34 ~64为实时信号。
9号信号不可被自定义捕捉,不可被屏蔽,这是一种OS的保护机制
一般而言信号的处理有3种方式:
#include
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
.1. 在linux中,当一个进程退出的时候,它的退出码和退出信号都会被设置。
如果需要,OS会设置退出信息中的core dump标志位,将进程中的数据传储到磁盘中,方便后期调试。
一般core dump 标志位是不被设置的,可以通过 ulimit -a 查看是否设置
进程如果异常退出,会生成一个core.pid的文件,但是并不是所有的异常都会生成core.pid文件,如9号信号
core dump文件可以帮助确定代码错误的地方,这称为事后调试
用户层调用kill函数,底层扔会转移到OS方面发送信息
#include
#include
#include
#include
#include
#include
#include
void Usage(const char *proc)
{
printf("Please Usage : %s -signo who \n", proc);
}
// ./test signo who
int main(int argc, char *argv[])
{
if (argc != 3)
{
Usage(argv[0]);
return 1;
}
int signo = atoi(argv[1]+1);
int who = atoi(argv[2]);
kill(who,signo );//用户层调用kill函数,底层扔会转移到OS方面发送信息
printf("signo %d who %d\n", signo, who);
}
站在进程的内外去分析信号产生的方式
无论什么产生方式,最终都会转为OS向进程发送信号,因为任何行为都不能越过OS,去访问内核数据
OS向进程发送了信号,但是进程可能会忙于自己的任何无法即可响应这个信号,因此在进程PCB中有必要需要一个保存信号的容器
每个信号都要其对应的信号码,OS内核中采取位图来标识是否收到信号,对于多个重复信号只响应一个
关于信号的保存相关的容器有3个:block,pending,handler表
block和pending都是一种由OS内核提供的位图结构,handler是函数指针数组表
block是阻塞又叫信号屏蔽字,用于表明是否阻塞某个信号
pending(未决表),用于存放没有被递达的信号,每个比特位表示是否保存某个信号
handler:函数指针表;信号递达时的处理动作:默认,忽略,自定义,其中默认是对0的函数指针强转,忽略是1的函数指针强转
依据这三个表产生3种转态:信号递达,信号未决,信号阻塞
实际执行信号的处理动作叫信号递达
动作有:默认,忽略,自定义捕捉
进程暂时屏蔽某种信号,也称为信号阻塞;
阻塞2个情况:
当信号第一次来的时候,保存信号,但是阻塞其递达
当进程需要信号递达时,阻塞其递达
block和pending是由内核提供的一种位图数据结构,这个内核数据结构类型是sigset_t
#include
//初始化
int sigemptyset(sigset_t *set);//位图全部置为0
int sigfillset(sigset_t *set);//位图全部置1
//添加
int sigaddset (sigset_t *set, int signo);//向位图中添加一个信号,即信号对应位置1
//删除
int sigdelset(sigset_t *set, int signo);//将某个信号位置0
//查找
int sigismember(const sigset_t *set, int signo);//判断某个信号是否存在set中
sigprocmask修改的是block表
#include
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
//oldset用于接收老的block中的数据
int how | 注意 | |
---|---|---|
SIG_SETMASK | 将set信号赋值到block中 | 赋值,用的比较多 |
SIG_BLOCK | 将set中的信号添加到block中 | 是添加,不是赋值 |
SIG_UNBLOCK | 从block中去除set中的信号 | |
void ShowSet(sigset_t *set)
{ for(int i=1;i<32;++i)
{
if(sigismember(set,i))
{
printf("1");
}else
{
printf("0");
}
}
cout<
对于pending,OS只允许我们获取pending表中的内容,对于信号的保存的由OS负责。
#include
//获取pending位图中的内容
int sigpending(sigset_t *se);
#include
#include
#include
#include
#include
#include
#include
using namespace std;
void ShowSet(sigset_t *set)
{
printf("cur pending process:");
for (int i = 1; i < 32; ++i)
{
if (sigismember(set, i))
{
printf("1");
}
else
{
printf("0");
}
}
cout << endl;
}
void handler(int signo)
{
sigset_t oset;
sigpending(&oset);
ShowSet(&oset);
sleep(1);
// exit(1);
}
int main()
{
signal(2, handler);
sigset_t iset; //输入参数
sigset_t oset; //输出参数
sigemptyset(&iset);
sigemptyset(&oset);
sigaddset(&iset, 2);
// sigaddset(&iset,3);
// sigaddset(&iset,9);
sigprocmask(SIG_SETMASK, &iset, &oset);
int cnt = 0;
while (1)
{
sigset_t tmp;
sigemptyset(&tmp);
sigpending(&tmp);
ShowSet(&tmp);
sleep(1);
++cnt;
if (cnt == 10)
{
printf("恢复2号\n");
sigprocmask(SIG_UNBLOCK, &iset, NULL);
//break;
// cnt=0;
}
}
return 0;
}
#include
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
#include
struct sigaction {
void (*sa_handler)(int);//这个就是函数指针
void (*sa_sigaction)(int, siginfo_t *, void *);
sigset_t sa_mask;
int sa_flags;
void (*sa_restorer)(void);
};
int sigaction(int signum, const struct sigaction *act,
struct sigaction *oldact);
sa_handler :函数指针
sa_mask:用与当进行信号处理动作时,屏蔽其他信号的一种内核位图结构
当某个信号的处理函数被调用时,内核自动将当前信号加入进程的信号屏蔽字,当信号处理函数返回时自动恢复原来的信号屏蔽字,这样就保证了在处理某个信号时,如果这种信号再次产生,那么 它会被阻塞到当前处理结束为止。 如果在调用信号处理函数时,除了当前信号被自动屏蔽之外,还希望自动屏蔽另外一些信号,则用sa_mask字段说明这些需要额外屏蔽的信号,当信号处理函数返回时自动恢复原来的信号屏蔽字 。
void handler(int signo)
{
while(1)
{
printf("get a signo %d\n",signo);
//printf函数底层会用到OS内核的write函数,也就是说其会从用户态转到内核态
//如果检测到其它信号就会递达
sleep(1);
}
}
int main()
{
// struct sigaction {
// void (*sa_handler)(int);
// void (*sa_sigaction)(int, siginfo_t *, void *);
// sigset_t sa_mask;
// int sa_flags;
// void (*sa_restorer)(void);
// };
//signal(2,handler);
struct sigaction act;
memset(&act,0,sizeof(struct sigaction));
act.sa_handler=handler;
//添加屏蔽信号
// sigemptyset(&act.sa_mask);
// sigaddset(&act.sa_mask,3);
// sigprocmask(SIG_BLOCK,&act.sa_mask,NULL);
sigaction(2,&act,NULL);
while(1)
{
printf("hello word\n");
sleep(1);
}
return 0;
}
信号的捕捉发生在:进程从内核态到用户态的转换时,OS会进行信号检测,如果有信号就递达即捕捉信号。递达的方式:默认,忽略,自定义方式;如果使用自定义方式,会转换为用户态,之后会进入内核态,再次检测信号,直到递达结束,返回用户态。会呈现以下图片
信号的产生方式很多,但最终只能通过OS向进程传递信号,对于信号的不能立即递达的信号,会被保存在pending表中,是否能被递达看的是block阻塞表,递达方式看的是handler表。
当进程从内核态返回到用户态时,OS会进行信号检测来决定是否递达某些信号。
对于普通信号,不会保存后续的重复信号,对于实时信号,采用链表保存重复的信号