linux C总结篇(信号)
其实前面已经提到过信号了(异步信号~_~ )
信号:信号是Unix、类Unix以及其他POSIX兼容的操作系统中进程间通讯的一种有限制的方式。它是一种异步的通知机制,用来提醒进程一个事件已经发生。当一个信号发送给一个进程,操作系统中断了进程正常的控制流程,此时,任何非原子操作都将被中断。如果进程定义了信号的处理函数,那么它将被执行,否则就执行默认的处理函数。
异步:与同步处理相对,异步处理不用阻塞当前线程来等待处理完成,而是允许后续操作,直至其它线程处理完成,并回调通知此线程。
原子操作:顾名思义,就是说像原子一样不可再细分不可被中途打断。一个操作是原子操作,意思就是说这个操作是以原子的方式被执行,要一口气执行完,执行过程不能够被OS的其他行为打断,是一个整体的过程,在其执行过程中,OS的其它行为是插不进来的。
信号未决:产生信号,内核在进程表中设置标志,说明内核向该进程递送了一个信号,产生与递送之间的时间间隔称为信号未决
可靠信号:在信号阻塞之前,信号被传递多次
不可靠信号:在信号阻塞之前,信号被传递一次(优先递送它)
对信号的响应方式:1.忽略。2.接受 。3.默认以系统所规定的方式处理
常用信号查询:http://charlee.li/useful-linux-signals.html
信号处理:
1. 信号的接受与处理
#include <signal.h>
void (*signal(int sig, void (*func)(int)))(int); //func 返回值是void ,包含一个参数 int
说明:1.func: 函数指针,指定接受到该信号时,以什么函数处理。(要么是忽略【SIG_IGN】要么默认以系统所规定的方式处理【SIG_DFL】)
小示例:
#include<stdio.h>
#include<stdlib.h>
#include<signal.h>
#include<sys/types.h>
void test1(int signo)
{
printf("SIGINT\n") ;
}
void test2(int signo)
{
printf("SIGQUIT\n") ;
}
int main(void)
{
signal(SIGINT,test1) ; //SIGINT就是ctrl+C
signal(SIGQUIT,test2); //SIGQUIT就是ctrl+\
while(1)
;
return 0;
}
执行结果:
sigaction 函数是signal 的底层实现 ,比较复杂,也比较难懂,等到需要时再来讨论。
pause 函数:使线程挂起,直到接受到一个信号 。只返回-1 ,并设置errno为EINIR
2.信号处理函数的返回
1.setjmp和longjmp
#include <setjmp.h>
int setjmp(jmp_buf env);
void longjmp(jmp_buf env, int val);
用法:
#include<stdio.h>
#include<stdlib.h>
#include<signal.h>
#include<setjmp.h>
jmp_buf env ;
void fun1(int signo)
{
printf("SIGRTMIN + 15\n");
longjmp(env,10);
}
void fun2(int signo)
{
printf("SIGTRMAX - 9 \n");
longjmp(env,20);
}
int main(void)
{
switch(setjmp(env))
{
case 0: break;
case 10:printf("fun1 \n"); break;
case 20:printf("fun2 \n");break ;
}
signal(SIGRTMIN+ 15,fun1);
signal(SIGRTMAX -9,fun2);
while(1)
;
return 0;
}
说明:从程序可以看出,该函数会像if..goto..破坏整体结构。最大的弊端是:信号处理时,会屏蔽正在被处理的信号,当处理函数返回时解除屏蔽,但处理函数并没有正常返回,所以屏蔽一直没有解除,导致出现问题
2.sigsetjmp和siglongjmp
int sigsetjmp(sigjmp_buf env, int savesigs); //savesigs 非0,就会在env,保存进程的当前信号屏蔽字,从而解决上面提到的问题
void siglongjmp(sigjmp_buf env, int val);
用法:
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<signal.h>
#include<setjmp.h>
int flag = 0;
jmp_buf env ;
void fun1(int signo)
{
if(flag == 0)
return ;
printf("SIGRTMAX - 9 \n");
sleep(1);
printf("after sleep !! \n");
sleep(20);
siglongjmp(env ,100); //信号不会丢失
}
int main(void)
{
switch(sigsetjmp(env,10000)) //第二个参数只要非零即可
{
case 0:flag= 1 ;break;
case 100:printf("fun1 !!! \n");break;
}
signal(SIGRTMAX- 9 ,fun1);
while(1)
;
return 0;
}
说明:假设接受到了4个SIGRTMAX- 9 信号,函数fun1,进行处理并屏蔽IGRTMAX- 9 信号,其他三个信号排队,siglongjmp函数去除屏蔽并返回 ,检查队列非空,继续执行。
3.信号的发送
1.kill 函数:kill()可以用来送参数sig 指定的信号给参数pid 指定的进程。
#include <signal.h>
int kill(pid_t pid, int sig);
参数说明:
参数pid 有几种情况:
1、pid>0 将信号传给进程号为pid 的进程.
2、pid=0 将信号传给和目前进程相同进程组的所有进程
3、pid=-1 将信号广播传送给系统内所有的进程(除1号进程和自身)
4、pid<0 将信号传给进程组号为-pid 的所有进程
小示例:
#include<stdio.h>
#include<stdlib.h>
#include<signal.h>
#include<unistd.h>
#include<sys/types.h>
int main(int argc ,char **argv) //kill -s 2 **** // kill **** 只支持这两种形式的命令!!!!
{
int i ,j ;
pid_t pid ;
char str[520]; //保存命令行输入
int signum = SIGTERM ; //默认为正常退出
if(argc < 2 || argc > 4 )
{
printf("Wrong commend !! \n");
exit(-1);
}
if(argc == 2)
pid = atoi(argv[1]) ;
if(argc == 4)
{
signum = atoi(argv[2]) ; // -s 为指定发送的信号,2 为Ctrl+C信号
pid = atoi(argv[3]) ;
}
if(kill(pid,signum) < 0)
{
printf("Kill error !!! \n");
exit(-1);
}
}
执行结果:
2.alarm 函数:alarm()用来设置信号SIGALRM 在经过参数seconds 指定的秒数后传送给目前的进程. 如果参数seconds 为0, 则之前设置的闹钟会被取消, 并将剩下的时间返回
#include <unistd.h>
unsigned alarm(unsigned seconds);
小示例:
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<signal.h>
void fun(int signo)
{
printf("liushengxi zui shuai !!! \n");
alarm(1) ; //一秒发一个SIGALRM 信号
}
int main(void)
{
signal(SIGALRM,fun);
raise(SIGALRM); //先触发一个SIGALRM 信号
while(1)
;
return 0;
}
执行结果:
3.abort 函数:解除进程对SIGABRT信号的阻止,然后向调用进程发送该信号。abort()函数会导致进程的异常终止除非SIGABRT信号被捕捉并且信号处理句柄没有返回。导致所有的流被关闭和冲洗,如果进程设置屏蔽这种信号,该函数覆盖这种设置。无返回值!!
#include <stdlib.h>
void abort(void);
4.信号的屏蔽
1.信号集:一个能表示多个信号的数据类型即sigset_t set ;set即一个信号集。
各操作函数:
int sigemptyset(sigset_t *set); 将set集合置空
int sigfillset(sigset_t *set); 将所有信号加入set集合
int sigaddset(sigset_t *set,int signo); 将signo信号加入到set集合
int sigdelset(sigset_t *set,int signo); 从set集合中移除signo信号
int sigismember(const sigset_t *set,int signo); signo判断信号是否存在于set集合中
注意事项:所有应用程序在使用信号集前,都要对该信号集调用sigemptyset和sigfillset 以初始化信号集。因为C语言的编译器将所有不赋初值的外部和静态度量都初始化为0
2.信号屏蔽:
#include <signal.h>
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
int sigpending(sigset_t *set);
int sigsuspend(const sigset_t *mask);
函数说明:
1.sigprocmask:改变进程的当前阻塞信号集,也可以用来检测当前进程的信号掩码。每个进程都有一个用来描述哪些信号递送到进程时将被阻塞的信号集,该信号集中的所有信号在递送到进程后都将被阻塞
如果参数oldset不是NULL指针,那么目前的信号屏蔽字会由此指针返回。如果set是一个非空指针,则参数how指示如何修改当前信号屏蔽字。
参数how的取值不同,带来的操作行为也不同,该参数可选值如下:
1.SIG_BLOCK: 该值代表的功能是将newset所指向的信号集中所包含的信号加到当前的信号掩码中,作为新的信号屏蔽字。
2.SIG_UNBLOCK:将参数newset所指向的信号集中的信号从当前的信号掩码中移除。
3.SIG_SETMASK:设置当前信号掩码为参数newset所指向的信号集中所包含的信号。
注意事项:sigprocmask()函数只为单线程的进程定义的,在多线程中要使用pthread_sigmask变量,在使用之前需要声明和初始化。
2.sigpending:返回在阻塞期间接收到阻塞信号的集合。
3.sigsuspend:有一套属于自己的屏蔽信号mask,能够选择性接收某些信号。在接收到没有被屏蔽的信号之前,运行到它时,它会一直悬挂着,有点类似pause()函数。接受到可行信号后,它会退出悬挂并执行相应的信号函数。接收到的信号源:1.之前运行sigprocmask()函数中阻塞的信号;2.悬挂后接受到的信号。
- sigsuspend的整个原子操作过程为:
- (1) 设置新的mask阻塞当前进程;
- (2) 收到信号,恢复原先mask;
- (3) 调用该进程设置的信号处理函数;
- (4) 待信号处理函数返回后,sigsuspend返回。
以下链接很不错的介绍了这三个函数:
http://blog.csdn.net/elbort/article/details/7594772