• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            posts - 15, comments - 9, trackbacks - 0, articles - 0
            原文地址:http://www.ibm.com/developerworks/cn/linux/thread/posix_thread2/

            POSIX 線(xiàn)程是提高代碼響應(yīng)和性能的有力手段。在此三部分系列文章的第二篇中,Daniel Robbins 將說(shuō)明,如何使用被稱(chēng)為互斥對(duì)象的靈巧小玩意,來(lái)保護(hù)線(xiàn)程代碼中共享數(shù)據(jù)結(jié)構(gòu)的完整性。

            互斥我吧!

            前一篇文章中 ,談到了會(huì)導(dǎo)致異常結(jié)果的線(xiàn)程代碼。兩個(gè)線(xiàn)程分別對(duì)同一個(gè)全局變量進(jìn)行了二十次加一。變量的值最后應(yīng)該是 40,但最終值卻是 21。這是怎么回事呢?因?yàn)橐粋€(gè)線(xiàn)程不停地“取消”了另一個(gè)線(xiàn)程執(zhí)行的加一操作,所以產(chǎn)生這個(gè)問(wèn)題。現(xiàn)在讓我們來(lái)查看改正后的代碼,它使用 互斥對(duì)象(mutex)來(lái)解決該問(wèn)題:


            thread3.c

            #include <pthread.h>
                        #include <stdlib.h>
                        #include <unistd.h>
                        #include <stdio.h>
                        int myglobal;
                        pthread_mutex_t mymutex=PTHREAD_MUTEX_INITIALIZER;
                        void *thread_function(void *arg) {
                        int i,j;
                        for ( i=0; i<20; i++) {
                        pthread_mutex_lock(&mymutex);
                        j=myglobal;
                        j=j+1;
                        printf(".");
                        fflush(stdout);
                        sleep(1);
                        myglobal=j;
                        pthread_mutex_unlock(&mymutex);
                        }
                        return NULL;
                        }
                        int main(void) {
                        pthread_t mythread;
                        int i;
                        if ( pthread_create( &mythread, NULL, thread_function, NULL) ) {
                        printf("error creating thread.");
                        abort();
                        }
                        for ( i=0; i<20; i++) {
                        pthread_mutex_lock(&mymutex);
                        myglobal=myglobal+1;
                        pthread_mutex_unlock(&mymutex);
                        printf("o");
                        fflush(stdout);
                        sleep(1);
                        }
                        if ( pthread_join ( mythread, NULL ) ) {
                        printf("error joining thread.");
                        abort();
                        }
                        printf("\nmyglobal equals %d\n",myglobal);
                        exit(0);
                        }
                        

            解讀一下

            如果將這段代碼與 前一篇文章 中給出的版本作一個(gè)比較,就會(huì)注意到增加了 pthread_mutex_lock() 和 pthread_mutex_unlock() 函數(shù)調(diào)用。在線(xiàn)程程序中這些調(diào)用執(zhí)行了不可或缺的功能。他們提供了一種 相互排斥的方法(互斥對(duì)象即由此得名)。兩個(gè)線(xiàn)程不能同時(shí)對(duì)同一個(gè)互斥對(duì)象加鎖。

            互斥對(duì)象是這樣工作的。如果線(xiàn)程 a 試圖鎖定一個(gè)互斥對(duì)象,而此時(shí)線(xiàn)程 b 已鎖定了同一個(gè)互斥對(duì)象時(shí),線(xiàn)程 a 就將進(jìn)入睡眠狀態(tài)。一旦線(xiàn)程 b 釋放了互斥對(duì)象(通過(guò) pthread_mutex_unlock() 調(diào)用),線(xiàn)程 a 就能夠鎖定這個(gè)互斥對(duì)象(換句話(huà)說(shuō),線(xiàn)程 a 就將從 pthread_mutex_lock() 函數(shù)調(diào)用中返回,同時(shí)互斥對(duì)象被鎖定)。同樣地,當(dāng)線(xiàn)程 a 正鎖定互斥對(duì)象時(shí),如果線(xiàn)程 c 試圖鎖定互斥對(duì)象的話(huà),線(xiàn)程 c 也將臨時(shí)進(jìn)入睡眠狀態(tài)。對(duì)已鎖定的互斥對(duì)象上調(diào)用 pthread_mutex_lock() 的所有線(xiàn)程都將進(jìn)入睡眠狀態(tài),這些睡眠的線(xiàn)程將“排隊(duì)”訪(fǎng)問(wèn)這個(gè)互斥對(duì)象。

            通常使用 pthread_mutex_lock() 和 pthread_mutex_unlock() 來(lái)保護(hù)數(shù)據(jù)結(jié)構(gòu)。這就是說(shuō),通過(guò)線(xiàn)程的鎖定和解鎖,對(duì)于某一數(shù)據(jù)結(jié)構(gòu),確保某一時(shí)刻只能有一個(gè)線(xiàn)程能夠訪(fǎng)問(wèn)它。可以推測(cè)到,當(dāng)線(xiàn)程試圖鎖定一個(gè)未加鎖的互斥對(duì)象時(shí),POSIX 線(xiàn)程庫(kù)將同意鎖定,而不會(huì)使線(xiàn)程進(jìn)入睡眠狀態(tài)。


            請(qǐng)看這幅輕松的漫畫(huà),四個(gè)小精靈重現(xiàn)了最近一次 pthread_mutex_lock() 調(diào)用的一個(gè)場(chǎng)面。

            圖中,鎖定了互斥對(duì)象的線(xiàn)程能夠存取復(fù)雜的數(shù)據(jù)結(jié)構(gòu),而不必?fù)?dān)心同時(shí)會(huì)有其它線(xiàn)程干擾。那個(gè)數(shù)據(jù)結(jié)構(gòu)實(shí)際上是“凍結(jié)”了,直到互斥對(duì)象被解鎖為止。pthread_mutex_lock() 和 pthread_mutex_unlock() 函數(shù)調(diào)用,如同“在施工中”標(biāo)志一樣,將正在修改和讀取的某一特定共享數(shù)據(jù)包圍起來(lái)。這兩個(gè)函數(shù)調(diào)用的作用就是警告其它線(xiàn)程,要它們繼續(xù)睡眠并等待輪到它們對(duì)互斥對(duì)象加鎖。當(dāng)然,除非在 每個(gè) 對(duì)特定數(shù)據(jù)結(jié)構(gòu)進(jìn)行讀寫(xiě)操作的語(yǔ)句前后,都分別放上 pthread_mutex_lock() 和 pthread_mutext_unlock() 調(diào)用,才會(huì)出現(xiàn)這種情況。

            為什么要用互斥對(duì)象?

            聽(tīng)上去很有趣,但究竟為什么要讓線(xiàn)程睡眠呢?要知道,線(xiàn)程的主要優(yōu)點(diǎn)不就是其具有獨(dú)立工作、更多的時(shí)候是同時(shí)工作的能力嗎?是的,確實(shí)是這樣。然而,每個(gè)重要的線(xiàn)程程序都需要使用某些互斥對(duì)象。讓我們?cè)倏匆幌率纠绦蛞员憷斫庠蛩凇?/p>

            請(qǐng)看 thread_function(),循環(huán)中一開(kāi)始就鎖定了互斥對(duì)象,最后才將它解鎖。在這個(gè)示例程序中,mymutex 用來(lái)保護(hù) myglobal 的值。仔細(xì)查看 thread_function(),加一代碼把 myglobal 復(fù)制到一個(gè)局部變量,對(duì)局部變量加一,睡眠一秒鐘,在這之后才把局部變量的值傳回給 myglobal。不使用互斥對(duì)象時(shí),即使主線(xiàn)程在 thread_function() 線(xiàn)程睡眠一秒鐘期間內(nèi)對(duì) myglobal 加一,thread_function() 蘇醒后也會(huì)覆蓋主線(xiàn)程所加的值。使用互斥對(duì)象能夠保證這種情形不會(huì)發(fā)生。(您也許會(huì)想到,我增加了一秒鐘延遲以觸發(fā)不正確的結(jié)果。把局部變量的值賦給 myglobal 之前,實(shí)際上沒(méi)有什么真正理由要求 thread_function() 睡眠一秒鐘。)使用互斥對(duì)象的新程序產(chǎn)生了期望的結(jié)果:

            $ ./thread3
                        o..o..o.o..o..o.o.o.o.o..o..o..o.ooooooo
                        myglobal equals 40
                        

            為了進(jìn)一步探索這個(gè)極為重要的概念,讓我們看一看程序中進(jìn)行加一操作的代碼:

            thread_function() 加一代碼:
                        j=myglobal;
                        j=j+1;
                        printf(".");
                        fflush(stdout);
                        sleep(1);
                        myglobal=j;
                        主線(xiàn)程加一代碼:
                        myglobal=myglobal+1;
                        

            如果代碼是位于單線(xiàn)程程序中,可以預(yù)期 thread_function() 代碼將完整執(zhí)行。接下來(lái)才會(huì)執(zhí)行主線(xiàn)程代碼(或者是以相反的順序執(zhí)行)。在不使用互斥對(duì)象的線(xiàn)程程序中,代碼可能(幾乎是,由于調(diào)用了 sleep() 的緣故)以如下的順序執(zhí)行:

                thread_function() 線(xiàn)程        主線(xiàn)程
                        j=myglobal;
                        j=j+1;
                        printf(".");
                        fflush(stdout);
                        sleep(1);                     myglobal=myglobal+1;
                        myglobal=j;
                        

            當(dāng)代碼以此特定順序執(zhí)行時(shí),將覆蓋主線(xiàn)程對(duì) myglobal 的修改。程序結(jié)束后,就將得到不正確的值。如果是在操縱指針的話(huà),就可能產(chǎn)生段錯(cuò)誤。注意到 thread_function() 線(xiàn)程按順序執(zhí)行了它的所有指令。看來(lái)不象是 thread_function() 有什么次序顛倒。問(wèn)題是,同一時(shí)間內(nèi),另一個(gè)線(xiàn)程對(duì)同一數(shù)據(jù)結(jié)構(gòu)進(jìn)行了另一個(gè)修改。


            線(xiàn)程內(nèi)幕 1

            在解釋如何確定在何處使用互斥對(duì)象之前,先來(lái)深入了解一下線(xiàn)程的內(nèi)部工作機(jī)制。請(qǐng)看第一個(gè)例子:

            假設(shè)主線(xiàn)程將創(chuàng)建三個(gè)新線(xiàn)程:線(xiàn)程 a、線(xiàn)程 b 和線(xiàn)程 c。假定首先創(chuàng)建線(xiàn)程 a,然后是線(xiàn)程 b,最后創(chuàng)建線(xiàn)程 c。

                pthread_create( &thread_a, NULL, thread_function, NULL);
                        pthread_create( &thread_b, NULL, thread_function, NULL);
                        pthread_create( &thread_c, NULL, thread_function, NULL);
                        

            在第一個(gè) pthread_create() 調(diào)用完成后,可以假定線(xiàn)程 a 不是已存在就是已結(jié)束并停止。第二個(gè) pthread_create() 調(diào)用后,主線(xiàn)程和線(xiàn)程 b 都可以假定線(xiàn)程 a 存在(或已停止)。

            然而,就在第二個(gè) create() 調(diào)用返回后,主線(xiàn)程無(wú)法假定是哪一個(gè)線(xiàn)程(a 或 b)會(huì)首先開(kāi)始運(yùn)行。雖然兩個(gè)線(xiàn)程都已存在,線(xiàn)程 CPU 時(shí)間片的分配取決于內(nèi)核和線(xiàn)程庫(kù)。至于誰(shuí)將首先運(yùn)行,并沒(méi)有嚴(yán)格的規(guī)則。盡管線(xiàn)程 a 更有可能在線(xiàn)程 b 之前開(kāi)始執(zhí)行,但這并無(wú)保證。對(duì)于多處理器系統(tǒng),情況更是如此。如果編寫(xiě)的代碼假定在線(xiàn)程 b 開(kāi)始執(zhí)行之前實(shí)際上執(zhí)行線(xiàn)程 a 的代碼,那么,程序最終正確運(yùn)行的概率是 99%。或者更糟糕,程序在您的機(jī)器上 100% 地正確運(yùn)行,而在您客戶(hù)的四處理器服務(wù)器上正確運(yùn)行的概率卻是零。

            從這個(gè)例子還可以得知,線(xiàn)程庫(kù)保留了每個(gè)單獨(dú)線(xiàn)程的代碼執(zhí)行順序。換句話(huà)說(shuō),實(shí)際上那三個(gè) pthread_create() 調(diào)用將按它們出現(xiàn)的順序執(zhí)行。從主線(xiàn)程上來(lái)看,所有代碼都是依次執(zhí)行的。有時(shí),可以利用這一點(diǎn)來(lái)優(yōu)化部分線(xiàn)程程序。例如,在上例中,線(xiàn)程 c 就可以假定線(xiàn)程 a 和線(xiàn)程 b 不是正在運(yùn)行就是已經(jīng)終止。它不必?fù)?dān)心存在還沒(méi)有創(chuàng)建線(xiàn)程 a 和線(xiàn)程 b 的可能性。可以使用這一邏輯來(lái)優(yōu)化線(xiàn)程程序。


            線(xiàn)程內(nèi)幕 2

            現(xiàn)在來(lái)看另一個(gè)假想的例子。假設(shè)有許多線(xiàn)程,他們都正在執(zhí)行下列代碼:

                myglobal=myglobal+1;
                        

            那么,是否需要在加一操作語(yǔ)句前后分別鎖定和解鎖互斥對(duì)象呢?也許有人會(huì)說(shuō)“不”。編譯器極有可能把上述賦值語(yǔ)句編譯成一條機(jī)器指令。大家都知道,不可能"半途"中斷一條機(jī)器指令。即使是硬件中斷也不會(huì)破壞機(jī)器指令的完整性。基于以上考慮,很可能傾向于完全省略 pthread_mutex_lock() 和 pthread_mutex_unlock() 調(diào)用。不要這樣做。

            我在說(shuō)廢話(huà)嗎?不完全是這樣。首先,不應(yīng)該假定上述賦值語(yǔ)句一定會(huì)被編譯成一條機(jī)器指令,除非親自驗(yàn)證了機(jī)器代碼。即使插入某些內(nèi)嵌匯編語(yǔ)句以確保加一操作的完整執(zhí)行――甚至,即使是自己動(dòng)手寫(xiě)編譯器!-- 仍然可能有問(wèn)題。

            答案在這里。使用單條內(nèi)嵌匯編操作碼在單處理器系統(tǒng)上可能不會(huì)有什么問(wèn)題。每個(gè)加一操作都將完整地進(jìn)行,并且多半會(huì)得到期望的結(jié)果。但是多處理器系統(tǒng)則截然不同。在多 CPU 機(jī)器上,兩個(gè)單獨(dú)的處理器可能會(huì)在幾乎同一時(shí)刻(或者,就在同一時(shí)刻)執(zhí)行上述賦值語(yǔ)句。不要忘了,這時(shí)對(duì)內(nèi)存的修改需要先從 L1 寫(xiě)入 L2 高速緩存、然后才寫(xiě)入主存。(SMP 機(jī)器并不只是增加了處理器而已;它還有用來(lái)仲裁對(duì) RAM 存取的特殊硬件。)最終,根本無(wú)法搞清在寫(xiě)入主存的競(jìng)爭(zhēng)中,哪個(gè) CPU 將會(huì)"勝出"。要產(chǎn)生可預(yù)測(cè)的代碼,應(yīng)使用互斥對(duì)象。互斥對(duì)象將插入一道"內(nèi)存關(guān)卡",由它來(lái)確保對(duì)主存的寫(xiě)入按照線(xiàn)程鎖定互斥對(duì)象的順序進(jìn)行。

            考慮一種以 32 位塊為單位更新主存的 SMP 體系結(jié)構(gòu)。如果未使用互斥對(duì)象就對(duì)一個(gè) 64 位整數(shù)進(jìn)行加一操作,整數(shù)的最高 4 位字節(jié)可能來(lái)自一個(gè) CPU,而其它 4 個(gè)字節(jié)卻來(lái)自另一 CPU。糟糕吧!最糟糕的是,使用差勁的技術(shù),您的程序在重要客戶(hù)的系統(tǒng)上有可能不是很長(zhǎng)時(shí)間才崩潰一次,就是早上三點(diǎn)鐘就崩潰。David R. Butenhof 在他的《POSIX 線(xiàn)程編程》(請(qǐng)參閱本文末尾的 參考資料部分)一書(shū)中,討論了由于未使用互斥對(duì)象而將產(chǎn)生的種種情況。


            許多互斥對(duì)象

            如果放置了過(guò)多的互斥對(duì)象,代碼就沒(méi)有什么并發(fā)性可言,運(yùn)行起來(lái)也比單線(xiàn)程解決方案慢。如果放置了過(guò)少的互斥對(duì)象,代碼將出現(xiàn)奇怪和令人尷尬的錯(cuò)誤。幸運(yùn)的是,有一個(gè)中間立場(chǎng)。首先,互斥對(duì)象是用于串行化存取*共享數(shù)據(jù)*。不要對(duì)非共享數(shù)據(jù)使用互斥對(duì)象,并且,如果程序邏輯確保任何時(shí)候都只有一個(gè)線(xiàn)程能存取特定數(shù)據(jù)結(jié)構(gòu),那么也不要使用互斥對(duì)象。

            其次,如果要使用共享數(shù)據(jù),那么在讀、寫(xiě)共享數(shù)據(jù)時(shí)都應(yīng)使用互斥對(duì)象。用 pthread_mutex_lock() 和 pthread_mutex_unlock() 把讀寫(xiě)部分保護(hù)起來(lái),或者在程序中不固定的地方隨機(jī)使用它們。學(xué)會(huì)從一個(gè)線(xiàn)程的角度來(lái)審視代碼,并確保程序中每一個(gè)線(xiàn)程對(duì)內(nèi)存的觀(guān)點(diǎn)都是一致和合適的。為了熟悉互斥對(duì)象的用法,最初可能要花好幾個(gè)小時(shí)來(lái)編寫(xiě)代碼,但是很快就會(huì)習(xí)慣并且*也*不必多想就能夠正確使用它們。


            使用調(diào)用:初始化

            現(xiàn)在該來(lái)看看使用互斥對(duì)象的各種不同方法了。讓我們從初始化開(kāi)始。在 thread3.c 示例 中,我們使用了靜態(tài)初始化方法。這需要聲明一個(gè) pthread_mutex_t 變量,并賦給它常數(shù) PTHREAD_MUTEX_INITIALIZER:

            pthread_mutex_t mymutex=PTHREAD_MUTEX_INITIALIZER;
                        

            很簡(jiǎn)單吧。但是還可以動(dòng)態(tài)地創(chuàng)建互斥對(duì)象。當(dāng)代碼使用 malloc() 分配一個(gè)新的互斥對(duì)象時(shí),使用這種動(dòng)態(tài)方法。此時(shí),靜態(tài)初始化方法是行不通的,并且應(yīng)當(dāng)使用例程 pthread_mutex_init():

            int pthread_mutex_init( pthread_mutex_t *mymutex, const pthread_mutexattr_t *attr)
                        

            正如所示,pthread_mutex_init 接受一個(gè)指針作為參數(shù)以初始化為互斥對(duì)象,該指針指向一塊已分配好的內(nèi)存區(qū)。第二個(gè)參數(shù),可以接受一個(gè)可選的 pthread_mutexattr_t 指針。這個(gè)結(jié)構(gòu)可用來(lái)設(shè)置各種互斥對(duì)象屬性。但是通常并不需要這些屬性,所以正常做法是指定 NULL。

            一旦使用 pthread_mutex_init() 初始化了互斥對(duì)象,就應(yīng)使用 pthread_mutex_destroy() 消除它。pthread_mutex_destroy() 接受一個(gè)指向 pthread_mutext_t 的指針作為參數(shù),并釋放創(chuàng)建互斥對(duì)象時(shí)分配給它的任何資源。請(qǐng)注意, pthread_mutex_destroy() 不會(huì) 釋放用來(lái)存儲(chǔ) pthread_mutex_t 的內(nèi)存。釋放自己的內(nèi)存完全取決于您。還必須注意一點(diǎn),pthread_mutex_init() 和 pthread_mutex_destroy() 成功時(shí)都返回零。


            使用調(diào)用:鎖定

            pthread_mutex_lock(pthread_mutex_t *mutex)
                        

            pthread_mutex_lock() 接受一個(gè)指向互斥對(duì)象的指針作為參數(shù)以將其鎖定。如果碰巧已經(jīng)鎖定了互斥對(duì)象,調(diào)用者將進(jìn)入睡眠狀態(tài)。函數(shù)返回時(shí),將喚醒調(diào)用者(顯然)并且調(diào)用者還將保留該鎖。函數(shù)調(diào)用成功時(shí)返回零,失敗時(shí)返回非零的錯(cuò)誤代碼。

            pthread_mutex_unlock(pthread_mutex_t *mutex)
                        

            pthread_mutex_unlock() 與 pthread_mutex_lock() 相配合,它把線(xiàn)程已經(jīng)加鎖的互斥對(duì)象解鎖。始終應(yīng)該盡快對(duì)已加鎖的互斥對(duì)象進(jìn)行解鎖(以提高性能)。并且絕對(duì)不要對(duì)您未保持鎖的互斥對(duì)象進(jìn)行解鎖操作(否則,pthread_mutex_unlock() 調(diào)用將失敗并帶一個(gè)非零的 EPERM 返回值)。

            pthread_mutex_trylock(pthread_mutex_t *mutex)
                        

            當(dāng)線(xiàn)程正在做其它事情的時(shí)候(由于互斥對(duì)象當(dāng)前是鎖定的),如果希望鎖定互斥對(duì)象,這個(gè)調(diào)用就相當(dāng)方便。調(diào)用 pthread_mutex_trylock() 時(shí)將嘗試鎖定互斥對(duì)象。如果互斥對(duì)象當(dāng)前處于解鎖狀態(tài),那么您將獲得該鎖并且函數(shù)將返回零。然而,如果互斥對(duì)象已鎖定,這個(gè)調(diào)用也不會(huì)阻塞。當(dāng)然,它會(huì)返回非零的 EBUSY 錯(cuò)誤值。然后可以繼續(xù)做其它事情,稍后再?lài)L試鎖定。


            等待條件發(fā)生

            互斥對(duì)象是線(xiàn)程程序必需的工具,但它們并非萬(wàn)能的。例如,如果線(xiàn)程正在等待共享數(shù)據(jù)內(nèi)某個(gè)條件出現(xiàn),那會(huì)發(fā)生什么呢?代碼可以反復(fù)對(duì)互斥對(duì)象鎖定和解鎖,以檢查值的任何變化。同時(shí),還要快速將互斥對(duì)象解鎖,以便其它線(xiàn)程能夠進(jìn)行任何必需的更改。這是一種非常可怕的方法,因?yàn)榫€(xiàn)程需要在合理的時(shí)間范圍內(nèi)頻繁地循環(huán)檢測(cè)變化。

            在每次檢查之間,可以讓調(diào)用線(xiàn)程短暫地進(jìn)入睡眠,比如睡眠三秒鐘,但是因此線(xiàn)程代碼就無(wú)法最快作出響應(yīng)。真正需要的是這樣一種方法,當(dāng)線(xiàn)程在等待滿(mǎn)足某些條件時(shí)使線(xiàn)程進(jìn)入睡眠狀態(tài)。一旦條件滿(mǎn)足,還需要一種方法以喚醒因等待滿(mǎn)足特定條件而睡眠的線(xiàn)程。如果能夠做到這一點(diǎn),線(xiàn)程代碼將是非常高效的,并且不會(huì)占用寶貴的互斥對(duì)象鎖。這正是 POSIX 條件變量能做的事!

            而 POSIX 條件變量將是我下一篇文章的主題,其中將說(shuō)明如何正確使用條件變量。到那時(shí),您將擁有了創(chuàng)建復(fù)雜線(xiàn)程程序所需的全部資源,那些線(xiàn)程程序可以模擬工作人員、裝配線(xiàn)等等。既然您已經(jīng)越來(lái)越熟悉線(xiàn)程,我將在下一篇文章中加快進(jìn)度。這樣,在下一篇文章的結(jié)尾就能放上一個(gè)相對(duì)復(fù)雜的線(xiàn)程程序。說(shuō)到等到條件產(chǎn)生,下次再見(jiàn)!



            參考資料



            關(guān)于作者

            Daniel Robbins 居住在新墨西哥州的 Albuquerque。他是 Gentoo Technologies, Inc. 的總裁兼 CEO, Gentoo 項(xiàng)目的總設(shè)計(jì)師,多本 MacMillan 出版書(shū)籍的作者,包括: Caldera OpenLinux UnleashedSuSE Linux UnleashedSamba Unleashed 。Daniel 自小學(xué)二年級(jí)起就與計(jì)算機(jī)結(jié)下不解之緣,那時(shí)他首先接觸的是 Logo 程序語(yǔ)言,并沉溺于 Pac-Man 游戲中。這也許就是他至今仍擔(dān)任 SONY Electronic Publishing/Psygnosis 的首席圖形設(shè)計(jì)師的原因所在。Daniel 喜歡與妻子 Mary 和剛出生的女兒 Hadassah 一起共渡時(shí)光。可通過(guò) drobbins@gentoo.org 與 Daniel 取得聯(lián)系。

            Daniel Robbins 居住在新墨西哥州的 Albuquerque。他是 Gentoo Technologies, Inc. 的總裁兼 CEO, Gentoo 項(xiàng)目的總設(shè)計(jì)師,多本 MacMillan 出版書(shū)籍的作者,包括: Caldera OpenLinux UnleashedSuSE Linux UnleashedSamba Unleashed 。Daniel 自小學(xué)二年級(jí)起就與計(jì)算機(jī)結(jié)下不解之緣,那時(shí)他首先接觸的是 Logo 程序語(yǔ)言,并沉溺于 Pac-Man 游戲中。這也許就是他至今仍擔(dān)任 SONY Electronic Publishing/Psygnosis 的首席圖形設(shè)計(jì)師的原因所在。Daniel 喜歡與妻子 Mary 和剛出生的女兒 Hadassah 一起共渡時(shí)光。可通過(guò) drobbins@gentoo.org 與 Daniel 取得聯(lián)系。







            Feedback

            # re: [轉(zhuǎn)載]POSIX 線(xiàn)程詳解-第2部分  回復(fù)  更多評(píng)論   

            2010-06-15 21:21 by FrankieRobertson22
            Sometimes, this can take a lot of time to be an experienced thesis methodology composer, but I do see that your writing connected with this post is brilliant. Hundreds of various students dream about a writing talent like you have. Because a <a href="http://www.greatdissertation.com">dissertation writing</a> service checks out the writing skillfulness opting for the thesis writers.

            # re: [轉(zhuǎn)載]POSIX 線(xiàn)程詳解-第2部分  回復(fù)  更多評(píng)論   

            2010-07-27 07:58 by humanities essay
            Different persons don’t really know the way to create their technology essay paper. Thence, from this moment they will come along your fantastic release referring to this good topic and just perform the best book reports ever.

            # re: [轉(zhuǎn)載]POSIX 線(xiàn)程詳解-第2部分  回復(fù)  更多評(píng)論   

            2010-10-07 11:51 by buy essay
            Students will learn referring to this post therefore, I offer to find the writing service and buy essay moreover, that is possible to purchase essays for sale.

            # re: [轉(zhuǎn)載]POSIX 線(xiàn)程詳解-第2部分  回復(fù)  更多評(píng)論   

            2010-10-07 12:31 by essay writer
            The piece of your writing seems to be incredible. People, which order the papers for money at buy essay papers service have to get know about your good enough article. With this information that will be easy to reach a success.

            # re: [轉(zhuǎn)載]POSIX 線(xiàn)程詳解-第2部分  回復(fù)  更多評(píng)論   

            2013-04-09 13:39 by CustomEssays rewiew
            Don’t have the faintest idea which writing centre to choose to get help from? Look through BestEssays testimonials essaysreview.com, and arrive at a sound decision.
            狠狠色综合网站久久久久久久| 色综合久久久久网| 中文字幕无码久久人妻| 久久久久久国产a免费观看黄色大片| 狠狠色伊人久久精品综合网| 一本一本久久a久久精品综合麻豆| 久久亚洲国产精品成人AV秋霞 | 99re这里只有精品热久久| 久久综合九色综合97_久久久| 欧美伊人久久大香线蕉综合69 | 久久99热精品| 久久最新免费视频| 久久91精品国产91久久小草| 久久久精品波多野结衣| 欧美牲交A欧牲交aⅴ久久| 国内精品伊人久久久久网站| 一本久久a久久精品亚洲| 精品国产综合区久久久久久| 亚洲AV日韩AV永久无码久久| 久久久久久av无码免费看大片| 久久久久久久亚洲Av无码| 亚洲天堂久久久| 久久婷婷五月综合97色直播| 久久久久一区二区三区| 蜜臀av性久久久久蜜臀aⅴ| 国产精品中文久久久久久久| 国产精品美女久久久网AV| 狠狠狠色丁香婷婷综合久久五月| 亚洲国产成人精品久久久国产成人一区二区三区综 | 久久青青草原精品国产不卡| 久久亚洲综合色一区二区三区| 亚洲国产精品无码久久一线| 久久久中文字幕日本| 国产精品午夜久久| 国产精品成人精品久久久| 国产农村妇女毛片精品久久| 丁香久久婷婷国产午夜视频| 久久综合狠狠色综合伊人| 国产成人精品久久亚洲高清不卡 | 久久精品视频免费| 精品久久久久久国产牛牛app|