菜鸟笔记
提升您的技术认知

linux c之信号signal处理机制-ag真人游戏

 signal机制在linux中是一个非常常用的进程间通信机制,很多人在使用的时候不会考虑该机制是具体如何实现的。signal机制可以被理解成进程的软中断,因此,在实时性方面还是相对比较高的。linux中signal机制的模型可以采用下图进行描述。

个进程都会采用一个进程控制块对其进行描述,进程控制块中设计了一个signal的位图信息,其中的每位与具体的signal相对应,这与中断机制是保持一致的。当系统中一个进程a通过signal系统调用向进程b发送signal时,设置进程b的对应signal位图,类似于触发了signal对应中断。发送signal只是“中断”触发的一个过程,具体执行会在两个阶段发生:

1、  system call返回。进程b由于调用了system call后,从内核返回用户态时需要检查他拥有的signal位图信息表,此时是一个执行点。

2、  中断返回。进程被系统中断打断之后,系统将cpu交给进程时,需要检查即将执行进程所拥有的signal位图信息表,此时也是一个执行点。

 

综上所述,signal的执行点可以理解成从内核态返回用户态时,在返回时,如果发现待执行进程存在被触发的signal,那么在离开内核态之后(也就是将cpu切换到用户模式),执行用户进程为该signal绑定的signal处理函数,从这一点上看,signal处理函数是在用户进程上下文中执行的。当执行完signal处理函数之后,再返回到用户进程被中断或者system call(软中断或者指令陷阱)打断的地方。

 

       signal机制实现的比较灵活,用户进程由于中断或者system call陷入内核之后,将断点信息都保存到了堆栈中,在内核返回用户态时,如果存在被触发的signal,那么直接将待执行的signal处理函数push到堆栈中,在cpu切换到用户模式之后,直接pop堆栈就可以执行signal处理函数并且返回到用户进程了。signal处理函数应用了进程上下文,并且应用实际的中断模拟了进程的软中断过程。

 

最近写程序,各种bug各种错,有一回程序莫名退出,没报错,也没产生日志和core文件,貌似正常退出一样。

但又不是在程序全部走完后退出,中途莫名退出,这就叫我想到了signal,应该是某些函数错误后发送kill信号给主进程,然后退出。

现在总结下signal各种类型:

signal

description

sigabrt

由调用abort函数产生,进程非正常退出

sigalrm

用alarm函数设置的timer超时或setitimer函数设置的interval timer超时

sigbus

某种特定的硬件异常,通常由内存访问引起

sigcancel

由solaris thread library内部使用,通常不会使用

sigchld

进程terminate或stop的时候,sigchld会发送给它的父进程。缺省情况下该signal会被忽略

sigcont

当被stop的进程恢复运行的时候,自动发送

sigemt

和实现相关的硬件异常

sigfpe

数学相关的异常,如被0除,浮点溢出,等等

sigfreeze

solaris专用,hiberate或者suspended时候发送

sighup

发送给具有terminal的controlling process,当terminal被disconnect时候发送

sigill

非法指令异常

siginfo

bsd signal。由status key产生,通常是ctrl t。发送给所有foreground group的进程

sigint

由interrupt key产生,通常是ctrl c或者delete。发送给所有foreground group的进程

sigio

异步io事件

sigiot

实现相关的硬件异常,一般对应sigabrt

sigkill

无法处理和忽略。中止某个进程

siglwp

由solaris thread libray内部使用

sigpipe

在reader中止之后写pipe的时候发送

sigpoll

当某个事件发送给pollable device的时候发送

sigprof

setitimer指定的profiling interval timer所产生

sigpwr

和系统相关。和ups相关。

sigquit

输入quit key的时候(ctrl \)发送给所有foreground group的进程

sigsegv

非法内存访问

sigstkflt

linux专用,数学协处理器的栈异常

sigstop

中止进程。无法处理和忽略。

sigsys

非法系统调用

sigterm

请求中止进程,kill命令缺省发送

sigthaw

solaris专用,从suspend恢复时候发送

sigtrap

实现相关的硬件异常。一般是调试异常

sigtstp

suspend key,一般是ctrl z。发送给所有foreground group的进程

sigttin

当background group的进程尝试读取terminal的时候发送

sigttou

当background group的进程尝试写terminal的时候发送

sigurg

当out-of-band data接收的时候可能发送

sigusr1

用户自定义signal 1

sigusr2

用户自定义signal 2

sigvtalrm

setitimer函数设置的virtual interval timer超时的时候

sigwaiting

solaris thread library内部实现专用

sigwinch

当terminal的窗口大小改变的时候,发送给foreground group的所有进程

sigxcpu

当cpu时间限制超时的时候

sigxfsz

进程超过文件大小限制

sigxres

solaris专用,进程超过资源限制的时候发送

signal对应的值:

posix.1中列出的信号:

sighup 1 a 终端挂起或者控制进程终止 
sigint 2 a 键盘中断(如break键被按下) 
sigquit 3 c 键盘的退出键被按下 
sigill 4 c 非法指令 
sigabrt 6 c 由abort(3)发出的退出指令 
sigfpe 8 c 浮点异常 
sigkill 9 aef kill信号 
sigsegv 11 c 无效的内存引用 
sigpipe 13 a 管道破裂: 写一个没有读端口的管道 
sigalrm 14 a 由alarm(2)发出的信号 
sigterm 15 a 终止信号 
sigusr1 30,10,16 a 用户自定义信号1 
sigusr2 31,12,17 a 用户自定义信号2 
sigchld 20,17,18 b 子进程结束信号 
sigcont 19,18,25 进程继续(曾被停止的进程) 
sigstop 17,19,23 def 终止进程 
sigtstp 18,20,24 d 控制终端(tty)上按下停止键 
sigttin 21,21,26 d 后台进程企图从控制终端读 
sigttou 22,22,27 d 后台进程企图从控制终端写 

没在posix.1中列出,而在susv2列出

sigbus 10,7,10 c 总线错误(错误的内存访问) 
sigpoll a sys v定义的pollable事件,与sigio同义 
sigprof 27,27,29 a profiling定时器到 
sigsys 12,-,12 c 无效的系统调用 (svid) 
sigtrap 5 c 跟踪/断点捕获 
sigurg 16,23,21 b socket出现紧急条件(4.2 bsd) 
sigvtalrm 26,26,28 a 实际时间报警时钟信号(4.2 bsd) 
sigxcpu 24,24,30 c 超出设定的cpu时间限制(4.2 bsd) 
sigxfsz 25,25,31 c 超出设定的文件大小限制(4.2 bsd) 

(对于sigsys,sigxcpu,sigxfsz,以及某些机器体系结构下的sigbus,linux缺省的动作是a (terminate),susv2 是c (terminate and dump core))。 

下面是其它的一些信号 

信号 值 处理动作 发出信号的原因 
---------------------------------------------------------------------- 
sigiot 6 c io捕获指令,与sigabrt同义 
sigemt 7,-,7 
sigstkflt -,16,- a 协处理器堆栈错误 
sigio 23,29,22 a 某i/o操作现在可以进行了(4.2 bsd) 
sigcld -,-,18 a 与sigchld同义 
sigpwr 29,30,19 a 电源故障(system v) 
siginfo 29,-,- a 与sigpwr同义 
siglost -,-,- a 文件锁丢失 
sigwinch 28,28,20 b 窗口大小改变(4.3 bsd, sun) 
sigunused -,31,- a 未使用的信号(will be sigsys) 

(在这里,- 表示信号没有实现;有三个值给出的含义为,第一个值通常在alpha和sparc上有效,中间的值对应i386和ppc以及sh,最后一个值对应mips。信号29在alpha上为siginfo / sigpwr ,在sparc上为siglost。) 

处理动作一项中的字母含义如下 
a 缺省的动作是终止进程 
b 缺省的动作是忽略此信号 
c 缺省的动作是终止进程并进行内核映像转储(dump core) 
d 缺省的动作是停止进程 
e 信号不能被捕获 
f 信号不能被忽略

代码测试

#include  
#include  
#include  
#include  
void when_alarm();  
void when_sigint();  
void when_sigchld(int);  
void when_sigusr1();  
void when_sigio();  
int main()  
{  
    int childpid;//子程序进程id号  
    printf("程序已经开始运行,5秒钟后将接收到时钟信号。/n");  
    if ((childpid=fork())>0)//父进程  
    {  
        signal(sigalrm,when_alarm);  //当接收到sigalrm信号时,调用when_alarm函数  
        signal(sigint,when_sigint);  //当接收到sigint信号时,调用when_sigint函数  
        signal(sigchld,when_sigchld);//当接收到sigchld信号时,调用when_sigchld函数  
        signal(sigusr1,when_sigusr1);//当接收到sigusr1信号时,调用when_sigusr1函数  
        signal(sigio,when_sigio);//当接收到sigio信号时,调用when_sigio函数  
        alarm(5);     //5秒钟之后产生sigalrm信号  
        raise(sigio); //向自己发送一个sigio信号  
        pause(); //将父进程暂停下来,等待sigalrm信号到来  
        pause(); //将父进程暂停下来,等待sigusr1信号到来  
        pause(); //将父进程暂停下来,等待sigchld信号到来  
        printf("------此时程序会停下来等待,请按下ctrl c送出sigint信号-------/n");  
        pause(); //将父进程暂停下来,等待sigint信号到来          
    }  
    else if(childpid==0) //子进程  
    {  
        int timer;  
        for(timer=7;timer>=0;timer--) //时钟计时5秒产生sigalrm信号,再过2秒子进程退出,产生sigchld信号  
        {  
            if(timer>2)      
                printf("距离sigalrm信号到来还有%d秒。/n",timer-2);  
            if(timer==4)  
                kill(getppid(),sigusr1); //向父进程发送一个sigusr1信号  
            if((timer<=2)&&(timer>0))  
                printf("子进程还剩%d秒退出,届时会产生sigchld信号。/n",timer);  
            if(timer==0) //子进程退出,产生sigchld信号  
                raise(sigkill); //子进程给自己发一个结束信号  
            sleep(1); //每个循环延时1秒钟  
        }          
    }  
    else  
        printf("fork()函数调用出现错误!/n");  
    return 0;  
}  
void when_alarm()  
{  
    printf("5秒钟时间已到,系统接收到了sigalrm信号!/n");  
}  
void when_sigint()  
{  
    printf("已经接收到了sigint信号,程序将退出!/n");  
    exit(0);  
}  
void when_sigchld(int sigchld_num)  
{  
    printf("收到sigchld信号,表明我的子进程已经中止,sigchld信号的数值是:%d。/n",sigchld_num);  
}  
void when_sigusr1()  
{  
    printf("系统接收到了用户自定义信号sigusr1。/n");  
}  
void when_sigio()  
{  
    printf("系统接收到了sigio信号。/n");  
}

 

网站地图