攔截malloc、free等庫函數(malloc鉤子)

注意,以下方法對於多線程來說不管用。如,A線程調用到malloc_hook 函數,把malloc_hook函數還原爲libc malloc函數,此時B線程調用到malloc函數便是libc malloc函數,根本不會進入到我們的malloc_hook設定的my_malloc函數。因此,多線程下是不能使用此方法,需要用__wrap_malloc方式解決。


__malloc_hook是一組glibc提供的malloc調試變量中的一個,這組變量包括:

void *(*__malloc_hook)(size_t size, const void *caller);

void *(*__realloc_hook)(void *ptr, size_t size, const void *caller);

void *(*__memalign_hook)(size_t alignment, size_t size, const void *caller);

void (*__free_hook)(void *ptr, const void *caller);

void (*__malloc_initialize_hook)(void);

void (*__after_morecore_hook)(void);

何謂鉤子函數,當一個函數掛載了鉤子函數後,你執行這個函數時,實際執行的是鉤子函數

只要你在程序中寫上”__malloc_hook= my_malloc_hook;”,之後的malloc調用都會使用my_malloc_hook函數,方便易行。但是這組調試變量不是線程安全的,當你想用系統malloc的時候不得不把他們改回來,多線程調用就得上鎖。

#include <stdio.h>
#include <malloc.h>

#define THREAD_AUTOLOCK

static void (*old_free)(void *ptr, const void *caller);
static void *(*old_malloc)(size_t size, const void *caller);

static void my_free(void *ptr, const void *caller);
static void *my_malloc(size_t size, const void *caller);

static void hook_back()
{
    old_malloc = __malloc_hook;
    old_free = __free_hook;
}

static void hook_init()
{
    __malloc_hook = my_malloc;
    __free_hook = my_free;
}

static void hook_restore()
{
    __malloc_hook = old_malloc;
    __free_hook = old_free;
}

static void my_free(void *ptr, const void *caller)
{
    THREAD_AUTOLOCK;
    hook_restore();// 這裏必須,不然會死循環
    printf("free address: %x\n", ptr);
    free(ptr);
    hook_init();
    return;
}

static void *my_malloc(size_t size, const void *caller)
{
    THREAD_AUTOLOCK;
    void *p = NULL;
    hook_restore();// 這裏必須,不然會死循環
    printf("malloc size: %d\n", size);
    p = malloc(size);
    hook_init();
    return p;
}

static void my_mempool_destroy()
{
    hook_restore();
}

static void my_mempool_init()
{
    hook_back();
    hook_init();
    atexit(my_mempool_destroy);
}

void (*__MALLOC_HOOK_VOLATILE __malloc_initialize_hook) (void) = my_mempool_init;
int main(int argc, char **argv)
{
    void *p = malloc(12);
    if (p != NULL)
        free(p);
    return 0;
}

int atexit(void(*func)(void));

    其中,atexit的參數是一個函數地址,當調用此函數時無須傳遞任何參數,該函數也不能返回值,atexit函數稱爲終止處理程序註冊程序,註冊完成以後,當函數終止是exit()函數會主動的調用前面註冊的各個函數,但是exit函數調用這些函數的順序於這些函數登記的順序是相反的,我認爲這實質上是參數壓棧造成的,參數由於壓棧順序而先入後出。同時如果一個函數被多次登記,那麼該函數也將多次的執行。
    #include<stdio.h>
    #include<stdlib.h>

    void func1(void)
    {
            printf("in func1\n");
    }

    void func2(void)
    {
            printf("in func2\n");
    }

    void func3(void)
    {
            printf("in func3\n");
    }

    int main()
    {
            atexit(func3);
            atexit(func2);
            atexit(func1);

            printf("In main\n");

            return 0;
    }

exit()和_exit()以及_Exit()函數的本質區別是是否立即進入內核,_exit()以及_Exit()函數都是在調用後立即進入內核,而不會執行一些清理處理,但是exit()則會執行一些清理處理,這也是爲什麼會存在atexit()函數的原因,因爲exit()函數需要執行清理處理,需要執行一系列的操作,這些終止處理函數實際上就是完成各種所謂的清除操作的實際執行體。atexit函數的定義也給了程序員一種運用exit執行一些清除操作的方法,比如有一些程序需要額外的操作,具體的清除操作可以採用這種方法對特殊操作進行清除等。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章