1.信号概念
信号(signal)是一种进程间通信机制,它给应用程序提供一种异步的软件中断,使应用程序有机会接受其他程序活终端发送的命令(即信号)。应用程序收到信号后,有三种处理方式:忽略,默认,或捕捉。进程收到一个信号后,会检查对该信号的处理机制。如果是SIG_IGN,就忽略该信号;如果是SIG_DFT,则会采用系统默认的处理动作,通常是终止进程或忽略该信号;如果给该信号指定了一个处理函数(捕捉),则会中断当前进程正在执行的任务,转而去执行该信号的处理函数,返回后再继续执行被中断的任务。
2.信号函数
2.1 signal函数
signal()是最简单的给进程安装信号处理器的函数,第一个参数指定信号,第二个参数为该信号指定一个处理函数。
typedef void (*sighandler_t) (int)
sighandler_t signal(int signum, sighandler_t handler);
使用SIG_IGN作为signal第二个参数时表示忽略
使用SIG_DFT作为signal第二个参数时表示默认处理
如果调用出错,那么则会返回SIG_ERR并设置errno.唯一的错误码是EINVAL
2.2 sigaction函数
sigaction函数用于改变进程接收到特定信号后的行为。该函数的第一个参数为信号的值,可以为除SIGKILL及SIGSTOP外的任何一个特定有效的信号(为这两个信号定义自己的处理函数,将导致信号安装错误)。第二个参数是指向结构sigaction的一个实例的指针,在结构sigaction的实例中,指定了对特定信号的处理,可以为空,进程会以缺省方式对信号处理;第三个参数oldact指向的对象用来保存原来对相应信号的处理,可指定oldact为NULL。如果把第二、第三个参数都设为NULL,那么该函数可用于检查信号的有效性。
int sigaction(int signum,const struct sigaction *act,struct sigaction *oldact));
struct sigaciton
{
void (*sa_handler)(int);
void (*sa_sigaction)(int ,siginfo_t *,void *);
sigset_t sa_mask;
int sa_flags;
}
sa_handler就是句柄.
sa_sigaction也是个句柄地址,当sa_flags设置了SA_SIGINFO的时候才起作用,否则用handler.
sa_mask指明信号句柄执行期间要阻塞的一组信号,在sigaction执行过程中会屏蔽这些信号.
sa_flags可取的值就多了,有以下这些:
SA_NOCLDSTOP 此标志只对SIGCHLD信号有效,正常情况下,子进程被停止时,总是向父进程发送信号,有时候被停止的子进程恢复运行时,可能向父进程发送信号.当signum参数传值是SIGCHLD时,在这两种情况下都不再发送信号.
SA_RESTART 如果设置该标志并且捕获信号,系统将在信号句柄返回时自动恢复被该信号中断了的系统调用.否则被中断的系统调用将返回-1并置errno为EINTR.多数情况下sa_flags的值为SA_RESTART.
SA_ONSTACK 如果设置此标志,系统将在用sigaltstack指定的替代信号栈上运行的信号句柄.否则使用用户栈来交付信号.
SA_NODEFER 如果设置此标志并且捕获信号,在信号句柄执行期间系统不自动阻塞该信号.这对应于不可靠信号signal的情形.(估计没什么人会用这个吧…)
SA_RESETHAND 如果设置此标志并且捕获信号,在信号句柄的入口,系统将重置信号动作为SIG_DFL并清楚SA_SIGINFO标志,并且sigaction的行为就和signal差不多了.
SA_NOCLDWAIT 只对SIGCHLD起作用.如果设置此标志,并且sig是SIGCHLD,调用进程的所有子进程在终止时不转变成僵死进程.这样父进程就不必调用wait函数了.如果调用了wait则进程会一直阻塞直到所有子进程都结束才返回,返回值为-1和设置errno为ECHILD.
SA_SIGINFO 未设置用sa_handler并且禁止修改sa_sigaction.
2.3 pause函数
pause()会使当前进程挂起,直到捕捉到一个信号,对指定为忽略的信号,pause()不会返回。只有执行了一个信号处理函数,并从其返回,puase()才返回-1,并将errno设为EINTR。
int pause(void);
2.4 raise函数
对当前进程发送指定信号
int raise(int sig);
2.5 kill函数
通过进程编号发送信号
int kill(pid_t pid,int sig);
pid > 0 将信号发送给ID为pid的进程
pid == 0 将信号发送给与发送进程属于同意个进程组的所有进程
pid < 0 将信号发送给进程组ID等于pid绝对值的所有进程
pid == -1 将信号发送给该进程有权限发送的系统里的所有进程
2.6 alarm函数
产生时钟信号SIGALRM
unsigned int alarm(unsigned int seconds);
alarm()函数可设置一个计时器,计时器超时就产生SIGALRM信号。由于每个进程只能有一个SIGALRM处理程序,所以只能为一个进程设置一个计时器,所以alarm()和setitimer()会共享同一个SIGALRM信号和该信号的处理函数。也就是说,alarm()和setitimer()彼此会互相影响。调用alarm(),会使先前设置的计时器失效,并把没有超时的时间作为当前alarm的返回值。如先前设置的时钟为100秒,当前调用alarm()时才经过30秒,剩余的70秒就作为alarm()的返回值,并用alarm()中指定的秒数重新设置计时器。如果seconds为0,则会取消先前设置的计时器,并将其余留值作为alarm()的返回值。
2.7 信号屏蔽字
每个进程都会有一个信号屏蔽字,它规定了当前进程要阻塞的信号集。对于每种可能的信号,信号屏蔽字中都会有一位与之对应,如果该位被设置,该信号当前就是阻塞的。进程可以通过sigprocmask()来获得和修改当前进程的信号屏蔽字。
signal.c
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
int flag = 1;
/******************************************
* 函数名:func
*函数功能:信号处理函数
*输 入:信号
*输 出:无
* 返回值:无
*修改日期:2017-03-15
*修改作者:zzj
*******************************************/
void func(int sig)
{
printf("I get a signal:%d\r\n", sig);
flag = 1;
}
/******************************************
* 函数名:main
*函数功能:signal的入口,安装进程处理器
*输 入:无
*输 出:无
* 返回值:OK
*修改日期:2017-03-15
*修改作者:zzj
*******************************************/
int main()
{
/* 给进程安装信号处理器 */
signal(1, func);
signal(2, func);
signal(3, func);
signal(4, func);
signal(5, func);
printf("pid:%ld\n",(long)getpid());
sleep(10); /* 等待10s */
raise(3); /* 向当前进程发送信号 */
while(flag)
pause(); /* 挂起当前进程,等待信号 */
return 0;
}
signal_send.c
#include <sys/time.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>
#include <unistd.h>
#include <assert.h>
/******************************************
* 函数名:main
*函数功能:向指定进程号的进程发送用户输入的信号
*输 入:要发送信号的目标进程号
*输 出:无
* 返回值:无
*修改日期:2017-03-15
*修改作者:zzj
*******************************************/
int main (int argc, char **argv)
{
int keyboard;
int ret,i;
char c;
fd_set readfd;
struct timeval timeout;
keyboard = open("/dev/tty",O_RDONLY | O_NONBLOCK);
assert(keyboard>0);
while(1)
{
timeout.tv_sec=1;
timeout.tv_usec=0;
FD_ZERO(&readfd);
FD_SET(keyboard,&readfd);
ret=select(keyboard+1,&readfd,NULL,NULL,&timeout);
if(FD_ISSET(keyboard,&readfd))
{
i=read(keyboard,&c,1);
if('\n'==c)
continue;
printf("your input is %c\n",c);
/* 在进程处理的函数中定义了信号1-5的处理 */
if (c >= '1' && c <= '5')
kill(atoi(argv[1]), c - '1' + 1);
if ('q'==c)
{
kill(atoi(argv[1]), SIGKILL); /* SIGKILL为9 */
break;
}
}
}
}
Makefile
APP_NAME = signal
APP_OBJS = signal.o
CC = gcc
INC = ./
CFLAG += -g
.PHONY : all
all : $(APP_NAME) signal_send
$(APP_NAME) : $(APP_OBJS)
$(CC) $(CFLAG) $(APP_OBJS) -o $(APP_NAME)
signal_send : signal_send.o
$(CC) $(CFLAG) $^ -o $@
%.o : %.c
$(CC) -c $(CFLAG) $^ -o $@
.PHONY : clean
clean :
rm -f .o
rm -f $(APP_NAME) $(APP_OBJS) signal_send