青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

posts - 126,  comments - 73,  trackbacks - 0

I/O完成端口背后的理論是同時運行的線程數必須有個上界;也就是,500個并發的客戶端請求不必要500個線程存在。那么,合適的并發線程數是多少呢?你會意識到,如果一個機器有兩個CPU,那么在此基礎上多余兩個以上的線程實在是沒有意義。因為,當有超過CPU數量的線程數時,系統不得不耗費時間來進行線程之間的切換,這會浪費寶貴的CPU時鐘周期。
為每個客戶端創建一個線程還有一個不足,就是創建一個線程雖然比創建一個進程廉價,但它遠遠不是免費的。如果服務器應用程序在啟動時創建一個線程池,并且池子中的線程留駐在程序的整個生命期內,那么服務器的性能會得到提高。I/O完成端口在設計上就是使用了線程池。
I/O完成端口可能是最復雜的核心對象。要創建一個完成端口你可以使用CreateIoCompletionPort函數。

HANDLE CreateIoCompletionPort(
?? HANDLE??? hfile,
?? HANDLE??? hExistingCompPort,
?? ULONG_PTR CompKey,
?? DWORD???? dwNumberOfConcurrentThreads);
???
或者封裝:
HANDLE CreateNewCompletionPort(DWORD dwNumberOfConcurrentThreads) {

?? return(CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0,
????? dwNumberOfConcurrentThreads));
}

這個函數根據傳入的參數的不同,可以完成兩個完全不同的功能。一是可以創建一個完成端口,另一個是可以將一個設備與完成端口關聯起來。例如要產生一個完成端口,看起來可以這樣調用函數:
hCompPort = CreateIoCompletionPort(
???INVALID_HANDLE_VALUE,
???NULL,
???0,
???0);

FileHandle —— 文件或設備的句柄。在產生完成端口時,這個參數應設置為INVALID_HANDLE_VALUE,于是產生一個沒有和任何句柄有關系的port。
ExistingCompletionPort —— 在產生完成端口時此參數設定為NULL,產生一個新的port。如果此參數指定為一個已存在的完成端口的句柄,那么在上一個參數FileHandle中指定的句柄就會被加到此port上,而不會產生新的port。
CompletionKey —— 用戶自定義的一個值,將被交給提供服務的線程。此值和FileHandle有關聯。
NumberOfConcurrentThreads —— 與此完成端口有關聯的線程個數。如果設定為0則為CPU的個數。
NumberOfConcurrentThreads這個參數是告訴完成端口可同時運行的最大線程數。如果傳0,則默認值為CPU的個數。在大多數情況下為了避免不必要的線程切換你可以傳0。有時你也許會想要增加這個值,那就是當來自客戶端的請求需要一個長時間的計算甚至發生阻塞時,但通常不鼓勵增加這個值。你可以嘗試往這個參數傳不同的值來測試比較服務器的性能。

關聯一個設備到完成端口
當你創建完成端口時,系統內核會創建5個不同的數據結構.

第一個數據結構是設備列表,指明了被關聯到完成端口上的設備。將設備關聯到完成端口上,你同樣是調用CreateIoCompletionPort函數??雌饋砜梢赃@樣調用函數:
HANDLE h = CreateIoCompletionPort(hDevice, hCompPort, dwCompKey, 0);

或者封裝:
BOOL AssociateDeviceWithCompletionPort(
?? HANDLE hCompPort, HANDLE hDevice, DWORD dwCompKey) {

?? HANDLE h = CreateIoCompletionPort(hDevice, hCompPort, dwCompKey, 0);
?? return(h == hCompPort);
}
這樣就添加一個記錄(entry)到一個已經存在的完成端口設備列表上。你在第一個參數上傳遞一個已經存在的完成端口句柄(在第一次調CreateIoCompletionPort時得到),在第二個參數上傳遞設備句柄(這個設備可以是file,socket,
mailslot,pipe等等),在第三個參數上傳遞一個完成鍵(由用戶定義的值,操作系統不關心你傳遞的是什么值)。每當你關聯一個設備到完成端口上時,系統就添加這些信息到完成端口設備列表上。
注意:CreateIoCompletionPort很復雜,推薦把它看成兩個函數來使用。下面有個例子把創建端口和關聯放在一次調用中完成,但是不推薦這樣使用。下面的代碼在打開一個文件并在同一時間創建一個新的完成端口,同時關聯該設備與完成端口。所有這個文件I/O請求都將在完成時使用CK_FILE這個完成鍵,完成端口將允許兩個線程來并發執行。
#define CK_FILE?? 1
HANDLE hfile = CreateFile(...);
HANDLE hCompPort = CreateIoCompletionPort(hfile, NULL, CK_FILE, 2);


第二個數據結構是一I/O完成隊列。當針對一個設備的異步I/O請求完成時,系統就檢查看這個設備在之前是否被關聯到一個完成端口過,如果是,系統就將這個已經完成的I/O請求作為一條記錄,添加到對應的完成端口的I/O完成隊列末尾。在隊列中的每條記錄都指明了4個值:已傳輸的字節數;完成鍵(當設備被關聯到端口上時通過第三個參數設定的);一個指針,指向I/O請求的OVERLAPPED結構;和一個錯誤碼。
注意:對一個設備發出一個I/O請求,但并不將它記錄到完成端口的完成隊列上是可以的。這不是常態,但有時卻派得上用場。比如,當你通過socket發送數據,但你并不在意這些數據是否被處理時。(或者有時候想用舊有的受激發的event對象機制來激發,而不用I/O完成隊列)
為了這么做,請你設定一個OVERLAPPED結構,內含一個合法的手動重置(manual-reset)event對象,放在hEvent欄位。然后把該handle的最低位設置為1。雖然這聽起來象是一種黑客行為,但檔案中有交代。看起來象這樣:
Overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
Overlapped.hEvent = (HANDLE) ((DWORD_PTR) Overlapped.hEvent | 1);
ReadFile(..., &Overlapped);

同樣,不要忘記在關閉event句柄之前重置低位:
CloseHandle((HANDLE) ((DWORD_PTR) Overlapped.hEvent & ~1));


Architecting Around an I/O Completion Port
當你的服務器應用程序啟動時,它將通過調用像CreateNewCompletionPort這樣的函數來創建I/O完成端口。然后應用程序創建一個線程池來處理client請求。現在你有個問題要問,“線程池中有多少線程?”。這是一個很難回答的問題,我將在“How Many Threads in the Pool?”這個小節中給出詳細的回答。目前,單憑經驗的標準是本地機器上的CPU數再乘以2。比如,一個雙CPU的機器上,你就應該在線程池中創建4個線程。
池子里的所有線程都將執行相同的線程函數。通常,這個線程函數執行一些初始化工作,然后就進入一個循環,直到服務進程被指示停止時才結束掉。在循環里,線程將自己休眠,等待關聯到完成端口上的設備的I/O請求完成。線程通過調用GetQueuedCompletionStatus函數進入等待。
下面是函數原型:

BOOL GetQueuedCompletionStatus(
?? HANDLE?????? hCompPort,
?? PDWORD?????? pdwNumBytes,
?? PULONG_PTR?? CompKey,
?? OVERLAPPED** ppOverlapped,
?? DWORD??????? dwMilliseconds);
第一個參數hCompPort傳遞一個完成端口句柄,表明線程監控的是哪一個完成端口。許多服務程序都是使用的單一完成端口,所有I/O請求的完成通知都在這一個端口上。基本上,GetQueuedCompletionStatus的工作就是將調用此函數的線程休眠,直到一條記錄出現在指定完成端口上的I/O完成隊列中,或者直到指定的超時時間到達(超時時間在dwMilliseconds參數中指定)。
第三個數據結構就是等待中的線程隊列(the waiting thread queue)。在線程池中的每一個調用了GetQueuedCompletionStatus的線程,其線程ID就被放置到等待中的線程隊列里,以使I/O完成端口核心對象能知道當前有哪些線程正等待著處理已經完成了的I/O請求。當有一條記錄出現在端口的I/O完成隊列時,完成端口就喚醒一個在等待隊列中的線程。這個線程會得到組成這條完成記錄的一些信息:已傳輸的字節數,完成鍵,和一個OVERLAPPED結構的地址。這些信息通過傳遞給GetQueuedCompletionStatus函數的三個參數(pdwNumBytes、CompKey、ppOverlapped)返回到線程中。GetQueuedCompletionStatus函數的返回值有點復雜,下面的代碼演示了如何處理不同的返回值:
DWORD dwNumBytes;
ULONG_PTR CompKey;
OVERLAPPED* pOverlapped;

// hIOCP is initialized somewhere else in the program
BOOL fOk = GetQueuedCompletionStatus(hIOCP,
?? &dwNumBytes, &CompKey, &pOverlapped, 1000);
DWORD dwError = GetLastError();

if (fOk) {
?? // Process a successfully completed I/O request
} else {
?? if (pOverlapped != NULL) {
????? // Process a failed completed I/O request
????? // dwError contains the reason for failure
?? } else {
????? if (dwError == WAIT_TIMEOUT) {
???????? // Time-out while waiting for completed I/O entry
????? } else {
???????? // Bad call to GetQueuedCompletionStatus
???????? // dwError contains the reason for the bad call
????? }
?? }
}
如你所預料到的,I/O完成隊列是以隊列的方式移出記錄的,也就是先進先出FIFO。然而,你可能沒有料到的是,調用GetQueuedCompletionStatus函數進入等待的線程卻是以棧的方式來喚醒的,也就是LIFO,最后調用函數進入等待的那個線程被最先喚醒。這樣做的理由是為了進一步提高處理性能。例如,有四個線程在“等待線程隊列”中等待著。如果這個時候只有一條“已完成的I/O”記錄出現了,那么最后一個調用GetQueuedCompletionStatus的線程被喚醒來處理這條記錄。當這個線程處理完這條記錄時,此線程再次調用GetQueuedCompletionStatus函數進入等待線程隊列。那么,此時當有另一條I/O完成記錄出現時,剛才那個線程被繼續喚醒來處理這條新的記錄。(這樣的好處就是可以避免線程間的切換)
只要I/O請求完成得夠遲緩,那么單個線程就足以處理他們。系統只需要保持喚醒同一個線程,并讓其他三個線程持續休眠。使用LIFO算法,線程就不必花時間將他們在內存中的資源置換到磁盤中(例如??臻g),并從CPU的cache中替換出去。

I/O完成端口如何管理線程池
現在該討論為什么完成端口是如此有用了。首先,在你創建完成端口的時候,你通過CreateIoCompletionPort函數的最后一個參數指定可以并行運行的線程數量。我們說過,這個值通常設置為主機上的CPU數量。當已經完成的I/O記錄進入隊列時,完成端口就喚醒等待中的線程。然而,完成端口只能喚醒你所指定的那么多個線程。于是,在雙CPU的機器中,如果有4個I/O請求完成,并有4個線程在等待,但完成端口卻只能喚醒2個線程,另2個線程保持休眠。每個得到喚醒在處理已完成的I/O記錄的線程,處理完成后,又重新調用GetQueuedCompletionStatus函數進入等待。因為線程的喚醒算法是后進先出,所以系統看到還有已完成的I/O記錄在排隊時,又喚醒剛才被喚醒過的線程。
仔細思考這個過程,你似乎會覺得好象有些問題:既然完成端口僅允許指定數量的線程被喚醒,為什么還要產生多余的線程等待在線程池中呢?比如,在上一段中所敘述的那種情況,在一個雙CPU的主機上,創建一個完成端口,并告之只允許2個線程并行處理。但是創建線程池時我們創建了4個線程(按照2倍CPU的數量),看上去我們好象創建了2個多余的線程——他們永遠也不會被喚醒。(因為LIFO)
但是不用擔心,完成端口是非常聰明的。當完成端口喚醒一個線程時,會將此線程的ID放入第四個數據結構中——被釋放的線程列表。這可以讓完成端口記住哪些線程是被喚醒了的,并監控這些線程的執行。如果某個在“被釋放的線程列表”中的線程因為調用了某功能而被掛起(即產生阻塞)那么完成端口會偵測到這一情況,并將此線程的ID從“被釋放的線程列表”中移動到“暫停的線程列表”——即第五個數據結構中。
完成端口的目標就是要保證在“被釋放的線程列表”中的記錄條數保持為你在創建完成端口時所指定的并發線程數。因此,當一個被喚醒的線程因為某種原因而被掛起時,完成端口就會釋放掉另一個在等待中的線程,以次來維持“被釋放的線程列表”中的記錄數保持為你所指定的并發線程數。如果被掛起的線程重新被喚醒,它又會從“暫停的線程列表”中移動到“被釋放的線程列表”。這個時候需要注意的是加上剛才被另外喚醒的一個線程,此時在“被釋放的線程列表”中的記錄數會超過你所指定的并發線程數(看上去很奇怪,但卻是這樣)。完成端口意識到這一情況后將不會再喚醒其他線程,除非并行線程數又降到CPU數量之下。實際并行線程數將只在你指定的最大并行線程數之上停留很短的時間并且線程在循環到再次調用GetQueuedCompletionStatus時這種情況將快速的停止下來。
注意:一旦線程調用GetQueuedCompletionStatus,那么這個線程就被“分配”到那指定的完成端口上去。系統會假定那些被“分配”的線程代表完成端口在工作。你可以終止線程和完成端口間的這種“分配”關系,通過以下三種方式:
??終止線程;
??讓線程調用GetQueuedCompletionStatus,并傳遞另一個完成端口的句柄,這樣就終止掉當前的“分配”關系,與另一完成端口產生新的“分配”關系。
??銷毀完成端口。

在線程池中創建多少個線程?
現在是討論線程池中需要多少個線程的好時機??紤]兩點:首先,當服務程序啟動時你應該創建一個最小的線程集,這樣你就不用象每客戶端每線程模型那樣頻繁地創建和銷毀線程了。因為創建和銷毀線程都需要耗費CPU時間,所以你應該盡可能的少做這些操作。其次,你應該設置線程數的最大值,因為創建太多的線程會相應耗費太多的系統資源。就算大多數資源能被交換出RAM,但你還是應該最小限度地使用系統資源,不要浪費,最好不要產生paging file space。
你也許應該測試不同的線程數。大多數服務程序(包括MS的IIS)都使用啟發式(heuristic,實際就是枚舉選擇,通過可選擇的方法發現的幾種解決辦法中最合適的一種,在一個程序的連續階段被選擇出來以用于該程序的下一步的一種解決問題的技巧的應用)算法來管理他們的線程池。我推薦你也使用同樣的方法。例如,你可以創建如下的變量來管理線程池。
LONG g_nThreadsMin;??? // Minimum number of threads in pool
LONG g_nThreadsMax;??? // Maximum number of threads in pool
LONG g_nThreadsCrnt;?? // Current number of threads in pool
LONG g_nThreadsBusy;?? // Number of busy threads in pool

當你的程序啟動時,你可以創建g_nThreadsMin這么多個線程,都執行相同的線程函數。線程函數看起來是這樣:
DWORD WINAPI ThreadPoolFunc(PVOID pv) {

?? // Thread is entering pool
?? InterlockedIncrement(&g_nThreadsCrnt);
?? InterlockedIncrement(&g_nThreadsBusy);

?? for (BOOL fStayInPool = TRUE; fStayInPool;) {

????? // Thread stops executing and waits for something to do
????? InterlockedDecrement(&m_nThreadsBusy);
????? BOOL fOk = GetQueuedCompletionStatus(...);
????? DWORD dwIOError = GetLastError();

????? // Thread has something to do, so it's busy
????? int nThreadsBusy = InterlockedIncrement(&m_nThreadsBusy);

????? // Should we add another thread to the pool?
????? if (nThreadsBusy == m_nThreadsCrnt) {??? // All threads are busy
???????? if (nThreadsBusy < m_nThreadsMax) {?? // The pool isn't full
??????????? if (GetCPUUsage() < 75) {?? // CPU usage is below 75%

?????????????? // Add thread to pool
?????????????? CloseHandle(chBEGINTHREADEX(...));
??????????? }
???????? }
????? }

????? if (!fOk && (dwIOError == WAIT_TIMEOUT)) {?? // Thread timed-out
???????? if (!ThreadHasIoPending()) {
??????????? // There isn't much for the server to do, and this thread
??????????? // can die because it has no outstanding I/O requests
??????????? fStayInPool = FALSE;
???????? }
????? }

????? if (fOk || (po != NULL)) {
???????? // Thread woke to process something; process it
???????? ...

???????? if (GetCPUUsage() > 90) {?????? // CPU usage is above 90%
??????????? if (!ThreadHasIoPending()) { // No pending I/O requests
?????????????? if (g_nThreadsCrnt > g_nThreadsMin)) { // Pool above min

????????????????? fStayInPool = FALSE;?? // Remove thread from pool
?????????????? }
??????????? }
???????? }
????? }
?? }

?? // Thread is leaving pool
?? InterlockedDecrement(&g_nThreadsBusy);
?? InterlockedDecrement(&g_nThreadsCurrent);
?? return(0);
}

注意:在這章稍早的時候(in the section “Canceling Queued Device I/O Requests”)。我說過:當線程退出的時候,系統自動取消所有已發起但處于掛起中的I/O請求。這就是為什么偽代碼像ThreadHasIoPending這樣的函數是必須的。如果線程還有未完成的I/O請求,就不要允許線程結束。
許多服務都提供了管理工具,以使管理員可以通過此來控制線程池的行為。例如,設置線程數的最小值和最大值,CPU使用極限,以及最大并發線程數。

Simulating Completed I/O Requests
I/O完成端口并不是只能用于device I/O,它也是用來解決線程間通訊問題的卓越的機制。
BOOL PostQueuedCompletionStatus(
?? HANDLE????? hCompPort,
?? DWORD?????? dwNumBytes,
?? ULONG_PTR?? CompKey,
?? OVERLAPPED* pOverlapped);

這個函數將一個“已完成的I/O”通知添加到一個完成端口的隊列上。第一個參數指定要針對的完成端口,剩下的三個參數,dwNumBtyes,ComKey和pOverlapped指出了線程調用 GetQueuedCompletionStatus時將從對應的返回參數中返回的值。當一個線程從“I/O完成隊列”中取出一條模擬記錄時,GetQueuedCompletionStatus函數將返回TRUE,表明已經成功地執行了一個I/O請求。
函數PostQueuedCompletionStatus難以置信的有用,它提供了一種方式讓你可以跟線程池里的所有線程通訊。例如,當用戶終止服務應用程序時,你當然希望所有的線程都能夠干干凈凈地退出。但如果有線程等待在完成端口上,并且此時又一直沒有I/O請求到達,那么這些線程就不能被喚醒。通過對線程池中的每個線程調用一次PostQueuedCompletionStatus,每個線程都能得以喚醒,你就可以設計檢查從GetQueuedCompletionStatus的返回參數中得到的值(比如設定完成鍵),可以判斷出是否是程序要終止了,由此可決定是否要做清理工作并退出。
當使用這項技術,你必須要特別小心。我的例子能工作是因為在池中的線程會死去并不會再次調用GetQueuedCompletionStatus。不管怎樣,如果你想要通知其池中的每個線程一些事情,并且它們會循環到再次調用GetQueuedCompletionStatus,你將會有一個問題。因為線程是按LIFO的順序醒來。所以你的應用程序中你將會使用一些額外的線程同步來確保每個線程獲得了合適的機會來查看它的偽I/O記錄。不使用這個格外的線程同步,一個線程可能會查看相同的通知好幾次。


?from:油庫七號
posted on 2008-10-29 18:36 我風 閱讀(1623) 評論(0)  編輯 收藏 引用
<2008年10月>
2829301234
567891011
12131415161718
19202122232425
2627282930311
2345678

常用鏈接

留言簿(12)

隨筆分類

隨筆檔案

文章檔案

相冊

收藏夾

C++

MyFavorite

搜索

  •  

積分與排名

  • 積分 - 328387
  • 排名 - 75

最新評論

閱讀排行榜

評論排行榜

青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <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>
            亚洲免费在线视频一区 二区| 久久久久www| 欧美电影资源| 久久成人精品无人区| 中文国产成人精品| 一本到高清视频免费精品| 亚洲精品专区| 亚洲精品在线视频| 亚洲美女诱惑| 裸体歌舞表演一区二区| 美女脱光内衣内裤视频久久网站| 麻豆精品精华液| 亚洲国产二区| 免费成人高清| 亚洲精品女av网站| 日韩视频精品在线| 亚洲欧美资源在线| 久久久精彩视频| 欧美精品久久久久久久久老牛影院 | 一区二区三区四区国产精品| 亚洲自拍偷拍麻豆| 久久久久久一区| 亚洲欧洲午夜| 欧美一区二区在线观看| 欧美三级小说| 欧美日韩亚洲成人| 在线观看亚洲a| 亚洲综合精品四区| 欧美夫妇交换俱乐部在线观看| 99热精品在线观看| 久久深夜福利免费观看| 国产精品毛片在线看| 亚洲毛片网站| 欧美成人午夜| 国产精品久久久久久av福利软件| 亚洲欧美国产高清| 欧美成人一区二免费视频软件| 国产精品一区二区你懂得| 亚洲精品乱码久久久久久蜜桃91| 欧美在线播放视频| 亚洲国产免费| 久久成年人视频| 国产精品视频xxx| 亚洲少妇在线| 欧美激情久久久久| 欧美在线视频观看| 国产免费成人| 亚洲少妇诱惑| 美女精品在线观看| 亚洲宅男天堂在线观看无病毒| 久久久久成人精品| 国产精品一区亚洲| 亚洲人成人一区二区在线观看| 欧美午夜精品久久久久久久| 国产一区二区高清| 亚洲国产精品成人久久综合一区| 亚洲国产裸拍裸体视频在线观看乱了中文| 久久九九电影| 亚洲国产高清在线观看视频| 99成人精品| 久久精品国产亚洲高清剧情介绍| 麻豆精品在线视频| 国产精品黄页免费高清在线观看| 国产人妖伪娘一区91| 亚洲国产日韩一区| 国一区二区在线观看| 老鸭窝毛片一区二区三区| 亚洲精品视频免费| 久久久久久高潮国产精品视| 欧美日韩国产精品自在自线| 国外成人性视频| 亚洲尤物视频网| 欧美电影免费观看| 欧美亚洲在线观看| 欧美日韩不卡视频| 午夜久久久久久| 亚洲国产精品黑人久久久| 欧美一级视频一区二区| 欧美日韩国产综合网| 激情综合电影网| 欧美专区在线观看| 一区二区三区日韩欧美精品| 美国三级日本三级久久99| 国产欧美日韩精品专区| 黄色成人在线网址| 欧美二区在线播放| 香蕉精品999视频一区二区| 欧美三级精品| 亚洲精品在线看| 亚洲第一狼人社区| 巨乳诱惑日韩免费av| 国产一区二区三区自拍| 欧美成人免费一级人片100| 久久久久久久久久久久久久一区| 亚洲三级影院| 亚洲国产日韩一级| 一区二区三区高清| 毛片精品免费在线观看| 在线免费一区三区| 蘑菇福利视频一区播放| 欧美一区精品| 国模私拍一区二区三区| 欧美亚洲免费在线| 亚洲无吗在线| 亚洲欧美日韩在线播放| 狼人天天伊人久久| 伊人激情综合| 美女日韩在线中文字幕| 久久婷婷一区| 亚洲国产另类 国产精品国产免费| 欧美.www| 欧美激情综合色| 亚洲美女精品久久| 一区二区三区色| 国产精品国色综合久久| 亚洲欧美成人一区二区在线电影| 99成人在线| 国产精品久久久久久久久免费桃花| 在线亚洲自拍| 久久久久久有精品国产| 国产精品成人va在线观看| 午夜激情亚洲| 久久精品国产精品亚洲| 亚洲欧洲一区二区三区在线观看| 亚洲国产精品一区二区第四页av| 欧美精品一区二区三区一线天视频| 亚洲精品一区在线观看香蕉| 亚洲欧洲日韩女同| 国产精品一二三| 亚洲免费不卡| 亚洲国产精品久久久久秋霞蜜臀 | 国产精品一区在线观看| 一区二区电影免费在线观看| 一区二区久久| 国产色综合久久| 欧美成人69| 欧美日韩伦理在线| 性久久久久久久久| 亚洲一区二区三区成人在线视频精品| 欧美v亚洲v综合ⅴ国产v| 一本一道久久综合狠狠老精东影业| 亚洲欧美在线免费观看| 在线成人小视频| 一区二区三区免费网站| 在线成人亚洲| 亚洲欧美激情在线视频| 伊人成人在线视频| 中日韩高清电影网| 欧美日韩成人免费| 欧美极品aⅴ影院| 欧美一区午夜视频在线观看| 欧美sm视频| 欧美在线视频a| 麻豆9191精品国产| 精品电影在线观看| 亚洲丝袜av一区| 亚洲国产精品一区在线观看不卡| 鲁大师成人一区二区三区 | 亚洲一区二区三区在线视频| 欧美日本韩国一区| 久久午夜电影网| 国产精品成人av性教育| 欧美高清日韩| 亚洲福利视频一区| 久久九九精品99国产精品| 亚洲一区在线播放| 欧美紧缚bdsm在线视频| 亚洲美女视频在线免费观看| 欧美一级专区免费大片| 亚洲午夜在线观看| 亚洲视频一区二区在线观看| 久久国产免费看| 国产欧美一区二区精品忘忧草| 亚洲精品一区二区三区樱花| 亚洲国产精品999| 久久综合婷婷| 免费的成人av| 亚洲国产精彩中文乱码av在线播放| 亚洲精品日韩精品| 久久久久久久综合色一本| 国产裸体写真av一区二区 | 亚洲韩国精品一区| 亚洲国产精品精华液网站| 欧美中在线观看| 久热成人在线视频| 一区免费视频| 亚洲欧洲日本专区| 亚洲自拍偷拍一区| 国产精品制服诱惑| 性欧美videos另类喷潮| 久久国产天堂福利天堂| 国产三级欧美三级| 久久久综合激的五月天| 欧美激情偷拍| 一区二区三区视频观看| 国产精品久久91| 欧美亚洲综合在线| 欧美aa国产视频| 亚洲人成在线观看网站高清| 欧美黑人在线播放|