[Linux]不可重入函數


一、概述

怎么會有可重入和不可重入

在多任務系統下,中斷可能在任務執行的任何時間發生;如果一個函數的執行期間被中斷后,到重新恢復到斷點進行執行的過程中,函數所依賴的環境沒有發生改變,那么這個函數就是可重入的,否則就不可重入

在中斷前后不都要保存和恢復上下文嗎,怎么會出現函數所依賴的環境發生改變了呢?

我們知道中斷時確實保存一些上下文,但是僅限於返回地址,cpu寄存器等之類的少量上下文,而函數內部使用的諸如全局或靜態變量,buffer等並不在保護之列,所以如果這些值在函數被中斷期間發生了改變,那么當函數回到斷點繼續執行時,其結果就不可預料了。

滿足下面條件之一的多數是不可重入函數:

(1)使用了靜態數據結構;

(2)調用了malloc或free;

(3)調用了標准I/O函數;

(4)進行了浮點運算.

malloc/free是不可重入的,它們使用了全局變量來指向空閑區;

標准I/O庫的很多實現都使用了全局數據結構;

許多的處理器/編譯器中,不可重入的 (浮點運算大多使用協處理器或者軟件模擬來實現)。

在信號處理程序及多線程編程時,要特別注意。

考慮這種情況

1) 信號處理程序A內外都調用了同一個不可重入函數B;B在執行期間被信號打斷,進入A (A中調用了B),完事之后返回B被中斷點繼續執行,這時B函數的環境可能改變,其結果就不可預料了。

2) 多線程共享進程內部的資源,如果兩個線程A,B調用同一個不可重入函數F,A線程進入F后,線程調度,切換到B,B也執行了F,那么當再次切換到線程A時,其調用F的結果也是不可預料的。

在信號處理程序中即使調用可重入函數也有問題要注意。作為一個通用的規則,當在信號處理程序中調用可重

入函數時,應當在其前保存errno,並在其后恢復errno。(要了解經常被捕捉到的信號是SIGCHLD,其信號處理程序通常要調用一種wait函數,而各種wait函數都能改變errno。)

 

二、實例

給出一段程序,這段程序從信號處理程序my_alarm調用非可重入函數getpwnam,而my_alarm每秒被調用一次。在該程序中調用alarm函數使得每秒產生一次SIGALRM信號。

 

#include <stdio.h>
#include <pwd.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>

static void my_alarm(int signo){
    struct passwd * rootptr;

    printf("in signal handler\n");
    if((rootptr = getpwnam("root")) == NULL){
        fprintf(stderr, "getpwnam(root) error\n");
    }
    alarm(1);
}

int main(void){
    struct passwd *ptr;

    signal(SIGALRM, my_alarm);
    alarm(1);
    for(;;){
        if((ptr = getpwnam("ives")) == NULL){
            fprintf(stderr, "getpwnam(ives) error\n");
        }
        if(strcmp(ptr->pw_name, "ives") != 0){
            printf("return value corrupted!, pw_name = %s\n", ptr->pw_name);
        }
    }
}

在大多數系統下,該代碼不會正常工作,只會輸出一串"in signal handler"就阻塞在那里不再運行。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM