2013-10-12 42 views
2

我在Linux上創建了一個C庫,它有幾個功能,它們共同操作一些全局數據。爲了使這些函數成爲線程安全的,他們必須在代碼中的適當位置使用互斥鎖。如何創建僅在pthread鏈接時才使用互斥鎖的庫?

在Linux中,爲了在應用程序中使用pthreads,需要鏈接到相應的庫中,即-lpthread。在我的庫被編譯的情況下,如果它的用戶決定在他們的應用程序中使用pthread,並且他們不這樣做,我想讓它工作。

在開發人員在其應用程序中不使用線程的情況下,它們將不會鏈接到pthread。因此,我希望我編譯的庫不要求它,此外,在單線程應用程序中使用互斥鎖使用不必要的開銷(更不用說是愚蠢的)。

是否有某種方式來編寫代碼(如果需要,使用GCC擴展)只有某些符號鏈接到某個代碼塊纔會運行?我知道我可以使用dlopen()和朋友,但這本身需要一些我試圖避免的。我想我所尋找的東西必須存在,因爲幾個標準函數在同一條船上,並且需要互斥體是線程安全的(而且它們是),但是即使不與pthread鏈接,也可以工作。

在這一點上,我注意到,FreeBSD's popen() function上線66 & 67採用非便攜式檢查 - isthreaded,以確定是否有使用或不使用線程,以及是否使用互斥。我懷疑任何這樣的東西都是以任何方式標準化的。但更重要的是,如果符號不被識別,這種代碼就無法編譯和鏈接,在Linux中,如果pthread未鏈接,互斥符號甚至不會出現。總結:在Linux上,如何創建一個庫,它知道何時使用線程,如果是,在適當的情況下使用互斥鎖,並且不需要鏈接到pthread,除非應用程序開發人員特別想使用線程在某處?

+0

「在開發人員在應用程序中不使用線程的情況下,它們不會與pthreads鏈接。」 - 但你的圖書館*將*。或者如果它是靜態庫,它將包含大量必須由調用程序解決的外部pthread引用,即使它們不使用pthread。如果沒有一對pthread/no-pthread共享對象模塊,我不認爲你會得到你想要的東西,正確的一個dyna-在運行時加載*由你根據調用者的初始化配置加載,所有在非線程配置中的鎖定操作。 – WhozCraig

+0

你可以隨時踢,順便說一句,並讓應用程序提供一組鎖*爲你*。例如,RSA BSAFE CryptoC-ME通過要求應用程序提供一個回調函數,該函數忠實地使用它自己的「鎖定表」,其大小爲由庫標題指定的預定義的「#define」大小,然而許多「鎖「是必要的。每個鎖操作都通過索引傳遞給回調函數,其中一個var聲明操作請求是(LOCK,UNLOCK)。然後,應用程序可以知道它是單線程的,只需提供一個回調,該回調總是返回true,而不會執行其他任何操作。 – WhozCraig

回答

3

經過一番測試,似乎Linux已經做了我想要的自動!如果你使用線程,你只需要鏈接到pthreads,而不是如果你只是想要pthread互斥體支持。

在這個測試用例:「互斥鎖」

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

int main() 
{ 
    pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; 

    if (!(errno = pthread_mutex_lock(&mutex))) { puts("Mutex locked!"); } 
    else { perror("Could not lock mutex"); } 

    if (!(errno = pthread_mutex_lock(&mutex))) { puts("Mutex locked!"); } 
    else { perror("Could not lock mutex"); } 

    return 0; 
} 

在編制本沒有掛並行線程,我看兩次。這表明pthread_mutex_lock()本質上是非操作。但是,如果鏈接了pthread,運行此應用程序將在第一次「Mutex鎖定!」後停止。被打印。

因此,我可以在適當的情況下在我的庫中使用互斥鎖,並且不需要使用pthreads,並且不需要它(signifigant?)開銷。

+0

我在各種方便的BSD上測試了它。 FreeBSD和NetBSD與Linux有相同的語義,這很好,OpenBSD需要-lpthread。另一方面,與-lpthread鏈接的FreeBSD和OpenBSD都沒有掛在第二個鎖上,但是避免了返回的死鎖。所有BSD似乎也在中定義__histhreaded,以便可以檢查是否有任何線程正在使用。 – Joe

+0

關於__isthreaded,結果證明NetBSD缺少它,就像Debian kFreeBSD一樣。 DragonFlyBSD原來與FreeBSD相匹配。對於OpenBSD,爲了實現我想要的,需要使用:int pthread_mutex_lock(pthread_mutex_t * mutex)__attribute __((weak)); – Joe

0

通常的解決方案是:

  1. 使用#define開關在編譯的時候來控制是否調用並行線程功能與否,和你的構建過程中創建庫的兩個版本:一個並行線程感知一個不是,名字不同。依靠你的圖書館的用戶鏈接到正確的。

  2. 不要直接調用pthreads函數,而是調用用戶提供的lockunlock回調(如果需要,也可以調用線程本地存儲)。庫用戶負責分配和調用適當的鎖定機制,這也允許他們使用非線程線程庫。

  3. 什麼都不做,只是記錄用戶代碼應該確保你的庫函數不能同時從多個線程進入。

的glibc做不同的東西再次 - 它使用與延遲綁定符號的技巧來調用並行線程功能,只有當它們被鏈接到二進制文件。這不是可移植的,因爲它依賴於pthread的glibc實現的具體細節。請參閱definition of __libc_maybe_call()

#ifdef __PIC__ 
# define __libc_maybe_call(FUNC, ARGS, ELSE) \ 
    (__extension__ ({ __typeof (FUNC) *_fn = (FUNC); \ 
        _fn != NULL ? (*_fn) ARGS : ELSE; })) 
#else 
# define __libc_maybe_call(FUNC, ARGS, ELSE) \ 
    (FUNC != NULL ? FUNC ARGS : ELSE) 
#endif