这节描述了IA-32架构的任务管理功能,只有当处理器运行在保护模式的时候,这个功能才是有效的,这节的侧重点在32位任务和32位TSS结构上,关于16位的任务和16位TSS结构,请看7.6节,关于64位模式中,具体任务管理的信息,请看7.7节

7.1 任务管理概述

任务是处理器可以调度,执行和暂停的一个工作单元,它可以执行一个程序,任务,进程,操作系统服务程序,中断例程,异常例程,内核实用程序

32位架构提供了一种机制,可以保存任务状态,调度要执行的任务,并且切换一个任务到另一个任务,当操作系统位于保护模式的时候,所有处理器的执行都发生在任务中,即使简单的操作系统,也必须定义至少一个任务,复杂的操作系统可以使用处理器的任务管理功能来支持多任务程序

7.1.1  任务结构

任务由两部分组成,任务执行空间和任务状态段(TSS),任务执行空间由一个代码段,一个栈段和一个或者更多的数据段组成,如果一个操作系统或者可执行程序使用了处理器的权限级保护机制,那么任务执行空间也提供了一个分开的栈为每种权限级

TSS指定了组成任务执行空间的段和为任务状态信息提供了存储空间,在多任务系统中,TSS也提供了一种链接任务机制

任务是由它的TSS段选择子定义的,当处理器加载一个任务并且执行的时候,段选择子,基址,界限和TSS段描述符属性加载进TR中(细看2.4.4),如果任务执行在分页模式中,那么任务使用的页目录基址则被加载进CR3中

7.1.2  任务状态

下面几项定义了当前正在执行任务的状态

1.  由段寄存器中的段选择子定义的任务的当前执行空间(CS DS SS ES FS GS)

2.   普通寄存器的状态

3.   EFLAGS寄存器的状态

4.   EIP寄存器的状态

5.    CR3寄存器的状态

6.   TR寄存器的状态

7.   LDTR寄存器的状态

8.   IO映射基址和IO映射(存在于TSS中)

9.   特权级0,1, 2的栈指针(存在于TSS中)

10.  链接上一个执行任务(存在于TSS中)

在调度一个任务之前,除了TR寄存器状态,上面的所有信息都被包含在TSS中,同样,LDTR寄存器的所有内容都不保存在TSS中,仅仅LDT的段选择子存在

7.1.3   执行一个任务

软件或者处理器可以利用下面的方法调度一个任务去执行

1.  显式的利用CALL指令调用一个任务

2.  显式的利用JMP指令跳转到任务中

3.   显式的调用一个中断处理程序任务(通过处理器)

4.  显式的调用一个异常处理程序任务

5.   一个return (一个IRET指令),当EFLAGS寄存器中的NT标志置1的时候

以上所有的调度一个任务的方法标识着任务被调度,并且段选择子指向了一个任务门或者任务的TSS,当用CALL指令或者JMP指令调度一个任务的时候,指令中的选择子可以直接选择一个TSS或者保存TSS选择子的任务门,当调度一个任务处理一个中断或者异常的时候,IDT项必须包含一个保存中断或者异常处理程序TSS的选择子任务门

当任务将要被调度执行的时候,任务切换就发生在当前正在执行的任务和被调度的任务之间,在任务切换期间,当前正在执行任务的执行环境(任务的状态或者上下文)被保存在它的TSS中并且任务被暂停,被调度任务的执行环境被加载进处理器中,并且刚刚加载的EIP寄存器指向了任务开始执行的指令,如果从上次系统初始化后,任务没有运行,EIP将执行任务代码的第一条指令,否则,当任务上次是active的时候,它将指向上次任务执行后的指令的后一条指令

如果当前正在执行的任务(calling task)被正在调度的任务(called task)调用,那么calling task的段选择子则被保存在called stack的TSS中,作为link back提供给calling task

对于所有的32位处理器来说,任务都不是递归的,一个任务不能CALL或者JMP它自己

中断和异常能被Handler task的任务切换处理,处理器执行一个任务切换去处理中断或者异常,并且返回的时候自动从一个中断程序任务或者异常程序任务切换回到被中断的程序,这种机制也能处理发生在中断任务期间的中断

作为任务切换的一部分,处理器也能切换到另一个LDT,LDT-BASED段中,允许每个任务都有一个逻辑地址到物理地址的映射,任务切换的时候,页目录基址寄存器CR3会重新加载,允许每个任务有它自己的页表,这种保护机制帮助了任务之间的隔离,并且预防了任务之间的干扰

如果保护机制不被使用,处理器不会提供任务之间的保护,即使操作系统使用了多种权限级保护,一个任务运行在R3 ,使用与其他R3任务相同的LDT和页表,访问代码,错误的数据和其他任务的栈

使用任务管理功能来处理多任务应用程序是可选的,多任务能在软件中处理,每个软件定义的任务都执行在单IA-32架构任务的上下文中




7.2  任务管理数据结构

     处理器定义了五种与任务相关的数据结构

    1.   任务状态段 (TSS)

    2.   任务门描述符

    3.   TSS描述符

    4.   任务寄存器

    5.   EFLAGS寄存器中的NT  Flag

    当在保护模式的时候,在最少一个任务时,一个TSS或者TSS描述符必须被创建,并且TSS的段选择子必须被加载到TR寄存器中

       

7.2.1   任务状态段-TSS

        任务需要恢复的处理器状态信息,保存在叫做系统段的TSS中,图片7-2显式了32位CPU下,任务的TSS格式,TSS字段主要分为两类:动态字段和静态字段

        任务切换期间,当一个任务被暂停的时候,处理器会更新动态字段,下面的这些都是动态字段

        1.   普通寄存器字段: EAX,EBX,ECX,EDX,ESI,EDI,EBP,ESP

        2.   段寄存器字段:      CS DS ES SS FS GS

        3.   EFLAGS寄存器

        4.   EIP寄存器字段

        5.   上一个任务的链接字段:包含了上一个任务的TSS的段选择子(更新发生在call   interrupt  exception初始化造成的任务切换),这个字段(有时叫做back link字段)允许一个任务使用IRET指令返回到上一个任务

     处理器读取静态字段,通常不会改变它们,这些字段是任务创建的时候设置的,下面的是静态字段

       1.    LDT断选择子字段:包含了任务的LDT的段选择子

       2.    CR3控制寄存器字段: 包含了任务使用的页表的基址,总所周知,CR3也被叫做PDBR

       3.    权限级0  1  2栈指针字段:这些栈字段包含了栈段的段选择子(SS0  SS1  SS2)和栈内偏移(ESP0  ESP1  ESP2)组成的逻辑地址,对于特定任务,这些字段是静态的,如果栈切换发生在任务中,SS和ESP将会改变

      4.    T-FLAG(debug trap): 当设置这个flag的时候,当任务切换到这个任务的时候,T-flag会造成处理器产生一个调试异常

     5.     I/O位图基址字段:包含一个相对于TSS中的基址的16位偏移到I/O允许位图和中断重定向位图,当存在的时候,这些映射被存储在TSS中的较高地址,I/O映射基址指向了I/O允许位图的基址和中断重定向位图的结束

    如果分页被使用:

     1.  避免任务切换期间,处理器读取104个字节时,TSS存在于两个物理页(也就是页边界只有部分TSS),处理器可能执行不正确的地址翻译,任务切换期间,处理器读写每个任务的TSS的104字节(使用连续的物理地址作为TSS的第一个字节的物理地址),如果这104个字节不是物理连续的,处理器将会访问不挣钱的信息,而且也不会产生PF异常

    2.  上一个任务的和这次任务的TSS的物理页,和每一个描述符表项都应该标记为可读可写

    3.  在任务切换初始化的时候,如果包含这些结构的页面存在于内存中,任务切换会更快的执行

 




7.2.2   ​TSS描述符

       TSS类似于其他的段,是被段描述符定义的,图片7-3显式了TSS描述符的格式,TSS描述符只能位于GDT中,它不能位于LDT或者IDT中

       在CALL 和 JMP期间,试图用TI位为1(代表当前的LDT)的段选择子访问TSS会造成GP异常,在IRET的期间,会造成无效的TSS异常(#TS),试图加载TSS的段选择子到其他段寄存器也会造成GP异常

       类型字段的Busy-Flag标志着任务是否繁忙,繁忙的任务代表正在运行或者暂停,类型字段的值如果是1001B,代表的就是一个inactive的任务,如果类型字段是1011B,那么就代表是一个繁忙的任务,任务不会递归,处理器使用Busy-FLag检测一个试图调用执行已经被中断的任务,为了确保一个Busy-Flag只与一个任务相关,每个TSS应该只有一个TSS描述符指向它    

 

    基址,段界限,DPL, G,P的功能类似于数据段描述符中的使用,在32位TSS中,如果TSS描述符的G为0,limit的值必须大于等于67h, TSS的最小size大于1字节,试图切换一个TSS描述符的limit小于67h的任务,将会造成无效的TSS异常(#TS),如果包含一个I/O允许位图,或者操作系统存储了附加数据,那么Limit则被要求更大,在任务切换的时候,处理器并不会检测Limit是否大于67h, 然而当访问I/O允许位图或者中断重定向位图的时候,则会检查

    任何程序访问描述符都能用CALL或者JMP调度任务(程序CPL的数值要小于TSS描述符的DPL)

    在大多数系统中,TSS描述符的DPL的值要小于3,因此只有特权软件能执行任务切换,然而在多任务应用程序中,一些TSS描述符的DPL可能设置为3,允许任务切换发生在R3程序中

7.2.3   64模式下的TSS描述符

     64位模式下,任务切换不再受支持,但是TSS描述符仍然存在,TSS描述符扩展到了16字节,这个扩展也应用到LDT中,下图描述了系统类型字段的编码信息

7.2.4  任务寄存器

       任务寄存器保存16位段选择子和当前任务的TSS的整个段描述符(32位基址(IA32-E模式下是64位),16位段界限和描述符属性),这些信息是从当前任务GDT的段描述符中复制的,下图显示了处理器使用这些信息访问TSS的路径

任务寄存器有可见部分(能读和能被软件修改)和不可见部分(由处理器维护,软件是无法访问的),段选择子在可见部分,指向了GDT中的TSS描述符,处理器使用任务寄存器的不可见部分来缓存TSS的段描述符,缓存这些值到寄存器中可以使任务的执行更加高效,LTR(加载到任务寄存器)和STR(读取任务寄存器)指令加载和读取任务寄存器的可见部分

      LTR指令加载一个段选择子到任务寄存器,并且指向了一个GDT中的TSS描述符,然后它从TSS描述符加载信息到任务寄存器的不可见部分,LTR是个特权级指令,只有CPL为0的时候才能被执行,它被用于系统初始化期间,把一个初始化的值push到任务寄存器,然后当任务切换发生的时候,任务寄存器的内容被隐式的改变

STR指令存储任务寄存器的可见部分到普通寄存器或者内存中,为了确定当前正在执行的任务,执行这条指令的代码能被用在任何特权级上,通常,它只被操作系统软件使用

     处理器在加电或者充值的时候,段选择子和基址默认设置为0,limit被设置为FFFFh

7.2.5   任务门描述符

     任务门描述符提供了一个间接的,被保护的任务,它能位于GDT, LDT 或者IDT中,任务门描述符的段选择子字段,指向了一个GDT中的TSS描述符,这个段选择子的RPL不被使用

     在一个任务切换期间,任务门描述符的DPL控制了TSS描述符的访问,当一个程序通过任务门,执行CALL或者JMP到一个任务,任务门选择子的CPL和RPL字段指向的任务门必须小于或者等于任务门描述符的DPL,当一个任务门被使用的时候,目标TSS描述符的DPL则不被使用

      任务可以被任务门描述符和TSS描述符访问,这些结构都满足下列需要

      1.  一个任务需要只有一个busy-flag:因为一个任务的busy-flag是存储在TSS描述符中,每个任务应该只有一个TSS描述符,然而,几个任务门可能会引用同一个TSS描述符

     2.  需要提供对任务的选择性访问:任务门满足这个需要,因为它们可以存在于LDT中,并且他们的DPL与TSS描述符的DPL不同,程序没有足够的权限来访问GDT中一个任务的TSS描述符,但是可能通过任务门的更高DPL的任务门允许访问任务,对于限制特殊任务的访问,任务门给出了更大的范围

     3.  需要通过一个独立的任务处理中断或者异常:任务门也可以存在于IDT中,允许中断和异常被任务处理程序处理,当一个中断或者异常向量指向了一个任务门的时候,处理器会切换到指定的任务

     下图展示了GDT中的任务门,LDT中的任务门,IDT中的任务门如何指向同一个任务的

     

7.3  任务切换

      下面几种情况,处理器回转移到另外一个任务执行

        1.  当前的程序或者任务执行了一个CALL 或者 JMP指令到一个GDT的TSS描述符

        2.  当前程序或者任务执行一个CALL或者JMP指令到一个GDT或者当前LDT的任务门描述符中

        3.  一个中断或者异常向量指向了一个IDT中的任务门描述符

        4.  档EFLAGS寄存器的NT flag置1的时候,当前任务执行了一个IRET指令

        JMP,CALL,IRET指令与中断和异常一样,都是重定向到一个程序的机制,引用一个TSS描述符或者一个任务门(calling或者Jmping到一个任务)或者NT flag的状态(当执行一个IRET指令的时候),取决于是否一个任务切换发生了

      当切换到一个新的任务的时候,处理器执行以下操作

      1.  从一个任务门或者上一个任务的link字段(使用IRET指令初始化的任务),获得新任务的TSS段选择子作为JMP或者CALL指令的操作数

      2.  检查当前任务是否被允许切换到一个新任务,数据访问特权规则应用与JMP和CALL指令,当前任务的CPL和新任务段选择子的RPL必须小于或者等于TSS描述符或者被引用的任务门的DPL。异常,中断(除了INT N指令产生的中断),和IRET指令被允许任务切换不管目标任务门或者TSS描述符的DPL如何,关于INT n指令产生的中断,DPL是被检查的

      3.  检查新任务的TSS描述符被标记为存在,并且有一个有效的界限(limit必须大于或者等于67h)

      4.  检查新任务是available(call  jmp  异常或者中断)或者busy(IRET返回)

      5.  检查被用于任务切换的,当前TSS,新的TSS和所有的段描述符 在系统内存中是分页的

      6.  如果任务切换是一个JMP或者IRET指令初始化的,处理器会把当前任务的TSS描述符的busy flag清零,如果被初始化是用了一个call指令,中断,或者异常,busy flag被置1

     7.   如果任务切换是一个IRET指令初始化的,处理器会清除临时被保存在EFLAGS寄存器镜像的NT flag,如果使用一个JMP CALL中断或者异常初始化,被保存在EFLAGS镜像的NT flag保持不变

     8.   保存当前任务的状态到当前任务的TSS,处理器查找当前TSS基址在TR寄存器中,并且复制下列寄存器的状态到当前TSS中:所有的通用寄存器,段寄存器中的段选择子,EFLAGS寄存器中暂时被保存的镜像,和指令指针寄存器EIP

    9.   如果任务切换是用一个CALL指令,一个异常,或者一个中断初始化的,处理器将设置被加载到新任务的EFLAGS寄存器中的NT flag为1。如果初始化是一个IRET指令,或者JMP指令,NT flag将映反射从新任务加载的EFLAGS中的NT的状态

   10.  如果任务切换是用一个CALL指令,JMP指令,异常或者中断初始化的,处理器将设置新任务TSS描述符的busy flag为1。如果是IRET指令初始化的,busy flag将不会设置

   11.  加载任务寄存器的段选择子和新任务的TSS描述符

   12.  处理器加载TSS状态:LDTR寄存器,PDBR (CR3寄存器),EFLAGS寄存器,EIP寄存器,普通寄存器,段选择子。加载这些状态期间发生错误,可能会造成架构状态损坏(如果分页模式没有开启,PDBR的值是从新任务的TSS中读取的,但是它不加载进CR3)

   13.  与段选择子相关的描述符合格的被加载,加载描述符的过程中,任何错误发生在新任务的上下文中,都有可能破坏架构状态

   14.  开始执行新的任务(关于异常例程,出现在新任务的第一条指令并没有被执行)

NOTES:

   如果所有的检查和保存已经被成功执行,处理器提交任务切换,如果在步骤1-11的过程中发生不可恢复的错误,处理器不会完成任务切换,确保处理器返回到它的状态在执行被初始化的任务切换的指令之前

   如果一个不可恢复的错误发生在步骤12,架构状态可能被破坏,但是将尝试处理执行环境之前的错误

   如果一个不可恢复的错误发生在步骤13,处理器完成任务切换,并在执行新任务的指令前产生一个适当的异常

   如果异常发生在步骤14,在允许处理器开始执行一个新任务之前,异常处理程序必须完成它自己的任务切换

      当前执行的任务状态总是被保存,当任务切换成功发生时,如果任务被恢复, 开始执行的指令是由被保存在EIP中的值指向的,并且寄存器被恢复为任务被暂停的时候的值

      当任务切换发生时,新任务的特权级并不继承被暂停任务的特权级,新的任务开始执行在由TSS的CS寄存器的CPL字段指定的特权级上,因为任务被独立空间和TSS隔离,因为特权级规则控制对一个TSS的访问,软件并不需要在任务切换上执行显示的特权级检查

      下图显示了当任务切换的时候处理器检查的异常条件,如果一个错误被发现和错误码引用的段,它也显示了每种检查产生的异常(表中检查的顺序,顺序在P6家族处理器中被使用,额外的顺序是特殊模式,在32位其他处理器中可能不同), 如果他们试图重新加载产生异常的段选择子,异常例程指定处理这些异常可能会受到递归调用,在重新加载选择子之前,应该修复造成的异常

      每次一个任务切换发生时,CR0寄存器的TS flag都被置1。当其他处理器产生浮点异常的时候,系统软件使用TS flag来协调浮点单元的操作,TS flag标识着浮点单元的上下文可能与当前任务的不同

 

7.4  任务链接

      TSS的backlink字段和EFLAGS寄存器的NT flag用于返回到上一个任务去执行,EFLAGS.NT=1代表着当前正在执行的任务嵌套在另一个可执行任务内

      当一个CALL指令,一个中断,或者一个异常造成任务切换的时候:处理器复制当前TSS的段选择子到新任务的TSS的backlink字段,然后处理器设置新人物的EFLAGS.NT为1,随后,它会使用backlink字段的值返回到上一个任务,看下图

       当一个JMP指令造成任务切换的时候,新的任务不会被嵌套,backlink字段不会被使用,并且EFLAGS.NT为0,当不希望嵌套的时候,可以使用JMP指令来调度一个新任务

下图显示了任务切换期间busy flag,NT flag, backlink字段和TS flag(CR0控制)

      NT flag可以被任何特权级的软件修改,程序可以设置NT flag和执行IRET指令,这可能会随机调用当前任务的TSS中backlink指定的任务,为了阻止这种欺骗性的任务切换成功,操作系统初始化TSS时,应该创建每个TSS的backlink字段为0

     

7.4.1    使用busy flag阻止任务切换递归

      一个TSS只允许一个上下文被保存到一个任务当中,因此,当任务被调度时,任务的递归调用将会造成当前任务的状态丢失,TSS描述符中的busy flag阻止任务切换递归和信息状态的丢失, 处理器管理busy flag如下:

    1.  当调度一个任务的时候,处理器将新任务的busy flag置1

    2.  任务切换期间,当前任务处于任务嵌套链时(由CALL 中断或者异常产生的任务切换),当前任务的busy flag保存置1状态

    3.  当切换到新任务时(由CALL, 中断或者异常产生的任务切换),如果新任务的busy flag已经置1,那么处理器就会产生一个GP异常,如果任务切换是由IRET指令初始化的,异常不会发生,因为处理器希望新任务的busy flag为1

    4.  当一个任务被一个jmp到一个新任务(jmp指令造成的任务切换)或者IRET指令结束的时候,处理器清除旧任务的busy flag,返回期间,任务设置为not busy状态

    处理器阻止任务递归,通过阻止任务切换到它自己或者切换到任务嵌套链中的任何一个任务,嵌套链中被暂停的任务可以增长到任何长度,由于多次call, 中断,或者异常,如果一个任务在这个嵌套链中,那么busy flag阻止该任务被调用

    busy flag可以被应用在多处理器配置中,因为当处理器设置或者清除busy flag的时候,它遵循一个LOCK协议。这个LOCK阻止两个处理器同时调用相同的任务,详细看“8.2.1 Automatic Locking”

7.4.2   修改任务嵌套链

    在单核系统中,下列情形需要从任务嵌套链中移除一个任务,使用下列过程来移除一个任务

    1.   禁止中断

    2.   改变抢占任务(pre-empting task:暂停任务的任务被移除)的TSS中的BackLink字段,假设抢占任务是任务链中的下个任务,改变TSS中的backlink,使其指向任务链中更老或者最老的一个任务

    3.  清除任务链中被移除任务的busy状态,如果任务链中多个任务被移除,那么被移除任务的每个busy flag都要被清除

   4.  开启中断

   在多处理器系统中,当backlink改变和busy flag被清零的时候,更多的同步和序列化操作都必须补充到这个过程中,确保TSS和它的段描述符都被上锁了,

pre-empting taskintel手册-Chapter7-Task Management的更多相关文章

  1. 【FreeRTOS学习03】小白都能懂的Task Management 任务管理基本概念介绍

    在FreeRTOS中,线程的术语又可以被称之为任务,或许这样更加合适,本文将介绍任务的创建/删除,任务参数的使用,以及任务优先级: 1 软实时和硬实时 硬实时系统的任务运行正确性与响应时限是紧密相关的 ...

  2. ES task管理

    Task Management API The Task Management API is new and should still be considered a beta feature. Th ...

  3. (转) [it-ebooks]电子书列表

    [it-ebooks]电子书列表   [2014]: Learning Objective-C by Developing iPhone Games || Leverage Xcode and Obj ...

  4. uC/OS-II 移植笔记

    用过51.AVR.Freescale.STM32,但是写程序一直没有用过实时操作系统,一是因为写的项目不大,二是不太想去看手册学东西.现在写的项目也算比较大,因为需要,所以就学一下,这样也不至于每次的 ...

  5. BuildTools Overview

    SCons Pros: Based on a full-fledged programming language, Python. This means you can make the build ...

  6. 【转】php里面也可以使用协程

    原文链接:http://blog.51cto.com/chinalx1/2089327 http://nikic.github.io/2012/12/22/Cooperative-multitaski ...

  7. Intent.java分析

    代码位于frameworks/base/core/java/anroid/Content/Intent.java Intent是对要进行操作的一种抽象描述.用action抽象操作,用data(andr ...

  8. MTK Android 权限大全

    Android权限大全 1.android.permission.WRITE_USER_DICTIONARY允许应用程序向用户词典中写入新词 2.android.permission.WRITE_SY ...

  9. 【FreeRTOS实战汇总】小白博主的RTOS学习实战快速进阶之路(持续更新)

    博主是个小白,打算把这段时间系统学习RTOS的文章统一整理到这里,另外本文会给出一些参考性资料和指导性建议: 本文宗旨 FreeRTOS 是由Richard Barry在2003年由设计的,由于其设计 ...

随机推荐

  1. Redux API之Store

    Store Store 就是用来维持应用所有的 state 树 的一个对象. 改变 store 内 state 的惟一途径是对它 dispatch 一个action. Store 不是类.它只是有几个 ...

  2. offsetLeft在各浏览器的值

    上网找了好久没有找到一个offsetLeft在各浏览器的值,自己用了一晚上的时间在各浏览器测试出来的offsetLeft的值. <!DOCTYPE html> <html lang= ...

  3. AnimationCurve

    http://blog.sina.com.cn/s/blog_471132920101f8nv.html 说明:关于animationCurve的使用总结1 创建,可以新建脚本,创建animation ...

  4. ps图层混合计算公式

    样式效果 http://www.jb51.net/photoshop/104100.html 注释: 1.混合模式的数学计算公式,另外还介绍了不透明度. 2.这些公式仅适用于RGB图像,对于Lab颜色 ...

  5. 洛谷P1602 Sramoc问题

    P1602 Sramoc问题 题目描述 话说员工们整理好了筷子之后,就准备将快餐送出了,但是一看订单,都傻眼了:订单上没有留电话号码,只写了一个sramoc(k,m)函数,这什么东西?什么意思?于是餐 ...

  6. 剑指Offer的学习笔记(C#篇)-- 求1+2+3+...+n

    题目描述 求1+2+3+...+n,要求不能使用乘除法.for.while.if.else.switch.case等关键字及条件判断语句(A?B:C). 一 . 直接解题吧 芽儿呦,突然觉得,我不说! ...

  7. 调试接口,返回的json数据,我定义了一个类,用来序列化,其中有一个字段定义为string 然后序列化的时候报错

    调试接口,返回的json数据,我定义了一个类,用来序列化,其中有一个字段定义为string 然后序列化的时候报错 在需要解析的类型类上加上声明 eg:

  8. 洛谷P1002 过河卒

    关于蒟蒻的我,刚刚接触DP....   那么就来做一道简单DP吧.... 首先先看题: 题目描述 棋盘上AA点有一个过河卒,需要走到目标BB点.卒行走的规则:可以向下.或者向右.同时在棋盘上CC点有一 ...

  9. C# 字符串string

    一.引言 在 C# 中,字符串是System.String类的一个引用类型.但与其他引用类型不同的是,C#将字符串视为一个基本类型,它可以申请为一个常量,也可以直接给它赋值. string关键字是Sy ...

  10. C# 数组之ArrayList

    一.引言 ArrayList类相当于一种高级的动态数组,是Array类的升级版本. 一般的Array,底层是数组实现的,对于数据的查找和修改十分高效.但是有2个大的缺点,其一为增删低效,其二为数组长度 ...