記得以前討論過(guò)一個(gè)關(guān)于reentrant函數與thread safe函數的帖子
很多人對于這兩種函數不是很了解,
尤其是發(fā)現malloc等函數是non-reentrant函數時(shí),對多線(xiàn)程編程都產(chǎn)生了"恐懼"
這里是我對這兩種函數的一些理解,希望和大家探討一些.歡迎批評指正.
1. reentrant函數
一個(gè)函數是reentrant的,如果它可以被安全地遞歸或并行調用。要想成為reentrant式的函數,該函數不能含有(或使用)靜態(tài)(或全局)數據(來(lái)存儲函數調用過(guò)程中的狀態(tài)信息),也不能返回指向靜態(tài)數據的指針,它只能使用由調用者提供的數據,當然也不能調用non-reentrant函數.
比較典型的non-reentrant函數有g(shù)etpwnam, strtok, malloc等.
reentrant和non-reentrant函數的例子
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <math.h>
int* getPower(int i)
{
static int result;
result = pow(2, i);
getchar();
return &result;
}
void getPower_r(int i, int* result)
{
*result = pow(2, i);
}
void handler (int signal_number) /*處理SIGALRM信號*/
{
getPower(3);
}
int main ()
{
int *result;
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
sa.sa_handler = &handler;
sigaction(SIGALRM, &sa, NULL);
result = getPower(5);
printf("2^5 = %d\n", *result);
return 0;
}
試驗方法:
1. 編譯 gcc test.c -lpthread
在一個(gè)終端中運行 ./a.out, 在另一個(gè)終端中運行 ps -A|grep a.out可以看到該進(jìn)程的id
2. 用如下方式運行a.out:
運行./a.out,在按回車(chē)前,在另外一個(gè)終端中運行kill -14 pid (這里的pid是運行上面的ps時(shí)看到的值)
然后,按回車(chē)繼續運行a.out就會(huì )看到2^5 = 8 的錯誤結論
對于函數int* getPower(int i)
由于函數getPower會(huì )返回一個(gè)指向靜態(tài)數據的指針,在第一次調用getPower的過(guò)程中,再次調用getPower,則兩次返回的指針都指向同一塊內存,第二次的結果將第一次的覆蓋了(很多non-reentrant函數的這種用法會(huì )導致不確定的后果).所以是non-reentrant的.
對于函數void getPower_r(int i, int* result)
getPower_r會(huì )將所得的信息存儲到result所指的內存中,它只是使用了由調用者提供的數據,所以是reentrant.在信號處理函數中可以正常的使用它.
2. thread-safe函數
Thread safety是多線(xiàn)程編程中的概念,thread safe函數是指那些能夠被多個(gè)線(xiàn)程同時(shí)并發(fā)地正確執行的函數.
thread safe和non thread safe的例子
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
pthread_mutex_t sharedMutex=PTHREAD_MUTEX_INITIALIZER;
int count; /*共享數據*/
void* func (void* unused)
{
if (count == 0)
count++;
}
void* func_s (void* unused)
{
pthread_mutex_lock(&sharedMutex); /*進(jìn)入臨界區*/
if (count == 0)
count++;
pthread_mutex_unlock(&sharedMutex); /*離開(kāi)臨界區*/
}
int main ()
{
pthread_t pid1, pid2;
pthread_create(&pid1, NULL, &func, NULL);
pthread_create(&pid2, NULL, &func, NULL);
pthread_join(pid1, NULL);
pthread_join(pid2, NULL);
return 0;
}
函數func是non thread safe的,這是因為它不能避免對共享數據count的race condition,
設想這種情況:一開(kāi)始count是0,當線(xiàn)程1進(jìn)入func函數,判斷過(guò)count == 0后,線(xiàn)程2進(jìn)入func函數
線(xiàn)程2判斷count==0,并執行count++,然后線(xiàn)程1開(kāi)始執行,此時(shí)count != 0 了,但是線(xiàn)程1仍然要執行
count++,這就產(chǎn)生了錯誤.
func_s通過(guò)mutex鎖將對共享數據的訪(fǎng)問(wèn)鎖定,從而避免了上述情況的發(fā)生.func_s是thread safe的
只要通過(guò)適當的"鎖"機制,thread safe函數還是比較好實(shí)現的.
3. reentrant函數與thread safe函數的區別
reentrant函數與是不是多線(xiàn)程無(wú)關(guān),如果是reentrant函數,那么要求即使是同一個(gè)進(jìn)程(或線(xiàn)程)同時(shí)多次進(jìn)入該函數時(shí),該函數仍能夠正確的運作.
該要求還蘊含著(zhù),如果是在多線(xiàn)程環(huán)境中,不同的兩個(gè)線(xiàn)程同時(shí)進(jìn)入該函數時(shí),該函數也能夠正確的運作.
thread safe函數是與多線(xiàn)程有關(guān)的,它只是要求不同的兩個(gè)線(xiàn)程同時(shí)對該函數的調用在邏輯上是正確的.
從上面的說(shuō)明可以看出,reentrant的要求比thread safe的要求更加嚴格.reentrant的函數必是thread safe的,而thread safe的函數
未必是reentrant的. 舉例說(shuō)明:
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <signal.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
pthread_mutex_t sharedMutex=PTHREAD_MUTEX_INITIALIZER;
int count; /*共享數據*/
void* func_s (void* unused)
{
pthread_mutex_lock(&sharedMutex); /*進(jìn)入臨界區*/
printf("locked by thead %d\n", pthread_self());
if (count == 0)
count++;
getchar();
pthread_mutex_unlock(&sharedMutex); /*離開(kāi)臨界區*/
printf("lock released by thead %d\n", pthread_self());
}
void handler (int signal_number) /*處理SIGALRM信號*/
{
printf("handler running in %d\n", pthread_self());
func_s(NULL);
}
int main ()
{
pthread_t pid1, pid2;
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
sa.sa_handler = &handler;
sigaction(SIGALRM, &sa, NULL);
printf("main thread's pid is: %d\n", pthread_self());
func_s(NULL);
pthread_create(&pid1, NULL, &func_s, NULL);
pthread_create(&pid2, NULL, &func_s, NULL);
pthread_join(pid1, NULL);
pthread_join(pid2, NULL);
func_s(NULL);
return 0;
}
試驗方法:
1. 編譯 gcc test.c -lpthread
在一個(gè)終端中運行 ./a.out, 在另一個(gè)終端中運行 ps -A|grep a.out可以看到該進(jìn)程的id
2. 進(jìn)行下面4次運行a.out:
每次運行分別在第1,2,3,4次回車(chē)前,在另外一個(gè)終端中運行kill -14 pid (這里的pid是上面ps中看到的值)
試驗結果:
1. 該進(jìn)程中有3個(gè)線(xiàn)程:一個(gè)主線(xiàn)程,兩個(gè)子線(xiàn)程
2. func_s是thread safe的
3. func_s不是reentrant的
4. 信號處理程序會(huì )中斷主線(xiàn)程的執行,不會(huì )中斷子線(xiàn)程的執行
5. 在第1,4次回車(chē)前,在另外一個(gè)終端中運行kill -14 pid會(huì )形成死鎖,這是因為
主線(xiàn)程先鎖住了臨界區,主線(xiàn)程被中斷后,執行handler(以主線(xiàn)程執行),handler試圖鎖定臨界區時(shí),
由于同一個(gè)線(xiàn)程鎖定兩次,所以形成死鎖
6. 在第2,3次回車(chē)前,在另外一個(gè)終端中運行kill -14 pid不會(huì )形成死鎖,這是因為一個(gè)子線(xiàn)程先鎖住
了臨界區,主線(xiàn)程被中斷后,執行handler(以主線(xiàn)程執行),handler試圖鎖定臨界區時(shí),被掛起,這時(shí),子線(xiàn)程
可以被繼續執行.當該子線(xiàn)程釋放掉鎖以后,handler和另外一個(gè)子線(xiàn)程可以競爭進(jìn)入臨界區,然后繼續執行.
所以不會(huì )形成死鎖.
結論:
1. reentrant是對函數相當嚴格的要求,絕大部分函數都不是reentrant的(APUE上有一個(gè)reentrant函數
的列表).
什么時(shí)候我們需要reentrant函數呢?只有一個(gè)函數需要在同一個(gè)線(xiàn)程中需要進(jìn)入兩次以上,我們才需要
reentrant函數.這些情況主要是異步信號處理,遞歸函數等等.(non-reentrant的遞歸函數也不一定會(huì )
出錯,出不出錯取決于你怎么定義和使用該函數). 大部分時(shí)候,我們并不需要函數是reentrant的.
2. 在多線(xiàn)程環(huán)境當中,只要求多個(gè)線(xiàn)程可以同時(shí)調用一個(gè)函數時(shí),該函數只要是thread safe的就可以了.
我們常見(jiàn)的大部分函數都是thread safe的,不確定的話(huà)請查閱相關(guān)文檔.
3. reentrant和thread safe的本質(zhì)的區別就在于,reentrant函數要求即使在同一個(gè)線(xiàn)程中任意地進(jìn)入兩次以上,
也能正確執行.
大家常用的malloc函數是一個(gè)典型的non-reentrant但是是thread safe函數,這就說(shuō)明,我們可以方便的
在多個(gè)線(xiàn)程中同時(shí)調用malloc,但是,如果將malloc函數放入信號處理函數中去,這是一件很危險的事情.
[ 本帖最后由 ypxing 于 2007-8-4 01:06 編輯 ]
--------------------------------------------------------------------------------
lenovo 回復于:2007-08-02 21:38:57
不錯,很好的帖子。
--------------------------------------------------------------------------------
科技牛 回復于:2007-08-03 15:38:14
受教很深!
--------------------------------------------------------------------------------
ypxing 回復于:2007-08-03 15:58:22
調用了malloc的函數肯定是non-reentrant的
引用:原帖由 bluster 于 2007-8-3 15:55 發(fā)表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=7155171&ptid=971102]
最后一點(diǎn)是錯的,比如一個(gè)函數調用malloc并不影響這個(gè)函數是否是reentrant。
--------------------------------------------------------------------------------
ypxing 回復于:2007-08-03 15:59:35
這家伙,怎么把自己的帖子給刪了?
--------------------------------------------------------------------------------
bluster 回復于:2007-08-03 16:01:11
引用:原帖由 ypxing 于 2007-8-3 15:58 發(fā)表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=7155198&ptid=971102]
調用了malloc的函數肯定是non-reentrant的
你是對的,我一時(shí)有點(diǎn)繞。
其實(shí),是對reentrant的定義有問(wèn)題。
可重入的意思,差不多是函數的任意部分都可以并行,而線(xiàn)程安全的意思則是多線(xiàn)程環(huán)境下使用沒(méi)有問(wèn)題,對于非可重入的函數,使用lock來(lái)保護不可并行的部分從而線(xiàn)程安全。
引用:原帖由 ypxing 于 2007-8-3 15:59 發(fā)表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=7155214&ptid=971102]
這家伙,怎么把自己的帖子給刪了?
無(wú)價(jià)值糊涂帖,所以刪了。
[ 本帖最后由 bluster 于 2007-8-3 16:05 編輯 ]
--------------------------------------------------------------------------------
jigloo 回復于:2007-08-03 16:11:55
>>3. reentrant和thread safe的本質(zhì)的區別就在于,reentrant函數要求在同一個(gè)線(xiàn)程中需要進(jìn)入兩次以上,
并能正確執行.
--------------------------------------------------------------------------------
思一克 回復于:2007-08-03 17:03:49
這個(gè)問(wèn)題很復雜。
LZ的帖子很好。改進(jìn)的地方是LZ應該多講WHY不可重入,如何才可重入,而不是下結論。
1)調用了不可重入函數的函數不一定是不可重入的。比如LINUX KERNEL中,設備中斷處理函數是不可重入的,而__do_IRQ()調用了他們,但__do_IRQ卻是可重入的。
只要保證被調用的函數部分沒(méi)有重入就可以了。
2)使用的全局變量的函數也不一定是不可重入的。還比如__do_IRQ()使用了全局變量來(lái)存儲數據,但它是可重入的。
類(lèi)似的例子:
[CODE]
int ia[32];
int func(int i)
{
ia++;
printf("%p i %d %d\n", &i, i, ia);
if(i == 31) return;
func(i+1);
}
main()
{
func(0);
}
[/CODE]
關(guān)于這個(gè)問(wèn)題,看LINUX中斷處理部分非常有啟發(fā)。那里邏輯復雜,各種重入(硬,軟中斷,多CPU)處理的非常巧妙。
--------------------------------------------------------------------------------
ypxing 回復于:2007-08-03 18:50:12
思一克,你好
首先謝謝你的鼓勵.
你給出的這個(gè)例子,函數func,既不是可重入的,也不是線(xiàn)程安全的,
原因如下:
假設有一個(gè)信號處理函數handler,里面調用了func
考慮這種情況:
主函數中調用了func(0) (這個(gè)時(shí)候,你的本意是先要ia[0]++,然后打印現在ia[0]的值,
再然后繼續后面的操作),
在func剛執行完ia[0]++時(shí),信號觸發(fā)了handler函數,
handler函數會(huì )調用func函數,然后執行對ia的一系列操作,完成后返回.
這時(shí),你的主函數調用的func繼續執行,也就是要printf了,
這時(shí)printf的東東就不是你想要的了,而且你無(wú)法確定現在ia[0]的值是什么(因為信號
可以中斷很多次很多層).所以func不是可重入的.
而且也不是線(xiàn)程安全的.
可重入的一個(gè)判定方法就是將它放入信號處理函數中,仔細推敲各種中斷情況下,
你是不是還能得到你想要的結果.
"使用的全局變量的函數也不一定是不可重入的。"這句是正確的,只要正確使用就可以了,
但是不使用全局變量是寫(xiě)可重入函數的簡(jiǎn)單方法.
"調用了不可重入函數的函數不一定是不可重入的。"這句是不對的,
因為你無(wú)法保證被調用的不可重入函數部分不被重入
int ia[32];
int func(int i)
{
ia++;
printf("%p i %d %d\n", &i, i, ia);
if(i == 31) return;
func(i+1);
}
main()
{
func(0);
}
--------------------------------------------------------------------------------
思一克 回復于:2007-08-03 19:39:57
你寫(xiě)可重入函數時(shí)候要考慮到保證不可重入部分不重入, 還有保證整個(gè)函數必須可重入.
__do_IRQ就是如此.
所以說(shuō)"調用了不可重入函數的函數不一定是不可重入的"是正確的.
而"調用了不可重入函數的函數一定是不可重入的"是不對的.因為有十分多的反例.
調用了不可重入函數的函數不一定是不可重入的。"這句是不對的,
因為你無(wú)法保證被調用的不可重入函數部分不被重入
--------------------------------------------------------------------------------
feasword 回復于:2007-08-03 20:09:35
一直想找這兩個(gè)概念是此非彼的例子,受教了
關(guān)于死鎖的問(wèn)題,apue里也有講,以前也遇到過(guò),當時(shí)干脆都弄成遞歸鎖了
--------------------------------------------------------------------------------
ypxing 回復于:2007-08-03 20:49:04
那么,怎么才能保證不可重入的部分不被重入呢?
引用:原帖由 思一克 于 2007-8-3 19:39 發(fā)表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=7156157&ptid=971102]
你寫(xiě)可重入函數時(shí)候要考慮到保證不可重入部分不重入, 還有保證整個(gè)函數必須可重入.
__do_IRQ就是如此.
所以說(shuō)"調用了不可重入函數的函數不一定是不可重入的"是正確的.
而"調用了不可重入函數的函數一定是不可 ...
--------------------------------------------------------------------------------
cugb_cat 回復于:2007-08-03 22:12:05
引用:原帖由 ypxing 于 2007-8-3 20:49 發(fā)表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=7156295&ptid=971102]
那么,怎么才能保證不可重入的部分不被重入呢?
我有同樓主相同的疑問(wèn)。
另外,從lz的例子中學(xué)到一些技巧,關(guān)于調試多線(xiàn)程程序,感謝lz。
[ 本帖最后由 cugb_cat 于 2007-8-3 22:45 編輯 ]
--------------------------------------------------------------------------------
飛灰橙 回復于:2007-08-03 22:18:09
引用:原帖由 思一克 于 2007-8-3 19:39 發(fā)表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=7156157&ptid=971102]
你寫(xiě)可重入函數時(shí)候要考慮到保證不可重入部分不重入, 還有保證整個(gè)函數必須可重入.
__do_IRQ就是如此.
所以說(shuō)"調用了不可重入函數的函數不一定是不可重入的"是正確的.
而"調用了不可重入函數的函數一定是不可重入的"是不對的(語(yǔ)句A).因為有十分多的反例.
調用了不可重入函數的函數不一定是不可重入的。"這句是不對的(語(yǔ)句B),
因為你無(wú)法保證被調用的不可重入函數部分不被重入
越看越糊涂了,撇開(kāi)討論的問(wèn)題不談, 上面的語(yǔ)句A和語(yǔ)句B,必定有一句是錯的
--------------------------------------------------------------------------------
cugb_cat 回復于:2007-08-03 22:44:57
引用:原帖由 飛灰橙 于 2007-8-3 22:18 發(fā)表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=7156605&ptid=971102]
越看越糊涂了,撇開(kāi)討論的問(wèn)題不談, 上面的語(yǔ)句A和語(yǔ)句B,必定有一句是錯的
兩句意思相反~:mrgreen:
--------------------------------------------------------------------------------
ypxing 回復于:2007-08-03 23:30:14
俺也看了好一會(huì )才看懂:em02:
引用:原帖由 飛灰橙 于 2007-8-3 22:18 發(fā)表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=7156605&ptid=971102]
越看越糊涂了,撇開(kāi)討論的問(wèn)題不談, 上面的語(yǔ)句A和語(yǔ)句B,必定有一句是錯的
--------------------------------------------------------------------------------
mingyanguo 回復于:2007-08-04 00:08:35
完了,簡(jiǎn)單的問(wèn)題復雜化了 :mrgreen:
--------------------------------------------------------------------------------
hakase 回復于:2007-08-08 20:37:06
好帖,受教了~~
--------------------------------------------------------------------------------
ypxing 回復于:2007-08-08 23:05:51
這兩天寫(xiě)了一個(gè)測試程序來(lái)驗證malloc的不可重入性
但是malloc一直沒(méi)有crash,有點(diǎn)郁悶
過(guò)段時(shí)間把自己的測試代碼貼出來(lái),讓大家來(lái)幫忙看看
--------------------------------------------------------------------------------
bluster 回復于:2007-08-09 10:08:56
引用:原帖由 ypxing 于 2007-8-8 23:05 發(fā)表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=7176529&ptid=971102]
這兩天寫(xiě)了一個(gè)測試程序來(lái)驗證malloc的不可重入性
但是malloc一直沒(méi)有crash,有點(diǎn)郁悶
過(guò)段時(shí)間把自己的測試代碼貼出來(lái),讓大家來(lái)幫忙看看
多線(xiàn)程條件下,signal的handler有可能在一個(gè)單獨的線(xiàn)程中執行,如果這樣那么malloc用鎖保護就夠了。
--------------------------------------------------------------------------------
ypxing 回復于:2007-08-09 10:29:51
在多線(xiàn)程條件下,
理論上,將malloc放入signal的handler也是會(huì )出問(wèn)題的,
鎖是不行的,會(huì )死鎖
引用:原帖由 bluster 于 2007-8-9 10:08 發(fā)表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=7177603&ptid=971102]
多線(xiàn)程條件下,signal的handler有可能在一個(gè)單獨的線(xiàn)程中執行,如果這樣那么malloc用鎖保護就夠了。
--------------------------------------------------------------------------------
ypxing 回復于:2007-08-09 16:22:20
試圖測試malloc不可重入性的代碼如下:
main.c
/*這是主程序,用來(lái)調用malloc*/
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
void setUnblock()
{
sigset_t sigset;
sigemptyset(&sigset);
sigprocmask(SIG_SETMASK, &sigset, NULL);
}
void usr1Handler (int signal_number) /*處理SIGUSR1信號*/
{
setUnblock(); /*使得SIGUSR1可以被嵌套*/
free((int*)malloc(sizeof(int)*1000));
//printf("enter handler\n");
//getchar();
}
int main ()
{
int *pi;
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
sa.sa_handler = &usr1Handler;
sigaction(SIGUSR1, &sa, NULL);
pause();
return 0;
}
kill.c
/*這個(gè)是用來(lái)發(fā)送SIGUSR1信號的*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int main(int argc,char *argv[])
{
int i;
char killstr[30]="kill -USR1 ";
if (argc == 2)
{
strcat(killstr, argv[1]);
}
for (i=0; i<3; i++)
{
fork(); /*這樣會(huì )有8個(gè)進(jìn)程同時(shí)發(fā)送*/
}
while(1)
{
system(killstr);
}
return 0;
}
驗證方法是:
1. 編譯main.c 和kill.c
gcc main.c -o main
gcc kill.c -o kill
2. 運行./main
并在另外一個(gè)終端運行ps -A|grep main查找出該進(jìn)程的進(jìn)程號為pid
3. 運行./kill pid (此處pid為第二步查到的pid)
運行了很長(cháng)時(shí)間,也沒(méi)有crash
請大家看看我的程序,討論一個(gè)測試方案出來(lái)
引用:原帖由 ypxing 于 2007-8-8 23:05 發(fā)表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=7176529&ptid=971102]
這兩天寫(xiě)了一個(gè)測試程序來(lái)驗證malloc的不可重入性
但是malloc一直沒(méi)有crash,有點(diǎn)郁悶
過(guò)段時(shí)間把自己的測試代碼貼出來(lái),讓大家來(lái)幫忙看看
--------------------------------------------------------------------------------
mingyanguo 回復于:2007-08-09 17:36:23
引用:原帖由 ypxing 于 2007-8-9 16:22 發(fā)表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=7180404&ptid=971102]
試圖測試malloc不可重入性的代碼如下:
main.c
/*這是主程序,用來(lái)調用malloc*/
#include
#include
#include
#include
#include
#include
void setUnblock()
{
sigset_t sigset;
s ...
我估計是因為現在的malloc是線(xiàn)程安全的原因所以不會(huì )crash但是死鎖。
我在debian上面的一個(gè)測試代碼,會(huì )死鎖,top一下會(huì )發(fā)現進(jìn)程狀態(tài)總是sleep
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#if 0
#define PRINT(a) do { \
printf a; \
fflush(stdout); \
}while(0)
#else
#define PRINT(a)
#endif
static void
run_malloc(void)
{
void *mem[8];
int sz;
int i;
for (i = 0; i < (sizeof(mem)/sizeof(mem[0])); i++) {
sz = random() % (1024 * 1024);
if (sz <= 0)
sz = 1024;
mem = malloc(sz);
if (mem == NULL) {
PRINT (("[%d] malloc null...\n", i));
exit(-1);
}
PRINT(("%d\n", i));
snprintf(mem, sz, "this is a test...");
}
for (--i; i >= 0; i--) {
free(mem);
}
}
static void
sighandler(int signo)
{
static void *mem = NULL;
PRINT ((".\n"));
if (mem == NULL) {
mem = malloc(1024);
} else {
free(mem);
mem = NULL;
}
}
static void
malloc_loop(void)
{
for (;;)
run_malloc();
}
static void
signal_loop(pid_t child)
{
int usec;
for (;;) {
kill(child, SIGUSR1);
usec = ((unsigned int)random()) % 10;
usleep(usec);
}
}
int
main(int argc, char **argv)
{
pid_t child;
if ((child = fork()) < 0) {
perror("fork()");
exit(-1);
} else if (child == 0) {
/* child */
if (signal(SIGUSR1, sighandler) < 0) {
perror("signal");
exit(-1);
}
malloc_loop();
} else {
/* parent */
signal_loop(child);
}
return 0;
}
--------------------------------------------------------------------------------
haohao06 回復于:2007-08-10 11:45:06
謝謝樓主講解.收藏先
--------------------------------------------------------------------------------
system888net 回復于:2008-02-23 12:12:14
頂...
--------------------------------------------------------------------------------
dxcnjupt 回復于:2008-02-23 19:46:50
不知道這個(gè)理解對不對:
thread-safe和reentrant的區別:在發(fā)生中斷時(shí),高優(yōu)先級代碼搶占,此時(shí)若低優(yōu)先級代碼持有鎖,則高優(yōu)先級代碼會(huì )一直等待鎖打開(kāi),但是低優(yōu)先級代碼失去了調度機會(huì ),于是造成死鎖。thread-safe不考慮這種情況,但是reentrant需要。
實(shí)現reentrant的幾種方法:
1不使用臨界區,把原先的全局/靜態(tài)變量變成函數參數,由函數調用者維護。優(yōu)點(diǎn)是實(shí)現簡(jiǎn)單,缺點(diǎn)是函數功能的封裝性可能會(huì )受到影響。
2在進(jìn)入臨界區之前,關(guān)中斷(屏蔽信號)。優(yōu)點(diǎn)是實(shí)現簡(jiǎn)單,缺點(diǎn)是影響實(shí)時(shí)性能,在多核機器上可能引起瓶頸(幾個(gè)核等待一個(gè)核釋放信號量)。
3嘗試加鎖,無(wú)法加鎖返回一個(gè)出錯值,而不是一直等待下去。缺點(diǎn)是出錯處理比較麻煩
4為一組臨界量開(kāi)啟一個(gè)專(zhuān)門(mén)的線(xiàn)程進(jìn)行處理。優(yōu)點(diǎn)是可以對臨界區的訪(fǎng)問(wèn)按優(yōu)先級排序,以及其它可擴展操作,缺點(diǎn)是性能受到IPC的影響。
5使用lock-free結構取代鎖。缺點(diǎn)是lock-free算法很多都需要memory-copy,影響效率。