观's profileTo the world,you may jus...PhotosBlogListsMore Tools Help

观 围

Occupation
Location
Photo 1 of 23
一些我收集的有的没的网站,希望对你有所帮助。

csdn技术中心文章

Loading...Loading...

To the world,you may just be somebody… but to somebody,you may be the world.

消逝是我的命运
21/11/2009

开发杂记

    一忙起来又有两个星期没有更新space了,麻烦的事情真的太多,nandflash分区还没搞定,那篇“下”都不知道什么时候发表了,往后拖拖吧。上个星期将产品的话逻辑整理了下,同时也解决了几个程序的bug,但是发现一个新的问题就是画图的速度比较慢,还是需要想办法解决这个问题。这个星期整理了下电源管理的资料,准备搞定电源管理,不过也很不容易,毕竟没有真正意义上的电源管理芯片,同时要做好功耗还是个难题。这些问题是从老大的原理图开始的,之前没有任何的沟通就直接改了,现在又要求这样低的功耗,真的是没有什么好的办法,尽力而为了!
    昨天又抽空将SD卡的问题解决了,真的不明白为什么要有这样的开发板公司,SD1和SD2竟然不能同时使用,初期为什么就不解决这样的问题,唉!现在修改了硬件之后两个SDIO接口都可以使用了,目前还没有发现什么问题,但是有点担心SD2的读写存在问题,先看看吧。
    最近加了个群,找到了些快速编译的方法,做个记录吧,挺实用的,以前浪费在这个上面太多的时间了。快速编译的前提是先整体的编译一次,当其他情况可以按照以下方法去编译:
  1. 如果更改了driver,以WinCE6.0为例,我们可以在菜单里面选择“build”->“Open Release Directory in Build Window” ,然后在弹出的命令行窗口中,通过dos命令切换到你要编译的driver的目录下面,然后运行“build”就可以了。也可以运行“build -c”会强制把所有的文件都编译一遍。被编译后的driver的dll会被自动拷贝到release目录下面,然后再切换的工程的release目录下面,运行一下“make image”就可以了。
  2. 如果改变了OAL部分的代码,同样用上面介绍的方法,需要注意的是,OAL部分可能包含多个文件夹,如果改变了OAL里面的代码,不要进入OAL里面的文件夹去编译,一定要在OAL这层进行编译,这样OAL部分的lib,dll才会被重新编译并拷贝到release目录下面。
  3. 如果改变了配置文件,比如config.bib,platform.reg文件,那么直接将这些文件拷贝到你的工程目录下面,然后运行一下“make image”就可以了。
  4. 如果改变了eboot部分的代码,那按照步骤1的方法就可以了,可能你都不需要运行“make image”命令,因为可能你只需要eboot.bin或者eboot.nb0。

    以上都是关于快速编译的一些技巧,同时PB的Build OS菜单有一些不同的编译选项,也记录下这些选项的功能吧。

  • Sysgen:不用多说,当你在"Catalog"中添加或删除了新的item的时候就用这个。
  • Build and Sysgen:当你更新了\public目录下的源代码的时候,你就需要用这个了。一般比如在打patch以后,可能就需要进行Build and Sysgen了。
  • Build and Sysgen current BSP: 当你只改变了你的BSP部分的代码,就可以用这个选项。据说,当你改变了\platform目录下的代码,也可以用这个,具体没有试过。

了解了这些之后大大的节省了编译的时间,本来沉浸在编译的痛苦之中的,这下可以解放出来了。

06/11/2009

飞思卡尔MX27+andflsh+wince5.0上的HIVE注册表实现(上)

今天整了一天的HIVE注册表的实现,发现这个和Flash的分区联系非常紧密。和官方中的说明比起来就显得非常的复杂了,下面来说下如何建立HIVE的具体操作:
官方的操作说明十分简单,总共分六步:
  1. Add the Hive-based Registry Catalog item to your OS design.
  2. Verify the following registry settings in the Platform.reg file for your OS design.

    [HKEY_LOCAL_MACHINE\init\BootVars]
        "SystemHive"="<your system hive location>"
        "Start DevMgr"=dword:<your value>
    For more information about configuring the registry to support the hive-based registry, see Hive-based Registry Setup.

  3. Set the following registry value in the Platform.reg file for your OS design to determine the default hive to load.

    [HKEY_LOCAL_MACHINE\init\BootVars]
        "DefaultUser"="<username>"

  4. Verify that all registry entries necessary for starting drivers in the first boot phase are wrapped in the comments.

    The following code example shows typical comments. 
    ; HIVE BOOT SECTION
    <your registry settings>
    ; END HIVE BOOT SECTION
    These tags are commands that tell the ROM registry builder to add the entries to the boot hive.

  5. Set the following flag bit on each driver that is loaded during the first boot phase.

    [HKEY_LOCAL_MACHINE\Drivers\...]
        "Flags"=dword:1000

    The flags are a bitmask to OR with any existing settings. This flag tells the Device Manager to load your driver in the first boot phase with the boot registry, and not to load it a second time in the second boot phase with the system registry. It prevents the driver from being started twice.

  6. Set the MountFlags registry value equal to DWORD:2.

    Set this value on the Storage Manager profile of the file system driver for the medium that contains the registry.This indicates that the file system contains the following registry key.  [HKEY_LOCAL_MACHINE\System\StorageManager\Profiles\<ProfileName>\<FileSystemName>]   "MountFlags"=dword:2

  7. Decide whether a registry flushing mechanism is necessary.

    Outstanding registry data will be flushed on a suspend or resume cycle and any time the system goes through a software shutdown.However, data may be lost if power is suddenly removed. If a software shutdown is not an option, you may need to create a thread that periodically flushes data. To ensure that data is not lost, call RegFlushKey. This will flush any unsaved changes in the hive to the persistent file. It will not damage anything to call RegFlushKey when no data has changed; in that case, the file will not be touched. RegFlushKey should be called on both the system hive HKEY_LOCAL_MACHINE and the user hive HKEY_CURRENT_USER.

以上就是官方建立HIVE注册表的步骤了,可是实际操作远远没有这么简单。

2009-11-06 10:35 在自己项目的平台上折腾了两天了,今天才恍然大悟,改动了eboot却没有将eboot烧到板子上,真是失策。现在板子又出问题了,真是祸不单行。

2009-11-06 15:38 问题终于解决,是串口板的问题,换了块串口板就可以烧eboot了。可是又有新的问题出现了,eboot烧了进去之后跑不起来。我的天啊!
现在问题找到了,浪费了大把的时间。具体问题记录下来:
    1. 通过Advanced ToolKit V1.60串口工具将nandloard和eboot烧入flash上电之后系统无反应。但是烧以前的eboot却可以。
    2. 这样只有烧入以前的eboot之后再通过eboot的选项来更新eboot,PB打开eboot.bin通过usb口下载更新然后出错,错误为:eboot的大小超出了保留的大小,崩溃!回头一查298k,确实是超过了256k的大小,可是为什么会变这么大,仔细一想,自己更新过开机启动的画面,加了一幅图进取,所以整个eboot变大了。
    3. 删除开机启动画面的图片,重新编译,更新eboot失败。再次崩溃!
    4. 重新回到串口更新eboot的程序,重新来一遍,终于ok了,唉!

通过更改eboot目前可以将整个flash分为三个分区了:part00(binfs),part001(fatfs),part03(boot)但是可惜的是fat分区还是没有挂起,明天来搞挂起吧,希望不要这么曲折了。

04/11/2009

NandFlash的分区实现

提到分区就需要知道MBR,了解分区表。
什么是MBR

     硬盘的0柱面、0磁头、1扇区称为主引导扇区,NANDFLASH由BLOCK和Sector组成,所以NANDFLASH的第0 BLOCK,第1 Sector为主引导扇区,FDISK程序写到该扇区的内容称为主引导记录(MBR)。该记录占用512个字节,它用于硬盘启动时将系统控制权交给用户指定的,并在分区表中登记了的某个操作系统区。

MBR的组成

一个扇区的硬盘主引导记录MBR由如图6-15所示的4个部分组成。
  • 主引导程序(偏移地址0000H—0088H),它负责从活动分区中装载,并运行系统引导程序。
  • 出错信息数据区,偏移地址0089H--00E1H为出错信息,00E2H--01BDH全为0字节。
  • 分区表(DPT,Disk Partition Table)含4个分区项,偏移地址01BEH--01FDH,每个分区表项长16个字节,共64字节为分区项1、分区项2、分区项3、分区项4。
  • 结束标志字,偏移地址01FE--01FF的2个字节值为结束标志55AA,如果该标志错误系统就不能启动。
0000-0088
Master Boot Record
主引导程序
主引导
程序
0089-01BD 出错信息数据区 数据区
01BE-01CD
分区项1(16字节)
分区表
01CE-01DD
分区项2(16字节)
01DE-01ED
分区项3(16字节)
01EE-01FD
分区项4(16字节)
01FE
55
结束标志
01FF
AA
                             图6-15 MBR的组成结构图

MBR中的分区信息结构

     占用512个字节的MBR中,偏移地址01BEH--01FDH的64个字节,为4个分区项内容(分区信息表)。它是由磁盘介质类型及用户在使用 FDISK定义分区说确定的。在实际应用中,FDISK对一个磁盘划分的主分区可少于4个,但最多不超过4个。每个分区表的项目是16个字节,其内容含义 如表6-19所示。
表6-19 分区项表(16字节)内容及含义

存贮字节位 内容及含义
第1字节 引导标志。若值为80H表示活动分区,若值为00H表示非活动分区。
第2、3、4字节
本分区的起始磁头号、扇区号、柱面号。其中:
磁头号——第2字节;
扇区号——第3字节的低6位;
柱面号——为第3字节高2位+第4字节8位。
第5字节
分区类型符:
00H——表示该分区未用(即没有指定);
06H——FAT16基本分区;
0BH——FAT32基本分区;
05H——扩展分区;
07H——NTFS分区;
0FH——(LBA模式)扩展分区(83H为Linux分区等)。
第6、7、8字节
本分区的结束磁头号、扇区号、柱面号,其中:
磁头号——第6字节;
扇区号——第7字节的低6位;
柱面号——第7字节的高2位+第8字节。
第9、10、11、12字节 本分区之前已用了的扇区数
第13、14、15、16字节 本分区的总扇区数

        EBOOT中对NAND分区主要代码,eboot目录下的fmd.cpp文件,与NAND驱动基本相同,所以,要对NAND进行分区,就得对NAND驱动非常熟悉。透彻了解。然后就是E:\WINCE500\PUBLIC\COMMON\OAK\DRIVERS\ETHDBG\BOOTPART\bootpart.cpp文件了。该文件主要通过调用NANDFLASH的读写操作来写入MBR,也是今天主要的分析对象。

主要函数。
/*  BP_OpenPartition
*
*  Opens/creates a partition depending on the creation flags.  If it is opening
*  and the partition has already been opened, then it returns a handle to the
*  opened partition.  Otherwise, it loads the state information of that partition
*  into memory and returns a handle.
*
*  ENTRY
*      dwStartSector - Logical sector to start the partition.  NEXT_FREE_LOC if none
*          specified.  Ignored if opening existing partition.
*      dwNumSectors - Number of logical sectors of the partition.  USE_REMAINING_SPACE
*          to indicate to take up the rest of the space on the flash for that partition (should
*          only be used when creating extended partitions).  This parameter is ignored
*          if opening existing partition.
*      dwPartType - Type of partition to create/open.
*      fActive - TRUE indicates to create/open the active partition.  FALSE for
*          inactive.
*      dwCreationFlags - PART_CREATE_NEW to create only.  Fail if it already
*          exists.  PART_OPEN_EXISTING to open only.  Fail if it doesn't exist.
*          PART_OPEN_ALWAYS creates if it does not exist and opens if it
*          does exist.
*
*  EXIT
*      Handle to the partition on success.  INVALID_HANDLE_VALUE on error.
*/

 

HANDLE BP_OpenPartition(DWORD dwStartSector, DWORD dwNumSectors, DWORD dwPartType, BOOL fActive, DWORD dwCreationFlags)
  (注:示例代码为本人EBOOT中分区实现源码(WINCE5.0+S3C2440+128MNAND,MBR写在第4个BLOCK,分一个BINFS格式分区和一个FAT格式分区)。)
BOOL WriteRegionsToBootMedia(DWORD dwImageStart, DWORD dwImageLength, DWORD dwLaunchAddr)
 
在把SDRAM中的NK烧写到NAND中去之前,先创建一个BINFS分区。
hPart = BP_OpenPartition( (NK_START_BLOCK+1)*PAGES_PER_BLOCK,  // next block of MBR     BINFS_BLOCK*PAGES_PER_BLOCK,//SECTOR_TO_BLOCK_SIZE(FILE_TO_SECTOR_SIZE(dwBINFSPartLength))*PAGES_PER_BLOCK,  //align to block
                              PART_BINFS,
                              TRUE,
                              PART_OPEN_ALWAYS);
第一个参数分区的起始sector 为(NK_START_BLOCK+1)*PAGES_PER_BLOCK,
第二个参数分区的结束 sector为BINFS_BLOCK*PAGES_PER_BLOCK,
第三个参数分区的格式为PART_BINFS,即BINFS格式,
第四个参数指示该分区为活动分区,fActive = TURE,
第五个参数PART_OPEN_ALWAYS指示如果分区不存在就创建该分区,存在就OPEN该分区,返回分区句柄。
 
HANDLE BP_OpenPartition(DWORD dwStartSector, DWORD dwNumSectors, DWORD dwPartType, BOOL fActive, DWORD dwCreationFlags)
{
        DWORD dwPartIndex;
        BOOL fExists;
        ASSERT (g_pbMBRSector);
        if (!IsValidMBR()) {
            DWORD dwFlags = 0;
            //fly
             RETAILMSG(1, (TEXT("BP_OpenPartition:: dwStartSector=0x%x ,dwNumSectors= 0x%x.,dwPartType = 0x%x\r\n"), dwStartSector, dwNumSectors,dwPartType));
            if (dwCreationFlags == PART_OPEN_EXISTING) {
                RETAILMSG(1, (TEXT("OpenPartition: Invalid MBR.  Cannot open existing partition 0x%x.\r\n"), dwPartType));
                return INVALID_HANDLE_VALUE;
            }
            RETAILMSG(1, (TEXT("OpenPartition: Invalid MBR.  Formatting flash.\r\n")));
            if (g_FlashInfo.flashType == NOR) {
                dwFlags |= FORMAT_SKIP_BLOCK_CHECK;
            }
            //fly
             RETAILMSG(1, (TEXT("BP_LowLevelFormat: g_pbMBRSector=0x%x, g_dwMBRSectorNum= 0x%x.\r\n"), *g_pbMBRSector, g_dwMBRSectorNum));
            BP_LowLevelFormat (SECTOR_TO_BLOCK(dwStartSector), SECTOR_TO_BLOCK(dwNumSectors), dwFlags);
            dwPartIndex = 0;
            fExists = FALSE;
        }
        else {
            fExists = GetPartitionTableIndex(dwPartType, fActive, &dwPartIndex);      
        }
        RETAILMSG(1, (TEXT("OpenPartition: Partition Exists=0x%x for part 0x%x.\r\n"), fExists, dwPartType));
        if (fExists) {
            // Partition was found.
            if (dwCreationFlags == PART_CREATE_NEW)
                return INVALID_HANDLE_VALUE;
            if (g_partStateTable[dwPartIndex].pPartEntry == NULL) {
                // Open partition.  If this is the boot section partition, then file pointer starts after MBR
                g_partStateTable[dwPartIndex].pPartEntry = (PPARTENTRY)(g_pbMBRSector + PARTTABLE_OFFSET + sizeof(PARTENTRY)*dwPartIndex);
                g_partStateTable[dwPartIndex].dwDataPointer = 0;
            }
           if ( dwNumSectors > g_partStateTable[dwPartIndex].pPartEntry->Part_TotalSectors )
              return CreatePartition (dwStartSector, dwNumSectors, dwPartType, fActive, dwPartIndex);
           else        
                   return (HANDLE)&g_partStateTable[dwPartIndex];          
        }
        else {
            // If there are already 4 partitions, or creation flag specified OPEN_EXISTING, fail.
            if ((dwPartIndex == NUM_PARTS) || (dwCreationFlags == PART_OPEN_EXISTING))
                return INVALID_HANDLE_VALUE;
            // Create new partition
            return CreatePartition (dwStartSector, dwNumSectors, dwPartType, fActive, dwPartIndex);
        }
        return INVALID_HANDLE_VALUE;
}
 
进入函数,首先做的事就是检测MBR的有效性。通过函数IsValidMBR()实现。
检测MBR的有效性,首先要知道MBR保存在哪里,前面说过NANDFLASH的第0 BLOCK,第1 Sector为主引导扇区,也就是MBR,但是NAND如果被当作启动芯片,○地址一般被BOOTLOADER代码占据,MBR只有放在后面的BLOCK中。所以我把第0 个BLOCK放NBOOT,第1个BLOCK放TOC,第2个BLOCK放EBOOT,第3个BLOCK保留,第4个BLOCK就放MBR。
 
static BOOL IsValidMBR()
{
    // Check to see if the MBR is valid
    // MBR block is always located at logical sector 0
    g_dwMBRSectorNum = GetMBRSectorNum();      
    RETAILMSG (1, (TEXT("IsValidMBR: MBR sector = 0x%x\r\n"), g_dwMBRSectorNum));
    if ((g_dwMBRSectorNum == INVALID_ADDR) || !FMD_ReadSector (g_dwMBRSectorNum, g_pbMBRSector, NULL, 1)) {
       RETAILMSG (1, (TEXT("IsValidMBR-----return FALSE-------------------\r\n")));
        return FALSE;
    }  
    return ((g_pbMBRSector[0] == 0xE9) &&
         (g_pbMBRSector[1] == 0xfd) &&
         (g_pbMBRSector[2] == 0xff) &&
         (g_pbMBRSector[SECTOR_SIZE_FS-2] == 0x55) &&
         (g_pbMBRSector[SECTOR_SIZE_FS-1] == 0xAA));
}
 
IsValidMBR()实现的第一行就是给全局变量g_dwMBRSectorNum 赋值,显而易见,g_dwMBRSectorNum就是指示保存MBR的那个Sector了。
g_dwMBRSectorNum = GetMBRSectorNum();   //是获得保存MBR的那个Sector
 
static DWORD GetMBRSectorNum ()
{
    DWORD dwBlockNum = 3, dwSector = 0;
    SectorInfo si;
   
while (dwBlockNum < g_FlashInfo
.dwNumBlocks) {
        if (!IS_BLOCK_UNUSABLE (dwBlockNum)) {
            dwSector = dwBlockNum * g_FlashInfo.wSectorsPerBlock;
            if (!FMD_ReadSector (dwSector, NULL, &si, 1)) {
                RETAILMSG(1, (TEXT("GetMBRSectorNum: Could not read sector 0x%x.\r\n"), dwSector));
                return INVALID_ADDR;
            }
            // Check to see if logical sector number is 0
            if (si.dwReserved1 == 0) {
              //RETAILMSG(1,(TEXT("dwBlockNum=%d\r\n"),dwBlockNum));
                return dwSector;
            }
        }
        dwBlockNum++;
    }
    return INVALID_ADDR;
}
 
这里dwBlockNum直接给了个3,因为NBOOT,TOC,EBOOT已经把前三个BLOCK用了。所以MBR的选择直接排除了前三个BLOCK了。
#define IS_BLOCK_UNUSABLE(blockID) ((FMD_GetBlockStatus (blockID) & (BLOCK_STATUS_BAD|BLOCK_STATUS_RESERVED)) > 0)
然后确定BLOCK是否可使用的BLOCK,最后通si.dwReserved1 == 0来判断是不是选择这个Sector来保存MBR。
IsValidMBR()中还有一个重要的结构就是g_pbMBRSector数组,它就是MBR了。
函数返回时,MBR必须符合下列记录。
   
return ((g_pbMBRSector[0] == 0xE9) &&
         (g_pbMBRSector[1] == 0xfd) &&
         (g_pbMBRSector[2] == 0xff) &&
         (g_pbMBRSector[SECTOR_SIZE_FS-2] == 0x55) &&
         (g_pbMBRSector[SECTOR_SIZE_FS-1] == 0xAA));
 
可以看到只有开始三个字节为0XE9,FD,FF,当然,还有熟悉的结束标志符0X55AA。
如果没有检测到MBR,则先对NANDFLASH进行低级格式化。BP_LowLevelFormat (SECTOR_TO_BLOCK(dwStartSector), SECTOR_TO_BLOCK(dwNumSectors), dwFlags);再创建分区,CreatePartition (dwStartSector, dwNumSectors, dwPartType, fActive, dwPartIndex);。
 
BOOL BP_LowLevelFormat(DWORD dwStartBlock, DWORD dwNumBlocks, DWORD dwFlags)
{
    dwNumBlocks = min (dwNumBlocks, g_FlashInfo.dwNumBlocks);
    RETAILMSG(1,(TEXT("fly::Enter LowLevelFormat [0x%x, 0x%x].\r\n"), dwStartBlock,dwNumBlocks));// dwStartBlock + dwNumBlocks - 1));
    // Erase all the flash blocks.
    if (!EraseBlocks(dwStartBlock, dwNumBlocks, dwFlags))
        return(FALSE);
    // Determine first good starting block
    while (IS_BLOCK_UNUSABLE (dwStartBlock) && dwStartBlock < g_FlashInfo.dwNumBlocks) {
        dwStartBlock++;
    }
    if (dwStartBlock >= g_FlashInfo.dwNumBlocks) {
        RETAILMSG(1,(TEXT("BP_LowLevelFormat: no good blocks\r\n")));      
        return FALSE;
    }
    // MBR goes in the first sector of the starting block.  This will be logical sector 0.
    g_dwMBRSectorNum = dwStartBlock * g_FlashInfo.wSectorsPerBlock;
    RETAILMSG(1,(TEXT("fly:g_dwMBRSectorNum=%d\r\n"),g_dwMBRSectorNum));
    // Create an MBR.
    CreateMBR();
    return(TRUE);
}
 
在对NANDFLASH进行低格时,主要对坏块的处理。if (!EraseBlocks(dwStartBlock, dwNumBlocks, dwFlags))检测每一个Sector,每个BLOCK只要有一个Sector不能读写这个块都会被处理成坏块,这样才能保证系统的稳定性。在函数的最后调用了    CreateMBR();来创建一个MBR。
 
static BOOL CreateMBR()
{
    // This, plus a valid partition table, is all the CE partition manager needs to recognize
    // the MBR as valid. It does not contain boot code.
    memset (g_pbMBRSector, 0xff, g_FlashInfo.wDataBytesPerSector);
    g_pbMBRSector[0] = 0xE9;
    g_pbMBRSector[1] = 0xfd;
    g_pbMBRSector[2] = 0xff;
    g_pbMBRSector[SECTOR_SIZE_FS-2] = 0x55;
    g_pbMBRSector[SECTOR_SIZE_FS-1] = 0xAA;
    // Zero out partition table so that mspart treats entries as empty.
    memset (g_pbMBRSector+PARTTABLE_OFFSET, 0, sizeof(PARTENTRY) * NUM_PARTS);
    return WriteMBR();
 
当然。因为还没有进行分区,这里写入的MBR分区表部分是空的。
 
static BOOL WriteMBR()
{
    DWORD dwMBRBlockNum = g_dwMBRSectorNum / g_FlashInfo.wSectorsPerBlock;
    //dwMBRBlockNum = 1 ;
    RETAILMSG(1, (TEXT("WriteMBR: MBR block = 0x%x,g_dwMBRSectorNum = 0x%x.\r\n"), dwMBRBlockNum,g_dwMBRSectorNum));
    memset (g_pbBlock, 0xff, g_dwDataBytesPerBlock);
    memset (g_pSectorInfoBuf, 0xff, sizeof(SectorInfo) * g_FlashInfo.wSectorsPerBlock);
    // No need to check return, since a failed read means data hasn't been written yet.
    ReadBlock (dwMBRBlockNum, g_pbBlock, g_pSectorInfoBuf);
    if (!FMD_EraseBlock (dwMBRBlockNum)) {
        RETAILMSG (1, (TEXT("CreatePartition: error erasing block 0x%x\r\n"), dwMBRBlockNum));
        return FALSE;
    }
    memcpy (g_pbBlock + (g_dwMBRSectorNum % g_FlashInfo.wSectorsPerBlock) * g_FlashInfo.wDataBytesPerSector, g_pbMBRSector, g_FlashInfo.wDataBytesPerSector);
    g_pSectorInfoBuf->bOEMReserved &= ~OEM_BLOCK_READONLY;
    g_pSectorInfoBuf->wReserved2 &= ~SECTOR_WRITE_COMPLETED;
    g_pSectorInfoBuf->dwReserved1 = 0;
    RETAILMSG(1, (TEXT("fly::WriteMBR: MBR block = 0x%x.\r\n"), dwMBRBlockNum));
    if (!WriteBlock (dwMBRBlockNum, g_pbBlock, g_pSectorInfoBuf)) {
        RETAILMSG (1, (TEXT("CreatePartition: could not write to block 0x%x\r\n"), dwMBRBlockNum));
        return FALSE;
    }
    return TRUE;
}
 
在WriteMBR()函数中,就写入了判断MBR 的一些标志到BLOCK,    g_pSectorInfoBuf->bOEMReserved &= ~OEM_BLOCK_READONLY;
    g_pSectorInfoBuf->wReserved2 &= ~SECTOR_WRITE_COMPLETED;
    g_pSectorInfoBuf->dwReserved1 = 0;
Wince系统启动时,具体是NANDFLASH驱动加载成功后,MOUNT文件系统到NANDFLASH之前,也会通过读取这些SectorInfo来得到MBR 保存的BLOCK,进而读取MBR,获得分区信息,从而把各分区MOUNT到相应文件系统。格式化完成,MBR也写入成功后就可以开始新建分区了。
 
/*  CreatePartition
*
*  Creates a new partition.  If it is a boot section partition, then it formats
*  flash.
*
*  ENTRY
*      dwStartSector - Logical sector to start the partition.  NEXT_FREE_LOC if
*          none specified.
*      dwNumSectors - Number of logical sectors of the partition.  USE_REMAINING_SPACE
*          to indicate to take up the rest of the space on the flash for that partition.
*      dwPartType - Type of partition to create.
*      fActive - TRUE indicates to create the active partition.  FALSE for
*          inactive.
*      dwPartIndex - Index of the partition entry on the MBR
*
*  EXIT
*      Handle to the partition on success.  INVALID_HANDLE_VALUE on error.
*/
static HANDLE CreatePartition (DWORD dwStartSector, DWORD dwNumSectors, DWORD dwPartType, BOOL fActive, DWORD dwPartIndex)
{
    DWORD dwBootInd = 0;
    RETAILMSG(1, (TEXT("CreatePartition: Enter CreatePartition for 0x%x.\r\n"), dwPartType));
    if (fActive)
        dwBootInd |= PART_IND_ACTIVE;
    if (dwPartType == PART_BOOTSECTION || dwPartType == PART_BINFS || dwPartType == PART_XIP)
        dwBootInd |= PART_IND_READ_ONLY;  
     // If start sector is invalid, it means find next free sector
    if (dwStartSector == NEXT_FREE_LOC) {      
        dwStartSector = FindFreeSector();
        if (dwStartSector == INVALID_ADDR) {
            RETAILMSG(1, (TEXT("CreatePartition: can't find free sector.\r\n")));
            return INVALID_HANDLE_VALUE;
        }
        // Start extended partition on a block boundary
        if ((dwPartType == PART_EXTENDED) && (dwStartSector % g_FlashInfo.wSectorsPerBlock)) {
            dwStartSector = (dwStartSector / g_FlashInfo.wSectorsPerBlock + 1) * g_FlashInfo.wSectorsPerBlock;
        }
    }
    // If num sectors is invalid, fill the rest of the space up
    if (dwNumSectors == USE_REMAINING_SPACE) {
        DWORD dwLastLogSector = LastLogSector();
        if (dwLastLogSector == INVALID_ADDR)
            return INVALID_HANDLE_VALUE;
        // Determine the number of blocks to reserve for the FAL compaction when creating an extended partition.
        DWORD dwReservedBlocks = g_FlashInfo.dwNumBlocks / PERCENTAGE_OF_MEDIA_TO_RESERVE;
        if((dwReservedBlocks = g_FlashInfo.dwNumBlocks / PERCENTAGE_OF_MEDIA_TO_RESERVE) < MINIMUM_FLASH_BLOCKS_TO_RESERVE) {
            dwReservedBlocks = MINIMUM_FLASH_BLOCKS_TO_RESERVE;
        }
        dwNumSectors = dwLastLogSector - dwStartSector + 1 - dwReservedBlocks * g_FlashInfo.wSectorsPerBlock;
    }
    if (!AreSectorsFree (dwStartSector, dwNumSectors)){
        RETAILMSG (1, (TEXT("fly:::::CreatePartition: sectors [0x%x, 0x%x] requested are out of range or taken by another partition\r\n"), dwStartSector, dwNumSectors));
        return INVALID_HANDLE_VALUE;
    }
    RETAILMSG(1, (TEXT("CreatePartition: Start = 0x%x, Num = 0x%x.\r\n"), dwStartSector, dwNumSectors));
    AddPartitionTableEntry (dwPartIndex, dwStartSector, dwNumSectors, (BYTE)dwPartType, (BYTE)dwBootInd);
    if (dwBootInd & PART_IND_READ_ONLY) {
        if (!WriteLogicalNumbers (dwStartSector, dwNumSectors, TRUE)) {
            RETAILMSG(1, (TEXT("CreatePartition: can't mark sector info.\r\n")));
            return INVALID_HANDLE_VALUE;
        }
    }
    if (!WriteMBR())
        return INVALID_HANDLE_VALUE;
    g_partStateTable[dwPartIndex].pPartEntry = (PPARTENTRY)(g_pbMBRSector + PARTTABLE_OFFSET + sizeof(PARTENTRY)*dwPartIndex);
    g_partStateTable[dwPartIndex].dwDataPointer = 0;
    return (HANDLE)&g_partStateTable[dwPartIndex];          
}
 
如果第二个参数为-1,则视为将余下的所有空间划为一个分区。LastLogSector();函数获得最后一个逻辑Sector。
 
static DWORD LastLogSector()
{
    if (g_dwLastLogSector) {
       return g_dwLastLogSector;
    }
    DWORD dwMBRBlock = g_dwMBRSectorNum / g_FlashInfo.wSectorsPerBlock;
    DWORD dwUnusableBlocks = dwMBRBlock;
    for (DWORD i = dwMBRBlock; i < g_FlashInfo.dwNumBlocks; i++) {
        if (IS_BLOCK_UNUSABLE (i))
            dwUnusableBlocks++;
    }
    g_dwLastLogSector = (g_FlashInfo.dwNumBlocks - dwUnusableBlocks) * g_FlashInfo.wSectorsPerBlock - 1;
    RETAILMSG(1, (TEXT("fly:::LastLogSector: Last log sector is: 0x%x.\r\n"), g_dwLastLogSector));
    return g_dwLastLogSector;
}
 
即g_dwLastLogSector = (g_FlashInfo.dwNumBlocks - dwUnusableBlocks) * g_FlashInfo.wSectorsPerBlock - 1;//(NAND 的BLOCK总数 – MBR保存的那个BLOCK)* 每个BLOCK的Sector数 – 保存MBR的那个Sector。得到的就是从MBR那个Sector之后的所有Sector,即逻辑大小。
AreSectorsFree (dwStartSector, dwNumSectors)函数判断参数提供的起始Sector和个数有没有超出来NAND的界限,或者逻辑分区的界限。
重头戏开始了。通过AddPartitionTableEntry (dwPartIndex, dwStartSector, dwNumSectors, (BYTE)dwPartType, (BYTE)dwBootInd); 准备分区信息写入分区表。
 
/*  AddPartitionTableEntry
*
*  Generates the partition entry for the partition table and copies the entry
*  into the MBR that is stored in memory.
*
*
*  ENTRY
*      entry - index into partition table
*      startSector - starting logical sector
*      totalSectors - total logical sectors
*      fileSystem - type of partition
*      bootInd - byte in partition entry that stores various flags such as
*          active and read-only status.
*
*  EXIT
*/
static void AddPartitionTableEntry(DWORD entry, DWORD startSector, DWORD totalSectors, BYTE fileSystem, BYTE bootInd)
{
    PARTENTRY partentry = {0};
    Addr startAddr;
    Addr endAddr;
    ASSERT(entry < 4);
    // no checking with disk info and start/total sectors because we allow
    // bogus partitions for testing purposes
    // initially known partition table entry
    partentry.Part_BootInd = bootInd;
    partentry.Part_FileSystem = fileSystem;
    partentry.Part_StartSector = startSector;
    partentry.Part_TotalSectors = totalSectors;
    // logical block addresses for the first and final sector (start on the second head)
    startAddr.type = LBA;
    startAddr.lba = partentry.Part_StartSector;
    endAddr.type = LBA;
    endAddr.lba = partentry.Part_StartSector + partentry.Part_TotalSectors-1;
    // translate the LBA addresses to CHS addresses
    startAddr = LBAtoCHS(&g_FlashInfo, startAddr);
    endAddr = LBAtoCHS(&g_FlashInfo, endAddr);
    // starting address
    partentry.Part_FirstTrack = (BYTE)(startAddr.chs.cylinder & 0xFF);
    partentry.Part_FirstHead = (BYTE)(startAddr.chs.head & 0xFF);
    // lower 6-bits == sector, upper 2-bits = cylinder upper 2-bits of 10-bit cylinder #
    partentry.Part_FirstSector = (BYTE)((startAddr.chs.sector & 0x3F) | ((startAddr.chs.cylinder & 0x0300) >> 2));
    // ending address:
    partentry.Part_LastTrack = (BYTE)(endAddr.chs.cylinder & 0xFF);
    partentry.Part_LastHead = (BYTE)(endAddr.chs.head & 0xFF);
    // lower 6-bits == sector, upper 2-bits = cylinder upper 2-bits of 10-bit cylinder #
    partentry.Part_LastSector = (BYTE)((endAddr.chs.sector & 0x3F) | ((endAddr.chs.cylinder & 0x0300) >> 2));
    memcpy(g_pbMBRSector+PARTTABLE_OFFSET+(sizeof(PARTENTRY)*entry), &partentry, sizeof(PARTENTRY));
}
 
这里面的地址信息是一种叫CHS(Cyinder/Head/Sector)的地址。eboot中有将逻辑地址LBS(Logical Block Addr)与这种地址互相转换的函数LBAtoCHS,CHSToLBA。
 
Addr LBAtoCHS(FlashInfo *pFlashInfo, Addr lba)
{
    Addr chs;
    DWORD tmp = pFlashInfo->dwNumBlocks * pFlashInfo->wSectorsPerBlock;
    chs.type = CHS;
    chs.chs.cylinder = (WORD)(lba.lba / tmp);                                      // 柱面,应该始终是0
    tmp = lba.lba % tmp;
    chs.chs.head = (WORD)(tmp / pFlashInfo->wSectorsPerBlock);                     // 块地址
    chs.chs.sector = (WORD)((tmp % pFlashInfo->wSectorsPerBlock) + 1);     // 扇区+1
    return chs;
}
Addr CHStoLBA(FlashInfo *pFlashInfo, Addr chs)
{
    Addr lba;
    lba.type = LBA;
    lba.lba = ((chs.chs.cylinder * pFlashInfo->dwNumBlocks + chs.chs.head)
        * pFlashInfo->wSectorsPerBlock)+ chs.chs.sector - 1;
return lba;
}
 
如果分区的格式有只读属性,则通过WriteLogicalNumbers()函数写分区的Sectorinfo,把这部分空间保护起来。
 
static BOOL WriteLogicalNumbers (DWORD dwStartSector, DWORD dwNumSectors, BOOL fReadOnly)
{
    DWORD dwNumSectorsWritten = 0;
    DWORD dwPhysSector = Log2Phys (dwStartSector);
    DWORD dwBlockNum = dwPhysSector / g_FlashInfo.wSectorsPerBlock;
    DWORD dwOffset = dwPhysSector % g_FlashInfo.wSectorsPerBlock;
    while (dwNumSectorsWritten < dwNumSectors) {
        // If bad block, move to the next block
        if (IS_BLOCK_UNUSABLE (dwBlockNum)) {
            dwBlockNum++;
            continue;
        }
        memset (g_pbBlock, 0xff, g_dwDataBytesPerBlock);
        memset (g_pSectorInfoBuf, 0xff, sizeof(SectorInfo) * g_FlashInfo.wSectorsPerBlock);
        // No need to check return, since a failed read means data hasn't been written yet.
        ReadBlock (dwBlockNum, g_pbBlock, g_pSectorInfoBuf);
        if (!FMD_EraseBlock (dwBlockNum)) {
            return FALSE;
        }
        DWORD dwSectorsToWrite = g_FlashInfo.wSectorsPerBlock - dwOffset;
        PSectorInfo pSectorInfo = g_pSectorInfoBuf + dwOffset;
        // If this is the last block, then calculate sectors to write if there isn't a full block to update
        if ((dwSectorsToWrite + dwNumSectorsWritten) > dwNumSectors)
            dwSectorsToWrite = dwNumSectors - dwNumSectorsWritten;
        for (DWORD iSector = 0; iSector < dwSectorsToWrite; iSector++, pSectorInfo++, dwNumSectorsWritten++) {
            // Assert read only by setting bit to 0 to prevent wear-leveling by FAL
            if (fReadOnly)
                pSectorInfo->bOEMReserved &= ~OEM_BLOCK_READONLY;
            // Set to write completed so FAL can map the sector
            pSectorInfo->wReserved2 &= ~SECTOR_WRITE_COMPLETED;      
            // Write the logical sector number
            pSectorInfo->dwReserved1 = dwStartSector + dwNumSectorsWritten;          
        }
        if (!WriteBlock (dwBlockNum, g_pbBlock, g_pSectorInfoBuf))
            return FALSE;
        dwOffset = 0;
        dwBlockNum++;
    }
    return TRUE;
}
 
这就是为什么系统启动后,我们无法写入文件的BINFS文件系统格式分区的原因了。而FAT格式就可以。最后调用WriteMBR()完全MBR的写入,分区完毕。
让我们继续回到BP_OpenPartition函数中,如果从一开始IsValidMBR()就检测到有效的MBR,GetPartitionTableIndex(dwPartType, fActive, &dwPartIndex);获得分区表。和dwPartIndex分区表的索引号。
 
static BOOL GetPartitionTableIndex (DWORD dwPartType, BOOL fActive, PDWORD pdwIndex)
{
    PPARTENTRY pPartEntry = (PPARTENTRY)(g_pbMBRSector + PARTTABLE_OFFSET);
    DWORD iEntry = 0;
    for (iEntry = 0; iEntry < NUM_PARTS; iEntry++, pPartEntry++) {
        if ((pPartEntry->Part_FileSystem == dwPartType) && (((pPartEntry->Part_BootInd & PART_IND_ACTIVE) != 0) == fActive)) {
            *pdwIndex = iEntry;
            return TRUE;
        }
        if (!IsValidPart (pPartEntry)) {
            *pdwIndex = iEntry;
            return FALSE;
        }
    }
    return FALSE;
}
重要结构:PARTENTRY
// end of master boot record contains 4 partition entries
typedef struct _PARTENTRY {
        BYTE            Part_BootInd;           // If 80h means this is boot partition
        BYTE            Part_FirstHead;         // Partition starting head based 0
        BYTE            Part_FirstSector;       // Partition starting sector based 1
        BYTE            Part_FirstTrack;        // Partition starting track based 0
        BYTE            Part_FileSystem;        // Partition type signature field
        BYTE            Part_LastHead;          // Partition ending head based 0
        BYTE            Part_LastSector;        // Partition ending sector based 1
        BYTE            Part_LastTrack;         // Partition ending track based 0
        DWORD           Part_StartSector;       // Logical starting sector based 0
        DWORD           Part_TotalSectors;      // Total logical sectors in partition
} PARTENTRY;
分区表就是通过这个结构写入MBR,起始地址,分区大小,分区格式,对应结构写入MBR所在的Sector就可以了。在检测有效分区时static BOOL IsValidPart (PPARTENTRY pPartEntry)
{
    return (pPartEntry->Part_FileSystem != 0xff) && (pPartEntry->Part_FileSystem != 0);
}
就是通过对分区表文件系统格式的判断了。
把NAND后面的空间,全部分为一个FAT格式的分区。
    //
    // create extended partition in whatever is left
    //
    hPartEx = BP_OpenPartition( (NK_START_BLOCK+1+BINFS_BLOCK) * PAGES_PER_BLOCK,
                                NEXT_FREE_LOC,   // (1024 - (NK_START_BLOCK+1+SECTOR_TO_BLOCK_SIZE(FILE_TO_SECTOR_SIZE(dwBINFSPartLength)))) * PAGES_PER_BLOCK,
                                PART_DOS32,
                                TRUE,
                                PART_OPEN_ALWAYS);
    if (hPartEx == INVALID_HANDLE_VALUE )
    {
        EdbgOutputDebugString("*** WARN: StoreImageToBootMedia: Failed to open/create Extended partition ***\r\n");
    }

03/11/2009

SOCKET模型之重叠I/O

最近的项目中有WIFI模块来和其他设备进行音视频的通信,在网络编程中发现有很多东西需要学习,所以就想记录下来。

整合了网上的一些文章。

目录:
  • 重叠模型的优点
  • 重叠模型的基本原理
  • 关于重叠模型的基础知识
  • 重叠模型的实现步骤
  • 多客户端情况的注意事项

一. 重叠模型的优点

  1. 可以运行在支持Winsock2的所有Windows平台 ,而不像完成端口只是支持NT系统。
  2. 比起阻塞、select、WSAAsyncSelect以及WSAEventSelect等模型,重叠I/O(Overlapped I/O)模型使应用程序能达到更佳的系统性能。因为它和这4种模型不同的是,使用重叠模型的应用程序通知缓冲区收发系统直接使用数据,也就是说,如果应用程序投递了一个10KB大小的缓冲区来接收数据,且数据已经到达套接字,则该数据将直接被拷贝到投递的缓冲区。而这4种模型种,数据到达并拷贝到单套接字接收缓冲区中,此时应用程序会被告知可以读入的容量。当应用程序调用接收函数之后,数据才从单套接字缓冲区拷贝到应用程序的缓冲区,差别就体现出来了。
  3. 从《windows网络编程》中提供的试验结果中可以看到,在使用了P4 1.7G Xero处理器(CPU很强啊)以及768MB的回应服务器中,最大可以处理4万多个SOCKET连接,在处理1万2千个连接的时候CPU占用率才40% 左右 ―― 非常好的性能,已经直逼完成端口了^_^

二. 重叠模型的基本原理

    说了这么多的好处,你一定也跃跃欲试了吧,不过我们还是要先提一下重叠模型的基本原理。
    概括一点说,重叠模型是让应用程序使用重叠数据结构(WSAOVERLAPPED),一次投递一个或多个Winsock I/O请求。针对这些提交的请求,在它们完成之后,应用程序会收到通知,于是就可以通过自己另外的代码来处理这些数据了。
    需要注意的是,有两个方法可以用来管理重叠IO请求的完成情况(就是说接到重叠操作完成的通知):
    1. 事件对象通知(event object notification)
    2. 完成例程(completion routines) ,注意,这里并不是完成端口
          而本文只是讲述如何来使用事件通知的的方法实现重叠IO模型,完成例程的方法准备放到下一篇讲 :) (内容太多了,一篇写不完啊) ,如没有特殊说明,本文的重叠模型默认就是指的基于事件通知的重叠模型。

          既然是基于事件通知,就要求将Windows事件对象与WSAOVERLAPPED结构关联在一起(WSAOVERLAPPED结构中专门有对应的参数),通俗一点讲,就是。。。。对了,忘了说了,既然要使用重叠结构,我们常用的send, sendto, recv, recvfrom也都要被WSASend, WSASendto, WSARecv, WSARecvFrom替换掉了, 它们的用法我后面会讲到,这里只需要注意一点,它们的参数中都有一个Overlapped参数,我们可以假设是把我们的WSARecv这样的操作操作“绑定”到这个重叠结构上,提交一个请求,其他的事情就交给重叠结构去操心,而其中重叠结构又要与Windows的事件对象“绑定”在一起,这样我们调用完WSARecv以后就可以“坐享其成”,等到重叠操作完成以后,自然会有与之对应的事件来通知我们操作完成,然后我们就可以来根据重叠操作的结果取得我们想要的数据了。

      三. 关于重叠模型的基础知识

          下面来介绍并举例说明一下编写重叠模型的程序中将会使用到的几个关键函数。

      1. WSAOVERLAPPED结构

      这个结构自然是重叠模型里的核心,它是这么定义的
      typedef struct _WSAOVERLAPPED {
                DWORD Internal;
                 DWORD InternalHigh;
               DWORD Offset;
               DWORD OffsetHigh;
               WSAEVENT hEvent;      // 唯一需要关注的参数,用来关联WSAEvent对象
      } WSAOVERLAPPED, *LPWSAOVERLAPPED;
          我们需要把WSARecv等操作投递到一个重叠结构上,而我们又需要一个与重叠结构“绑定”在一起的事件对象来通知我们操作的完成,看到了和hEvent参数,不用我说你们也该知道如何来来把事件对象绑定到重叠结构上吧?大致如下:
      WSAEVENT event;                   // 定义事件
      WSAOVERLAPPED AcceptOverlapped ; // 定义重叠结构
      event = WSACreateEvent();         // 建立一个事件对象句柄
      ZeroMemory(&AcceptOverlapped, sizeof(WSAOVERLAPPED)); // 初始化重叠结构
      AcceptOverlapped.hEvent = event;    // Done !!
       

      2. WSARecv系列函数

      在重叠模型中,接收数据就要靠它了,它的参数也比recv要多,因为要用刀重叠结构嘛,它是这样定义的:
      int WSARecv(
              SOCKET         s,                            // 当然是投递这个操作的套接字
              LPWSABUF    lpBuffers,               // 接收缓冲区,与Recv函数不同,这里需要一个由WSABUF结构构成的数组
              DWORD        dwBufferCount,      // 数组中WSABUF结构的数量
              LPDWORD     lpNumberOfBytesRecvd// 如果接收操作立即完成,这里会返回函数调用所接收到的字节数
              LPDWORD    lpFlags,                    // 说来话长了,我们这里设置为0 即可
              LPWSAOVERLAPPED lpOverlapped,   // “绑定”的重叠结构
              LPWSAOVERLAPPED_COMPLETION_ROUTINE    lpCompletionRoutine   // 完成例程中将会用到的参数,我们这里设置为 NULL
      );
      返回值:
      WSA_IO_PENDING : 最常见的返回值,这是说明我们的WSARecv操作成功了,但是I/O操作还没有完成,所以我们就需要绑定一个事件来通知我们操作何时完成
      举个例子:(变量的定义顺序和上面的说明的顺序是对应的,下同)
      SOCKET s;
      WSABUF DataBuf;           // 定义WSABUF结构的缓冲区,初始化一下DataBuf
      #define DATA_BUFSIZE 5096
      char buffer[DATA_BUFSIZE];
      ZeroMemory(buffer, DATA_BUFSIZE);
      DataBuf.len = DATA_BUFSIZE;
      DataBuf.buf = buffer;
      DWORD dwBufferCount = 1, dwRecvBytes = 0, Flags = 0;

       

      // 建立需要的重叠结构
      WSAOVERLAPPED AcceptOverlapped ;// 如果要处理多个操作,这里当然需要一个WSAOVERLAPPED数组
      WSAEVENT event;     // 如果要多个事件,这里当然也需要一个WSAEVENT数组。需要注意的是可能一个SOCKET同时会有一个以上的重叠请求,也就会对应一个以上的WSAEVENT
      Event = WSACreateEvent();
      ZeroMemory(&AcceptOverlapped, sizeof(WSAOVERLAPPED));
      AcceptOverlapped.hEvent = event;     // 关键的一步,把事件句柄“绑定”到重叠结构上作了这么多工作,终于可以使用WSARecv来把我们的请求投递到重叠结构上了,呼。。。。
      WSARecv(s, &DataBuf, dwBufferCount, &dwRecvBytes,
      &Flags, &AcceptOverlapped, NULL);
      其他的函数我这里就不一一介绍了,因为我们毕竟还有MSDN这么个好帮手,而且在讲后面的完成例程和完成端口的时候我还会讲到一些 ^_^
       
      3. WSAWaitForMultipleEvents 函数

      熟悉WSAEventSelect模型的朋友对这个函数肯定不会陌生,不对,其实大家都不应该陌生,这个函数与线程中常用的WaitForMultipleObjects函数有些地方还是比较像的,因为都是在等待某个事件的触发嘛。
      因为我们需要事件来通知我们重叠操作的完成,所以自然需要这个等待事件的函数与之配套。
      DWORD WSAWaitForMultipleEvents(
               DWORD  cEvents,                        // 等候事件的总数量
                const WSAEVENT* lphEvents,       // 事件数组的指针
                BOOL     fWaitAll,     // 这个要多说两句:
                                           // 如果设置为 TRUE,则事件数组中所有事件被传信的时候函数才会返回
                                           // FALSE则任何一个事件被传信函数都要返回
                                           // 我们这里肯定是要设置为FALSE的
                DWORD  dwTimeout,   // 超时时间,如果超时,函数会返回 WSA_WAIT_TIMEOUT
                                               // 如果设置为0,函数会立即返回
                                               // 如果设置为 WSA_INFINITE只有在某一个事件被传信后才会返回
                                               // 在这里不建议设置为WSA_INFINITE,因为。。。后面再讲吧..-_-b
                BOOL     fAlertable     // 在完成例程中会用到这个参数,这里我们先设置为FALSE
      );
      返回值:
          WSA_WAIT_TIMEOUT :最常见的返回值,我们需要做的就是继续Wait
          WSA_WAIT_FAILED :   出现了错误,请检查cEvents和lphEvents两个参数是否有效
          如果事件数组中有某一个事件被传信了,函数会返回这个事件的索引值,但是这个索引值需要减去预定义值 WSA_WAIT_EVENT_0才是这个事件在事件数组中的位置。
          具体的例子就先不在这里举了,后面还会讲到

      注意:WSAWaitForMultipleEvents函数只能支持由WSA_MAXIMUM_WAIT_EVENTS对象定义的一个最大值,是 64,就是说WSAWaitForMultipleEvents只能等待64个事件,如果想同时等待多于64个事件,就要 创建额外的工作者线程,就不得不去管理一个线程池,这一点就不如下一篇要讲到的完成例程模型了。

      4. WSAGetOverlappedResult 函数

      既然我们可以通过WSAWaitForMultipleEvents函数来得到重叠操作完成的通知,那么我们自然也需要一个函数来查询一下重叠操作的结果,定义如下         
       BOOL WSAGetOverlappedResult  (
                SOCKET      s,                   // SOCKET,不用说了
                LPWSAOVERLAPPED    lpOverlapped,  // 这里是我们想要查询结果的那个重叠结构的指针
                LPDWORD   lpcbTransfer,     // 本次重叠操作的实际接收(或发送)的字节数
                BOOL         fWait,               // 设置为TRUE,除非重叠操作完成,否则函数不会返回
                                                        // 设置FALSE,而且操作仍处于挂起状态,那么函数就会返回FALSE
                                                        // 错误为WSA_IO_INCOMPLETE
                                                        // 不过因为我们是等待事件传信来通知我们操作完成,所以我们这里设
                                                        // 置成什么都没有作用…..-_-b  别仍鸡蛋啊,我也想说得清楚一些…
                LPDWORD  lpdwFlags       // 指向DWORD的指针,负责接收结果标志
      );
          这个函数没什么难的,这里我们也不需要去关注它的返回值,直接把参数填好调用就可以了,这里就先不举例了
      唯一需要注意一下的就是如果WSAGetOverlappedResult完成以后,第三个参数返回是 0 ,则说明通信对方已经关闭连接,我们这边的SOCKET, Event之类的也就可以关闭了。

      项目中待解决问题

         项目的时间越来越近了,听到公司的意向订单也越来越多,压力袭来,必须尽快了,还有一些待解决问题:

      • 电源管理中对电池电量的探测以及通知(公司为了节约成本,将本来用的一块电源管理芯片MC13783替换成了一个简单的供电芯片,很多东西都要自己来改。以后一定要记住,对功耗要求高的产品,前期一定要重视起电源管理!)
      • Winceshell的定制,需要解决将wince自带的UI屏蔽掉。
      • 处理音视频解码中,定时器对整个程序性能的影响问题。
      • 低功耗处理(这个是最难的了,我会另起一篇文章来写这个的)

        总的就是这么多了,想到就继续补充吧!
       
      加入我至MSN好友清單