Da Vinci's CyberSpace

手把青秧插满田, 低头便见水中天; 心地清净方为道, 退步原来是向前.
posts - 16, comments - 10, trackbacks - 0, articles - 0
  博客园 :: 首页 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理

2010年9月1日

摘要: Normal 0 7.8 磅 0 2 false false false MicrosoftInternetExplorer4 最近遇到项目的一个bug,安装产品之后系统hang住。大致的现象是系统logon之后,Taskbar显示,然后hang住,Ctrl+Alt+Del不能调出TaskManager,Mouse不能移动,硬盘LED不闪烁。由于机器不在手边,远程调试又极其慢(实际上是国外的机器)...阅读全文

posted @ 2010-09-01 14:08 Da Vinci 阅读(819) 评论(0) 编辑

2009年7月29日

一、可重入函数
1)什么是可重入性?
可重入(reentrant)函数可以由多于一个任务并发使用,而不必担心数据错误。相反, 不可重入(non-reentrant)函数不能由超过一个任务所共享,除非能确保函数的互斥(或者使用信号量,或者在代码的关键部分禁用中断)。可重入 函数可以在任意时刻被中断,稍后再继续运行,不会丢失数据。可重入函数要么使用本地变量,要么在使用全局变量时保护自己的数据。

2)可重入函数:
不为连续的调用持有静态数据。
不返回指向静态数据的指针;所有数据都由函数的调用者提供。
使用本地数据,或者通过制作全局数据的本地拷贝来保护全局数据。
如果必须访问全局变量,记住利用互斥信号量来保护全局变量。
绝不调用任何不可重入函数。

3)不可重入函数:
函数中使用了静态变量,无论是全局静态变量还是局部静态变量。
函数返回静态变量。
函数中调用了不可重入函数。
函数体内使用了静态的数据结构;
函数体内调用了malloc()或者free()函数;
函数体内调用了其他标准I/O函数。
函数是singleton中的成员函数而且使用了不使用线程独立存储的成员变量 。
总的来说,如果一个函数在重入条件下使用了未受保护的共享的资源,那么它是不可重入的。

4)示例
在多线程条件下,函数应当是线程安全的,进一步,更强的条件是可重入的。可重入函数保证了在多线程条件下,函数的状态不会出现错误。以下分别是一个不可重入和可重入函数的示例:
//c code
static int tmp;
void func1(int* x, int* y) {
    tmp=*x;
    *x=*y;
    *y=tmp;
}
void func2(int* x, int* y) {
    int tmp;
    tmp=*x;
    *x=*y;
    *y=tmp;
}
func1是不可重入的,func2是可重入的。因为在多线程条件下,操作系统会在func1还没有执行完的情况下,切换到另一个线程中,那个线程可能再次调用func1,这样状态就错了。

二、函数编写规范
1 :对所调用函数的错误返回码要仔细、全面地处理 
 
2 :明确函数功能,精确(而不是近似)地实现函数设计 
 
3 :编写可重入函数时,应注意局部变量的使用(如编写C/C++ 语言的可重入函数时,应使用auto 即缺省态局部变量或寄存器变量)
说明:编写C/C++语言的可重入函数时,不应使用static局部变量,否则必须经过特殊处理,才能使函数具有可重入性。
 
4 :编写可重入函数时,若使用全局变量,则应通过关中断、信号量(即P 、V 操作)等手段对其加以保护
说明:若对所使用的全局变量不加以保护,则此函数就不具有可重入性,即当多个进程调用此函数时,很有可能使有关全局变量变为不可知状态。
示例:假设Exam是int型全局变量,函数Squre_Exam返回Exam平方值。那么如下函数不具有可重入性。
unsigned int example( int para )
{
    unsigned int temp;
    Exam = para; // (**)
    temp = Square_Exam( );
    return temp;
}
此 函数若被多个进程调用的话,其结果可能是未知的,因为当(**)语句刚执行完后,另外一个使用本函数的进程可能正好被激活,那么当新激活的进程执行到此函 数时,将使Exam赋与另一个不同的para值,所以当控制重新回到“temp = Square_Exam( )”后,计算出的temp很可能不是预想中的结果。此函数应如下改进。
unsigned int example( int para )
{
    unsigned int temp;
    [申请信号量操作]        // 若申请不到“信号量”,说明另外的进程正处于
    Exam = para;            // 给Exam赋值并计算其平方过程中(即正在使用此
    temp = Square_Exam( );  // 信号),本进程必须等待其释放信号后,才可继
    [释放信号量操作]        // 续执行。若申请到信号,则可继续执行,但其
                            // 它进程必须等待本进程释放信号量后,才能再使
                            // 用本信号。
    return temp;
}
 
5 :在同一项目组应明确规定对接口函数参数的合法性检查应由函数的调用者负责还是由接口函数本身负责,缺省是由函数调用者负责
说 明:对于模块间接口函数的参数的合法性检查这一问题,往往有两个极端现象,即:要么是调用者和被调用者对参数均不作合法性检查,结果就遗漏了合法性检查这 一必要的处理过程,造成问题隐患;要么就是调用者和被调用者均对参数进行合法性检查,这种情况虽不会造成问题,但产生了冗余代码,降低了效率。 
  
6 :防止将函数的参数作为工作变量
说明:将函数的参数作为工作变量,有可能错误地改变参数内容,所以很危险。对必须改变的参数,最好先用局部变量代之,最后再将该局部变量的内容赋给该参数。
示例:如下函数的实现就不太好。
void sum_data( unsigned int num, int *data, int *sum )
{
    unsigned int count;
    *sum = 0;
 
    for (count = 0; count < num; count++)
    {
        *sum  += data[count]; // sum成了工作变量,不太好。
    }
}
若改为如下,则更好些。
void sum_data( unsigned int num, int *data, int *sum )
{
    unsigned int count ;
    int sum_temp;
    sum_temp = 0;
 
    for (count = 0; count < num; count ++)
    {
        sum_temp  += data[count];
    }
 
    *sum = sum_temp;
}
  
7 :函数的规模尽量限制在200 行以内
说明:不包括注释和空格行。 
8 :一个函数仅完成一件功能 

9 :为简单功能编写函数
说明:虽然为仅用一两行就可完成的功能去编函数好象没有必要,但用函数可使功能明确化,增加程序可读性,亦可方便维护、测试。
示例:如下语句的功能不很明显。
value = ( a > b ) ? a : b ;
改为如下就很清晰了。
 
int max (int a, int b)
{
    return ((a > b) ? a : b);
}
 
value = max (a, b);
 
或改为如下。
 
#define MAX (a, b) (((a) > (b)) ? (a) : (b))
 
value = MAX (a, b);
 
10:不要设计多用途面面俱到的函数
说明:多功能集于一身的函数,很可能使函数的理解、测试、维护等变得困难。 
 
11:函数的功能应该是可以预测的,也就是只要输入数据相同就应产生同样的输出
说 明:带有内部“存储器”的函数的功能可能是不可预测的,因为它的输出可能取决于内部存储器(如某标记)的状态。这样的函数既不易于理解又不利于测试和维 护。在C/C++语言中,函数的static局部变量是函数的内部存储器,有可能使函数的功能不可预测,然而,当某函数的返回值为指针类型时,则必须是 STATIC的局部变量的地址作为返回值,若为AUTO类,则返回为错针。
示例:如下函数,其返回值(即功能)是不可预测的。
 
unsigned int integer_sum( unsigned int base )
{
    unsigned int index;
    static unsigned int sum = 0; // 注意,是static类型的。
                                 // 若改为auto类型,则函数即变为可预测。
    for (index = 1; index <= base; index++)
    {
        sum += index;
    }
    return sum;
}
  
12 :尽量不要编写依赖于其他函数内部实现的函数
说明:此条为函数独立性的基本要求。由于目前大部分高级语言都是结构化的,所以通过具体语言的语法要求与编译器功能,基本就可以防止这种情况发生。但在汇编语言中,由于其灵活性,很可能使函数出现这种情况。
示例:如下是在DOS下TASM的汇编程序例子。过程Print_Msg的实现依赖于Input_Msg的具体实现,这种程序是非结构化的,难以维护、修改。
 
...  // 程序代码
proc Print_Msg // 过程(函数)Print_Msg
    ...  // 程序代码
    jmp  LABEL
    ...  // 程序代码
endp
 
proc Input_Msg // 过程(函数)Input_Msg
    ...  // 程序代码
LABEL:
    ...  // 程序代码
endp

13 :避免设计多参数函数,不使用的参数从接口中去掉
说明:目的减少函数间接口的复杂度。 
  
14 :非调度函数应减少或防止控制参数,尽量只使用数据参数
说 明:本建议目的是防止函数间的控制耦合。调度函数是指根据输入的消息类型或控制命令,来启动相应的功能实体(即函数或过程),而本身并不完成具体功能。控 制参数是指改变函数功能行为的参数,即函数要根据此参数来决定具体怎样工作。非调度函数的控制参数增加了函数间的控制耦合,很可能使函数间的耦合度增大, 并使函数的功能不唯一。
示例:如下函数构造不太合理。
int add_sub( int a, int b, unsigned char add_sub_flg )
{
    if (add_sub_flg == INTEGER_ADD)
    {
        return (a + b);
    }
    else
    {
        return (a  b);
    }
}
不如分为如下两个函数清晰。
int add( int a, int b )
{
    return (a + b);
}
 
int sub( int a, int b )
{
    return (a  b);
}
15 :检查函数所有参数输入的有效性 
 
16 :检查函数所有非参数输入的有效性,如数据文件、公共变量等
说明:函数的输入主要有两种:一种是参数输入;另一种是全局变量、数据文件的输入,即非参数输入。函数在使用输入之前,应进行必要的检查。 
  
17 :函数名应准确描述函数的功能 
  
18 :使用动宾词组为执行某操作的函数命名。如果是OOP 方法,可以只有动词(名词是对象本身)
示例:参照如下方式命名函数。
void print_record( unsigned int rec_ind ) ;
int  input_record( void ) ;
unsigned char get_current_color( void ) ;
  
19 :避免使用无意义或含义不清的动词为函数命名
说明:避免用含义不清的动词如process、handle等为函数命名,因为这些动词并没有说明要具体做什么。
  
20 :函数的返回值要清楚、明了,让使用者不容易忽视错误情况
说明:函数的每种出错返回值的意义要清晰、明了、准确,防止使用者误用、理解错误或忽视错误返回码。 
  
21 :除非必要,最好不要把与函数返回值类型不同的变量,以编译系统默认的转换方式或强制的转换方式作为返回值返回 
  
22 :让函数在调用点显得易懂、容易理解 
  
23 :在调用函数填写参数时,应尽量减少没有必要的默认数据类型转换或强制数据类型转换
说明:因为数据类型转换或多或少存在危险。 
  
24 :避免函数中不必要语句,防止程序中的垃圾代码
说明:程序中的垃圾代码不仅占用额外的空间,而且还常常影响程序的功能与性能,很可能给程序的测试、维护等造成不必要的麻烦。 
  
25 :防止把没有关联的语句放到一个函数中
说 明:防止函数或过程内出现随机内聚。随机内聚是指将没有关联或关联很弱的语句放到同一个函数或过程中。随机内聚给函数或过程的维护、测试及以后的升级等造 成了不便,同时也使函数或过程的功能不明确。使用随机内聚函数,常常容易出现在一种应用场合需要改进此函数,而另一种应用场合又不允许这种改进,从而陷入 困境。
在编程时,经常遇到在不同函数中使用相同的代码,许多开发人员都愿把这些代码提出来,并构成一个新函数。若这些代码关联较大并且是完成一个功能的,那么这种构造是合理的,否则这种构造将产生随机内聚的函数。
示例:如下函数就是一种随机内聚。
 
void Init_Var( void )
{
    Rect.length = 0;
    Rect.width = 0; /* 初始化矩形的长与宽 */
    Point.x = 10;
    Point.y = 10;   /* 初始化“点”的坐标 */
}
 
矩形的长、宽与点的坐标基本没有任何关系,故以上函数是随机内聚。
应如下分为两个函数:
void Init_Rect( void )
{
    Rect.length = 0;
    Rect.width = 0; /* 初始化矩形的长与宽 */
}
 
void Init_Point( void )
{
    Point.x = 10;
    Point.y = 10;   /* 初始化“点”的坐标 */
}
 
26:如果多段代码重复做同一件事情,那么在函数的划分上可能存在问题
说明:若此段代码各语句之间有实质性关联并且是完成同一件功能的,那么可考虑把此段代码构造成一个新的函数。 
 
27:功能不明确较小的函数,特别是仅有一个上级函数调用它时,应考虑把它合并到上级函数中,而不必单独存在
说明:模块中函数划分的过多,一般会使函数间的接口变得复杂。所以过小的函数,特别是扇入很低的或功能不明确的函数,不值得单独存在。 
  
28 :设计高扇入、合理扇出(小于7 )的函数
说明:扇出是指一个函数直接调用(控制)其它函数的数目,而扇入是指有多少上级函数调用它。
扇 出过大,表明函数过分复杂,需要控制和协调过多的下级函数;而扇出过小,如总是1,表明函数的调用层次可能过多,这样不利程序阅读和函数结构的分析,并且 程序运行时会对系统资源如堆栈空间等造成压力。函数较合理的扇出(调度函数除外)通常是3-5。扇出太大,一般是由于缺乏中间层次,可适当增加中间层次的 函数。扇出太小,可把下级函数进一步分解多个函数,或合并到上级函数中。当然分解或合并函数时,不能改变要实现的功能,也不能违背函数间的独立性。
扇入越大,表明使用此函数的上级函数越多,这样的函数使用效率高,但不能违背函数间的独立性而单纯地追求高扇入。公共模块中的函数及底层函数应该有较高的扇入。
较良好的软件结构通常是顶层函数的扇出较高,中层函数的扇出较少,而底层函数则扇入到公共模块中。 
  
29 :减少函数本身或函数间的递归调用
说明:递归调用特别是函数间的递归调用(如A->B->C->A),影响程序的可理解性;递归调用一般都占用较多的系统资源(如栈空间);递归调用对程序的测试有一定影响。故除非为某些算法或功能的实现方便,应减少没必要的递归调用。
  
30 :仔细分析模块的功能及性能需求,并进一步细分,同时若有必要画出有关数据流图,据此来进行模块的函数划分与组织
说明:函数的划分与组织是模块的实现过程中很关键的步骤,如何划分出合理的函数结构,关系到模块的最终效率和可维护性、可测性等。根据模块的功能图或/及数据流图映射出函数结构是常用方法之一。
  
31 :改进模块中函数的结构,降低函数间的耦合度,并提高函数的独立性以及代码可读性、效率和可维护性
优化函数结构时,要遵守以下原则:
(1)不能影响模块功能的实现。
(2)仔细考查模块或函数出错处理及模块的性能要求并进行完善。
(3)通过分解或合并函数来改进软件结构。
(4)考查函数的规模,过大的要进行分解。
(5)降低函数间接口的复杂度。
(6)不同层次的函数调用要有较合理的扇入、扇出。
(7)函数功能应可预测。
(8)提高函数内聚。(单一功能的函数内聚最高)
说明:对初步划分后的函数结构应进行改进、优化,使之更为合理。 
  
32 :在多任务操作系统的环境下编程,要注意函数可重入性的构造
说 明:可重入性是指函数可以被多个任务进程调用。在多任务操作系统中,函数是否具有可重入性是非常重要的,因为这是多个进程可以共用此函数的必要条件。另 外,编译器是否提供可重入函数库,与它所服务的操作系统有关,只有操作系统是多任务时,编译器才有可能提供可重入函数库。如DOS下BC和MSC等就不具 备可重入函数库,因为DOS是单用户单任务操作系统。
  
33 :避免使用BOOL 参数
说明:原因有二,其一是BOOL参数值无意义,TURE/FALSE的含义是非常模糊的,在调用时很难知道该参数到底传达的是什么意思;其二是BOOL参数值不利于扩充。还有NULL也是一个无意义的单词。
  
34 : 对于提供了返回值的函数,在引用时最好使用其返回值 
  
35 :当一个过程(函数)中对较长变量(一般是结构的成员)有较多引用时,可以用一个意义相当的宏代替
说明:这样可以增加编程效率和程序的可读性。
示例:在某过程中较多引用TheReceiveBuffer[FirstSocket].byDataPtr,
则可以通过以下宏定义来代替:
# define pSOCKDATA TheReceiveBuffer[FirstScoket].byDataPtr

posted @ 2009-07-29 13:52 Da Vinci 阅读(127) 评论(0) 编辑

2009年5月29日

Dump 文件分析很大程度上就是分析蓝屏产生的原因。这种系统级的错误算是Windows提示错误中比较严重的一种(更严重的还有启动黑屏等硬件或软件兼容性错误等等)。说它是比较严重,是因为毕竟Windows还提供了dump文件给用户分析,至少能比较容易的找到错误的原因。一般蓝屏要么是内核程序中的异常或违规,要么是数据结构的损坏,也有boot或shutdown的时候内核出错。有时候蓝屏是一闪而过,紧接着是系统重启;有时候是蓝屏等待。总之蓝屏的时候都提示了一些停止代码和错误信息,不过这些提示是不全面的,最多知道哪个模块出错(比如驱动)。想了解进一步的信息,或者通过搜索引擎,最好的方式当然是dump文件分析。当然,如果有更进一步研究的欲望,内核调试是更好的方法,不过这需要某些软件支持和调试技巧。

类型
Dump文件有三种:完整内存转储,内核内存转储,小内存转储。System Properties中的高级选项中可以看到这些设置。
完整内存转储太大,一般是物理内存大小或多一些,包括了用户进程页面,这种方式不实用,2GB的物理内存转储出来至少要2GB的磁盘空间(还有文件头信息)。内核转储一般是200MB大小(物理内存小于4GB),它只是包含了所有属于内核模式的物理内存。小内存转储一般是64KB(64位上是 128KB),这两种方式是更常用的。
小内存转储在\Windows\Minidump下生成了一个叫Mini日期+序列号.dmp的文件,这个珍贵的资源就是系统Crash时刻的状态,只不过小内存转储只记录的有限的信息,而且在你分析时,如果windbg没有设置符号服务器的路径(关于符号服务器,请参考Windbg内核调试之二: 常用命令),那么你的当前系统必须和发生蓝屏的系统的Ntoskrnl.exe版本相同,否则就有找不到符号的问题产生。
启动windbg,用Open Crash Dump打开dump文件,或者直接拖动文件到windbg中,windbg显示如下信息:


Loading Dump File [C:\dbg\Mini052809-01.dmp]
Mini Kernel Dump File: Only registers and stack trace are available
Symbol search path is: SRV*d:/temp/*http://msdl.microsoft.com/download/symbols
Executable search path is:
Windows Vista Kernel Version 6000 (Service Pack 1) UP Free x86 compatible
Kernel base = 0x80400000 PsLoadedModuleList = 0x8046e8f0
Debug session time: Thu May 28 16:12:29.031 2009 (GMT+8)
System Uptime: not available
Loading Kernel Symbols...............................................................
Loading unloaded module list.....................................................................

Loading UserSymbols
********************************************************************************                                                                                                                                           **                        Bugcheck Analysis                                                                                         **                                                                                                                                           ********************************************************************************
Use !analyze -v to get detailed debugging information.
BugCheck 7F, {0, 0, 0, 0}

大致上提示了Crash系统的版本,加载符号的过程,如果找不到符号文件,还会提示Unable to load image。如下错误就是找不到ntoskrnl.exe的符号文件:

Unable to load image \SystemRoot\system32\ntoskrnl.exe, Win32 error 0n2
*** WARNING: Unable to verify timestamp for ntoskrnl.exe
*** ERROR: Module load completed but symbols could not be loaded for ntoskrnl.exe

命令
通过lm命令查看模块列表。另外,如果出现Unable to load image,说明没有找到这个文件,这个时候需要查看是否加载了正确的符号文件。设置符号服务器路径(.symfix命令)是很有必要的,因为调试机器和Crash机器的环境很可能不一致。
运行命令kb,显示调用栈的信息。如果有正确的符号设置,可以看到调用的函数名。如果你在调试自己驱动程序的蓝屏问题,请确保设置正确该驱动程序的符号路径,不然就会出现Stack unwind information not available的问题。加入正确的符号文件(pdb)后,可以用命令!reload重新加载符号文件。
通过!thread和!process,可以显示当前进程和线程。或者通过dt nt!_KTHREAD 地址和dt nt!_EPROCESS地址来查看线程和进程结构。

Windbg提供了自动分析dump文件的机制。通过命令!analyze –v,windbg可以自动做分析,显示如下信息:


*******************************************************************************
*                                                                                                                                          *
*                         Bugcheck Analysis                                                                                       *
*                                                                                                                                          *
*******************************************************************************

DRIVER_IRQL_NOT_LESS_OR_EQUAL (d1)
An attempt was made to access a pageable (or completely invalid) address at an
interrupt request level (IRQL) that is too high.   This is usually
caused by drivers using improper addresses.
If kernel debugger is available get stack backtrace.
Arguments:
Arg1: e1147008, memory referenced
Arg2: 0000001c, IRQL
Arg3: 00000000, value 0 = read operation, 1 = write operation
Arg4: fbe93403, address which referenced memory

Debugging Details:
------------------
READ_ADDRESS:   e1147008 Paged pool
CURRENT_IRQL:   1c
FAULTING_IP:
myfault+403
fbe93403 8b06             mov      eax,dword ptr [esi]

DEFAULT_BUCKET_ID:   DRIVER_FAULT
BUGCHECK_STR:   0xD1
PROCESS_NAME:   NotMyfault.exe

TRAP_FRAME:   f9357b80 --(trap fffffffff9357b80)
ErrCode = 00000000
eax=00000000 ebx=8111f330 ecx=000000d1 edx=0000001c esi=e1147008 edi=00000000
eip=fbe93403 esp=f9357bf4 ebp=f9357c58 iopl=0          nv up ei pl zr na pe nc
cs=0008   ss=0010   ds=0023   es=0023   fs=0030   gs=0000              efl=00010246
myfault+0x403:
fbe93403 8b06             mov      eax,dword ptr [esi]   ds:0023:e1147008=????????
Resetting default scope

LAST_CONTROL_TRANSFER:   from 804f880d to 80527da8

STACK_TEXT:
f9357734 804f880d 00000003 f9357a90 00000000 nt!RtlpBreakWithStatusInstruction
f9357780 804f93fa 00000003 e1147008 fbe93403 nt!KiBugCheckDebugBreak+0x19
f9357b60 80540853 0000000a e1147008 0000001c nt!KeBugCheck2+0x574
f9357b60 fbe93403 0000000a e1147008 0000001c nt!KiTrap0E+0x233
WARNING: Stack unwind information not available. Following frames may be wrong.
f9357c58 805759d1 ffb5c3b0 8111f318 811d9130 myfault+0x403
f9357d00 8056e33c 00000090 00000000 00000000 nt!IopXxxControlFile+0x5e7
f9357d34 8053d808 00000090 00000000 00000000 nt!NtDeviceIoControlFile+0x2a
f9357d34 7c92eb94 00000090 00000000 00000000 nt!KiFastCallEntry+0xf8
0012f9f0 7c92d8ef 7c801671 00000090 00000000 ntdll!KiFastSystemCallRet
0012f9f4 7c801671 00000090 00000000 00000000 ntdll!ZwDeviceIoControlFile+0xc
0012fa54 004018c2 00000090 83360018 00000000 0x7c801671

STACK_COMMAND:   kb

FOLLOWUP_IP:
myfault+403
fbe93403 8b06             mov      eax,dword ptr [esi]

SYMBOL_STACK_INDEX:   4
FOLLOWUP_NAME:   MachineOwner
MODULE_NAME: myfault
IMAGE_NAME:   myfault.sys
DEBUG_FLR_IMAGE_TIMESTAMP:   43774e1d
SYMBOL_NAME:   myfault+403
FAILURE_BUCKET_ID:   0xD1_myfault+403
BUCKET_ID:   0xD1_myfault+403
Followup: MachineOwner

一般是按照如下:停止码解释,陷阱帧寄存器信息,蓝屏属性(有些除零错误就在这里显示),栈调用,错误指令位置(FOLLOWUP_IP),出错源代码和汇编代码行,错误代码行,出错模块信息(包括负责人等信息),来组织自动分析信息。

通过r命令,可以显示Crash时刻寄存器的状态和最后的命令状态。

通过d命令,可以显示当前内存的地址。在定位了错误代码行了之后,就可以进一步进行内核调试和系统调试了。

posted @ 2009-05-29 21:35 Da Vinci 阅读(4602) 评论(2) 编辑

2009年5月7日

需求

  1. 你能给出一些非功能性(或者质量)需求的例子么?

在某个时间范围内,产品运行稳定的程度;在某些压力极端情况(断电、饱和度),产品运行稳定的程度;不正常宕机后,产品的恢复度量。

  1. 如果客户需要高性能、使用极其方便而又高度安全,你会给他什么建议?

方便与高性能、高度安全是不完全统一的。方便带来的问题是安全性的降低,或性能不能达到最合适的程度;高性能会导致复杂性;安全性会让性能不能达到最佳。最好的方式是选择一种优先级,比如以安全性为主要的考核标准,适当安排性能和难易程度;或者以性能为主。尽量达到三者的平衡点,在某些极端的情况可比保证性能降低,但安全有保证。等等建议。

  1. 你能给出一些用来描述需求的不同技术么?它们各自适用于什么场景?
  2. 需求跟踪是什么意思?什么是向前追溯,什么是向后追溯?
  3. 你喜欢用什么工具跟踪需求?
  4. 你怎么看待需求变化?它是好是坏?给出你的理由。
  5. 你怎样研究需求,发现需求?有哪些资源可以用到?
  6. 你怎么给需求制定优先级?有哪些技术?
  7. 在需求过程中,用户、客户、开发人员各自的职责是什么?
  8. 你怎么对待不完整或是令人费解的需求?

以客户为导向,和客户召开需求review meeting,向客户表述自己对当前需求的理解。如果有不完整或者理解有误的地方,客户会及时纠正。一定要在需求明确的前提下进一步设计,不正确的理解需求会导致项目失败。

功能设计

  1. 在功能设计中有哪些隐喻?给出几个成功的例子。
  2. 如果有些功能的执行时间很长,怎么能让用户感觉不到太长的等待?
  3. 如果用户必须要在一个很小的区域内,从一个常常的列表中选择多个条目,你会用什么控件?
  4. 有哪些方法可以保证数据项的完整?
  5. 建立系统原型有哪些技术?
  6. 应用程序怎样建立对用户行为的预期?给出一些例子。
  7. 如何入手设计一组数量庞大而又复杂的特性,你能举出一些设计思路吗?
  8. 有一个列表,其中有10个元素,每个元素都有20个字段可以编辑,你怎样设计这种情况?如果是1000个元素,每个元素有3个字段呢?
  9. 用不同的颜色对一段文本中的文字标记高亮,这种做法有什么问题?
  10. Web环境和Windows环境各有些什么限制?

技术设计

  1. 什么是低耦合和高聚合?封装原则又是什么意 思?                          

低耦合是针对类与类间的依赖关系而言,类具有单一职责,低耦合度的类不依赖其它类,从而保持其独立性;高聚合针对类中各个职责的关联程度而言。封装原则是对类的可变性而言,应遵守开放-封闭原则,对修改封闭,对添加开放。

  1. Web应用中,你怎样避免几个人编辑同一段数据所造成的冲突?
  2. 知道设计模式吗?你用过哪些设计模式?在什么场合下用 的?        

可复用面向对象的设计中,设计模式是被反复使用的,经过分类的代码设计经验总结。其目的是为了可重用代码,让代码容易理解。设计模式的四个要素是模式名称、问题、解决方案、效果。
用过很多设计模式,Singlton,Factory,Template,Adapter,Composite等等。

  1. 是否了解什么是无状态的业务层?长事务如何与之相适应?
  2. 在搭建一个架构,或是技术设计时,你用过几种图?

UML图,类图,接口图。

  1. N层架构中都有哪些层?它们各自的职责是什么?
  2. 有哪些方法可以确保架构中数据的正确和健壮?
  3. 面向对象设计和面向组件设计有哪些不同之处?
  4. 怎样在数据库中对用户授权、用户配置、权限管理这几项功能建模?
  5. 怎样按照等级制度给动物王国(包括各种物种和各自的行为)建模?

程序设计

  1. 你怎样保证你的代码可以处理各种错误事件?
  2. 解释一下什么是测试驱动开发,举出极限编程中的一 些原 则。       

TDD是极限编程的一种,特点是用测试来驱动开发,需求和详细设计后,根据测试用例,先编写测试代码,最后根据需求,逐步完成功能代码的添加。典型的模型有"V"模型和"X"模型。极限编程原则:持续整合,测试,快速反馈,接受变化,简单设计,小版本,计划制定....

  1. 别人代码的时候,你最关心什么地 方?                                  

 目的,设计方法。

  1. 什么时候使用抽象类,什么时候使用接口?

抽象类(is-a)中的方法可以实现,接口中的方法不能在定义时实现(like-a)。两者都不能实例化。抽象类对其子类部分可见。接口可以实现类似多重继承(针对java)。因为接口一旦创建就不能改变,所以当要创建多个版本的组件时,应使用抽象类,因为通过更新基类,子类也会更新;或者希望通过基类控制子类的行为时(设计模式中的),也应该使用抽象类,因为可以通过在抽象基类中添加实现,控制子类的状态。接口的原则是接口不变性(对象的定义与实现),当创建的功能被大量不相关对象使用时,适合使用接口。

  1. 除了IDE以外,你还喜欢哪些必不可少的工具?

文本编辑和比较工具,sysinternalsdebug工具。

  1. 你怎么保证代码执行速度快,而又不出问题?

正确的算法设计;降低依赖耦合度;运用代码检测工具查找耗时模块。

  1. 什么时候用多态,什么时候用委派?
  2. 什么时候使用带有静态成员的类,什么时候使用单例?

带有静态成员的类能保证其不会被实例化。单例算是一个应用,其中构造函数设置成私有的,有一个静态方法获取该类的实例。单例是实现延迟实例化的方法,这种对象创建型的模式保证了一个类只有一个实例,可以控制其客户何时访问它。这对注册表的管理设计或多线程等一些地方有很大的左右。

  1. 你在代码里面怎么提前处理需求的变化?给一些例子。

增加读取配置文件的代码,若需求变化,修改相关的配置文件就可以了。

  1. 描述一下实现一段代码的过程,从需求到最终交付。

定义需求,概要设计,详细设计(组件划分,结构图),编码,单元测试,集成测试,完全测试,发布。

算法

  1. 怎样知道一个数字是不是2的乘方?怎样判断一个数是不是奇数?

2取余。(0现在也被认为是偶数了吧)

  1. 怎样找出链表中间的元素?

已得到长度:单个循环;未知长度:可用两个指针(一头一尾)实现。

  1. 怎样改变10,000个静态HTML页面中所有电话号码的格式?
  2. 举出一个你所用过的递归的例子。

删除非空文件夹所有内容(C++)

  1. 在散列表和排序后的列表中找一个元素,哪个查找速度最快?

散列表。

  1. 不管是书、杂志还是网络,你从中所学到的最后一点算法知识是什么?

KMP算法。

  1. 怎样把字符串反转?你能不用临时的字符串么?

递归、逐个交换、用栈结构实现等等。

  1. 你愿意用什么类型的语言来编写复杂的算法?

C/C++

  1. 有一个数组,里面是从11,000,000的整数,其中有一个数字出现了两次,你怎么找出那个重复的数字?
  2. 你知道旅行商问题(Traveling Salesman Problem么?

数据结构

  1. 怎样在内存中实现伦敦地铁的结构?
  2. 怎样以最有效的方式在数据库中存储颜色值?
  3. 队列和堆栈区别是什么?

队列是先进先出结构,堆栈是先进后出结构

  1. 用堆或者栈存储数据的区别是什么?
  2. 怎样在数据库中存储N维向量?
  3. 你倾向于用哪种类型的语言编写复杂的数据结构?

C/C++

  1. 21的二进制值是什么?十六制值呢?

二进制:00010101;十六进制:0x15

  1. 不管是书、杂志还是网络,你从中所学到的最后一点数据结构的知识是什么?

Dijkstra最短路径。

  1. 怎样在XML文档中存储足球比赛结果(包括队伍和比分)?
  2. 有哪些文本格式可以保存Unicode字符?

测试

  1. 什么是回归测试?怎样知道新引入的变化没有给现有的功能造成破坏?
  2. 如果业务层和数据层之间有依赖关系,你该怎么写单元测试?
  3. 你用哪些工具测试代码质量?

Rational PurifyRational QuantifyCode ReviewerIntel vTunesSource Monitor

  1. 在产品部署之后,你最常碰到的是什么类型的问题?
  2. 什么是代码覆盖率?有多少种代码覆盖率?

代码覆盖率是单元测试中衡量测试好坏的方式,是一种代码覆盖程度的衡量。种类很多,语句覆盖、判定覆盖、条件覆盖、路径覆盖等等。

  1. 功能测试和探索性测试的区别是什么?你怎么对网站进行测试?
  2. 测试套件、测试用例、测试计划,这三者之间的区别是什么?你怎么组织测试?
  3. 要对电子商务网站做冒烟测试,你会做哪些类型的测试?
  4. 客户在验收测试中会发现不满意的东西,怎样减少这种情况的发生?
  5. 你去年在测试和质量保证方面学到了哪些东西?

维护

  1. 你用哪些工具在维护阶段对产品进行监控?

Windbgsysinternals,脚本。

  1. 要想对一个正在产品环境中被使用的产品进行升级,该注意哪些重要事项?

升级不影响现有环境;升级的组件不影响其它组件的正常使用;不引入bug;若出现重大问题可顺利取消升级并具有rollback机制。

  1. 如果在一个庞大的文件中有错误,而代码又无法逐步跟踪,你怎么找出错误?

代码中加入Log机制;或者利用某些静态代码分析工具(限于语法或简单逻辑错误)

  1. 你怎样保证代码中的变化不会影响产品的其他部分?

运用合适的开发和设计方法,比如某些设计模式,组件模型等,减小组件间的耦合度。

  1. 你怎样为产品编写技术文档?

根据产品满足的需求,结合实际产品功能的划分,采用由顶向下逐步细化的方式(top-down)

  1. 你用过哪些方式保证软件产品容易维护?

代码中加入灵活的Log机制,产生bug后能及时发现错误点;尽量使用一些脚本、可配置ini文件或二进制文件,控制代码的执行方式,增加灵活度。

  1. 怎样在产品运行的环境中进行系统调试?
  2. 什么是负载均衡?负载均衡的方式有哪些种?
  3. 为什么在应用程序的生命周期中,软件维护费用所占的份额最高?
  4. 再造工程(re-engineering)和逆向工程(reverse engineering)的区别是什么?

配置管理

  1. 你知道配置管理中基线的含义么?怎样把项目中某个重要的时刻冻结?

基线标志着开发过程中的各个millstones。形成文档并复审通过后,基线就形成了,标志着开发过程中的一个阶段进入正式的可控状态。基线不是不可以修改,但必须经过合适的标准来评估,即Change Management中要求的过程进行。基线是进一步开发的的基准和出发点。

冻结项目中的重要时刻:形成规范文档,评估之后获得认证。

  1. 你一般会把哪些东西纳入版本控制?

Source Code,二进制文件

  1. 怎样可以保证团队中每个人都知道谁改变了哪些东西?

版本控制工具,脚本追踪。

  1. TagBranch的区别是什么?在什么情况下该使用tag,什么时候用branch

Tag即标签,不可提交。Branch是分支可以提交。Branch可以包含多个TagBranch是同一个系统上功能差异大或实现了不同需求的不同产品线,一般在新的产品线生成的时候产生;Tag是记录版本历史的Millstones,不能修改,一般在重要功能完成或去掉了某个功能,新建Branch之后,或者测试之前,生成Tag

  1. 怎样管理技术文档——如产品架构文档——的变化?
  2. 你用什么工具管理项目中所有数字信息的状态?你最喜欢哪种工具?
  3. 如果客户想要对一款已经发布的产品做出变动,你怎么处理?

对此产品的Source Code利用版本控制工具做Branch,不影响其他产品的维护,并在此Branch基础上做改动。

  1. 版本管理和发布管理有什么差异?
  2. 对文本文件的变化和二进制文件的变化进行管理,这二者有什么不同?
  3. 同时处理多个变更请求,或是同时进行增量开发和维护,这种事情你怎么看待?

项目管理

  1. 范围、时间、成本,这三项中哪些是可以由客户控制 的?                     

应该说客户对三者都具有直接或间接可控性。其中scope是客户可控性最大 的,项目初始化期,scope根据客户需求进行定义,planning阶段,客户可根据实际情况选择改变或添加需求,从而改变或增加scope。一个好的 PM或项目负责人应该在项目初期预见到scope可能的变化,从而进行相关的risk评估和change管理,以便降低scope对项目的影响。
对于timebudget,客户也可以进行间接控制。如果scope的变动对项目产生影响,PM应联系timebudget的实际责任人并进行相关的调整。当然这种情况对项目并不利。

  1. 谁该对项目中所要付出的一切做出估算?谁有权设置最后期限?

应该是PM需要做出估算。最后期限应该是协商的结果(个人理解)

  1. 少交付的次数,或是减少每个每个交付中的工作量,你喜欢哪种做 ?

第二种,能让客户切实感受到项目的进度,同时又对项目的完成产生信心。能看到实际的产生物才是客户想要的。

  1. 你喜欢用哪种图来跟踪项目进度? 

Gannt chart

  1. 代和增量的区别在哪 里?  

迭代和增量在开发过程中联系紧密。迭代是针对工作流而言,而增量是产品的完 善而言。迭代是一个完整的过程,开发过程中完成的目标就是一个迭代,比如第一次迭代完成初步的产品模型,第二次迭代实现产品功能的细节;增量是对某个功能 的添加或者完善,每次的增量都增加部分功能,最终形成完整产品。
现实中这两种开发方法常混合在一起使用。

  1. 试着解释一下风险管理中用到的实践。风险该如何管理?
  2. 你喜欢任务分解还是滚动式计划?
  3. 你需要哪些东西帮助你判断项目是否符合时间要求,在预算范围内运作?
  4. DSDMPrince2Scrum,这三者之间有哪些区别?

只了解Scrum,一种敏捷软件开发模型,能快速响应变化,有backlogsprintsprint backlogscrumMastertime-box等要点。

  1. 如果客户想要的东西太多,你在范围和时间上怎样跟他达成一致呢?

Scope 上,对于客户的需求进行分解,评估实际可能需要的时间,告知客户如果需求过多,TimeBudget会相应的增加。并和客户协调,尽可能在某个 deliverable交付部分主要的需求功能,把Scope的内容先缩小到一个可控时间的范围并让客户实际得到粗略的但是覆盖了重要功能的产品,而在下 一个deliverable完成另外某个Scope。按客户的要求分解Scope,从而实现时间上的细化。分清必须做的和不必要做的,立刻需要完成的和可 以延后完成的。

 

posted @ 2009-05-07 22:04 Da Vinci 阅读(186) 评论(0) 编辑

2008年7月8日

    任何时候系统内存资源相对磁盘空间来说都是相形见拙的。因为虚拟内存机制,使我们可以有相对丰富的地址资源(通常32bit的虚拟地址,可以有4G的寻址 空间),而这些资源对物理内存来说一般情况是总是绰绰有余的。所以在现代操作系统中,总是在相对紧张时使用一些策略,如FIFO、LRU等将物理内存的一 些页面置入相对便宜的磁盘空间资源中。一般的UNIX系统,独立使用一个分区,即swap partition。而这方面Windows只是使用普通的文 件,通常命名为pagefile.sys,位于各分区的根目录中。由于受到用于pagefile的PTE的限制(PTE中使用4bit来识别操作的 pagefile),所以Windows最多可以支持16个pagefile.sys。

    从上描述,pagefile.sys本身 就是一个比较特殊的文件,根据系统的情况它的大小是可扩展的,通常我们可以使用“控制面板”的“系统”小Applet来设置。由于其特殊性, Windows在启动阶段会对每个pagefile.sys建立相应的FILE_OBJECT,并且设置SharedRead字段为False,而且在 System进程,每个FILE_OBJECT都分别有一个句柄指向,这样即只允许系统自身对其操作,避免用户对其进行删除等误操作。

     为了对pagefile.sys进行管理,Windows中有一个长度为16的数组,用于对pagefile.sys的组织。每个成员分别对应一个 pagefile。这个数组由系统变量MmPagingFile指向,每个成员都是一个指向MMPAGING_FILE的结构,这个结构有如下的格式:

    +0x000 Size             : Uint4B
    +0x004 MaximumSize      : Uint4B
    +0x008 MinimumSize      : Uint4B
    +0x00c FreeSpace        : Uint4B
    +0x010 CurrentUsage     : Uint4B
    +0x014 PeakUsage        : Uint4B
    +0x018 Hint             : Uint4B
    +0x01c HighestPage      : Uint4B
    +0x020 Entry            : [2] Ptr32 _MMMOD_WRITER_MDL_ENTRY
    +0x028 Bitmap           : Ptr32 _RTL_BITMAP
    +0x02c File             : Ptr32 _FILE_OBJECT
    +0x030 PageFileName     : _UNICODE_STRING
    +0x038 PageFileNumber   : Uint4B
    +0x03c Extended         : UChar
    +0x03d HintSetToZero    : UChar
    +0x03e BootPartition    : UChar
    +0x040 FileHandle       : Ptr32 Void

通 过这个结构,我们可以很容易的得到相应pagefile的使用情况(MaximumSize、MinimumSize、FreeSpace、 CurrentUsage、PeakUsage,请参阅windbg的!vm命令),其对应的FILE_OBJECT等。另外通过FILE_OBJECT 的DeviceObject与Vpb字段,我们就可知道这个pagefile所处的分区及分区使用的文件系统等等信息。我们来详细介绍一下Bitmap成 员。

    Bitmap是一个RTL_BITMAP的结构,其定义在ntddk.h中:

    typedef struct _RTL_BITMAP {
        ULONG SizeOfBitMap;                     // Number of bits in bit map
        PULONG Buffer;                          // Pointer to the bit map itself
    } RTL_BITMAP;

     与页框数据库(Pfn Database)与虚拟内存(x86平台PAGE_SIZE 4k)一样,Windows也将pagefile分割成4K一块块 的大小,称为一页,每页由Bitmap对应的1 bit指定状态。1为占用,0为空闲。通过使用RtlFindClearBits或是 RtlFindClearBitsAndSet等函数对Bitmap进行操作,寻找这些文件的未用页面。虽然Bitmap指明占用状态时,Windows 常以4k为单位,但为了提高性能,Windows在一次写pagefile的单位通常为64k(MmModifiedWriteClusterSize个 页)。还有MMMOD_WRITER_MDL_ENTRY,等我下面提及相关内容时再加以说明。

    先用windbg来消化一下上面的讨论:

    kd> dd MmPagingFile l 10  //从输出结果可以看出我的机子上设了两个pagefile。
    80547020  80d2af80 feec1548 00000000 00000000
    80547030  00000000 00000000 00000000 00000000
    80547040  00000000 00000000 00000000 00000000
    80547050  00000000 00000000 00000000 00000000
    kd> dd @$p l 40   //第一个pagefile的情况。
    80d2af80  00006400 0000c800 00006400 00000c38
    80d2af90  000057c7 000057c7 00000000 00000000
    80d2afa0  feea1cb8 feea1c18 fecbb000 feddc428
            .
            .
            .

    kd> dd feddc428 l 4  //从上面给出的MMPAGING_FILE,很容易得到file object(Offset 0x2c)。
    feddc428  00700005 80ecf2f0 80ecf268 fee66c10

    kd> !devobj 80ecf2f0   //aFILE_OBJECT的结构在ntddk.h中给出,其第三个dword就是DEVICE_OBJECT。
    Device object (80ecf2f0) is for:
     HarddiskVolume2 \Driver\Ftdisk DriverObject 80d97030
    Current Irp 00000000 RefCount 1316 Type 00000007 Flags 00001150
    Vpb 80ecf268 Dacl e13d1484 DevExt 80ecf3a8 DevObjExt 80ecf490 Dope 80ecf210 DevNode 80d95bd0 
    ExtensionFlags (0000000000)  
    AttachedDevice (Upper) 80d954b8 \Driver\VolSnap
    Device queue is not busy.

    另外FILE_OBJECT的第四个dword(fee66c10)就是VPB结构,你使用!vpb分析分析,限于篇幅,我就不在这儿列出了。

    通过上面windbg的分析,我们已经基本上对pagefile有了一定的了解了,下面转入内存子系列与IO子系统(调用FSD)对pagefile的组织管理。

     通常情况下,对于进程可见的永远是虚拟地址,存取某个虚拟地址,对于不存在的地址(对于X86,即其PTE的P位为0),通过触发硬件中断(X86为 int e),由软件来对这些PTE进行解析,譬如原型PTE(我在《探究Windows 2000/XP原型PTE》中详细介绍),或是过渡PTE (Transition PTE,某些页面由于进程工作集修整等原因,成为可被使用的页面,但这些页面的内容当前对这些进程仍有效,可随时重新使用,所以 Windows使用Transition这个术语区别于纯粹的Free或Zeroed列表,我在《解析Winndows 2000/XP物理内存管理》中 提及PFN列表)等,而对于Page File,实际上也有一个对应的pte指向相应pagefile.sys,完成解析工作 (MiResolvePageFileFault),处理页面错误(通过IoPageRead,下面会介绍)。

    所以在继续讨论之前我们来说明一下Pagefile PTE,它的格式如下:

    Valid            : Pos 0, 1 Bit
    PageFileLow      : Pos 1, 4 Bits
    Protection       : Pos 5, 5 Bits
    Prototype        : Pos 10, 1 Bit
    Transition       : Pos 11, 1 Bit
    PageFileHigh     : Pos 12, 20 Bits

     对于Prototype PTE与Transition PTE,总有1bit用于识别相应的PTE,如上的Prototype字段,但对于 PageFile PTE,却没有对应的识别bit,实际上MiDispatchFault(由KiTrap0E调用),是在解析完 Prototype PTE(MiResolveProtoPteFault)、Transition PTE (MiResolveTransitionFault)、还有MiResolveDemandZeroFault后,才调用 MiResolvePageFileFault的,当然在MiResolveProtoPteFault处理过程中也是最后调用 MiResolvePageFileFault的。

    假设我们存取一个当前驻留在pagefile中的页面,通过 MiDispatchFault,控制权转到MiResolvePageFileFault后,他会根据PTE的PageFileLow来索引 MMPAGING_FILE数组,即判断这一页面位于哪个pagefile.sys中,因为PageFileLow为4个bit,所以Windows最多 可以支持16个pagefile.sys。这样内存子系统根据这个索引从MmPagingFile中描述的页文件结构取出这个pagefile的 FILE_OBJECT(上面介绍过)。加上PageFileHigh所指定的pagefile.sys的偏移值, MiResolvePageFileFault通过返回一个值为0xC0033333的特殊NTSTATUS通知MiDispatchFault调用 IoPageRead得到此页面。IoPageRead的原型如下(定义于ntifs.h中):

    NTKERNELAPI
    NTSTATUS
    IoPageRead(
        IN PFILE_OBJECT FileObject,
        IN PMDL MemoryDescriptorList,
        IN PLARGE_INTEGER StartingOffset,
        IN PKEVENT Event,
        OUT PIO_STATUS_BLOCK IoStatusBlock
    ); 

     当然在调用IoPageRead之前,内存管理器必须分配一个物理页面,必要的时候还要调用MiRemoveAnyPage腾出空间,然后调用 MiInitializeReadInProgressPfn,将这一页框置成ReadInProgress状态,然后将IoPageRead所需要的 MDL参数MemoryDescriptorList指向这一页面。MDL的虚拟地址字段也就是IoPageRead读入的页面映射的虚拟地址,也即满足 我们先前假设的页面错误。

    IoPageRead实际上通过Allocate一个IRP,用DIRECT_IO的方式(即我们提供 的MDL),然后设置一个Complete Routine,用于取消页面读取之前的ReadInProgress状态,再通过IoCallDriver 调用IO子系统调用对应的File System Driver(通常由FILE_OBJECT的VPB参数确定),至于FSD是如何读取 pagefile.sys的,这儿不加以讨论,ntifs提供的fastfat的源代码是学习的方向。

    需要指出的是 IoPageRead是一个同步操作,即只有等待页面读完毕以后才可以往下处理。这也是MiDispatchFault只能运行于 DISPATCH_LEVEL IRQL之下的主要原因。IoPageRead通过设备分配的IRP的 IRP_SYNCHRONOUS_PAGING_IO的标志来实现同步的。另外他也设置了IRP_PAGING_IO、IRP_NOCACHE标志,用于 与FSD之间的特殊通信要求。

    由于工作集修整等的需要,通过MiModifiedPageWriter(MPW)线程实行将某些 页面置入pagefile中。MPW使用MMPAGING_FILE结构的_MMMOD_WRITER_MDL_ENTRY类型的Entry进行操作, _MMMOD_WRITER_MDL_ENTRY不仅仅由MiModifiedPageWriter使用,他还要让MiMappedPageWriter 使用(用于Mapped file),所以_MMMOD_WRITER_MDL_ENTRY结构不仅函有MDL成员,还包含Control Area等 等。限于篇幅,我不将其结构列出。MPW通过IoAsynchronousPageRead将页面按前面说的一次 MmModifiedWriteClusterSize个页面写入pagefile中。对于IoAsynchronousPageRead其使用的 IRP flag是IRP_PAGING_IO与IRP_NOCACHE,说明他是异步操作的。这也可从他的名字看出,区别于Windows提供的另一个 相关过程IoSynchronousPageWrite,他是同步的。

    讲到这儿,对于page file的组织管理的一些基本的 印象应该是有的。最后需要指出的一点是,对于IoPageRead不仅仅是对于pagefile的,其也可以针对mapped file,还有 MiModifiedPageWriter,要不是避免死锁也不会区分出MiMappedPageWriter,实际上Windows内部内存管理器对于 pagefile与mappedfile的管理使用是基本相同的,而FSD的处理也只是一点点的区别而已。所以结合我以前介绍的如 Control Area等概念,对mapped file等的理解也是可以参照本文的。还有同样的一句话,错误地方希望得到你的指点。

posted @ 2008-07-08 14:27 Da Vinci 阅读(560) 评论(1) 编辑

2008年6月23日

摘要: C++字符串完全指引之二 —— 字符串封装类原著:Michael Dunn原文出处:CodeProject:TheComplete Guide to C++ Strings, Part II 引言因为C语言风格的字符串容易出错且不易管理,黑客们甚至利用可能存在的缓冲区溢出bug把C语言风格的字符串作为攻击目标,所以出现了很多字符串封装类。不幸的是,在某些场合下我们不知道该使用哪个字符串类,也不知道...阅读全文

posted @ 2008-06-23 16:39 Da Vinci 阅读(155) 评论(0) 编辑

2008年5月24日

posted @ 2008-05-24 18:20 Da Vinci 阅读(167) 评论(0) 编辑

2008年3月27日

posted @ 2008-03-27 21:16 Da Vinci 阅读(4162) 评论(1) 编辑

2008年3月25日

posted @ 2008-03-25 22:25 Da Vinci 阅读(334) 评论(0) 编辑

2008年3月22日

posted @ 2008-03-22 12:30 Da Vinci 阅读(3130) 评论(0) 编辑

Firefox 3