1.更多信号发送函数
-
alarm
闹钟函数,向本进程只能发送SIGALRM信号:14) SIGALRM -
setitimer
闹钟函数(定时器的使用),向本进程可以发送信号 :14) SIGALRM 26) SIGVTALRM 27) SIGPROF -
abort
向本进程发送信号:6) SIGABRT -
eg:P18alarm.c
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <signal.h>
#define ERR_EXIT(m) \
do \
{ \
perror(m); \
exit(EXIT_FAILURE); \
} while(0)
void handle(int sig);
int main(int argc, char *argv[])
{
//man alarm
if (signal(SIGALRM, handle) == SIG_ERR)
ERR_EXIT("signal error");
alarm(1);//alarm函数可以指定秒,过了多少秒之后产生一个SIGALRM信号
for(;;)
pause();
return 0;
}
void handle(int sig)
{
printf("recv a sig=%d\n", sig);
}
- 测试:
手工发送信号:
也可以人为的发送14号信号(可以用数字,也可以用名称),SIG名称也可以省略
- eg:P18alarm1.c
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <signal.h>
#define ERR_EXIT(m) \
do \
{ \
perror(m); \
exit(EXIT_FAILURE); \
} while(0)
void handle(int sig);
int main(int argc, char *argv[])
{
//man alarm
//alarm函数可以指定一个秒,过了多少秒之后产生一个SIGALRM信号
if (signal(SIGALRM, handle) == SIG_ERR)
ERR_EXIT("signal error");
alarm(1);
for(;;)
pause();
return 0;
}
void handle(int sig)
{
printf("recv a sig=%d\n", sig);
alarm(1);//间接递归调用:handler——>SIGALRM——>handler
}
- 测试:
每隔1秒发送一个alarm信号
2.可重入函数与不可重入函数
-
为了增强程序的稳定性,在信号处理函数中使用可重入函数
-
所谓可重入函数是指:一个可以被多个任务调用的过程,任务在调度时不必担心数据是否会出错。
因为进程在收到信号后,就将跳转到信号处理程序去接着执行。
如果信号处理函数中使用了不可重入函数,那么信号处理函数可能会修改原来进程中不应该被修改的数据,这样进程从信号处理函数中返回接着执行时,可能会出现不可预料的后果。
不可重入函数在信号处理函数中被视为不安全函数。 -
满足下列条件的函数多数是不可重入的:
(1)使用静态的数据结构结构(eg全局变量):eg:getlogin(),gmtime(),getgrgid(),getgrnam(),getpwuid()以及getpwnam()等等
(2)函数实现时,调用了malloc()或者free()函数
(3)实现时,使用了标准I/O函数 -
其它
(1)在中断处理函数中,要尽可能使用可重入函数
(2)原来程序跟信号处理程序之间如果存在数据共享的话,那么这样的函数就有可能是不安全的函数(不可重入的函数),被中断之前的程序可能跟中断处理程序共享数据,这样的程序(函数)称之为不可重入函数
(3)可重入函数:不需要关心这些数据是否存在问题 -
eg:P18reentrant.c
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <signal.h>
#define ERR_EXIT(m) \
do \
{ \
perror(m); \
exit(EXIT_FAILURE); \
} while(0)
typedef struct
{
int a;
int b;
}TEST;
TEST g_data;
void handle(int sig);
int main(int argc, char *argv[])
{
TEST zeros = {0, 0};
TEST ones = {1, 1};
if (signal(SIGALRM, handle) == SIG_ERR)
ERR_EXIT("signal error");
g_data = zeros;
for(;;)
{
g_data = zeros;
g_data = ones;
}
return 0;
}
void unsafe_fun()
{
printf("%d_%d\n", g_data.a, g_data.b);//printf本身就是不可重入函数
//因为printf访问了全局变量,有可能导致不可重入函数
//若printf没有访问全局变量,则还算是一个可重入函数
}
void handle(int sig)
{
unsafe_fun();
alarm(1);
}
- 测试:
产生的原因是:下面的赋值操作不是一个原子操作,可能会被信号中断;
不可重入函数发生的原因在于:被中断前的处理程序与中断处理程序访问了共享的数据,这样的全局变量造成了不可重入
g_data = zeros;若在这里产生了中断
赋值相当于下面的操作
g_data.a= zeros.a;
g_data.b= zeros.b;
当zeros.a赋值给g_data.a时,zeros.b还没有赋值给g_data.b,此时1秒的闹钟alarm到了,先前只赋值了
一个过去,所以输出0 1,也就是说g_data.a改成了0,g_data.b还没改成0,它还为1
g_data = ones;若在这里产生了中断
赋值相当于下面的操作
g_data.a= ones.a;
g_data.b= ones.b;
g_data.a这里赋成了1,但是此时中断,g_data.b还是0,所所以输出1 0
不可重入函数尽量不要在信号处理函数中调用,否则出现逻辑错误
- 安全函数
man 7 signal
安全函数:可以在信号处理函数内部安全的调用
POSIX.1-2004 (also known as POSIX.1-2001 Technical Corrigendum 2)
requires an implementation to guarantee that the following functions
can be safely called inside a signal handler:
_Exit()
_exit()
abort()
。。。。
waitpid()
write()
- Makefile
.PHONY:clean all
CC=gcc
CFLAGS=-Wall -g
BIN=01alarm
all:$(BIN)
%.o:%.c
$(CC) $(CFLAGS) -c $< -o $@
clean:
rm -f *.o $(BIN)