• <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 - 34,comments - 2,trackbacks - 0
                 摘要: 為什么要使用線程池:
            創(chuàng)建多線程應(yīng)用程序是非常困難的。需要會面臨兩個大問題。
            一個是要對線程的創(chuàng)建和撤消進行管理,另一個是要對線程對資源的訪問實施同步 。 
              閱讀全文
            posted @ 2011-10-10 09:25 Yu_ 閱讀(845) | 評論 (0)編輯 收藏
                 摘要: 若干種內(nèi)核對象,包括進程,線程和作業(yè)。可以將所有這些內(nèi)核對象用于同步目的。對于線程同步來說,這些內(nèi)核對象中的每種對象都可以說是處于已通知或未通知的狀態(tài)之中。

            例如::當(dāng)進程正在運行的時候,進程內(nèi)核對象處于未通知狀態(tài),當(dāng)進程終止運行的時候,它就變?yōu)橐淹ㄖ獱顟B(tài)。進程內(nèi)核對象中是個布爾值,當(dāng)對象創(chuàng)建時,該值被初始化為FALSE(未通知狀態(tài))。當(dāng)進程終止運行時,操作系統(tǒng)自動將對應(yīng)的對象布爾值改為TRUE,表示該對象已經(jīng)得到通知。當(dāng)線程終止運行時,操作系統(tǒng)會自動將線程對象的狀態(tài)改為已通知狀態(tài)。因此,可以將相同的方法用于應(yīng)用程序,以確定線程是否不再運行。
              閱讀全文
            posted @ 2011-10-08 00:10 Yu_ 閱讀(401) | 評論 (0)編輯 收藏
                 摘要: 線程需要在下面兩種情況下互相進行通信:
            ? 當(dāng)有多個線程訪問共享資源而不使資源被破壞時。
            ? 當(dāng)一個線程需要將某個任務(wù)已經(jīng)完成的情況通知另外一個或多個線程時。
              閱讀全文
            posted @ 2011-10-07 23:58 Yu_ 閱讀(417) | 評論 (0)編輯 收藏
                 摘要: 1、線程的組成
            (1)、一個是線程的內(nèi)核對象,操作系統(tǒng)用它管理線程。內(nèi)核對象還是系統(tǒng)用來存放線程統(tǒng)計信息的地方。
            (2)、一個線程堆棧,用于維護線程執(zhí)行時所需的所有函數(shù)參數(shù)和局部變量。
              閱讀全文
            posted @ 2011-10-07 23:10 Yu_ 閱讀(257) | 評論 (0)編輯 收藏
            討論三個問題:
            1、進程間如何通信呢,如何來相互傳遞信息呢?
            (1)、低級通信:只能傳遞狀態(tài)和整數(shù)值(控制信息)
            信號量(semaphore
            信號(signal
            (2)、高級通信:能夠傳送任意數(shù)量的數(shù)據(jù)
            共享內(nèi)存(shared memory
            消息傳遞(message passing
            管道(pipe
            剪貼板:

            基本機制是:系統(tǒng)預(yù)留的一塊全局共享內(nèi)存,可用于被各進程暫時存儲數(shù)據(jù)。寫入進程首先創(chuàng)建一個全局內(nèi)存塊,并將數(shù)據(jù)寫到該內(nèi)存塊;接受數(shù)據(jù)的進程通過剪貼板機制獲取此內(nèi)存塊的句柄,并完成對該內(nèi)存塊數(shù)據(jù)的讀取。

            管道包括三種:
            管道(Pipe)實際是用于進程間通信的一段共享內(nèi)存,創(chuàng)建管道的進程稱為管道服務(wù)器,連接到一個管道的進程為管道客戶機。一個進程在向管道寫入數(shù)據(jù)后,另一進程就可以從管道的另一端將其讀取出來。匿名管道(Anonymous Pipes)是在父進程和子進程間單向傳輸數(shù)據(jù)的一種未命名的管道,只能在本地計算機中使用,而不可用于網(wǎng)絡(luò)間的通信。
                  1)普通管道PIPE, 通常有種限制,一是半雙工,只能單向傳輸;  二是只能在父子或者兄弟進程間使用
                  2)流管道s_pipe: 去除了第一種限制,可以雙向傳輸
                  3)管道:name_pipe, 去除了第二種限制,可以在許多并不相關(guān)的進程之間進行通訊.

            郵件槽:
              郵件槽(Mailslots)提供進程間單向通信能力,任何進程都能建立郵件槽成為郵件槽服務(wù)器。其它進程,稱為郵件槽客戶,可以通過郵件槽的名字給郵件槽服務(wù)器進程發(fā)送消息。進來的消息一直放在郵件槽中,直到服務(wù)器進程讀取它為止。一個進程既可以是郵件槽服務(wù)器也可以是郵件槽客戶,因此可建立多個郵件槽實現(xiàn)進程間的雙向通信。
              通過郵件槽可以給本地計算機上的郵件槽、其它計算機上的郵件槽或指定網(wǎng)絡(luò)區(qū)域中所有計算機上有同樣名字的郵件槽發(fā)送消息。廣播通信的消息長度不能超過400字節(jié),非廣播消息的長度則受郵件槽服務(wù)器指定的最大消息長度的限制。
              郵件槽與命名管道相似,不過它傳輸數(shù)據(jù)是通過不可靠的數(shù)據(jù)報(如TCP/IP協(xié)議中的UDP包)完成的,一旦網(wǎng)絡(luò)發(fā)生錯誤則無法保證消息正確地接收,而命名管道傳輸數(shù)據(jù)則是建立在可靠連接基礎(chǔ)上的。不過郵件槽有簡化的編程接口和給指定網(wǎng)絡(luò)區(qū)域內(nèi)的所有計算機廣播消息的能力,所以郵件槽不失為應(yīng)用程序發(fā)送和接收消息的另一種選擇。

            優(yōu)缺點:
            郵槽最大的一個缺點便是只允許從客戶機到服務(wù)器,建立一種不可靠的單向數(shù)據(jù)通信。
            而另一方面,郵槽最大的一個優(yōu)點在于,它們使客戶機應(yīng)用能夠非常容易地將廣播消息發(fā)送給一個或多個服務(wù)器應(yīng)用。

            共享內(nèi)存:

            存在于內(nèi)核級別的一種資源,共享內(nèi)存指在多處理器的計算機系統(tǒng)中,可以被不同中央處理器(CPU)訪問的大容量內(nèi)存。由于多個CPU需要快速訪問存儲器,這樣就要對存儲器進行緩存(Cache)。任何一個緩存的數(shù)據(jù)被更新后,由于其他處理器也可能要存取,共享內(nèi)存就需要立即更新,否則不同的處理器可能用到不同的數(shù)據(jù)。共享內(nèi)存 (shared memory)是 Unix下的多進程之間的通信方法 ,這種方法通常用于一個程序的多進程間通信,實際上多個程序間也可以通過共享內(nèi)存來傳遞信息。



            2、當(dāng)兩個或者多個進程訪問共享資源時,如何確保他們不會相互妨礙-----進程互斥問題。

            原因:進程宏觀上并發(fā)執(zhí)行,依靠時鐘中斷來實現(xiàn)微觀上輪流執(zhí)行。當(dāng)兩個或者多個進程對同一個共享內(nèi)存訪問,結(jié)果不能預(yù)測。在同一時刻,只允許一個進程訪問該共享數(shù)據(jù),即如果當(dāng)前已有一個進程正在使用該數(shù)據(jù),那么其他進程暫時不能訪問。這就是互斥的概念。
            實現(xiàn)互斥訪問的四個條件: 
            (1)、任何兩個進程都不能同時進入臨界區(qū);
            (2)、不能事先假定CPU的個數(shù)和運行速度;
             (3)、當(dāng)一個進程運行在它的臨界區(qū)外面時,不能妨礙其他的進程進入臨界區(qū);
            (4)、任何一個進程進入臨界區(qū)的要求應(yīng)該在有限時間內(nèi)得到滿足。

            (解決辦法)
            (1)、用標(biāo)志位加鎖。

            lock的初始值為0,當(dāng)一個進程想進入臨界區(qū)時,先查看lock的值,若為1,說明已有進程在臨界區(qū)內(nèi),只好循環(huán)等待。等它變成了0,才可進入。


            缺點是:lock也是一個共享資源,當(dāng)進程競爭lock時,可能會出現(xiàn)問題。加鎖標(biāo)志位法的缺點在于可能出現(xiàn)針對共享變量 lock 的競爭狀態(tài)。例如,當(dāng)進程 0 執(zhí)行完循環(huán)判斷語句后,被時鐘中斷打斷,從而可能使多個進程同時進入臨界區(qū)。
            是一種不安全的做法、
            (2)、強制輪流法

            基本思想:每個進程嚴(yán)格地按照輪流的順序來進入臨界區(qū)。

            優(yōu)點:保證在任何時刻最多只有一個進程在臨界區(qū)
            缺點:違反了互斥訪問四條件中的第三個條件,當(dāng)一個進程運行在它的臨界區(qū)外面時,不能妨礙其他的進程進入臨界區(qū)



            (3)、Peterson方法。

            當(dāng)一個進程想進入臨界區(qū)時,先調(diào)用enter_region函數(shù),判斷是否能安全進入,不能的話等待;當(dāng)它從臨界區(qū)退出后,需調(diào)用leave_region函數(shù),允許其它進程進入臨界區(qū)。兩個函數(shù)的參數(shù)均為進程號。



            小結(jié):

            當(dāng)一個進程想要進入它的臨界區(qū)時,首先檢查一下是否允許它進入,若允許,就直接進入了;若不允許,就在那里循環(huán)地等待,一直等到允許它進入。

            缺點:
                1)浪費CPU時間;
                2)可能導(dǎo)致預(yù)料之外的結(jié)果(如:一個低優(yōu)先級進程位于臨界區(qū)中,這時有一個高優(yōu)先級的進程也試圖進入臨界區(qū))

            3、當(dāng)進程間存在某種依存關(guān)系時,如何來調(diào)整他們運行的先后次序-----進程同步問題。
            用P,V原語操作實現(xiàn)同步(略)
            另外:上述的問題也適合線程嗎?? 

            posted @ 2011-10-07 15:44 Yu_ 閱讀(1380) | 評論 (0)編輯 收藏
            1、什么是進程?
            ::一般將進程定義成一個正在運行的程序的一個實例。進程由兩部分組成:
            ①、一個內(nèi)核對象,操作系統(tǒng)用它來管理進程。內(nèi)核對象也是系統(tǒng)保存進程統(tǒng)計信息的地方。
            ②、一個地址空間,其中包含所有執(zhí)行體(executable)或DLL模塊的代碼和數(shù)據(jù)。此外,它還包含動態(tài)內(nèi)存分配,比如線程堆棧和堆的分配。
            進程與線程的關(guān)系:
            ①、一個進程創(chuàng)建的時候,系統(tǒng)會自動創(chuàng)建它的第一個線程,這稱為主線程(primary thread)。
            ②、進程要做任何事情,都必須讓一個線程在它的上下文中運行。如果沒有線程要執(zhí)行進程地址空間包含的代碼,進程就失去了繼續(xù)存在的理由。所以,系統(tǒng)會自動銷毀進程及其地址空間。
            ③、一個進程可以有多個線程,所有線程都在進程的地址空間中“同時”執(zhí)行代碼。為此,每個線程都有它自己的一組CPU寄存器和它自己的堆棧。對于所有要運行的線程,操作系統(tǒng)會輪流為每個線程調(diào)度一些CPU時間。它會采取round-robin(輪詢或輪流)方式,為每個線程都分配時間片,從而營造出所有線程都在“并發(fā)”運行的假象。

            2、系統(tǒng)如何創(chuàng)建一個進程內(nèi)核對象來管理每個進程。
            當(dāng)一個進程被初始化時,系統(tǒng)要為它分配一個句柄表(空的,也就是用來管理進程的內(nèi)核對象)。該句柄表只用于內(nèi)核對象(而不用于用戶對象和gdi對象)。句柄表是一個數(shù)據(jù)結(jié)構(gòu)的數(shù)組,每個結(jié)構(gòu)都包含一個指向內(nèi)核對象的指針,一個 訪問屏蔽(DWORD)和一個標(biāo)志(DWORD)。
            :::當(dāng)進程中的線程調(diào)用創(chuàng)建內(nèi)核對象的函數(shù)(比如 CreatFileMapping)時,內(nèi)核就為該對象分配一個內(nèi)存塊并對它初始化。同時對進程的句柄表進行掃描,找出一個空項,填充內(nèi)核對象數(shù)據(jù)結(jié)構(gòu)的內(nèi)存地址到該頂?shù)闹羔槼蓡T,設(shè)置訪問屏蔽和標(biāo)志。
            :::
             用于創(chuàng)建內(nèi)核對象的所有函數(shù)均返回與進程相關(guān)的句柄。該句柄實際上是放入進程的句柄表中的索引 (由此可知,句柄是與進程相關(guān)的,不能由其他進程直接成功地使用)。但這只適用部分系統(tǒng),句柄的含義可能隨時變更。 應(yīng)用程序在運行時有可能泄漏內(nèi)核對象,但是當(dāng)進程終止時系統(tǒng)將能確保所有內(nèi)容均被正確地清除。這個情況也適用于所有對象,資源和內(nèi)存塊,也就是說當(dāng)進程終止運行時,系統(tǒng)將保證進程不會留 下任何對象。


            3、如何利用與一個進程關(guān)聯(lián)的內(nèi)核對象來操縱該進程。

            4、進程的各種不同的屬性(或特性),以及用于查詢和更改這些屬性的幾個函數(shù)。
            實例句柄、前一個實例句柄、進程的命令行、進程的環(huán)境變量、進程當(dāng)前所在的驅(qū)動器和目錄、還有版本問題等

            5、如何利用一些函數(shù)在系統(tǒng)中創(chuàng)建或生成額外的進程。
            我們用CreateProcess函數(shù)來創(chuàng)建一個進程,參考MSDN。當(dāng)一個線程調(diào)用CreateProcess時,系統(tǒng)會做如下工作:
            (1)、系統(tǒng)將創(chuàng)建一個進程內(nèi)核對象,其初始使用計數(shù)為1。進程內(nèi)核對象不是進程本身,而是操作系統(tǒng)用來管理這個進程的一個小型數(shù)據(jù)結(jié)構(gòu)(該內(nèi)核對象是用來管理新進程的)。
            (2)、系統(tǒng)為新進程創(chuàng)建一個虛擬地址空間,并將執(zhí)行體文件(和所有必要的DLL)的代碼及數(shù)據(jù)加載到進程的地址空間。
            (3)、系統(tǒng)為新進程的主線程創(chuàng)建一個線程內(nèi)核對象(使用計數(shù)為1)。和進程內(nèi)核對象一樣,線程內(nèi)核對象也是一個小型數(shù)據(jù)結(jié)構(gòu),操作系統(tǒng)用它來管理這個線程。這個主線程一開始就會執(zhí)行由鏈接器設(shè)為應(yīng)用程序入口的C/C++運行時啟動例程,并最終調(diào)用你的WinMain,wWinMain,main或wmain函數(shù)。
            (4)、如果系統(tǒng)成功創(chuàng)建了新進程和主線程,CreateProcess將返回TRUE。
            創(chuàng)建子進程后:
            創(chuàng)建一個進程內(nèi)核對象時,系統(tǒng)會為此對象分配一個獨一無二的標(biāo)識符,系統(tǒng)中沒有別的進程內(nèi)核對象會有相同的ID編號


            6、如何終止線程。
            關(guān)閉到一個進程或線程的句柄,不會強迫系統(tǒng)殺死此進程或線程。關(guān)閉句柄只是告訴系統(tǒng)你對進程或線程的統(tǒng)計數(shù)據(jù)
            不再感興趣了。進程或線程會繼續(xù)執(zhí)行,直至自行終止。(計數(shù),重點是計數(shù))
            進程可以通過以下4種方式終止:
            (1)、主線程的入口函數(shù)返回(強烈推薦的方式)。
                    讓主線程的入口函數(shù)返回,可以保證發(fā)生以下幾件事情:
            ?? 該線程創(chuàng)建的任何C++對象都將由這些對象的析構(gòu)函數(shù)正確銷毀。
            ?? 操作系統(tǒng)將正確釋放線程堆棧使用的內(nèi)存。
            ?? 系統(tǒng)將進程的退出代碼(在進程內(nèi)核對象中維護)設(shè)為你的入口函數(shù)的返回值。
            ?? 系統(tǒng)遞減進程內(nèi)核對象的使用計數(shù)。
            (2)、進程中的一個線程調(diào)用ExitProcess函數(shù)(要避免這個方式)
            進程會在該進程中的一個線程調(diào)用ExitProcess函數(shù)時終止:
            VOID ExitProcess(UINT fuExitCode);
            一旦你的應(yīng)用程序的主線程從它的入口函數(shù)返回,那么不管當(dāng)前在進程中是否正在運行其他線程,都會調(diào)用ExitProcess來終止進程。不過,如果在入口函數(shù)中調(diào)用ExitThread,而不是調(diào)用ExitProcess或者簡單地返回,應(yīng)用程序的主線程將停止執(zhí)行,但只要進程中還有其他線程正在運行,進程就不會終止。

            (3)、另一個進程中的線程調(diào)用TerminateProcess函數(shù)(要避免這個方式)
            調(diào)用TerminateProcess也可以終止一個進程,但是進程無法將它在內(nèi)存中的任何信息轉(zhuǎn)儲到磁盤上。
            (4)、進程中的所有線程都“自然死亡”(這是很難發(fā)生的)
            posted @ 2011-10-07 11:19 Yu_ 閱讀(412) | 評論 (0)編輯 收藏
            1、什么是內(nèi)核對象?
            內(nèi)核對象的數(shù)據(jù)結(jié)構(gòu)只能由內(nèi)核訪問。
            他們有:令牌(access token)對象、事件對象、文件對象、文件映射對象、I/O完成端口對象、作業(yè)對象、mailslot對象、mutex對象、pipe對象、進程對象、semaphore對象、線程對象、waitable timer對象以及thread pool worker factory對象等等。大多數(shù)成員都是不同的對象類型特有的。
            2、生命周期。
            取決與其計數(shù):
            內(nèi)核對象的所有者是內(nèi)核,而非進程。換言之,如果你的進程調(diào)用一個函數(shù)來創(chuàng)建了一個內(nèi)核對象,然后進程終止運行,則內(nèi)核對象并不一定會銷毀。大多數(shù)情況下,這個內(nèi)核對象是會銷毀的,但假如另一個進程正在使用你的進程創(chuàng)建的內(nèi)核對象,那么在其他進程停止使用它之前,它是不會銷毀的。總之,內(nèi)核對象的生命期可能長于創(chuàng)建它的那個進程。
            內(nèi)核知道當(dāng)前有多少個進程正在使用一個特定的內(nèi)核對象,因為每個對象都包含一個使用計數(shù)(usage count)。

            使用計數(shù)是所有內(nèi)核對象類型都有的一個數(shù)據(jù)成員。初次創(chuàng)建一個對象的時候,其使用計數(shù)被設(shè)為1。另一個進程獲得對現(xiàn)有內(nèi)核對象的訪問后,使用計數(shù)就會遞增。進程終止運行后,內(nèi)核將自動遞減此進程仍然打開的所有內(nèi)核對象的使用計數(shù)。一個對象的使用計數(shù)變成0,內(nèi)核就會銷毀該對象。這樣一來,可以保證系統(tǒng)中不存在沒有被任何進程引用的內(nèi)核對象。

            3、管理內(nèi)核對象
            一個進程在初始化時,系統(tǒng)將為它分配一個句柄表。一個進程的句柄表,它只是一個由數(shù)據(jù)結(jié)構(gòu)組成的數(shù)組。每個結(jié)構(gòu)
            都包含指向一個內(nèi)核對象的指針、一個訪問掩碼(access mask)和一些標(biāo)志。
            (1)、創(chuàng)建一個內(nèi)核對象
            一個進程首次初始化的時候,其句柄表為空。當(dāng)進程內(nèi)的一個線程調(diào)用一個會創(chuàng)建內(nèi)核對象的函數(shù)時,內(nèi)核將為這個對象分配并初始化一個內(nèi)存塊。然后,內(nèi)核掃描進程的句柄表,查找一個空白的記錄項(empty entry)。指針成員會被設(shè)置成內(nèi)核對象的數(shù)據(jù)結(jié)構(gòu)的內(nèi)部內(nèi)存地址,訪問掩碼將被設(shè)置成擁有完全訪問權(quán)限,標(biāo)志也會設(shè)置。

            如果創(chuàng)建調(diào)用失敗,那么返回的句柄值通常為0(NULL)。之所以失敗,可能是由于系統(tǒng)內(nèi)存不足,或者遇到了一個安全問題。遺憾的是,有幾個函數(shù)在調(diào)用失敗時會返回句柄值–1。所以要看清楚再判斷。
            (2)、關(guān)閉內(nèi)存對象
            無論以什么方式創(chuàng)建內(nèi)核對象,都要調(diào)用CloseHandle向系統(tǒng)指出你已經(jīng)結(jié)束使用對象:
            BOOL CloseHandle(HANDLE hobject);
            結(jié)束時需要注意::;
               ①、在內(nèi)部,該函數(shù)首先檢查主調(diào)進程的句柄表,驗證“傳給函數(shù)的句柄值”標(biāo)識的是“進程確實有權(quán)訪問的一個對象”。如果句柄是有效的,系統(tǒng)就將獲得內(nèi)核對象的數(shù)據(jù)結(jié)構(gòu)的地址,并在結(jié)構(gòu)中遞減“使用計數(shù)”成員。如果使用計數(shù)變成0,內(nèi)核對象將被銷毀,并從內(nèi)存中刪除。
               ②、一旦調(diào)用CloseHandle,你的進程就不能訪問那個內(nèi)核對象。
               ③、如果對象的使用計數(shù)沒有遞減至0,它就不會被銷毀。它表明另外還有一個或多個進程在使用該對象。當(dāng)其他進程全部停止使用這個對象后(通過調(diào)用CloseHandle),對象就會被銷毀。
               ④、在創(chuàng)建一個內(nèi)核對象時,我們會將它的句柄保存到一個變量中。將此變量作為參數(shù)調(diào)用了CloseHandle函數(shù)后,還應(yīng)同時將這個變量設(shè)為NULL。
               ⑤、當(dāng)進程終止運行,操作系統(tǒng)會確保此進程所使用的所有資源都被釋放!對于內(nèi)核對象,操作系統(tǒng)執(zhí)行的是以下操作:進程終止時,系統(tǒng)自動掃描該進程的句柄表。如果這個表中有任何有效的記錄項(即進程終止前沒有關(guān)閉的對象),操作系統(tǒng)會為你關(guān)閉這些對象句柄。任何這些對象的使用計數(shù)遞減至0,內(nèi)核就會銷毀對象。

            (3)、進程共享內(nèi)核對象、
            很多地方需要用到進程共享內(nèi)核對象。例如
            ①、利用文件映射對象,可以在同一臺機器上運行的兩個不同進程之間共享數(shù)據(jù)塊。  
            ②、借助mailslots(信箱)和named pipes(匿名管道),在網(wǎng)絡(luò)中的不同計算機上運行的進程可以相互發(fā)送數(shù)據(jù)塊。
            ③、mutexes(互斥對象)、semaphores(信標(biāo)對象)和事件允許不同進程中的線程同步執(zhí)行。例如,一個應(yīng)用程序可能需要在完成某個任務(wù)之后,向另一個應(yīng)用程序發(fā)出通知。

            三種不同的機制來允許進程共享內(nèi)核對象:使用對象句柄繼承;為對象命名;以及復(fù)制對象句柄。
              1 使用對象句柄繼承
            設(shè)置可繼承標(biāo)志為1。對象句柄的繼承只會在生成子進程的時候發(fā)生。
            程序初始化時會為父進程創(chuàng)建一個進程句柄表,使用對象句柄繼承,系統(tǒng)會為子進程創(chuàng)建一個新的、空白的進程句柄表——就像它為任何一個新進程所做的那樣。系統(tǒng)會遍歷父進程的句柄表,對它的每一個記錄項進行檢查。凡是包含一個有效的“可繼承的句柄”的項,都會被完整地拷貝到子進程的句柄表。在子進程的句柄表中,拷貝項的位置與它在父進程句柄表中的位置是完全一樣的。這是非常重要的一個設(shè)計,因為它意味著:在父進程和子進程中,對一個內(nèi)核對象進行標(biāo)識的句柄值是完全一樣的。內(nèi)核對象的使用計數(shù)將遞增。
            2、改變句柄的標(biāo)志
             父進程創(chuàng)建了一個內(nèi)核對象,得到了一個可繼承的句柄,然后生成了兩個子進程。但是,父進程只希望其中的一個子進程繼承內(nèi)核對象句柄。調(diào)用SetHandleInformation函數(shù)來改變內(nèi)核對象句柄的繼承標(biāo)志。函數(shù)具體參考MSDN。 
            3、為對象命名
            不能創(chuàng)建相同名字的對象,類型不同也不行。進程間共享:
            進程A 有某個命名"JeffMutex" 對象 hMutexProcessA,那么進程B 創(chuàng)建一個同樣"JeffMutex" 對象時。,
            系統(tǒng)首先會查看是否存在一個名為“JeffMutex”的內(nèi)核對象。由于確實存在這樣的一個對象,所以內(nèi)核接著檢查對象的類型。由于試圖創(chuàng)建一個mutex,而名為“JeffMutex”的對象也是一個mutex,所以系統(tǒng)接著執(zhí)行一次安全檢查,驗證調(diào)用者是否擁有對象的完全訪問權(quán)限。如果答案是肯定的,系統(tǒng)就會在Process B的句柄表中查找一個空白記錄項,并將其初始化為指向現(xiàn)有的內(nèi)核對象。如果對象的類型不匹配,或調(diào)用者被拒絕訪問,CreateMutex就會失敗(返回NULL)。
            這樣就實現(xiàn)了進程共享對象。但問題是進程B不知道自己創(chuàng)建的是新對象,還是用久對象。
            (4)、復(fù)制對象句柄
            為了跨越進程邊界來共享內(nèi)核對象,最后一個技術(shù)是使用DuplicateHandle函數(shù)。
            這個函數(shù)獲得一個進程的句柄表中的一個記錄項,然后在另一個進程的句柄表中創(chuàng)建這個記錄項的一個拷貝。
                  
            posted @ 2011-10-06 17:27 Yu_ 閱讀(785) | 評論 (0)編輯 收藏

            1、B樹的定義
                B樹是一種平衡的多分樹(m叉樹),通常我們說m階的B樹,它必須滿足如下條件:
                (1)每個結(jié)點至多有m個子結(jié)點;
                (2)若根結(jié)點不是葉子結(jié)點,則至少有兩棵子樹;
                (3)所有的葉結(jié)點在同一層;
                (4)有k個子結(jié)點的非根結(jié)點恰好包含k-1個關(guān)鍵碼。

            2、B-樹數(shù)據(jù)結(jié)構(gòu)
            #define
            M 4        //B-樹的階,暫設(shè)為4
            #define false 0
            #define true 1

            typedef
            struct BTNode
            {
               
            int                keynum;            //節(jié)點中關(guān)鍵字個數(shù),即節(jié)點的大小
                struct BTNode    *parent;        //指向雙親結(jié)點
                int                key[M+1];        //關(guān)鍵字向量,0號單元未用
                struct BTNode    *son[M+1];        //子樹指針向量
               
            //Record        *recptr[M+1];    //記錄指針向量,0號單元未用(文件中使用)
            }BTNode, *BTree;        //B-樹節(jié)點和B-樹的類型

            typedef
            struct
            {
                BTNode           
            *pt;            //指向找到的節(jié)點
                int pos; //1...m,在節(jié)點中的關(guān)鍵字序號
                int                tag;            //1:查找成功,0:查找失敗
            }Result;        //B-樹的查找結(jié)果類型

            //初始化
            void init_BTree(BTree &root)
            {
                root
            =NULL;
            }

            2、B樹的查找
                B樹上的查找是一個順指針查找結(jié)點和在結(jié)點內(nèi)的關(guān)鍵碼中查找交叉進行的過程。從根結(jié)點開始,在結(jié)點包含的關(guān)鍵碼中查找給定的關(guān)鍵碼,找到則查找成功;否則確定給定關(guān)鍵碼可能在的子樹,重復(fù)上面的操作,直到查找成功或者指針為空為止。
                下圖顯示了在B樹中查找關(guān)鍵碼21的過程。



            int search(BTree &p,int key)
            {
               
            int j;
               
            for(j=1; j<=p->keynum; j++)
                   
            if(p->key[j] > key)
                    {
                       
            break;
                    }
               
            return j-1;        //應(yīng)該插入的位置的前一位
            }
            Result searchBtree(BTree
            &root, int key)
            {
               
            //在m階B樹t上查找關(guān)鍵碼key,反回(pt,i,tag)。
               
            //若查找成功,則特征值tag=1,指針pt所指結(jié)點中第i個關(guān)鍵碼等于key;
               
            //否則,特征值tag=0,等于key的關(guān)鍵碼記錄,應(yīng)插入在指針pt所指結(jié)點中第i個和第i+1個關(guān)鍵碼之間
                int found=false;
               
            int i;
                BTree p
            =root,father=NULL;    //初始化,p指向待查節(jié)點,q指向p的雙親
                Result    result;        //SearchBTree函數(shù)返回值

               
            while(p && !found)
                {
                    i
            =search(p,key);    //p->node[i].key≤K<p->node[i+1].key
                    if(i>0 && p->key[i]==key)
                    {
                        found
            =true;        //找到待查關(guān)鍵字
                    }
                   
            else
                    {
                        father
            =p;
                        p
            =p->son[i];
                    }
                }
                result.pos
            =i+1;        //pos是插入的位置,記住加1
                if(found)    //查找成功
                {
                    result.pt
            =p;
                    result.tag
            =1;   
                }
               
            else    //查找不成功,返回key的插入位置i
                {
                    result.pt
            =father;
                    result.tag
            =0;   
                }
               
            return result;
            }
            //SearchBTree
             

            3、B樹的插入
                首先是在恰當(dāng)?shù)娜~子結(jié)點中添加關(guān)鍵碼,如果該結(jié)點中關(guān)鍵碼不超過m-1個,則插入成功。否則要把這個結(jié)點分裂為兩個。并把中間的一個關(guān)鍵碼拿出來插到結(jié)點的父結(jié)點里去。父結(jié)點也可能是滿的,就需要再分裂,再往上插。最壞的情況,這個過程可能一直傳到根,如果需要分裂根,由于根是沒有父結(jié)點的,這時就建立一個新的根結(jié)點。插入可能導(dǎo)致B樹朝著根的方向生長。 

            B-樹的生成從空樹開始,逐個插入關(guān)鍵字而得。關(guān)鍵字的個數(shù)必須至少為[m/2]-1,每次插入總在最底層某個終端結(jié)點添加一個關(guān)鍵字,如果該結(jié)點關(guān)鍵字個數(shù)小于m-1則直接插入,如果發(fā)現(xiàn)新插入關(guān)鍵字后,關(guān)鍵字總數(shù)超過m-1個則結(jié)點需要分裂,做法如下:

              (a)假設(shè)結(jié)點p中已經(jīng)含有m-1個關(guān)鍵字,再插入一個關(guān)鍵字之后(插入總要保持關(guān)鍵字?jǐn)?shù)組的大小有序,從小到大排好序),可以將p分裂為p和p’,其中p含有的信息為[m/2]-1([m]表示大于m的最小整數(shù)),p’含有的信息為m-[m/2] ([m]表示大于m的最小整數(shù))。然后將關(guān)鍵字K[m/2]和指向p’的指針則一起插入到p的雙親結(jié)點中去。

              (b)檢查雙親結(jié)點,如果雙親結(jié)點出現(xiàn)(a)的情況,則回到步驟a繼續(xù)執(zhí)行。直到插入滿足條件為止,樹的深度增加過程是隨著插入而自下而上生長的過程。
                下圖顯示了在B樹中插入關(guān)鍵碼33的過程。



            void split(BTree &q, int s, BTree &ap)
            {
               
            // 將結(jié)點q分裂成兩個結(jié)點,前一半保留,后一半移入新生結(jié)點ap
                int i;
                cout
            <<"分裂!"<<"  "<<q->key[s]<<endl;
                ap
            =(BTree)malloc(sizeof(BTNode));        //生成新結(jié)點ap
                ap->son[0] = q->son[s];            //原來結(jié)點中間位置關(guān)鍵字相應(yīng)指針指向的子樹放到新生成結(jié)點的0棵子樹中去
                for(i=s+1;i<=M;i++)        //后一半移入ap
                {
                    ap
            ->key[i-s]=q->key[i];
                    ap
            ->son[i-s]=q->son[i];
                }
            //for
                ap->keynum=M-s;
                ap
            ->parent=q->parent;
                q
            ->keynum=s-1;        //q的前一半保留,修改keynum
            }//split

            void NewRoot(BTree &root, int x, BTree &ap)        //生成新的根節(jié)點
            {
               
            //生成含信息(root,r,ap)的新的根結(jié)點*root,原root和ap為子樹指針
                BTree p;
                p
            =(BTree)malloc(sizeof(BTNode));
               
            if(root)    //如果原來的樹不是空樹
                    root->parent=p;                    //遠(yuǎn)來的根的雙親指針指向新根
                p->son[0]=root;                        //新根的第一個孩子節(jié)點是原來的根節(jié)點
                root=p;        //root指向新根   
                root->parent=NULL;                    //新根的雙親是空指針
                root->keynum=1;           
                root
            ->key[1]=x;                        //新根的第一個關(guān)鍵字就是前面分裂出來的關(guān)鍵字
                root->son[1]=ap;                    //新根的第二個孩子節(jié)點是原來的根中分裂出來的節(jié)點
                if(ap)        //如果原來的樹不是空樹
                    ap->parent=root;                //原來的根中分裂出來的節(jié)點的雙親指針指向新根
            }//NewRoot

            void insert(BTree &q, int i, int key, BTree &ap)    //插入
            {
               
            int j;
               
            for(j=q->keynum; j>=i; j--)
                {
                    q
            ->key[j+1]=q->key[j];
                }
                q
            ->key[i]=key;
               
            for(j=q->keynum; j>=i; j--)
                {
                    q
            ->son[j+1]=q->son[j];
                }
                q
            ->son[i]=ap;
                q
            ->keynum++;
            }
            //insert
            void insertBtree(BTree &root, int key, BTree &q, int i)
            {
               
            //在B-樹T上節(jié)點q的key[i]和key[i+1]之間插入關(guān)鍵字key
               
            //若引起節(jié)點過大,則沿雙親鏈進行必要的節(jié)點分裂整理,使T仍是M階的B-樹
                BTree ap=NULL;
               
            int x=key;
               
            int finished = false;
               
            int s;
               
            while(q && !finished)
                {
                    insert(q, i, x, ap);   
            //將key和ap分別插入到q->key[i+1]和q->son[i+1]
                    if(q->keynum <    M)
                        finished
            = true;    //插入完成
                    else
                    {   
            //分裂結(jié)點*q
                        s=ceil(M/2);
                        x
            =q->key[s];
                        split(q, s, ap);   
            //將q->key[s+1...M],q->son[s...M]和q->recptr[s+1...M]移入到新節(jié)點*ap
                        q=q->parent;
                       
            if(q)
                            i
            =search(q,x)+1;        //在雙親結(jié)點*q中去查找x的插入位置,記住加1,因為search()返回的是插入位置的前一位
                    }//else
                }//while
                if(!finished)                //root是空樹(參數(shù)q初值為NULL)或者根節(jié)點已分裂為節(jié)點*q和*ap
                    NewRoot(root, x, ap);    //生成含信息(root,x,ap)的新的根節(jié)點*root,原root和ap為子樹指針
            }//insertBtree

            void SearchInsertBTree(BTree &root,int key)//搜索插入
            {
               
            //在m階B樹*t上結(jié)點*q的key[i],key[i+1]之間插入關(guān)鍵碼key
               
            //若引起結(jié)點過大,則沿雙親鏈進行必要的結(jié)點分裂調(diào)整,使*t仍為m階B樹
                Result    rs;
                rs
            = searchBtree(root,key);
               
            if(!rs.tag)    //tag=0查找不成功,插入
                {
                    cout
            <<"樹中沒有相同的節(jié)點,插入!"<<endl;
                    insertBtree(root, key, rs.pt, rs.pos);   
            //在B-樹T上節(jié)點re.pt的key[i]和key[i+1]之間插入關(guān)鍵字key
                }
               
            else
                {
                    cout
            <<"樹中已有相同的節(jié)點!"<<endl;
                }
            }
            //InserBTree

            4、B樹的刪除
                B樹中的刪除操作與插入操作類似,但要稍微復(fù)雜些。如果刪除的關(guān)鍵碼不在葉結(jié)點層,則先把此關(guān)鍵碼與它在B樹里的后繼對換位置,然后再刪除該關(guān)鍵碼。如果刪除的關(guān)鍵碼在葉結(jié)點層,則把它從它所在的結(jié)點里去掉,這可能導(dǎo)致此結(jié)點所包含的關(guān)鍵碼的個數(shù)小于 -1。這種情況下,考察該結(jié)點的左或右兄弟,從兄弟結(jié)點移若干個關(guān)鍵碼到該結(jié)點中來(這也涉及到它們的父結(jié)點中的一個關(guān)鍵碼要做相應(yīng)變化),使兩個結(jié)點所含關(guān)鍵碼個數(shù)基本相同。只有在兄弟結(jié)點的關(guān)鍵碼個數(shù)也很少,剛好等于 -1時,這個移動不能進行。這種情況下,要把將刪除關(guān)鍵碼的結(jié)點,它的兄弟結(jié)點及它們的父結(jié)點中的一個關(guān)鍵碼合并為一個結(jié)點。

            B+樹
             B+樹是應(yīng)文件系統(tǒng)所需而出的一種B-樹的變型樹。一棵m階的B+樹和m階的B-樹的差異在于:

              1.有n棵子樹的結(jié)點中含有n個關(guān)鍵字。

              2.所有的葉子結(jié)點中包含了全部關(guān)鍵字的信息,及指向含這些關(guān)鍵字記錄的指針,且葉子結(jié)點本身依關(guān)鍵字的大小自小而大順序鏈接。

              3.所有的非終端結(jié)點可以看成是索引部分,結(jié)點中僅含其子樹(根結(jié)點)中的最大(或最小)關(guān)鍵字。

              通常在B+樹上有兩個頭指針,一個指向根結(jié)點,一個指向關(guān)鍵字最小的葉子結(jié)點。

             

            1、B+樹的查找

              對B+樹可以進行兩種查找運算:

              1.從最小關(guān)鍵字起順序查找;

              2.從根結(jié)點開始,進行隨機查找。

              在查找時,若非終端結(jié)點上的劇組機等于給定值,并不終止,而是繼續(xù)向下直到葉子結(jié)點。因此,在B+樹中,不管查找成功與否,每次查找都是走了一條從根到葉子結(jié)點的路徑。其余同B-樹的查找類似。

            2、B+樹的插入

              m階B樹的插入操作在葉子結(jié)點上進行,假設(shè)要插入關(guān)鍵值a,找到葉子結(jié)點后插入a,做如下算法判別:

              ①如果當(dāng)前結(jié)點是根結(jié)點并且插入后結(jié)點關(guān)鍵字?jǐn)?shù)目小于等于m,則算法結(jié)束;

              ②如果當(dāng)前結(jié)點是非根結(jié)點并且插入后結(jié)點關(guān)鍵字?jǐn)?shù)目小于等于m,則判斷若a是新索引值時轉(zhuǎn)步驟④后結(jié)束,若a不是新索引值則直接結(jié)束;

              ③如果插入后關(guān)鍵字?jǐn)?shù)目大于m(階數(shù)),則結(jié)點先分裂成兩個結(jié)點X和Y,并且他們各自所含的關(guān)鍵字個數(shù)分別為:u=大于(m+1)/2的最小整數(shù),v=小于(m+1)/2的最大整數(shù);

              由于索引值位于結(jié)點的最左端或者最右端,不妨假設(shè)索引值位于結(jié)點最右端,有如下操作:

              如果當(dāng)前分裂成的X和Y結(jié)點原來所屬的結(jié)點是根結(jié)點,則從X和Y中取出索引的關(guān)鍵字,將這兩個關(guān)鍵字組成新的根結(jié)點,并且這個根結(jié)點指向X和Y,算法結(jié)束;

              如果當(dāng)前分裂成的X和Y結(jié)點原來所屬的結(jié)點是非根結(jié)點,依據(jù)假設(shè)條件判斷,如果a成為Y的新索引值,則轉(zhuǎn)步驟④得到Y(jié)的雙親結(jié)點P,如果a不是Y結(jié)點的新索引值,則求出X和Y結(jié)點的雙親結(jié)點P;然后提取X結(jié)點中的新索引值a’,在P中插入關(guān)鍵字a’,從P開始,繼續(xù)進行插入算法;

              ④提取結(jié)點原來的索引值b,自頂向下,先判斷根是否含有b,是則需要先將b替換為a,然后從根結(jié)點開始,記錄結(jié)點地址P,判斷P的孩子是否含有索引值b而不含有索引值a,是則先將孩子結(jié)點中的b替換為a,然后將P的孩子的地址賦值給P,繼續(xù)搜索,直到發(fā)現(xiàn)P的孩子中已經(jīng)含有a值時,停止搜索,返回地址P。

            3、B+樹的刪除

              B+樹的刪除也僅在葉子結(jié)點進行,當(dāng)葉子結(jié)點中的最大關(guān)鍵字被刪除時,其在非終端結(jié)點中的值可以作為一個“分界關(guān)鍵字”存在。若因刪除而使結(jié)點中關(guān)鍵字的個數(shù)少于m/2 (m/2結(jié)果取上界,如5/2結(jié)果為3)時,其和兄弟結(jié)點的合并過程亦和B-樹類似。

              另外的看法,當(dāng)作補充和豐富吧。B樹,B-樹和B+樹是三個不同的概念。
             
            B樹

              二叉排序樹(Binary Sort Tree)又稱二叉查找樹,也叫B樹。

              它或者是一棵空樹;或者是具有下列性質(zhì)的二叉樹:

              (1)若左子樹不空,則左子樹上所有結(jié)點的值均小于左子樹所在樹的根結(jié)點的值;

              (2)若右子樹不空,則右子樹上所有結(jié)點的值均大于右子樹所在樹的根結(jié)點的值;

              (3)左、右子樹也分別為二叉排序樹;



            1、二叉排序樹(B樹)的查找:

              時間復(fù)雜度與樹的深度的有關(guān)。

              步驟:若根結(jié)點的關(guān)鍵字值等于查找的關(guān)鍵字,成功。

              否則:若小于根結(jié)點的關(guān)鍵字值,遞歸查左子樹。

              若大于根結(jié)點的關(guān)鍵字值,遞歸查右子樹。

              若子樹為空,查找不成功。

            2、二叉排序樹(B樹)的插入和刪除:

              二叉排序樹是一種動態(tài)樹表。其特點是:樹的結(jié)構(gòu)通常不是一次生成的,而是在查找過程中,當(dāng)樹中不存在關(guān)鍵字等于給定值的節(jié)點時再進行插入。新插入的結(jié)點一定是一個新添加的葉子節(jié)點,并且是查找不成功時查找路徑上訪問的最后一個結(jié)點的左孩子或右孩子結(jié)點。

              插入算法:

              首先執(zhí)行查找算法,找出被插結(jié)點的父親結(jié)點。

              判斷被插結(jié)點是其父親結(jié)點的左兒子還是右兒子。將被插結(jié)點作為葉子結(jié)點插入。

              若二叉樹為空。則首先單獨生成根結(jié)點。

              注意:新插入的結(jié)點總是葉子結(jié)點,所以算法復(fù)雜度是O(h)。

              刪除算法:

              如果刪除的結(jié)點沒有孩子,則刪除后算法結(jié)束;

              如果刪除的結(jié)點只有一個孩子,則刪除后該孩子取代被刪除結(jié)點的位置;

              如果刪除的結(jié)點有兩個孩子,則選擇右孩子為根的樹,它的左子樹中,值最小的點作為新的根,同時在該最小值處開始,執(zhí)行刪除算法,如此繼續(xù)到刪除算法的前兩種情況時,刪除算法結(jié)束。

              B樹用途:查找信息快速,但是隨著查找深度的增加,會影響查找的效率,所以,通常會使用平衡二叉樹的平衡算法來進行動態(tài)平衡。

            posted @ 2011-10-05 19:09 Yu_ 閱讀(2593) | 評論 (1)編輯 收藏
                 摘要: 1、平衡二叉樹它是一棵空樹或它的左右兩個子樹的高度差的絕對值不超過1,并且左右兩個子樹都是一棵平衡二叉樹。 如圖:2、動態(tài)平衡技術(shù) 動態(tài)平衡技術(shù)Adelson-Velskii 和 Landis 提出了一個動態(tài)地保持二叉排序樹平衡的方法,其基本思想是:  在構(gòu)造二叉排序樹的過程中,每當(dāng)插入一個結(jié)點時,首先檢查是否因插入而破壞了樹的平衡性,如果是因插入結(jié)點而破壞了樹的平衡性,則找出其中最小不平衡子樹,...  閱讀全文
            posted @ 2011-10-04 01:09 Yu_ 閱讀(756) | 評論 (0)編輯 收藏
            1、順序查找:(有兩種方法)
            在一個已知無(或有序)序隊列中找出與給定關(guān)鍵字相同的數(shù)的具體位置。
            ①、讓關(guān)鍵字與隊列中的值從第一個開始逐個比較,直到找出與給定關(guān)鍵字相同的值為止。
              int SeqSearch(Seqlist R,KeyType K)
                {
                  //在順序表R[1..n]中順序查找關(guān)鍵字為K的結(jié)點,
                  //成功時返回找到的結(jié)點位置,失敗時返回-1      
                  int i;
                  for(i=0;i<R.len;i++)
                  {
                       if(R[i].key==K) return i;
                   }
                  return -1; 
                } //SeqSearch


            ②、從表中最后一個記錄開始,逐個進行與關(guān)鍵字比較。直至第一個值,elem[0]=key,為設(shè)置“哨兵”。找不到返回0
              int SeqSearch(Seqlist R,KeyType K)
                {
                  //在順序表R[1..n]中順序查找關(guān)鍵字為K的結(jié)點,
                  //成功時返回找到的結(jié)點位置,失敗時返回0
                  int i;
                  R[0].key=K; //設(shè)置哨兵
                  for(i=R.len;R[i].key!=K;i--); //從表后往前找
                  return i; //若i為0,表示查找失敗,否則R[i]是要找的結(jié)點
                } //SeqSearch

            比較:成功時的順序查找的平均查找長度:
                
             
                 在等概率情況下,pi=1/n(1≤i≤n),故成功的平均查找長度為
                    (n+…+2+1)/n=(n+1)/2
            即查找成功時的平均比較次數(shù)約為表長的一半。
            若K值不在表中,則須進行n+1次比較之后才能確定查找失敗。

            2、折半查找:(二分法查找)(必須有序)          
            ①、假設(shè)數(shù)據(jù)是按升序排序的,對于給定值x,從序列的中間位置開始比較,如果當(dāng)前位置值等于x,則查找成功;
            ②、若x小于當(dāng)前位置值,則在數(shù)列的前半段中查找;若x大于當(dāng)前位置值則在數(shù)列的后半段中繼續(xù)查找,直到找到為止。        
                  int search(int *a,int key,int low,int high) 
               {
                  int mid;
                 mid = (low + high)/2;
                  while(low<high)
                  {
                     if(a[mid] == key) return mid; 
                     else 
                     if (a[mid]>key)   high=mid;
                           else   low=mid;

                       mid = (low + high)/2;           
                   }//while
                  return -1;    //沒有找到
               }//search
            用遞歸思想:
             int search(int *a,int key,int low,int high)
              {
                 int mid;
                 if(low > high)
                 return -1;
                 mid = (low + high)/2;
                 if(a[mid] == key) return mid;
                 else if(a[mid] > key)   return search(a,key,low,mid -1);
                 else return    search(a,key,mid + 1,high);
              }

            3、
            /////////////////////待續(xù)...

            posted @ 2011-10-03 11:00 Yu_ 閱讀(1076) | 評論 (0)編輯 收藏
            僅列出標(biāo)題
            共4頁: 1 2 3 4 
            97久久精品午夜一区二区| 无码任你躁久久久久久| 久久99国产精品99久久| 久久一区二区三区99| 亚洲人成精品久久久久| 久久久久综合网久久| 99精品国产综合久久久久五月天| 国产一级持黄大片99久久| 久久天天婷婷五月俺也去| 91精品久久久久久无码| 国产69精品久久久久久人妻精品| 成人a毛片久久免费播放| 国产亚洲精久久久久久无码77777 国产亚洲精品久久久久秋霞 | 色婷婷综合久久久久中文字幕| 欧美熟妇另类久久久久久不卡| 久久精品国产第一区二区| 国产亚洲精品美女久久久| 久久中文字幕人妻丝袜| 99久久免费国产精品| 国产成人精品白浆久久69| 中文字幕精品无码久久久久久3D日动漫| 国产精品久久久久天天影视| 久久久久久久久久久久久久| 久久亚洲高清综合| 精品水蜜桃久久久久久久| 成人国内精品久久久久一区| 久久精品国产亚洲αv忘忧草| 伊人丁香狠狠色综合久久| 91精品国产色综合久久| 亚洲中文字幕无码一久久区| 一极黄色视频久久网站| 久久婷婷色综合一区二区| 久久久精品国产亚洲成人满18免费网站 | 人人狠狠综合88综合久久| 久久精品国产一区二区电影| 91性高湖久久久久| 久久精品免费网站网| 国产叼嘿久久精品久久| 精品视频久久久久| 久久国产影院| 热综合一本伊人久久精品|