• <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>

            山寨:不是最好的,是最適合我們的!歡迎體驗(yàn)山寨 中文版MSDN

            Blog @ Blog

            當(dāng)華美的葉片落盡,生命的脈絡(luò)才歷歷可見(jiàn)。 -- 聶魯達(dá)

            常用鏈接

            統(tǒng)計(jì)

            積分與排名

            BBS

            Blog

            Web

            最新評(píng)論

            Overlapped I/O模型深入分析

            簡(jiǎn)述:
                Overlapped I/O也稱(chēng)Asynchronous  I/O,異步I/O模型。異步I/O和同步I/O不同,同步I/O時(shí),程序被掛起,一直到I/O處理完,程序才能獲得控制。異步I/O,調(diào)用一個(gè)函數(shù)告訴 OS,進(jìn)行I/O操作,不等I/O結(jié)束就立即返回,繼續(xù)程序執(zhí)行,操作系統(tǒng)完成I/O之后,通知消息給你。Overlapped I/O只是一種模型,它可以由內(nèi)核對(duì)象(hand),事件內(nèi)核對(duì)象(hEvent), 異步過(guò)程調(diào)用(apcs) 和完成端口(I/O completion)實(shí)現(xiàn)。

            Overlapped I/O的設(shè)計(jì)的目的:
              
            取代多線(xiàn)程功能,(多線(xiàn)程存在同步機(jī)制,錯(cuò)誤處理,在成千上萬(wàn)個(gè)線(xiàn)程I/O中,線(xiàn)程上下文切換是十分消耗CPU資源的)。
               
            Overlapped I/O模型是OS為你傳遞數(shù)據(jù),完成上下文切換,在處理完之后通知你。由程序中的處理,變?yōu)镺S的處理。內(nèi)部也是用線(xiàn)程處理的。

            Overlapped數(shù)據(jù)結(jié)構(gòu):
              
            typedef struct _OVERLAPPED { // o 
                DWORD  Internal;        //通常被保留,當(dāng)GetOverlappedResult()傳回False并且GatLastError()并非傳回ERROR_IO_PENDINO時(shí),該狀態(tài)置為系統(tǒng)定的狀態(tài)。
                DWORD  InternalHigh;    //通常被保留,當(dāng)GetOverlappedResult()傳回False時(shí),為被傳輸數(shù)據(jù)的長(zhǎng)度。
                DWORD  Offset;            //指定文件的位置,從該位置傳送數(shù)據(jù),文件位置是相對(duì)文件開(kāi)始處的字節(jié)偏移量。調(diào)用 ReadFile或WriteFile函數(shù)之前調(diào)用進(jìn)程設(shè)置這個(gè)成員,讀寫(xiě)命名管道及通信設(shè)備時(shí)調(diào)用進(jìn)程忽略這個(gè)成員;
                DWORD  OffsetHigh;      //指定開(kāi)始傳送數(shù)據(jù)的字節(jié)偏移量的高位字,讀寫(xiě)命名管道及通信設(shè)備時(shí)調(diào)用進(jìn)程忽略這個(gè)成員;
                HANDLE hEvent;            //標(biāo)識(shí)事件,數(shù)據(jù)傳送完成時(shí)把它設(shè)為信號(hào)狀態(tài),調(diào)用ReadFil,eWriteFile,ConnectNamedPipe   TransactNamedPipe函數(shù)前,調(diào)用進(jìn)程設(shè)置這個(gè)成員. 相關(guān)函數(shù)CreateEvent  ResetEvent   GetOverlappedResult  WaitForSingleObject   CWinThread   GetLastError   
            } OVERLAPPED, *LPOVERLAPPED; 

            二個(gè)重要功能:
            1. 標(biāo)識(shí)每個(gè)正在overlapped 的操作。
            2.
            程序和系統(tǒng)之間提供了共享區(qū)域。參數(shù)可以在區(qū)域內(nèi)雙向傳遞。

            OVERLAPPED和數(shù)據(jù)緩沖區(qū)釋放問(wèn)題:
            在請(qǐng)求時(shí),不能釋放,只有在I/O請(qǐng)求完成之后,才可以釋放。如果發(fā)出多個(gè)overlapped請(qǐng)求,每個(gè)overlapped讀寫(xiě)操作,都必須包含文件位置(socket),另外,如果有多個(gè)磁盤(pán),I/O執(zhí)行次序無(wú)法保證。(每個(gè)overlapped都是獨(dú)立的請(qǐng)求操作)。

            內(nèi)核對(duì)象(hand)實(shí)現(xiàn):
            例子:用overlapped模型讀一個(gè)磁盤(pán)文件內(nèi)容。
            1.
            把設(shè)備句柄看作同步對(duì)象,ReadFile將設(shè)備句柄設(shè)為無(wú)信號(hào)。ReadFile 異步I/O字節(jié)位置必須在OVERLAPPED結(jié)構(gòu)中指定。
            2.
            完成I/O,設(shè)置信息狀態(tài)。為有信號(hào)。
            3.
            WaitForSingleObject或WaitForMultipleObject判斷或者異步設(shè)備調(diào)用GetOverLappedResult函數(shù)。
            int main()
            {
                BOOL rc;
                HANDLE hFile;
                DWORD numread;
                OVERLAPPED overlap;
                
            char buf[READ_SIZE];
                
            char szPath[MAX_PATH];
                CheckOsVersion();
             
                GetWindowsDirectory(szPath, 
            sizeof(szPath));
                strcat(szPath, 
            "\\WINHLP32.EXE");
                hFile 
            = CreateFile( szPath,
                                GENERIC_READ,
                                FILE_SHARE_READ
            |FILE_SHARE_WRITE,
                                NULL,
                                OPEN_EXISTING,
                                FILE_FLAG_OVERLAPPED,
                                NULL
                            );
                
            if (hFile == INVALID_HANDLE_VALUE)
                {
                    printf(
            "Could not open %s\n", szPath);
                    
            return -1;
                }
             
                memset(
            &overlap, 0sizeof(overlap));
                overlap.Offset 
            = 1500;
             
                rc 
            = ReadFile(
                            hFile,
                            buf,
                            READ_SIZE,
                            
            &numread,
                            
            &overlap
                        );
                printf(
            "Issued read request\n");
                
            if (rc)
                {
                    printf(
            "Request was returned immediately\n");
                }
                
            else
                {
                    
            if (GetLastError() == ERROR_IO_PENDING)
                    {
                        printf(
            "Request queued, waiting\n");
                        WaitForSingleObject(hFile, INFINITE);
                        printf(
            "Request completed.\n");
                        rc 
            = GetOverlappedResult(
                                                hFile,
                                                
            &overlap,
                                                
            &numread,
                                                FALSE
                                            );
                        printf(
            "Result was %d\n", rc);
                    }
                    
            else
                    {
                        printf(
            "Error reading file\n");
                    }
                }
                CloseHandle(hFile);
                
            return EXIT_SUCCESS;
            }
            事件內(nèi)核對(duì)象(hEvent):
            內(nèi)核對(duì)象(hand)實(shí)現(xiàn)的問(wèn)題:
                 不能區(qū)分那一個(gè)overlapped操作,對(duì)同一個(gè)文件handle,系統(tǒng)有多個(gè)異步操作時(shí)(一邊讀文件頭,一邊寫(xiě)文件尾, 有一個(gè)完成,就會(huì)有信號(hào),不能區(qū)分是那種操作。),為每個(gè)進(jìn)行中的overlapped調(diào)用GetOverlappedResult是不好的作法。
            事件內(nèi)核對(duì)象(hEvent)實(shí)現(xiàn)方案:
                Overlapped成員hEven標(biāo)識(shí)事件內(nèi)核對(duì)象。CreateEvent,為每個(gè)請(qǐng)求創(chuàng)建一個(gè)事件,初始化每個(gè)請(qǐng)求的hEvent成員(對(duì)同一文件多個(gè)讀寫(xiě)請(qǐng)求,每個(gè)操作綁定一個(gè)event對(duì)象)。調(diào)用WaitForMultipleObject來(lái)等等其中一個(gè)(或全部)完成。
               
            另外Event對(duì)象必須是手動(dòng)重置。使用自動(dòng)重置(在等待event之前設(shè)置,WaitForSingleObject()和 WaitForMultipleObjects()函數(shù)永不返回)。
                自動(dòng)重置事件
               
            WaitForSingleObject()和 WaitForMultipleObjects()會(huì)等待事件到信號(hào)狀態(tài),隨后又自動(dòng)將其重置為非信號(hào)狀態(tài),這樣保證了等待此事件的線(xiàn)程中只有一個(gè)會(huì)被喚醒。
               
            手動(dòng)重置事件
               
            需要用戶(hù)調(diào)用ResetEvent()才會(huì)重置事件。可能有若干個(gè)線(xiàn)程在等待同一事件, 這樣當(dāng)事件變?yōu)樾盘?hào)狀態(tài)時(shí),所有等待線(xiàn)程都可以運(yùn)行了。 SetEvent()函數(shù)用來(lái)把事件對(duì)象設(shè)置成信號(hào)狀態(tài),ResetEvent()把事件對(duì)象重置成非信號(hào)狀態(tài),兩者均需事件對(duì)象句柄作參數(shù)。
                
            相關(guān)例子如下:
            int main()
            {
                BOOL rc;
                HANDLE hFile;
                DWORD numread;
                OVERLAPPED overlap;
                
            char buf[READ_SIZE];
                
            char szPath[MAX_PATH];
                CheckOsVersion();
             
                GetWindowsDirectory(szPath, 
            sizeof(szPath));
                strcat(szPath, 
            "\\WINHLP32.EXE");
                hFile 
            = CreateFile( szPath,
                                GENERIC_READ,
                                FILE_SHARE_READ
            |FILE_SHARE_WRITE,
                                NULL,
                                OPEN_EXISTING,
                                FILE_FLAG_OVERLAPPED,
                                NULL
                            );
                
            if (hFile == INVALID_HANDLE_VALUE)
                {
                    printf(
            "Could not open %s\n", szPath);
                    
            return -1;
                }
             
                memset(
            &overlap, 0sizeof(overlap));
                overlap.Offset 
            = 1500;
             
                rc 
            = ReadFile(
                            hFile,
                            buf,
                            READ_SIZE,
                            
            &numread,
                            
            &overlap
                        );
                printf(
            "Issued read request\n");
                
            if (rc)
                {
                    printf(
            "Request was returned immediately\n");
                }
                
            else
                {
                    
            if (GetLastError() == ERROR_IO_PENDING)
                    {
                        printf(
            "Request queued, waiting\n");
                        WaitForSingleObject(hFile, INFINITE);
                        printf(
            "Request completed.\n");
                        rc 
            = GetOverlappedResult(
                                                hFile,
                                                
            &overlap,
                                                
            &numread,
                                                FALSE
                                            );
                        printf(
            "Result was %d\n", rc);
                    }
                    
            else
                    {
                        printf(
            "Error reading file\n");
                    }
                }
                CloseHandle(hFile);
                
            return EXIT_SUCCESS;
            }

            異步過(guò)程調(diào)用(apcs):
            事件內(nèi)核對(duì)象(hEvent)的問(wèn)題:
                事件內(nèi)核對(duì)象在使用WaitForMultipleObjects時(shí),只能等待64個(gè)對(duì)象。需要另建兩個(gè)數(shù)據(jù)組,并gOverlapped[nIndex].hEvent = ghEvents[nIndex]綁定起來(lái)。
            異步過(guò)程調(diào)用(apcs)實(shí)現(xiàn)方案:
               
            異步過(guò)程調(diào)用,callback回調(diào)函數(shù),在一個(gè)Overlapped I/O完成之后,系統(tǒng)調(diào)用該回調(diào)函數(shù)。OS在有信號(hào)狀態(tài)下(設(shè)備句柄),才會(huì)調(diào)用回調(diào)函數(shù)(可能有很多APCS等待處理了),傳給它完成I/O請(qǐng)求的錯(cuò)誤碼,傳輸字節(jié)數(shù)和Overlapped結(jié)構(gòu)的地址。
            五個(gè)函數(shù)可以設(shè)置信號(hào)狀態(tài):
            1.SleepEx
            2.WaitForSingleObjectEx
            3.WaitForMultipleObjectEx
            4.SingalObjectAndWait
            5.MsgWaitForMultipleObjectsEx
            Main函數(shù)調(diào)用WaitForSingleObjectEx, APCS被處理,調(diào)用回調(diào)函數(shù)FileIOCompletionRoutine
            VOID WINAPI FileIOCompletionRoutine(
                DWORD dwErrorCode, 
            // completion code
                DWORD dwNumberOfBytesTransfered,    // number of bytes transferred
                LPOVERLAPPED lpOverlapped   // pointer to structure with I/O information 
               )
            {
                
            int nIndex = (int)(lpOverlapped->hEvent);
                printf(
            "Read #%d returned %d. %d bytes were read.\n",
                    nIndex,
                    dwErrorCode,
                    dwNumberOfBytesTransfered);
             
                
            if (++nCompletionCount == MAX_REQUESTS)
                    SetEvent(ghEvent); 
            // Cause the wait to terminate
            }
             
            int main()
            {
                
            int i;
                
            char szPath[MAX_PATH];
                CheckOsVersion();
             
                MTVERIFY(
                    ghEvent 
            = CreateEvent(
                                 NULL,    
            // No security
                                 TRUE,    // Manual reset - extremely important!
                                 FALSE,   // Initially set Event to non-signaled state
                                 NULL     // No name
                                )
                );
             
                GetWindowsDirectory(szPath, 
            sizeof(szPath));
                strcat(szPath, 
            "\\WINHLP32.EXE");
              
                ghFile 
            = CreateFile( szPath,
                                GENERIC_READ,
                                FILE_SHARE_READ
            |FILE_SHARE_WRITE,
                                NULL,
                                OPEN_EXISTING,
                                FILE_FLAG_OVERLAPPED,
                                NULL
                            );
                
            if (ghFile == INVALID_HANDLE_VALUE)
                {
                    printf(
            "Could not open %s\n", szPath);
                    
            return -1;
                }
             
                
            for (i=0; i<MAX_REQUESTS; i++)
            {
                    QueueRequest(i, i
            *16384, READ_SIZE);
                }
                printf(
            "QUEUED!!\n");
             
                
            for (;;)
                {
                    DWORD rc;
                    rc 
            = WaitForSingleObjectEx(ghEvent, INFINITE, TRUE );
                    
            if (rc == WAIT_OBJECT_0)
                        
            break;
                    MTVERIFY(rc 
            == WAIT_IO_COMPLETION);
                }
             
                CloseHandle(ghFile);
                
            return EXIT_SUCCESS;
            }
             
            int QueueRequest(int nIndex, DWORD dwLocation, DWORD dwAmount)
            {
                
            int i;
                BOOL rc;
                DWORD err;
             
                gOverlapped[nIndex].hEvent 
            = (HANDLE)nIndex;
                gOverlapped[nIndex].Offset 
            = dwLocation;
             
                
            for (i=0; i<MAX_TRY_COUNT; i++)
                {
                    rc 
            = ReadFileEx(
                        ghFile,
                        gBuffers[nIndex],
                        dwAmount,
                        
            &gOverlapped[nIndex],
                        FileIOCompletionRoutine
                    );
             
                    
            if (rc)
                    {
                        printf(
            "Read #%d queued for overlapped I/O.\n", nIndex);
                        
            return TRUE;
                    }
                    err 
            = GetLastError();
             
                    
            if ( err == ERROR_INVALID_USER_BUFFER ||
                         err 
            == ERROR_NOT_ENOUGH_QUOTA ||
                         err 
            == ERROR_NOT_ENOUGH_MEMORY )
                    {
                        Sleep(
            50); // Wait around and try later
                        continue;
                    }
                    
            break;
                }
             
                printf(
            "ReadFileEx failed.\n");
                
            return -1;
            }
            完成端口(I/O completion):
            異步過(guò)程調(diào)用(apcs)問(wèn)題:
               
            只有發(fā)overlapped請(qǐng)求的線(xiàn)程才可以提供callback函數(shù)(需要一個(gè)特定的線(xiàn)程為一個(gè)特定的I/O請(qǐng)求服務(wù))。
            完成端口(I/O completion)的優(yōu)點(diǎn):
               
            不會(huì)限制handle個(gè)數(shù),可處理成千上萬(wàn)個(gè)連接。I/O completion port允許一個(gè)線(xiàn)程將一個(gè)請(qǐng)求暫時(shí)保存下來(lái),由另一個(gè)線(xiàn)程為它做實(shí)際服務(wù)。
            并發(fā)模型與線(xiàn)程池:
                在典型的并發(fā)模型中,服務(wù)器為每一個(gè)客戶(hù)端創(chuàng)建一個(gè)線(xiàn)程,如果很多客戶(hù)同時(shí)請(qǐng)求,則這些線(xiàn)程都是運(yùn)行的,那么CPU就要一個(gè)個(gè)切換,CPU花費(fèi)了更多的時(shí)間在線(xiàn)程切換,線(xiàn)程確沒(méi)得到很多CPU時(shí)間。到底應(yīng)該創(chuàng)建多少個(gè)線(xiàn)程比較合適呢,微軟件幫助文檔上講應(yīng)該是2*CPU個(gè)。但理想條件下最好線(xiàn)程不要切換,而又能象線(xiàn)程池一樣,重復(fù)利用。I/O完成端口就是使用了線(xiàn)程池。


            理解與使用:
            第一步:
                在我們使用完成端口之前,要調(diào)用CreateIoCompletionPort函數(shù)先創(chuàng)建完成端口對(duì)象。定義如下:
            HANDLE CreateIoCompletionPort(
                                            HANDLE FileHandle,
                                            HANDLE ExistingCompletionPort,
                                            DWORD CompletionKey,
                                            DWORD NumberOfConcurrentThreads
            );
            FileHandle:
                文件或設(shè)備的handle, 如果值為INVALID_HANDLE_VALUE則產(chǎn)生一個(gè)沒(méi)有和任何文件handle有關(guān)系的port.( 可以用來(lái)和完成端口聯(lián)系的各種句柄,文件,套接字)
            ExistingCompletionPort:
                NULL時(shí)生成一個(gè)新port, 否則handle會(huì)加到此port上。
            CompletionKey:
                用戶(hù)自定義數(shù)值,被交給服務(wù)的線(xiàn)程。GetQueuedCompletionStatus函數(shù)時(shí)我們可以完全得到我們?cè)诖寺?lián)系函數(shù)中的完成鍵(申請(qǐng)的內(nèi)存塊)。在GetQueuedCompletionStatus中可以完封不動(dòng)的得到這個(gè)內(nèi)存塊,并且使用它。
            NumberOfConcurrentThreads:
                參數(shù)NumberOfConcurrentThreads用來(lái)指定在一個(gè)完成端口上可以并發(fā)的線(xiàn)程數(shù)量。理想的情況是,一個(gè)處理器上只運(yùn)行一 個(gè)線(xiàn)程,這樣可以避免線(xiàn)程上下文切換的開(kāi)銷(xiāo)。如果這個(gè)參數(shù)的值為0,那就是告訴系統(tǒng)線(xiàn)程數(shù)與處理器數(shù)相同。我們可以用下面的代碼來(lái)創(chuàng)建I/O完成端口。


            隱藏在之創(chuàng)建完成端口的秘密:
            1.創(chuàng)建一個(gè)完成端口
                CreateIoCompletionPort(INVALID_HANDLE_VALUE, 0, 0, dwNumberOfConcurrentThreads);
            2.設(shè)備列表,完成端口把它同一個(gè)或多個(gè)設(shè)備相關(guān)聯(lián)。
                CreateIoCompletionPort(hDevice, hCompPort, dwCompKey, 0) ;
            第二步:
              
            根據(jù)處理器個(gè)數(shù),創(chuàng)建cpu*2個(gè)工作線(xiàn)程:CreateThread(NULL, 0, ServerWorkerThread, CompletionPort,0, &ThreadID))與此同時(shí),服務(wù)器調(diào)用WSASocket,bind, listen, WSAAccept,之后,調(diào)用CreateIoCompletionPort((HANDLE) Accept, CompletionPort... )把一個(gè)套接字句柄和一個(gè)完成端口綁定到一起。完成端口又同一個(gè)或多個(gè)設(shè)備相關(guān)聯(lián)著,所以以套接字為基礎(chǔ),投遞發(fā)送和請(qǐng)求,對(duì)I/O處理。接著,可以依賴(lài)完成端口,接收有關(guān)I/O操作完成情況的通知。再看程序里:WSARecv(Accept, &(PerIoData->DataBuf), 1, &RecvBytes, &Flags,&(PerIoData->Overlapped), NULL)開(kāi)始調(diào)用,這里象前面講過(guò)的一樣,既然是異步I/O,所以WSASend和WSARecv的調(diào)用會(huì)立即返回。

            系統(tǒng)處理:
               
            當(dāng)一個(gè)設(shè)備的異步I/O請(qǐng)求完成之后,系統(tǒng)會(huì)檢查該設(shè)備是否關(guān)聯(lián)了一個(gè)完成端口,如果是,系統(tǒng)就向該完成端口的I/O完成隊(duì)列中加入完成的I/O請(qǐng)求列。
               
            然后我們需要從這個(gè)完成隊(duì)列中,取出調(diào)用后的結(jié)果(需要通過(guò)一個(gè)Overlapped結(jié)構(gòu)來(lái)接收調(diào)用的結(jié)果)。怎么知道這個(gè)隊(duì)列中已經(jīng)有處理后的結(jié)果呢,調(diào)用GetQueuedCompletionStatus函數(shù)。
            工作線(xiàn)程與完成端口:
                和異步過(guò)程調(diào)用不同(在一個(gè)Overlapped I/O完成之后,系統(tǒng)調(diào)用該回調(diào)函數(shù)。OS在有信號(hào)狀態(tài)下(設(shè)備句柄),才會(huì)調(diào)用回調(diào)函數(shù)(可能有很多APCS等待處理了))
            GetQueuedCompletionStatus在工作線(xiàn)程內(nèi)調(diào)用GetQueuedCompletionStatus函數(shù)。
            GetQueuedCompletionStatus(
                HANDLE CompletionPort,
                LPDWORD lpNumberOfBytesTransferred,
                LPDWORD lpCompletionKey,
                LPOVERLAPPED *lpOverlapped,
                DWORD dwMilliseconds
            );
            CompletionPort:指出了線(xiàn)程要監(jiān)視哪一個(gè)完成端口。很多服務(wù)應(yīng)用程序只是使用一個(gè)I/O完成端口,所有的I/O請(qǐng)求完成以后的通知都將發(fā)給該端口。
            lpNumberOfBytesTransferred:傳輸?shù)臄?shù)據(jù)字節(jié)數(shù)
            lpCompletionKey:
                完成端口的單句柄數(shù)據(jù)指針,這個(gè)指針將可以得到我們?cè)贑reateIoCompletionPort中申請(qǐng)那片內(nèi)存。
            lpOverlapped:
                重疊I/O請(qǐng)求結(jié)構(gòu),這個(gè)結(jié)構(gòu)同樣是指向我們?cè)谥丿B請(qǐng)求時(shí)所申請(qǐng)的內(nèi)存塊,同時(shí)和lpCompletionKey,一樣我們也可以利用這個(gè)內(nèi)存塊來(lái)存儲(chǔ)我們要保存的任意數(shù)據(jù)。
            dwMilliseconds:
                等待的最長(zhǎng)時(shí)間(毫秒),如果超時(shí),lpOverlapped被設(shè)為NULL,函數(shù)返回False.
            GetQueuedCompletionStatus功能及隱藏的秘密:
            GetQueuedCompletionStatus使調(diào)用線(xiàn)程掛起,直到指定的端口的I/O完成隊(duì)列中出現(xiàn)了一項(xiàng)或直到超時(shí)。(I/0完成隊(duì)列中出現(xiàn)了記錄)調(diào)用GetQueuedCompletionStatus時(shí),調(diào)用線(xiàn)程的ID(cpu*2個(gè)線(xiàn)程,每個(gè)ServerWorkerThread的線(xiàn)程ID)就被放入該等待線(xiàn)程隊(duì)列中。
                 等待線(xiàn)程隊(duì)列很簡(jiǎn)單,只是保存了這些線(xiàn)程的ID。完成端口會(huì)按照后進(jìn)先出的原則將一個(gè)線(xiàn)程隊(duì)列的ID放入到釋放線(xiàn)程列表中。
                這樣,I/O完成端口內(nèi)核對(duì)象就知道哪些線(xiàn)程正在等待處理完成的I/O請(qǐng)求。當(dāng)端口的I/O完成隊(duì)列出現(xiàn)一項(xiàng)時(shí),完成端口就喚醒(睡眠狀態(tài)中變?yōu)榭烧{(diào)度狀態(tài))等待線(xiàn)程隊(duì)列中的一個(gè)線(xiàn)程。線(xiàn)程將得到完成I/O項(xiàng)中的信息:傳輸?shù)淖止?jié)數(shù),完成鍵(單句柄數(shù)據(jù)結(jié)構(gòu))和Overlapped結(jié)構(gòu)地址,線(xiàn)程是通過(guò)GetQueuedCompletionStatus返回這些信息,等待CPU的調(diào)度。
            GetQueuedCompletionStatus返回可能有多種原因,如果傳遞無(wú)效完成端口句柄,函數(shù)返回False,GetLastError返回一個(gè)錯(cuò)誤(ERROR_INVALID_HANDLE),如果超時(shí),返回False, GetLastError返回WAIT_TIMEOUT, i/o完成隊(duì)列刪除一項(xiàng),該表項(xiàng)是一個(gè)成功完成的I/O請(qǐng)求,則返回True。
             
                調(diào)用GetQueuedCompletionStatus的線(xiàn)程是后進(jìn)先出的方式喚醒的,比如有4個(gè)線(xiàn)程等待,如果有一個(gè)I/O,最后一個(gè)調(diào)用GetQueuedCompletionStatus的線(xiàn)程被喚醒來(lái)處理。處理完之后,再調(diào)用 GetQueuedCompletionStatus進(jìn)入等待線(xiàn)程隊(duì)列中。
             
            深入分析完成端口線(xiàn)程池調(diào)度原理:
                假設(shè)我們運(yùn)行在2CPU的機(jī)器上。創(chuàng)建完成端口時(shí)指定2個(gè)并發(fā),創(chuàng)建了4個(gè)工作線(xiàn)程加入線(xiàn)程池中等待完成I/O請(qǐng)求,且完成端口隊(duì)列(先入先出)中有3個(gè)完成I/O的請(qǐng)求的情況:
               工作線(xiàn)程運(yùn)行, 創(chuàng)建了4個(gè)工作線(xiàn)程,調(diào)用GetQueuedCompletionStatus時(shí),該調(diào)用線(xiàn)程就進(jìn)入了睡眠狀態(tài),假設(shè)這個(gè)時(shí)候,I/O完成隊(duì)列出現(xiàn)了三項(xiàng),調(diào)用線(xiàn)程的ID就被放入該等待線(xiàn)程隊(duì)列中, (如圖):
            等待線(xiàn)程隊(duì)列(先進(jìn)后出)
            進(jìn)隊(duì)列
            出隊(duì)列
            線(xiàn)程A

            線(xiàn)程B

            線(xiàn)程C


            線(xiàn)程D

                I/O完成端口內(nèi)核對(duì)象(第3個(gè)參數(shù)等級(jí)線(xiàn)程隊(duì)列),因此知道哪些線(xiàn)程正在等待處理完成的I/O請(qǐng)求。當(dāng)端口的I/O完成隊(duì)列出現(xiàn)一項(xiàng)時(shí),完成端口就喚醒(睡眠狀態(tài)中變?yōu)榭烧{(diào)度狀態(tài))等待線(xiàn)程隊(duì)列中的一個(gè)線(xiàn)程(前面講過(guò)等待線(xiàn)程隊(duì)列是后進(jìn)先出)。所以線(xiàn)程D將得到完成I/O項(xiàng)中的信息:傳輸?shù)淖止?jié)數(shù),完成鍵(單句柄數(shù)據(jù)結(jié)構(gòu))和Overlapped結(jié)構(gòu)地址,線(xiàn)程是通過(guò)GetQueuedCompletionStatus返回這些信息。
                在前面我們指定了并發(fā)線(xiàn)程的數(shù)目是2,所以I/O完成端口喚醒2個(gè)線(xiàn)程,線(xiàn)程D和線(xiàn)程C,另兩個(gè)繼續(xù)休眠(線(xiàn)程B,線(xiàn)程A),直到線(xiàn)程D處理完了,發(fā)現(xiàn)表項(xiàng)里還有要處理的,就喚醒同一線(xiàn)程繼續(xù)處理。
            等待線(xiàn)程隊(duì)列(后進(jìn)先出
            進(jìn)隊(duì)列
            出隊(duì)列
            線(xiàn)程A

            線(xiàn)程B
                    
            釋放線(xiàn)程隊(duì)列
            線(xiàn)程C

            線(xiàn)程D


            線(xiàn)程并發(fā)量:
                并發(fā)量限制了與該完成端口相關(guān)聯(lián)的可運(yùn)行線(xiàn)程的數(shù)目, 它類(lèi)似閥門(mén)的作用。 當(dāng)與該完成端口相關(guān)聯(lián)的可運(yùn)行線(xiàn)程的總數(shù)目達(dá)到了該并發(fā)量,系統(tǒng)就會(huì)阻塞任何與該完成端口相關(guān)聯(lián)的后續(xù)線(xiàn)程的執(zhí)行, 直到與該完成端口相關(guān)聯(lián)的可運(yùn)行線(xiàn)程數(shù)目下降到小于該并發(fā)量為止。所以解釋了線(xiàn)程池中的運(yùn)行線(xiàn)程可能會(huì)比設(shè)置的并發(fā)線(xiàn)程多的原因。
                它的作用:
            最有效的假想是發(fā)生在有完成包在隊(duì)列中等待,而沒(méi)有等待被滿(mǎn)足,因?yàn)榇藭r(shí)完成端口達(dá)到了其并發(fā)量的極限。此時(shí),一個(gè)正在運(yùn)行中的線(xiàn)程調(diào)用 GetQueuedCompletionStatus時(shí),它就會(huì)立刻從隊(duì)列中取走該完成包。這樣就不存在著環(huán)境的切換,因?yàn)樵撎幱谶\(yùn)行中的線(xiàn)程就會(huì)連續(xù)不斷地從隊(duì)列中取走完成包,而其他的線(xiàn)程就不能運(yùn)行了。
            注意:如果池中的所有線(xiàn)程都在忙,客戶(hù)請(qǐng)求就可能拒絕,所以要適當(dāng)調(diào)整這個(gè)參數(shù),獲得最佳性能。
            線(xiàn)程并發(fā):D線(xiàn)程掛起,加入暫停線(xiàn)程,醒來(lái)后又加入釋放線(xiàn)程隊(duì)列。
            線(xiàn)程C
             
            線(xiàn)程B
             
            線(xiàn)程A

            出隊(duì)列
            進(jìn)隊(duì)列
            等待的線(xiàn)程隊(duì)列(后進(jìn)先出)
            釋放線(xiàn)程隊(duì)列
            暫停線(xiàn)程
             
            線(xiàn)程D
            線(xiàn)程的安全退出:
            PostQueudCompletionStatus函數(shù),我們可以用它發(fā)送一個(gè)自定義的包含了OVERLAPPED成員變量的結(jié)構(gòu)地址,里面包含一個(gè)狀態(tài)變量,當(dāng)狀態(tài)變量為退出標(biāo)志時(shí),線(xiàn)程就執(zhí)行清除動(dòng)作然后退出。
             
            完成端口使用需要注意的地方:
             1.在執(zhí)行wsasend和wsarecv操作前,請(qǐng)先將overlapped結(jié)構(gòu)體使用memset進(jìn)行清零。





             

            posted on 2008-01-07 22:47 isabc 閱讀(13261) 評(píng)論(1)  編輯 收藏 引用 所屬分類(lèi): Win32 多線(xiàn)程

            評(píng)論

            # re: Overlapped I/O模型深入分析 2008-01-07 23:08 isabc

            從網(wǎng)上整理的文章,同樣,這只是為了我增加理解記憶而做到得筆記,
            不存在利用價(jià)值,純粹是學(xué)習(xí)和記憶.抄襲也好學(xué)習(xí)也好只是讓人明
            白道理.主要干活的還是自己的程序.

            I/O設(shè)備處理必然讓主程序停下來(lái)干等I/O的完成,
            對(duì)這個(gè)問(wèn)題有

            方法一:使用另一個(gè)線(xiàn)程進(jìn)行I/O。這個(gè)方案可行,但是麻煩。

            方法二:使用overlapped I/O。
            正如書(shū)上所說(shuō):“overlapped I/O是WIN32的一項(xiàng)技術(shù),
            你可以要求操作系統(tǒng)為你傳送數(shù)據(jù),并且在傳送完畢時(shí)通知你。
            這項(xiàng)技術(shù)使你的程序在I/O進(jìn)行過(guò)程中仍然能夠繼續(xù)處理事務(wù)。
            事實(shí)上,操作系統(tǒng)內(nèi)部正是以線(xiàn)程來(lái)I/O完成overlapped I/O。
            你可以獲得線(xiàn)程的所有利益,而不需付出什么痛苦的代價(jià)”。


            怎樣使用overlapped I/O:

            進(jìn)行I/O操作時(shí),指定overlapped方式
            使用CreateFile (),將其第6個(gè)參數(shù)指定為FILE_FLAG_OVERLAPPED,
            就是準(zhǔn)備使用overlapped的方式構(gòu)造或打開(kāi)文件;
            如果采用 overlapped,那么ReadFile()、WriteFile()的第5個(gè)參數(shù)必須提供一個(gè)指針,
            指向一個(gè)OVERLAPPED結(jié)構(gòu)。 OVERLAPPED用于記錄了當(dāng)前正在操作的文件一些相關(guān)信息。

            //功能:從指定文件的1500位置讀入300個(gè)字節(jié)
            int main()
            {
            BOOL rc;
            HANDLE hFile;
            DWORD numread;
            OVERLAPPED overlap;
            char buf[512];
            char szPath=”x:\\xxxx\xxxx”;

            //檢查系統(tǒng),確定是否支持overlapped,(NT以上操作系統(tǒng)支持OVERLAPPED)
            CheckOsVersion();
            // 以overlapped的方式打開(kāi)文件
            hFile = CreateFile( szPath,
            GENERIC_READ,
            FILE_SHARE_READ|FILE_SHARE_WRITE,
            NULL,
            OPEN_EXISTING,
            FILE_FLAG_OVERLAPPED,
            NULL
            );

            // OVERLAPPED結(jié)構(gòu)實(shí)始化為0
            memset(&overlap, 0, sizeof(overlap));
            //指定文件位置是1500;
            overlap.Offset = 1500;

            rc = ReadFile(hFile,buf,300,&numread,&overlap);
            //因?yàn)槭莖verlapped操作,ReadFile會(huì)將讀文件請(qǐng)求放入讀隊(duì)列之后立即返回(false),
            //而不會(huì)等到文件讀完才返回(true)
            if (rc)
            {
            //文件真是被讀完了,rc為true
            // 或當(dāng)數(shù)據(jù)被放入cache中,或操作系統(tǒng)認(rèn)為它可以很快速地取得數(shù)據(jù),rc為true
            }
            else
            {
            if (GetLastError() == ERROR_IO_PENDING)
            {//當(dāng)錯(cuò)誤是ERROR_IO_PENDING,那意味著讀文件的操作還在進(jìn)行中
            //等候,直到文件讀完
            WaitForSingleObject(hFile, INFINITE);
            rc = GetOverlappedResult(hFile,&overlap,&numread,FALSE);
            //上面二條語(yǔ)句完成的功能與下面一條語(yǔ)句的功能等價(jià):
            // GetOverlappedResult(hFile,&overlap,&numread,TRUE);
            }
            else
            {
            //出錯(cuò)了
            }
            }
            CloseHandle(hFile);
            return EXIT_SUCCESS;
            }

            在實(shí)際工作中,若有幾個(gè)操作同一個(gè)文件時(shí),
            怎么辦?我們可以利用OVERLAPPED結(jié)構(gòu)中提供的event來(lái)解決上面遇到的問(wèn)題。
            注意,你所使用的event對(duì)象必須是一個(gè)MANUAL型的;否則,可能產(chǎn)生競(jìng)爭(zhēng)條件。
            原因見(jiàn)書(shū)P159。
            int main()
            {
            int i;
            BOOL rc;
            char szPath=”x:\\xxxx\xxxx”;
            // 以overlapped的方式打開(kāi)文件
            ghFile = CreateFile( szPath,
            GENERIC_READ,
            FILE_SHARE_READ|FILE_SHARE_WRITE,
            NULL,
            OPEN_EXISTING,
            FILE_FLAG_OVERLAPPED,
            NULL
            );
            for (i=0; i<MAX_REQUESTS; i++)
            {
            //將同一文件按幾個(gè)部分按overlapped方式同時(shí)讀
            //注意看QueueRequest函數(shù)是如何運(yùn)做的,每次讀16384個(gè)塊
            QueueRequest(i, i*16384, READ_SIZE);
            }
            // 等候所有操作結(jié)束;
            //隱含條件:當(dāng)一個(gè)操作完成時(shí),其對(duì)應(yīng)的event對(duì)象會(huì)被激活
            WaitForMultipleObjects(MAX_REQUESTS, ghEvents, TRUE, INFINITE);
            // 收尾操作
            for (i=0; i<MAX_REQUESTS; i++)
            {
            DWORD dwNumread;
            rc = GetOverlappedResult(
            ghFile,
            &gOverlapped[i],
            &dwNumread,
            FALSE
            );
            CloseHandle(gOverlapped[i].hEvent);
            }
            CloseHandle(ghFile);
            return EXIT_SUCCESS;
            }

            //當(dāng)讀操作完成以后,gOverlapped[nIndex].hEvent會(huì)系統(tǒng)被激發(fā)
            int QueueRequest(int nIndex, DWORD dwLocation, DWORD dwAmount)
            {
            //構(gòu)造一個(gè)MANUAL型的event對(duì)象
            ghEvents[nIndex] = CreateEvent(NULL, TRUE, FALSE, NULL);
            //將此event對(duì)象置入OVERLAPPED結(jié)構(gòu)
            gOverlapped[nIndex].hEvent = ghEvents[nIndex];
            gOverlapped[nIndex].Offset = dwLocation;
            for (i=0; i<MAX_TRY_COUNT; i++)
            {
            //文件ghFile唯一
            rc = ReadFile(ghFile, gBuffers[nIndex],&dwNumread,&gOverlapped[nIndex]);
            if (rc)
            return TRUE;
            err = GetLastError();
            if (err == ERROR_IO_PENDING)
            {
            //當(dāng)錯(cuò)誤是ERROR_IO_PENDING,那意味著讀文件的操作還在進(jìn)行中
            return TRUE;
            }
            // 處理一些可恢復(fù)的錯(cuò)誤
            if ( err == ERROR_INVALID_USER_BUFFER ||
            err == ERROR_NOT_ENOUGH_QUOTA ||
            err == ERROR_NOT_ENOUGH_MEMORY )
            {
            sleep(50);
            continue;//重試
            }
            // 如果GetLastError()返回的不是以上列出的錯(cuò)誤,放棄
            break;
            }

            return -1;

            }

              回復(fù)  更多評(píng)論   

            廣告信息(免費(fèi)廣告聯(lián)系)

            中文版MSDN:
            歡迎體驗(yàn)

            99国产欧美久久久精品蜜芽| 日韩乱码人妻无码中文字幕久久| 久久久无码精品亚洲日韩按摩 | 久久受www免费人成_看片中文| 国产精品永久久久久久久久久| 青青青国产成人久久111网站| 精品久久久久久中文字幕人妻最新| 久久精品aⅴ无码中文字字幕不卡| 热久久国产欧美一区二区精品| 精品人妻伦九区久久AAA片69| 99久久精品费精品国产| 久久97精品久久久久久久不卡| 99久久er这里只有精品18| 亚洲AV日韩精品久久久久久久| 亚洲国产精品无码久久久蜜芽 | 亚洲va久久久噜噜噜久久男同| 精品国产乱码久久久久软件| 久久久久免费精品国产| 久久热这里只有精品在线观看| 久久久精品人妻一区二区三区蜜桃| 久久人人爽人人爽人人爽| 亚洲va久久久噜噜噜久久天堂| 久久久久无码精品国产不卡| 国产一区二区三区久久| 青青草原综合久久| 欧美性大战久久久久久| 久久午夜福利无码1000合集| 久久久久亚洲AV无码专区首JN| 日韩乱码人妻无码中文字幕久久 | 老司机午夜网站国内精品久久久久久久久| 激情综合色综合久久综合| 久久久久99这里有精品10| 久久久久久人妻无码| 99久久精品费精品国产| 亚洲欧美一级久久精品| 久久精品午夜一区二区福利| 99久久www免费人成精品| 国产精品久久久久久五月尺| MM131亚洲国产美女久久| 日日狠狠久久偷偷色综合96蜜桃| 久久精品人人槡人妻人人玩AV|