UNBUFCP2.C
上传用户:bangxh
上传日期:2007-01-31
资源大小:42235k
文件大小:19k
源码类别:

Windows编程

开发平台:

Visual C++

  1. /*++
  2. Copyright (c) 1994-1997  Microsoft Corporation
  3. Module Name:
  4.     unbufcp2.c
  5. Abstract:
  6.     Intended to demonstrate how to complete I/O in a different thread 
  7.     asynchronously than the I/O was started from.  This is useful for 
  8.     people who want a more powerful mechanism of asynchronous completion 
  9.     callbacks than Win32 provides (i.e. VMS developers who are used to ASTs).
  10.     Two threads are used. The first thread posts overlapped reads from the
  11.     source file. These reads complete to an I/O completion port the second
  12.     thread is waiting on. The second thread sees the I/O completion and
  13.     posts an overlapped write to the destination file. The write completes
  14.     to another I/O completion port that the first thread is waiting on.
  15.     The first thread sees the I/O completion and posts another overlapped
  16.     read.
  17.     Thread 1                                        Thread 2
  18.        |                                               |
  19.        |                                               |
  20.     kick off a few                           -->GetQueuedCompletionStatus(ReadPort)
  21.     overlapped reads                         |         |
  22.        |                                     |         |
  23.        |                                     |  read has completed,
  24.   ->GetQueuedCompletionStatus(WritePort)     |  kick off the corresponding
  25.   |    |                                     |  write.
  26.   | write has completed,                     |         |
  27.   | kick off another                         |_________|
  28.   | read
  29.   |    |
  30.   |____|
  31. Author:
  32.     John Vert (jvert) 21-Dec-1994
  33. Revision History:
  34.     24-Apr-1995       Brian Sherrell (briansh) - Added platform detection & 
  35.                       I/O Completion Port creation with INVALID_HANDLE_VALUE.
  36.     22-March-1996     Brian Sherrell (briansh) - Fixed error on files greater
  37.                       than 1 Meg.
  38.      3-Apr-1996       Brian Sherrell (briansh) - Correct versioning logic.
  39. --*/
  40. #include <windows.h>
  41. #include <stdio.h>
  42. #include <stdlib.h>
  43. #include <ctype.h>
  44. //
  45. // File handles for the copy operation. All read operations are
  46. // from SourceFile. All write operations are to DestFile.
  47. //
  48. HANDLE SourceFile;
  49. HANDLE DestFile;
  50. //
  51. // I/O completion ports. All reads from the source file complete
  52. // to ReadPort. All writes to the destination file complete to
  53. // WritePort.
  54. //
  55. HANDLE ReadPort;
  56. HANDLE WritePort;
  57. //
  58. //version information
  59. //
  60. OSVERSIONINFO     ver;
  61. //
  62. // Structure used to track each outstanding I/O. The maximum
  63. // number of I/Os that will be outstanding at any time is
  64. // controllable by the MAX_CONCURRENT_IO definition.
  65. //
  66. #define MAX_CONCURRENT_IO 20
  67. typedef struct _COPY_CHUNK {
  68.     OVERLAPPED Overlapped;
  69.     LPVOID Buffer;
  70. } COPY_CHUNK, *PCOPY_CHUNK;
  71. COPY_CHUNK CopyChunk[MAX_CONCURRENT_IO];
  72. //
  73. // Define the size of the buffers used to do the I/O.
  74. // 64K is a nice number.
  75. //
  76. #define BUFFER_SIZE (64*1024)
  77. //
  78. // The system's page size will always be a multiple of the
  79. // sector size. Do all I/Os in page-size chunks.
  80. //
  81. DWORD PageSize;
  82. //
  83. // Local function prototypes
  84. //
  85. VOID
  86. WINAPI
  87. WriteLoop(
  88.     ULARGE_INTEGER FileSize
  89.     );
  90. VOID
  91. ReadLoop(
  92.     ULARGE_INTEGER FileSize
  93.     );
  94. int
  95. _CRTAPI1
  96. main(
  97.     int argc,
  98.     char *argv[],
  99.     char *envp
  100.     )
  101. {
  102.     HANDLE WritingThread;
  103.     DWORD ThreadId;
  104.     ULARGE_INTEGER FileSize;
  105.     ULARGE_INTEGER InitialFileSize;
  106.     BOOL Success;
  107.     DWORD Status;
  108.     DWORD StartTime, EndTime;
  109.     SYSTEM_INFO SystemInfo;
  110.     HANDLE BufferedHandle;
  111.     if (argc != 3) {
  112.         fprintf(stderr, "Usage: %s SourceFile DestinationFilen", argv[0]);
  113.         exit(1);
  114.     }
  115.     //
  116.     //confirm we are running on Windows NT 3.5 or greater, if not, display notice and 
  117.     //terminate.  Completion ports are only supported on Win32 & Win32s.  Creating a 
  118.     //Completion port with no handle specified is only supported on NT 3.51, so we need 
  119.     //to know what we're running on.  Note, Win32s does not support console apps, thats 
  120.     //why we exit here if we are not on Windows NT.
  121.     //
  122.     //
  123.     //ver.dwOSVersionInfoSize needs to be set before calling GetVersionInfoEx()
  124.     //
  125.     ver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
  126.     //
  127.     //Failure here could mean several things 1. On an NT system, 
  128.     //it indicates NT version 3.1 because GetVersionEx() is only 
  129.     //implemented on NT 3.5.  2. On Windows 3.1 system, it means 
  130.     //either Win32s version 1.1 or 1.0 is installed.
  131.     //
  132.     Success = GetVersionEx((LPOSVERSIONINFO) &ver);
  133.     if ( (!Success) ||                                   //GetVersionEx() failed - see above.
  134.          (ver.dwPlatformId != VER_PLATFORM_WIN32_NT) )   //GetVersionEx() succeeded but we are not on NT.
  135.       {
  136.        MessageBox(NULL,
  137.                   "This sample application can only be run on Windows NT. 3.5 or greatern"
  138.                   "This application will now terminate.",
  139.                   "UnBufCp2",
  140.                   MB_OK           | 
  141.                   MB_ICONSTOP     | 
  142.                   MB_SETFOREGROUND );
  143.        exit( 1 );
  144.       }
  145.     //
  146.     // Get the system's page size.
  147.     //
  148.     GetSystemInfo(&SystemInfo);
  149.     PageSize = SystemInfo.dwPageSize;
  150.     //
  151.     // Open the source file and create the destination file.
  152.     // Use FILE_FLAG_NO_BUFFERING to avoid polluting the
  153.     // system cache with two copies of the same data.
  154.     //
  155.     SourceFile = CreateFile(argv[1],
  156.                             GENERIC_READ | GENERIC_WRITE,
  157.                             FILE_SHARE_READ,
  158.                             NULL,
  159.                             OPEN_EXISTING,
  160.                             FILE_FLAG_NO_BUFFERING | FILE_FLAG_OVERLAPPED,
  161.                             NULL);
  162.     if (SourceFile == INVALID_HANDLE_VALUE) {
  163.         fprintf(stderr, "failed to open %s, error %dn", argv[1], GetLastError());
  164.         exit(1);
  165.     }
  166.     FileSize.LowPart = GetFileSize(SourceFile, &FileSize.HighPart);
  167.     if ((FileSize.LowPart == 0xffffffff) && (GetLastError() != NO_ERROR)) {
  168.         fprintf(stderr, "GetFileSize failed, error %dn", GetLastError());
  169.         exit(1);
  170.     }
  171.     DestFile = CreateFile(argv[2],
  172.                           GENERIC_READ | GENERIC_WRITE,
  173.                           FILE_SHARE_READ | FILE_SHARE_WRITE,
  174.                           NULL,
  175.                           CREATE_ALWAYS,
  176.                           FILE_FLAG_NO_BUFFERING | FILE_FLAG_OVERLAPPED,
  177.                           SourceFile);
  178.     if (DestFile == INVALID_HANDLE_VALUE) {
  179.         fprintf(stderr, "failed to open %s, error %dn", argv[2], GetLastError());
  180.         exit(1);
  181.     }
  182.     //
  183.     // Extend the destination file so that the filesystem does not
  184.     // turn our asynchronous writes into synchronous ones.
  185.     //
  186.     InitialFileSize.QuadPart = (FileSize.QuadPart + PageSize - 1) & ~(PageSize - 1);
  187.     Status = SetFilePointer(DestFile,
  188.                             InitialFileSize.LowPart,
  189.                             (PLONG)&InitialFileSize.HighPart,
  190.                             FILE_BEGIN);
  191.     if ((Status == 0xffffffff) && (GetLastError() != NO_ERROR)) {
  192.         fprintf(stderr, "SetFilePointer failed, error %dn", GetLastError());
  193.         exit(1);
  194.     }
  195.     Success = SetEndOfFile(DestFile);
  196.     if (!Success) {
  197.         fprintf(stderr, "SetEndOfFile failed, error %dn", GetLastError());
  198.         exit(1);
  199.     }
  200.     //
  201.     //In NT 3.51 it is not necessary to specify the FileHandle parameter
  202.     //of CreateIoCompletionPort()--It is legal to specify the FileHandle
  203.     //as INVALID_HANDLE_VALUE.  However, for NT 3.5 an overlapped file
  204.     //handle is needed.
  205.     //
  206.     //We know already that we are running on NT, or else we wouldn't have
  207.     //gotten this far, so lets see what version we are running on.
  208.     //
  209.     if (ver.dwMajorVersion == 3 && ver.dwMinorVersion == 50) //we're running on NT 3.5 - Completion Ports exists                       
  210.         {
  211.           ReadPort = CreateIoCompletionPort(SourceFile,        //file handle to associate with I/O completion port
  212.                                             NULL,              //optional handle to existing I/O completion port
  213.                                             (DWORD)SourceFile, //completion key
  214.                                             1);                //# of threads allowed to execute concurrently
  215.        
  216.           if (ReadPort == NULL) {
  217.             fprintf(stderr, "failed to create ReadPort, error %dn", GetLastError());
  218.             exit(1);
  219.           }
  220.         }
  221.         
  222.     else   //we are running on NT 3.51 or greater
  223.       //                                                       
  224.       //Create the I/O Completion Port
  225.       //
  226.         {
  227.           ReadPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, //file handle to associate with I/O completion port
  228.                                             NULL,                 //optional handle to existing I/O completion port
  229.                                             (DWORD)SourceFile,    //completion key
  230.                                             1);                   //# of threads allowed to execute concurrently
  231.     
  232.         
  233.          //
  234.          //If we need to, aka we're running on NT 3.51, let's associate a file handle with the
  235.          //completion port.
  236.          //
  237.          ReadPort = CreateIoCompletionPort(SourceFile,
  238.                                            ReadPort,
  239.                                            (DWORD)SourceFile,  //should be the previously specified key.
  240.                                            1);
  241.          if (ReadPort == NULL)
  242.           {
  243.             fprintf(stderr,
  244.                     "failed to create ReadPort, error %dn", 
  245.                     GetLastError());
  246.             exit(1);
  247.           }
  248.         }
  249.     WritePort = CreateIoCompletionPort(DestFile,
  250.                                        NULL,
  251.                                        (DWORD)DestFile,
  252.                                        1);
  253.     if (WritePort == NULL) {
  254.         fprintf(stderr, "failed to create WritePort, error %dn", GetLastError());
  255.         exit(1);
  256.     }
  257.     //
  258.     // Start the writing thread
  259.     //
  260.     WritingThread = CreateThread(NULL,
  261.                                  0,
  262.                                  (LPTHREAD_START_ROUTINE)WriteLoop,
  263.                                  &FileSize,
  264.                                  0,
  265.                                  &ThreadId);
  266.     if (WritingThread == NULL) {
  267.         fprintf(stderr, "failed to create write thread, error %dn", GetLastError());
  268.         exit(1);
  269.     }
  270.     CloseHandle(WritingThread);
  271.     StartTime = GetTickCount();
  272.     //
  273.     // Start the reads
  274.     //
  275.     ReadLoop(FileSize);
  276.     EndTime = GetTickCount();
  277.     //
  278.     // We need another handle to the destination file that is
  279.     // opened without FILE_FLAG_NO_BUFFERING. This allows us to set
  280.     // the end-of-file marker to a position that is not sector-aligned.
  281.     //
  282.     BufferedHandle = CreateFile(argv[2],
  283.                                 GENERIC_WRITE,
  284.                                 FILE_SHARE_READ | FILE_SHARE_WRITE,
  285.                                 NULL,
  286.                                 OPEN_EXISTING,
  287.                                 0,
  288.                                 NULL);
  289.     if (BufferedHandle == INVALID_HANDLE_VALUE) {
  290.         fprintf(stderr,
  291.                 "failed to open buffered handle to %s, error %dn",
  292.                 argv[2],
  293.                 GetLastError());
  294.         exit(1);
  295.     }
  296.     //
  297.     // Set the destination's file size to the size of the
  298.     // source file, in case the size of the source file was
  299.     // not a multiple of the page size.
  300.     //
  301.     Status = SetFilePointer(BufferedHandle,
  302.                             FileSize.LowPart,
  303.                             (PLONG)&FileSize.HighPart,
  304.                             FILE_BEGIN);
  305.     if ((Status == 0xffffffff) && (GetLastError() != NO_ERROR)) {
  306.         fprintf(stderr, "final SetFilePointer failed, error %dn", GetLastError());
  307.         exit(1);
  308.     }
  309.     Success = SetEndOfFile(BufferedHandle);
  310.     if (!Success) {
  311.         fprintf(stderr, "SetEndOfFile failed, error %dn", GetLastError());
  312.         exit(1);
  313.     }
  314.     CloseHandle(BufferedHandle);
  315.     printf("nn%d bytes copied in %.3f secondsn",
  316.            FileSize.LowPart,
  317.            (float)(EndTime-StartTime)/1000.0);
  318.     printf("%.2f MB/secn",
  319.            ((LONGLONG)FileSize.QuadPart/(1024.0*1024.0)) / (((float)(EndTime-StartTime)) / 1000.0));
  320.     return(0);
  321. }
  322. VOID
  323. ReadLoop(
  324.     ULARGE_INTEGER FileSize
  325.     )
  326. {
  327.     ULARGE_INTEGER ReadPointer;
  328.     BOOL Success;
  329.     DWORD NumberBytes;
  330.     LPOVERLAPPED CompletedOverlapped;
  331.     DWORD Key;
  332.     PCOPY_CHUNK Chunk;
  333.     int PendingIO = 0;
  334.     int i;
  335.     //
  336.     // Start reading the file. Kick off MAX_CONCURRENT_IO reads, then just
  337.     // loop waiting for writes to complete.
  338.     //
  339.     ReadPointer.QuadPart = 0;
  340.     for (i=0; i < MAX_CONCURRENT_IO; i++) {
  341.         if (ReadPointer.QuadPart >= FileSize.QuadPart) {
  342.             break;
  343.         }
  344.         //
  345.         // Use VirtualAlloc so we get a page-aligned buffer suitable
  346.         // for unbuffered I/O.
  347.         //
  348.         CopyChunk[i].Buffer = VirtualAlloc(NULL,
  349.                                            BUFFER_SIZE,
  350.                                            MEM_COMMIT,
  351.                                            PAGE_READWRITE);
  352.         if (CopyChunk[i].Buffer == NULL) {
  353.             fprintf(stderr, "VirtualAlloc %d failed, error %dn",i, GetLastError());
  354.             exit(1);
  355.         }
  356.         CopyChunk[i].Overlapped.Offset = ReadPointer.LowPart;
  357.         CopyChunk[i].Overlapped.OffsetHigh = ReadPointer.HighPart;
  358.         CopyChunk[i].Overlapped.hEvent = NULL;     // not needed
  359.         Success = ReadFile(SourceFile,
  360.                            CopyChunk[i].Buffer,
  361.                            BUFFER_SIZE,
  362.                            &NumberBytes,
  363.                            &CopyChunk[i].Overlapped);
  364.         if (!Success && (GetLastError() != ERROR_IO_PENDING)) {
  365.             fprintf(stderr,
  366.                     "ReadFile at %lx failed, error %dn",
  367.                     ReadPointer.LowPart,
  368.                     GetLastError());
  369.         } else {
  370.             ReadPointer.QuadPart += BUFFER_SIZE;
  371.             ++PendingIO;
  372.         }
  373.     }
  374.     //
  375.     // We have started the initial async. reads, enter the main loop.
  376.     // This simply waits until a write completes, then issues the next
  377.     // read.
  378.     //
  379.     while (PendingIO) {
  380.         Success = GetQueuedCompletionStatus(WritePort,
  381.                                             &NumberBytes,
  382.                                             &Key,
  383.                                             &CompletedOverlapped,
  384.                                             (DWORD)-1);
  385.         if (!Success && (CompletedOverlapped == NULL)) {
  386.             //
  387.             // The call has failed.
  388.             //
  389.             fprintf(stderr,
  390.                     "GetQueuedCompletionStatus on the ReadPort failed, error %dn",
  391.                     GetLastError());
  392.             exit(1);
  393.         }
  394.         if (!Success) {
  395.             //
  396.             // The call has succeeded, but the initial I/O operation
  397.             // has failed.
  398.             //
  399.             fprintf(stderr,
  400.                     "GetQueuedCompletionStatus on the ReadPort removed a failedn"
  401.                     "I/O packet, error %dn",GetLastError());
  402.             //
  403.             // This is probably bad, but what the heck, it's only a demo program,
  404.             // so proceed blindly ahead.
  405.             //
  406.         }
  407.         //
  408.         // Issue the next read using the buffer that has just completed.
  409.         //
  410.         if (ReadPointer.QuadPart < FileSize.QuadPart) {
  411.             Chunk = (PCOPY_CHUNK)CompletedOverlapped;
  412.             Chunk->Overlapped.Offset = ReadPointer.LowPart;
  413.             Chunk->Overlapped.OffsetHigh = ReadPointer.HighPart;
  414.             ReadPointer.QuadPart += BUFFER_SIZE;
  415.             Success = ReadFile(SourceFile,
  416.                                Chunk->Buffer,
  417.                                BUFFER_SIZE,
  418.                                &NumberBytes,
  419.                                &Chunk->Overlapped);
  420.             if (!Success && (GetLastError() != ERROR_IO_PENDING)) {
  421.                 fprintf(stderr,
  422.                         "ReadFile at %lx failed, error %dn",
  423.                         Chunk->Overlapped.Offset,
  424.                         GetLastError());
  425.             }
  426.         }
  427.         else {
  428.             //
  429.             // There are no more reads left to issue, just wait
  430.             // for the pending writes to drain.
  431.             //
  432.             --PendingIO;
  433.           
  434.         }
  435.     }
  436. }
  437. VOID
  438. WINAPI
  439. WriteLoop(
  440.     ULARGE_INTEGER FileSize
  441.     )
  442. {
  443.     BOOL Success;
  444.     DWORD Key;
  445.     LPOVERLAPPED CompletedOverlapped;
  446.     PCOPY_CHUNK Chunk;
  447.     DWORD NumberBytes;
  448.     while (TRUE) {
  449.         Success = GetQueuedCompletionStatus(ReadPort,
  450.                                             &NumberBytes,
  451.                                             &Key,
  452.                                             &CompletedOverlapped,
  453.                                             (DWORD)-1);
  454.         if (!Success && (CompletedOverlapped == NULL)) {
  455.             //
  456.             // The call has failed.
  457.             //
  458.             fprintf(stderr,
  459.                     "GetQueuedCompletionStatus on the WritePort failed, error %dn",
  460.                     GetLastError());
  461.             exit(1);
  462.         }
  463.         if (!Success) {
  464.             //
  465.             // The call has succeeded, but the initial I/O operation
  466.             // has failed.
  467.             //
  468.             fprintf(stderr,
  469.                     "GetQueuedCompletionStatus on the WritePort removed a failedn"
  470.                     "I/O packet, error %dn", GetLastError());
  471.             //
  472.             // This is probably bad, but what the heck, it's only a demo program,
  473.             // so proceed blindly ahead.
  474.             //
  475.         }
  476.         //
  477.         // Issue the next write using the buffer that has just been read into.
  478.         //
  479.         Chunk = (PCOPY_CHUNK)CompletedOverlapped;
  480.         //
  481.         // Round the number of bytes to write up to a sector boundary
  482.         //
  483.         NumberBytes = (NumberBytes + PageSize - 1) & ~(PageSize-1);
  484.         Success = WriteFile(DestFile,
  485.                             Chunk->Buffer,
  486.                             NumberBytes,
  487.                             &NumberBytes,
  488.                             &Chunk->Overlapped);
  489.         if (!Success && (GetLastError() != ERROR_IO_PENDING)) {
  490.             fprintf(stderr,
  491.                     "WriteFile at %lx failed, error %dn",
  492.                     Chunk->Overlapped.Offset,
  493.                     GetLastError());
  494.         }
  495.         //
  496.         //Check to see if we've copied the complete file, if so return
  497.         // 
  498.         else if (Chunk->Overlapped.InternalHigh >= FileSize.QuadPart)
  499.                     return; 
  500.         
  501.     }
  502. }