(P18)信号:更多发送信号函数、可重入函数与不可重入函数

文章目录

    • 1.更多信号发送函数
    • 2.可重入函数与不可重入函数

1.更多信号发送函数

  • alarm
    闹钟函数,向本进程只能发送SIGALRM信号:14) SIGALRM

  • setitimer
    闹钟函数(定时器的使用),向本进程可以发送信号 :14) SIGALRM 26) SIGVTALRM 27) SIGPROF

  • abort
    向本进程发送信号:6) SIGABRT

  • eg:P18alarm.c

#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 
#include 


#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名称也可以省略
    (P18)信号:更多发送信号函数、可重入函数与不可重入函数_第1张图片
    (P18)信号:更多发送信号函数、可重入函数与不可重入函数_第2张图片
  • eg:P18alarm1.c
#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 
#include 


#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信号
    (P18)信号:更多发送信号函数、可重入函数与不可重入函数_第3张图片

2.可重入函数与不可重入函数

  • 为了增强程序的稳定性,在信号处理函数中使用可重入函数

  • 所谓可重入函数是指:一个可以被多个任务调用的过程,任务在调度时不必担心数据是否会出错。
    因为进程在收到信号后,就将跳转到信号处理程序去接着执行。
    如果信号处理函数中使用了不可重入函数,那么信号处理函数可能会修改原来进程中不应该被修改的数据,这样进程从信号处理函数中返回接着执行时,可能会出现不可预料的后果。
    不可重入函数在信号处理函数中被视为不安全函数。

  • 满足下列条件的函数多数是不可重入的:
    (1)使用静态的数据结构结构(eg全局变量):eg:getlogin(),gmtime(),getgrgid(),getgrnam(),getpwuid()以及getpwnam()等等
    (2)函数实现时,调用了malloc()或者free()函数
    (3)实现时,使用了标准I/O函数

  • 其它
    (1)在中断处理函数中,要尽可能使用可重入函数
    (2)原来程序跟信号处理程序之间如果存在数据共享的话,那么这样的函数就有可能是不安全的函数(不可重入的函数),被中断之前的程序可能跟中断处理程序共享数据,这样的程序(函数)称之为不可重入函数
    (3)可重入函数:不需要关心这些数据是否存在问题

  • eg:P18reentrant.c

#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 
#include 


#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

(P18)信号:更多发送信号函数、可重入函数与不可重入函数_第4张图片
不可重入函数尽量不要在信号处理函数中调用,否则出现逻辑错误

  • 安全函数
    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)


你可能感兴趣的:(Linux高性能编程)