Linux 8 信号sig

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

猜你喜欢

转载自blog.csdn.net/zzj244392657/article/details/92567530