天天看點

C/C++:多線程下使用dlopen、dlsym、dlclose裝載動态庫

C/C++:多線程下使用dlopen、dlsym、dlclose裝載動态庫

當在多線程下dlopen同一個動态庫,使用的會是同一個動态庫執行個體還是不同的動态庫執行個體呢?

count.h

#ifndef _COUNT_H
#define _COUNT_H

#include <pthread.h>

int count;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;


int get();
void inc();

#endif
           

count.c

#include "count.h"

int get()
{
    return count;
}

void inc()
{
    pthread_mutex_lock(&mutex);
    count++;
    pthread_mutex_unlock(&mutex);
}
           

main.c

#include <stdio.h>
#include <dlfcn.h>
#include <pthread.h>

#define NUM 1000 
#define LIBPATH "/home/test1280/libcount.so"

static const char *threadA = "Thread-A";
static const char *threadB = "Thread-B";

void *ThreadRun(void *arg)
{
    usleep(*);
    printf("Start:%s\n", (char *)arg);

    void *handler = dlopen(LIBPATH, RTLD_LAZY);
    if (handler == NULL)
    {
        printf("ERROR:%s:dlopen\n", dlerror());
    }

    void (*inc)() = (void (*)())dlsym(handler, "inc");
    if (inc == NULL)
    {
        printf("ERROR:%s:dlsym\n", dlerror());
    }

    int i = ;
    for (; i < NUM; i++)
    {
        inc();
    }

    int (*get)() = (int (*)())dlsym(handler, "get");
    if (get == NULL)
    {
        printf("ERROR:%s:dlsym\n", dlerror());
    }
    printf("INFO:%s:get() return %d\n", (char *)arg, get());

    dlclose(handler);
}

int main()
{
    pthread_t tid1;
    pthread_t tid2;

    pthread_create(&tid1, NULL, ThreadRun, (void *)threadA);
    printf("create Thread-A OK!!!\n");
    pthread_create(&tid2, NULL, ThreadRun, (void *)threadB);
    printf("create Thread-B OK!!!\n");

    while ();

    return ;
}
           
[test128@localhost ~]$ gcc -o main main.c -ldl -lpthread
           
[test128@localhost ~]$ ./main
create Thread-A OK!!!
create Thread-B OK!!!
Start:Thread-A
INFO:Thread-A:get() return 
Start:Thread-B
INFO:Thread-B:get() return 
^C
           

這麼看來同一程序實體不同線程dlopen相同的庫會得到不同的執行個體?

一個動态庫會在同一程序同一時刻同時被加載多次?

當然不對啦!

那按照我們期待的,希望輸出的最後的值應該是2000,那為何是1000呢?

原因:

線程A或者線程B跑的太快了,在一個時間片内就已經做完:

1.裝載動态庫到目前程序;

2.獲得函數位址;

3.調用inc指定次數;

4.輸出get傳回值;

5.dlclose解除安裝動态庫;

注意:

dlclose時,此時此刻可能僅僅隻有一個線程在跑着,另一個線程還沒有dlopen那個動态庫,此時隻有一個線程持有libcount.so的句柄,然後做完所有操作,dlclose,發現對庫的引用減少至0,就将libcount.so從目前程序解除安裝;

然後呢,就是另一個線程在跑,再打開dlopen同一個動态庫,然後裡面的全局變量(庫内全局)靜态初始化為0,然後inc,然後…

結果很明顯:是由于某個線程跑的太快,導緻另一個線程還沒有執行dlopen時,前一個程序已經做完所有活了(包括解除安裝libcount.so庫),當另一個線程執行dlopen時,完全不知道這個庫曾經被加載到目前程序…于是庫中全局變量又靜态初始化…

這樣的時序看來,輸出兩個1000合情合理.

如何驗證我們剛剛說的時序呢?

修改下main.c:

#include <stdio.h>
#include <dlfcn.h>
#include <pthread.h>

#define NUM 1000 
#define LIBPATH "/home/test1280/libcount.so"

static const char *threadA = "Thread-A";
static const char *threadB = "Thread-B";

static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

void *ThreadRun(void *arg)
{
    usleep(*);
    printf("Start:%s\n", (char *)arg);

    void *handler = dlopen(LIBPATH, RTLD_LAZY);
    if (handler == NULL)
    {
        printf("ERROR:%s:dlopen\n", dlerror());
    }

    void (*inc)() = (void (*)())dlsym(handler, "inc");
    if (inc == NULL)
    {
        printf("ERROR:%s:dlsym\n", dlerror());
    }

    int i = ;
    for (; i < NUM; i++)
    {
        inc();
    }

    int (*get)() = (int (*)())dlsym(handler, "get");
    if (get == NULL)
    {
        printf("ERROR:%s:dlsym\n", dlerror());
    }
    printf("INFO:%s:get() return %d\n", (char *)arg, get());

    pthread_mutex_lock(&mutex);
    dlclose(handler);
    printf("INFO:lib unload && %s END...\n", (char *)arg);
    pthread_mutex_unlock(&mutex);
}

int main()
{
    pthread_t tid1;
    pthread_t tid2;

    pthread_create(&tid1, NULL, ThreadRun, (void *)threadA);
    printf("create Thread-A OK!!!\n");
    pthread_create(&tid2, NULL, ThreadRun, (void *)threadB);
    printf("create Thread-B OK!!!\n");

    while ();

    return ;
}
           
[test128@localhost ~]$ gcc -o main main.c -ldl -lpthread
[test128@localhost ~]$ ./main
create Thread-A OK!!!
create Thread-B OK!!!
Start:Thread-A
INFO:Thread-A:get() return 
INFO:lib unload && Thread-A END...
Start:Thread-B
INFO:Thread-B:get() return 
INFO:lib unload && Thread-B END...
^C
           

另外,我想看看兩個線程對同一個庫執行個體進行操作是什麼效果…?

我們隻要讓兩個線程同時(同一時刻)對一個庫保持打開狀态即可(不dlclose):

将ThreadRun中最後的dlclose删除,如下main.c所示:

void *ThreadRun(void *arg)
{
    usleep(*1000);
    printf("Start:%s\n", (char *)arg);

    void *handler = dlopen(LIBPATH, RTLD_LAZY);
    if (handler == NULL)
    {
        printf("ERROR:%s:dlopen\n", dlerror());
    }

    void (*inc)() = (void (*)())dlsym(handler, "inc");
    if (inc == NULL)
    {
        printf("ERROR:%s:dlsym\n", dlerror());
    }

    int i = ;
    for (; i < NUM; i++)
    {
        inc();
    }

    int (*get)() = (int (*)())dlsym(handler, "get");
    if (get == NULL)
    {
        printf("ERROR:%s:dlsym\n", dlerror());
    }
    printf("INFO:%s:get() return %d\n", (char *)arg, get());

    //delete...
    //dlclose(handler);
}
           
[test128@localhost ~]$ ./main
create Thread-A OK!!!
create Thread-B OK!!!
Start:Thread-A
INFO:Thread-A:get() return 
Start:Thread-B
INFO:Thread-B:get() return 
^C
           

當然,這個示例并不好,有dlopen,就應該有配套的dlclose,雖然如此,但是可以很好地驗證我們的設想.

結論:

同一程序同一時刻,不同線程通過dlopen打開相同的動态庫,隻是把一份動态庫裝載到目前程序,程序中僅僅有一個動态庫執行個體,動态庫中全局變量對各個線程來說是“同一份”.

同一程序不同時刻,不同線程雖然dlopen了,但是可能由于時序問題以及dlclose解除安裝動态庫,存在多次加載動态庫的情況,那麼也就是從程序的整體生命周期來看是不同的庫執行個體了,庫中的全局變量對于不同的線程,在不同的時間段來說是“不相同的”.

同一時刻,一個程序内僅僅有同一個動态庫執行個體哦.

繼續閱讀