Profilo di jiyu人生路漫漫,快乐先行FotoBlogElenchi Strumenti Guida

Blog


28 maggio

迁移博客,欢迎大家访问 http://jiyu.wordpress.com.cn/

博客迁移,欢迎大家访问  http://jiyu.wordpress.com.cn/

新的blog关注python, Google App Engine, 奇思异想, webapplication, mobileapplication, javascript等



14 maggio

嵌入式程序员应知道的基本问题-C语言(zz)

来源:21ICbbs  作者:lhf

C语言测试:想成为嵌入式程序员应知道的0x10个基本问题

其中少量灰色的文字是我添加的,表达一些我的看法,很不成熟,希望朋友们指正。

C语言测试是招聘嵌入式系统程序员过程中必须而且有效的方法。这些年,我既参加也组织了许多这种测试,在这过程中我意识到这些测试能为带面试者和被面试者提供许多有用信息,此外,撇开面试的压力不谈,这种测试也是相当有趣的。
从被面试者的角度来讲,你能了解许多关于出题者或监考者的情况。这个测试只是出题者为显示其对ANSI标准细节的知识而不是技术技巧而设计吗?这个愚蠢的问题吗?如要你答出某个字符的ASCII值。这些问题着重考察你的系统调用和内存分配策略方面的能力吗?这标志着出题者也许花时间在微机上而不上在嵌入式系统上。如果上述任何问题的答案是“是”的话,那么我知道我得认真考虑我是否应该去做这份工作。
从面试者的角度来讲,一个测试也许能从多方面揭示应试者的素质:最基本的,你能了解应试者C语言的水平。不管怎么样,看一下这人如何回答他不会的问题也是满有趣。应试者是以好的直觉做出明智的选择,还是只是瞎蒙呢?当应试者在某个问题上卡住时是找借口呢,还是表现出对问题的真正的好奇心,把这看成学习的机会呢?我发现这些信息与他们的测试成绩一样有用。
有了这些想法,我决定出一些真正针对嵌入式系统的考题,希望这些令人头痛的考题能给正在找工作的人一点帮住。这些问题都是我这些年实际碰到的。其中有些题很难,但它们应该都能给你一点启迪。
这个测试适于不同水平的应试者,大多数初级水平的应试者的成绩会很差,经验丰富的程序员应该有很好的成绩。为了让你能自己决定某些问题的偏好,每个问题没有分配分数,如果选择这些考题为你所用,请自行按你的意思分配分数。


预处理器(Preprocessor)

1 . 用预处理指令#define 声明一个常数,用以表明1年中有多少秒(忽略闰年问题)


#define SECONDS_PER_YEAR (60 * 60 * 24 * 365)UL
我在这想看到几件事情:
? #define 语法的基本知识(例如:不能以分号结束,括号的使用,等等)


? 懂得预处理器将为你计算常数表达式的值,因此,直接写出你是如何计算一年中有多少秒而不是计算出实际的值,是更清晰而没有代价的。
? 意识到这个表达式将使一个16位机的整型数溢出-因此要用到长整型符号L,告诉编译器这个常数是的长整型数。
? 如果你在你的表达式中用到UL(表示无符号长整型),那么你有了一个好的起点。记住,第一印象很重要。


2 . 写一个“标准”宏MIN ,这个宏输入两个参数并返回较小的一个。


#define MIN(A,B) ((A) <= (B) ? (A) : (B))

这个测试是为下面的目的而设的:
? 标识#define在宏中应用的基本知识。这是很重要的,因为直到嵌入(inline)操作符变为标准C的一部分,宏是方便产生嵌入代码的唯一方法,对于嵌入式系统来说,为了能达到要求的性能,嵌入代码经常是必须的方法。
? 三重条件操作符的知识。这个操作符存在C语言中的原因是它使得编译器能产生比if-then-else更优化的代码,了解这个用法是很重要的。
? 懂得在宏中小心地把参数用括号括起来
? 我也用这个问题开始讨论宏的副作用,例如:当你写下面的代码时会发生什么事?

least = MIN(*p++, b);


3. 预处理器标识#error的目的是什么?


如果你不知道答案,请看参考文献1。这问题对区分一个正常的伙计和一个书呆子是很有用的。只有书呆子才会读C语言课本的附录去找出象这种问题的答案。当然如果你不是在找一个书呆子,那么应试者最好希望自己不要知道答案。


死循环(Infinite loops)
4. 嵌入式系统中经常要用到无限循环,你怎么样用C编写死循环呢?
这个问题用几个解决方案。我首选的方案是:

while(1)
{
?}

 do{

...

} while (1)

一些程序员更喜欢如下方案:

for(;;)
{
?}

 

这个实现方式让我为难,因为这个语法没有确切表达到底怎么回事。如果一个应试者给出这个作为方案,我将用这个作为一个机会去探究他们这样做的基本原理。如果他们的基本答案是:“我被教着这样做,但从没有想到过为什么。”这会给我留下一个坏印象。
第三个方案是用 goto

Loop:
...
goto Loop;


应试者如给出上面的方案,这说明或者他是一个汇编语言程序员(这也许是好事)或者他是一个想进入新领域的BASIC/FORTRAN程序员。

数据声明(Data declarations)

5. 用变量a给出下面的定义
a) 一个整型数(An integer)
b)一个指向整型数的指针( A pointer to an integer)
c)一个指向指针的的指针,它指向的指针是指向一个整型数( A pointer to a pointer to an intege)r
d)一个有10个整型数的数组( An array of 10 integers)
e) 一个有10个指针的数组,该指针是指向一个整型数的。(An array of 10 pointers to integers)
f) 一个指向有10个整型数数组的指针( A pointer to an array of 10 integers)
g) 一个指向函数的指针,该函数有一个整型参数并返回一个整型数(A pointer to a function that takes an integer as an argument and returns an integer)
h)一个有10个指针的数组,该指针指向一个函数,该函数有一个整型参数并返回一个整型数( An array of ten pointers to functions that take an integer argument and return an integer )

答案是:
a) int a; // An integer
b) int *a; // A pointer to an integer
c) int **a; // A pointer to a pointer to an integer
d) int a[10]; // An array of 10 integers
e) int *a[10]; // An array of 10 pointers to integers
f) int (*a)[10]; // A pointer to an array of 10 integers
g) int (*a)(int); // A pointer to a function a that takes an integer argument and returns an integer

typedef int (*PFUNCTION)(int)

PFUNCTION pfun;
h) int (*a[10])(int); // An array of 10 pointers to functions that take an integer argument and return an integer

typedef int (*PFUNCTION)(int)

PFUNCTION pfun[10];
人们经常声称这里有几个问题是那种要翻一下书才能回答的问题,我同意这种说法。当我写这篇文章时,为了确定语法的正确性,我的确查了一下书。但是当我被面试的时候,我期望被问到这个问题(或者相近的问题)。因为在被面试的这段时间里,我确定我知道这个问题的答案。应试者如果不知道所有的答案(或至少大部分答案),那么也就没有为这次面试做准备,如果该面试者没有为这次面试做准备,那么他又能为什么出准备呢?


Static
6. 关键字static的作用是什么?
这个简单的问题很少有人能回答完全。在C语言中,关键字static有三个明显的作用:
? 在函数体,一个被声明为静态的变量在这一函数被调用过程中维持其值不变。
? 在模块内(但在函数体外),一个被声明为静态的变量可以被模块内所用函数访问,但不能被模块外其它函数访问。它是一个本地的全局变量。
? 在模块内,一个被声明为静态的函数只可被这一模块内的其它函数调用。那就是,这个函数被限制在声明它的模块的本地范围内使用。
大多数应试者能正确回答第一部分,一部分能正确回答第二部分,同是很少的人能懂得第三部分。这是一个应试者的严重的缺点,因为他显然不懂得本地化数据和代码范围的好处和重要性。


Const

7.关键字const有什么含意?
我只要一听到被面试者说:“const意味着常数”,我就知道我正在和一个业余者打交道。去年Dan Saks已经在他的文章里完全概括了const的所有用法,因此ESP(译者:Embedded Systems Programming)的每一位读者应该非常熟悉const能做什么和不能做什么.如果你从没有读到那篇文章,只要能说出const意味着“只读”就可以了。尽管这个答案不是完全的答案,但我接受它作为一个正确的答案。(如果你想知道更详细的答案,仔细读一下Saks的文章吧。)
如果应试者能正确回答这个问题,我将问他一个附加的问题:
下面的声明都是什么意思?

const int a;
int const a;
const int *a;
int * const a;
int const * a const;   //    int const * const a;

/******/
前两个的作用是一样,a是一个常整型数。第三个意味着a是一个指向常整型数的指针(也就是,整型数是不可修改的,但指针可以)。第四个意思a是一个指向整型数的常指针(也就是说,指针指向的整型数是可以修改的,但指针是不可修改的)。最后一个意味着a是一个指向常整型数的常指针(也就是说,指针指向的整型数是不可修改的,同时指针也是不可修改的)。如果应试者能正确回答这些问题,那么他就给我留下了一个好印象。顺带提一句,也许你可能会问,即使不用关键字const,也还是能很容易写出功能正确的程序,那么我为什么还要如此看重关键字const呢?我也如下的几下理由:
? 关键字const的作用是为给读你代码的人传达非常有用的信息,实际上,声明一个参数为常量是为了告诉了用户这个参数的应用目的。如果你曾花很多时间清理其它人留下的垃圾,你就会很快学会感谢这点多余的信息。(当然,懂得用const的程序员很少会留下的垃圾让别人来清理的。)
? 通过给优化器一些附加的信息,使用关键字const也许能产生更紧凑的代码。
? 合理地使用关键字const可以使编译器很自然地保护那些不希望被改变的参数,防止其被无意的代码修改。简而言之,这样可以减少bug的出现。


Volatile

8. 关键字volatile有什么含意?并给出三个不同的例子。
一个定义为volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。下面是volatile变量的几个例子:
? 并行设备的硬件寄存器(如:状态寄存器)
? 一个中断服务子程序中会访问到的非自动变量(Non-automatic variables)
? 多线程应用中被几个任务共享的变量
回答不出这个问题的人是不会被雇佣的。我认为这是区分C程序员和嵌入式系统程序员的最基本的问题。搞嵌入式的家伙们经常同硬件、中断、RTOS等等打交道,所有这些都要求用到volatile变量。不懂得volatile的内容将会带来灾难。
假设被面试者正确地回答了这是问题(嗯,怀疑是否会是这样),我将稍微深究一下,看一下这家伙是不是直正懂得volatile完全的重要性。
? 一个参数既可以是const还可以是volatile吗?解释为什么。
? 一个指针可以是volatile 吗?解释为什么。
? 下面的函数有什么错误:
int square(volatile int *ptr)
{
return *ptr * *ptr;
}

下面是答案:
? 是的。一个例子是只读的状态寄存器。它是volatile因为它可能被意想不到地改变。它是const因为程序不应该试图去修改它。
? 是的。尽管这并不很常见。一个例子是当一个中服务子程序修该一个指向一个buffer的指针时。
? 这段代码有点变态。这段代码的目的是用来返指针*ptr指向值的平方,但是,由于*ptr指向一个volatile型参数,编译器将产生类似下面的代码:


int square(volatile int *ptr)
{
int a,b;
a = *ptr;
b = *ptr;
return a * b;
}


由于*ptr的值可能被意想不到地该变,因此a和b可能是不同的。结果,这段代码可能返不是你所期望的平方值!正确的代码如下:

long square(volatile int *ptr)
{
int a;
a = *ptr;
return a * a;
}

位操作(Bit manipulation)

9. 嵌入式系统总是要用户对变量或寄存器进行位操作。给定一个整型变量a,写两段代码,第一个设置a的bit 3,第二个清除a 的bit 3。在以上两个操作中,要保持其它位不变。
对这个问题有三种基本的反应
? 不知道如何下手。该被面者从没做过任何嵌入式系统的工作。
? 用bit fields。Bit fields是被扔到C语言死角的东西,它保证你的代码在不同编译器之间是不可移植的,同时也保证了的你的代码是不可重用的。我最近不幸看到Infineon为其较复杂的通信芯片写的驱动程序,它用到了bit fields因此完全对我无用,因为我的编译器用其它的方式来实现bit fields的。从道德讲:永远不要让一个非嵌入式的家伙粘实际硬件的边。
? 用 #defines 和 bit masks 操作。这是一个有极高可移植性的方法,是应该被用到的方法。最佳的解决方案如下:


#define BIT3 (0x1 << 3)
static int a;

void set_bit3(void) {
a |= BIT3;
}
void clear_bit3(void) {
a &= ~BIT3;
}

一些人喜欢为设置和清除值而定义一个掩码同时定义一些说明常数,这也是可以接受的。我希望看到几个要点:说明常数、|=和&=~操作。


访问固定的内存位置(Accessing fixed memory locations)

10. 嵌入式系统经常具有要求程序员去访问某特定的内存位置的特点。在某工程中,要求设置一绝对地址为0x67a9的整型变量的值为0xaa66。编译器是一个纯粹的ANSI编译器。写代码去完成这一任务。
这一问题测试你是否知道为了访问一绝对地址把一个整型数强制转换(typecast)为一指针是合法的。这一问题的实现方式随着个人风格不同而不同。典型的类似代码如下:

int *ptr;
ptr = (int *)0x67a9;
*ptr = 0xaa55;

A more obscure approach is:
一个较晦涩的方法是:

*(int * const)(0x67a9) = 0xaa55;

即使你的品味更接近第二种方案,但我建议你在面试时使用第一种方案。

中断(Interrupts)

11. 中断是嵌入式系统中重要的组成部分,这导致了很多编译开发商提供一种扩展—让标准C支持中断。具代表事实是,产生了一个新的关键字__interrupt。下面的代码就使用了__interrupt关键字去定义了一个中断服务子程序(ISR),请评论一下这段代码的。

__interrupt double compute_area (double radius)
{
double area = PI * radius * radius;
printf("\nArea = %f", area);
return area;
}

这个函数有太多的错误了,以至让人不知从何说起了:
? ISR 不能返回一个值。如果你不懂这个,那么你不会被雇用的。
? ISR 不能传递参数。如果你没有看到这一点,你被雇用的机会等同第一项。
? 在许多的处理器/编译器中,浮点一般都是不可重入的。有些处理器/编译器需要让额处的寄存器入栈,有些处理器/编译器就是不允许在ISR中做浮点运算。此外,ISR应该是短而有效率的,在ISR中做浮点运算是不明智的。
? 与第三点一脉相承,printf()经常有重入和性能上的问题。如果你丢掉了第三和第四点,我不会太为难你的。不用说,如果你能得到后两点,那么你的被雇用前景越来越光明了。


代码例子(Code examples)

12 . 下面的代码输出是什么,为什么?

void foo(void)
{
unsigned int a = 6;
int b = -20;
(a+b > 6) ? puts("> 6") : puts("<= 6");
}
这个问题测试你是否懂得C语言中的整数自动转换原则,我发现有些开发者懂得极少这些东西。不管如何,这无符号整型问题的答案是输出是 ”>6”。原因是当表达式中存在有符号类型和无符号类型时所有的操作数都自动转换为无符号类型。 因此-20变成了一个非常大的正整数,所以该表达式计算出的结果大于6。这一点对于应当频繁用到无符号数据类型的嵌入式系统来说是丰常重要的。如果你答错了这个问题,你也就到了得不到这份工作的边缘。

13. 评价下面的代码片断:

unsigned int zero = 0;
unsigned int compzero = 0xFFFF;
/*1's complement of zero */

对于一个int型不是16位的处理器为说,上面的代码是不正确的。应编写如下:

unsigned int compzero = ~0;

这一问题真正能揭露出应试者是否懂得处理器字长的重要性。在我的经验里,好的嵌入式程序员非常准确地明白硬件的细节和它的局限,然而PC机程序往往把硬件作为一个无法避免的烦恼。
到了这个阶段,应试者或者完全垂头丧气了或者信心满满志在必得。如果显然应试者不是很好,那么这个测试就在这里结束了。但如果显然应试者做得不错,那么我就扔出下面的追加问题,这些问题是比较难的,我想仅仅非常优秀的应试者能做得不错。提出这些问题,我希望更多看到应试者应付问题的方法,而不是答案。不管如何,你就当是这个娱乐吧…

动态内存分配(Dynamic memory allocation)
14. 尽管不像非嵌入式计算机那么常见,嵌入式系统还是有从堆(heap)中动态分配内存的过程的。那么嵌入式系统中,动态分配内存可能发生的问题是什么?
这里,我期望应试者能提到内存碎片,碎片收集的问题,变量的持行时间等等。这个主题已经在ESP杂志中被广泛地讨论过了(主要是 P.J. Plauger, 他的解释远远超过我这里能提到的任何解释),所有回过头看一下这些杂志吧!让应试者进入一种虚假的安全感觉后,我拿出这么一个小节目:
下面的代码片段的输出是什么,为什么?

char *ptr;
if ((ptr = (char *)malloc(0)) == NULL)
else
puts("Got a null pointer");
puts("Got a valid pointer");

所以在这个时候,一定要使用pc-lint来检察代码规范性,起码pc-lint能检查出if-else语句格式不对,要求开发人员对格式进行修改,那样的话,看起来就方便多了。

这是一个有趣的问题。最近在我的一个同事不经意把0值传给了函数malloc,得到了一个合法的指针之后,我才想到这个问题。这就是上面的代码,该代码的输出是“Got a valid pointer”。我用这个来开始讨论这样的一问题,看看被面试者是否想到库例程这样做是正确。得到正确的答案固然重要,但解决问题的方法和你做决定的基本原理更重要些。


Typedef 
15 Typedef 在C语言中频繁用以声明一个已经存在的数据类型的同义字。也可以用预处理器做类似的事。例如,思考一下下面的例子:

#define dPS struct s *
typedef struct s * tPS;

以上两种情况的意图都是要定义dPS 和 tPS 作为一个指向结构s指针。哪种方法更好呢?(如果有的话)为什么?
这是一个非常微妙的问题,任何人答对这个问题(正当的原因)是应当被恭喜的。答案是:typedef更好。思考下面的例子:

dPS p1,p2;
tPS p3,p4;

第一个扩展为

struct s * p1, p2;

.
上面的代码定义p1为一个指向结构的指,p2为一个实际的结构,这也许不是你想要的。第二个例子正确地定义了p3 和p4 两个指针。

晦涩的语法

16 . C语言同意一些令人震惊的结构,下面的结构是合法的吗,如果是它做些什么?

int a = 5, b = 7, c;
c = a+++b;

这个问题将做为这个测验的一个愉快的结尾。不管你相不相信,上面的例子是完全合乎语法的。问题是编译器如何处理它?水平不高的编译作者实际上会争论这个问题,根据最处理原则,编译器应当能处理尽可能所有合法的用法。因此,上面的代码被处理成:

c = a++ + b;

因此, 这段代码持行后a = 6, b = 7, c = 12。
如果你知道答案,或猜出正确答案,做得好。如果你不知道答案,我也不把这个当作问题。我发现这个问题的最大好处是这是一个关于代码编写风格,代码的可读性,代码的可修改性的好的话题。
好了,伙计们,你现在已经做完所有的测试了。这就是我出的C语言测试题,我怀着愉快的心情写完它,希望你以同样的心情读完它。如果是认为这是一个好的测试,那么尽量都用到你的找工作的过程中去吧。天知道也许过个一两年,我就不做现在的工作,也需要找一个。

让你明白什么是ERP(zz)

ERP(Enterprise Resource Planning)企业资源计划系统,是指建立在信息技术基础上,以系统化的管理思想,为企业决策层及员工提供决策运行手段的管理平台。
一天中午,丈夫在外给家里打电话:"亲爱的老婆,晚上我想带几个同事回家吃饭可以吗?"(订货意向)
妻子:"当然可以,来几个人,几点来,想吃什么菜?"
丈夫:"6个人,我们7点左右回来,准备些酒、烤鸭、番茄炒蛋、凉菜、蛋花汤......。你看可以吗?"(商务沟通)
妻子:"没问题,我会准备好的。"(订单确认)
妻子记录下需要做的菜单(MPS计划),具体要准备的东西:鸭、酒、番茄、鸡蛋、调料......(BOM物料清单),发现需要:1只鸭蛋,5瓶酒,4个鸡蛋......(BOM展开),炒蛋需要6个鸡蛋,蛋花汤需要4个鸡蛋(共用物料)。
打开冰箱一看(库房),只剩下2个鸡蛋(缺料)。
来到自由市场,妻子:"请问鸡蛋怎么卖?"(采购询价)
小贩:"1个1元,半打5元,1打9.5元。"
妻子:"我只需要8个,但这次买1打。"(经济批量采购)
妻子:"这有一个坏的,换一个。"(验收、退料、换料)
回到家中,准备洗采、切菜、炒菜......(工艺线路),厨房中有燃气灶、微波炉、电饭煲......(工作中心)。妻子发现拨鸭毛最费时间(瓶颈工序,关键工艺路线),用微波炉自己做烤鸭可能来不及(产能不足),于是阅览室在楼下的餐厅里买现成的(产品委外)。
下午4点,电话铃又响:"妈妈,晚上几个同学想来家里吃饭,你帮忙准备一下。"(紧急订单)
"好的,你们想吃什么,爸爸晚上也有客人,你愿意和他们一起吃吗?"
"菜你看着办吧,但一定要有番茄炒鸡蛋,我们不和大人一起吃,6:30左右回来。"(不能并单处理)
"好的,肯定让你们满意。"(订单确定)
鸡蛋又不购了,打电话叫小贬送来。(紧急采购)
6:30,一切准备就绪,可烤鸭还没送来,急忙打电话询问:"我是李太,怎么订的烤鸭还不送来?"(采购委外单跟催)
"不好意思,送货的人已经走了,可能是堵车吧,马上就会到的。"
门铃响了。"李太太,这是您要的烤鸭。请在单上签一个字。"(验收、入库、转应付账款)
6:45,女儿的电话:"妈妈,我想现在带几个朋友回家吃饭可以吗?"(呵呵,又是紧急订购意向,要求现货)
"不行呀,女儿,今天妈已经需要准备两桌饭了,时间实在是来不及,真的非常抱歉,下次早点说,一定给你们准备好。"(哈哈,这就是ERP的使用局限,要有稳定的外部环境,要有一个起码的提前期)
送走了所有客人,疲惫的妻子坐在沙发上对丈夫说:"亲
爱的,现在咱们家请客的频率非常高,应该要买些厨房用品了(设备采购),最好能再雇个小保姆(连人力资源系统也有接口了)。
丈夫:"家里你做主,需要什么你就去办吧。"(通过审核)
妻子:"还有,最近家里花销太大,用你的私房钱来补贴一下,好吗?"(最后就是应收货款的催要)
现在还有人不理解ERP吗?记住,每一个合格的家庭都是生产厂长的有力竞争者!
07 maggio

关于汇编语言内存寻址方式浅析(转)

指令操作数的寻址方式
  一、与数据有关,寻找参加操作的数据(8种---->7种基本方式+字符串寻址)
  
  ★涉及的一些概念
  DISP/16位:AX(累加器),BX(基侄寄存器),CX(计数器),DX(数据与地址寄存器),SP(堆栈指针),BP(基址指针),SI(源变址器),DI(目的变址器)
  DISP/8位:AH(高8位),AL(低8位),BH,BL,CH,CL.DH,DL
  IP指令指针,存放代码段中的偏移地址;EA偏移地址,段内相对地址,有效地址
  CS代码段,SS堆栈段,DS数据段,ES附加段
  堆栈:方便事项程序要求保留和恢复有关信息的特殊存储部件,是一种数据结构。
  pws(状态):CF(进位),PF(奇偶),SF(符号),OF(溢出),ZF(零),DF(方向),IF(中断),TF(跟踪)
  PA:20位物理地址 ;MOV传送
  
  1、立即寻址
  用来表示常数
  MOV AX,1946H;1946H(立即数)-->AX
  
  2、寄存器寻址
  MOV AX,CX ;cx-->ax
  
  3、直接寻址
  MOV BX,[1000H] ;[1000H]→EA,操作数默认DS
  MOV AX,ES:VAR ;越段前缀
  
  4、寄存器间接寻址
  EA=BX∨SI∨DI∨BP;∨表示或者
  a、PA=DS×16+{BX∨SI∨DI}
  b、PA=SS×16+BP
  c、PA=越段寄存器×16+{BX∨SI∨DI∨BP}
  
  5、寄存器相对寻址
  直接变址寻址
   ┍BX┑
  EA=├SI┥+{DISP}
   ├DI┥
   ┕BP┚
  mov ax,count[si] ;ea=count+si,(ea)→ax,约定为DS
  mov al,es:string[bp] ;越段前缀
  
  6、基址变址寻址
  使用数组和表格
  EA={BX∨BP}+{SI∨DI}
  pA=DS×16+BX+{SI∨DI}
  pA=SS×16+BP+{SI∨DI}
  mov cx,es:[bx][di]
  mov ax,[bx][si] ;ea=bx+si,ea→ax
  
  7、相对基址变址寻址
  EA={BX∨BP}+{SI∨DI}+{DISP}
  mov ax,array[bx][si] ;ea=array+bx+si,ea→ax
  
  8、字符串寻址
  movsb ;([si])=>([di]),si+1=>si,di+1=>di
   ;源=>目,可理解为mov [di],[si]

我对寻址的一些分析看看对大家对他的理解有没有帮助。
  
  物理地址(PA)=段地址+偏移地址(EA)
   =段寄存器的内容×16+偏移地址
   =约定默认段地址×16+偏移地址
   =越段地址×16+偏移地址
  
  1、立即寻址
  A→B,A就是PA(物理地址)可以直接给B
  2、寄存器寻址
  A→B,A(寄存器)的值是PA可以给B
  3、直接寻址
  A→B,A是EA,EA=确定的寄存器的值,由EA得到PA,然后给B
  4、寄存器间接寻址
  A→B,A是EA,EA=变动的寄存器的值,由EA得到PA,然后给B
  5、寄存器相对寻址
  A→B,A是EA,EA=变动的寄存器的值+DISP的值,由EA得到PA,然后给B
  6、基址变址寻址
  A→B,A是EA,EA=变动的寄存器的值+另一变动的寄存器的值,由EA得到PA
  
  ,然后给B
  7、相对基址变址寻址
  A→B,A是EA,EA=变动的寄存器的值+另一变动的寄存器的值+DISP的值,
  
  由EA得到PA,然后给B
  8、字符串寻址
  A→B,A源PA,然后给B(目的pA)
  
  理解寻址方式的关键是EA的值是什么和怎样由EA得到PA,方式4,5,6,7容易混淆,我这样解释可能不科学,但能达到明白它的意思就行。4和3的区别是EA=变动的寄存器的值,5在4的基础上加了DISP的值,6相当于2个5相加(EA=变动的寄存器的值+另一变动的寄存器的),7相当于5+6,哈哈,都什么和什么呀。
06 maggio

让Editplus支持汇编语言语法高亮显示

-- 2006.5.6 By A.TNG
 
最近在看汇编,发现好多知识都还给老师了,得恶补回来。写了几行代码,发现SourceInsight感觉好不爽,还是用回最喜欢的Editplus。不过突然发现ep不支持汇编语言的语法高亮显示,很是郁闷,心想,如此强大的ep怎能如此弱,找了找ep的各个设置,发现在:工具-〉参数设置-〉文件-〉设置和语法 中有个可以设置语法高亮显示的选项,里头已经有了:cpp css html java js jsp ...。马上在网搜索了一个ASM的语法高亮设置,贴出来,分享一下,同时也感谢原作者的工作。
 
所需要做的是,打开ep,至对应的设置面板,选择添加,在描述中输入:ASM,在文件扩展名中输入:asm,然后在语法文件一栏选择对应的stx文件就好了。(asm的stx文件,就是把下面的东东保存为you_decide_the_name.stx)
 
 
#TITLE=ASM
; ASM syntax file written by kylin.
; This file is required for EditPlus to run correctly.
#DELIMITER=,(){}[]-+*/=~!&|<>?:.
#QUOTATION1='
#QUOTATION2="
#CONTINUE_QUOTE=n
#LINECOMMENT=;
#ESCAPE=\
#CASE=y
#PREFIX3=$
#PREFIX4=@
#PREFIX5=%
#NUMBER_PATTERN=asm
#SPECIAL_STX=asm
#KEYWORD=Reserved words
db
dw
dd
dq
mov
movsx
movzx
xchg
push
pusha
pushad
pop
popa
popad
in
out
xlat
lea
lds
les
lfs
lgs
lss
lahf
sahf
pushf
pushfd
popf
popfd
add
sub
adc
sbb
inc
dec
mul
div
imul
idiv
cbw
cwd
cwde
cdq
cmp
neg
daa
das
aaa
aas
aam
aad
and
or
xor
not
test
shl
sal
rol
ror
rcl
rcr
shld
shrd
movs
movsb
movsw
movsd
cmps
cmpsb
cmpsw
cmpsd
scas
scasb
scasw
scasd
lods
lodsb
lodsw
lodsd
stos
stosb
stosw
stosd
ins
insb
insw
insd
outs
outsb
outsw
outsd
jmp
jc
jnc
jz
je
jnz
jne
js
jns
jo
jno
jp
jpe
jnp
jpo
ja
jneb
jae
jnb
jb
jnae
jbe
jna
jg
jnle
jge
jnl
jl
jnge
jle
jng
jcxz
jecxz
loop
loopz
loope
loopnz
loopne
ret
retn
retf
int
into
iret
iretd
set
clc
stc
cmc
cld
std
cli
sti
hlt
wait
esc
lock
nop
bt
btc
btr
bts
bsf
bsr
bound
enter
leave
lar
lsl
lgdt
lidt
sgdt
sidt
ltr
str
lmsw
smsw
lldt
sldt
arpl
clts
verr
verw
DW
DD
DQ
MOV
MOVSX
MOVZX
XCHG
PUSH
PUSHA
PUSHAD
POP
POPA
POPAD
IN
OUT
XLAT
LEA
LDS
LES
LFS
LGS
LSS
LAHF
SAHF
PUSHF
PUSHFD
POPF
POPFD
ADD
SUB
ADC
SBB
INC
DEC
MUL
DIV
IMUL
IDIV
CBW
CWD
CWDE
CDQ
CMP
NEG
DAA
DAS
AAA
AAS
AAM
AAD
AND
OR
XOR
NOT
TEST
SHL
SAL
ROL
ROR
RCL
RCR
SHLD
SHRD
MOVS
MOVSB
MOVSW
MOVSD
CMPS
CMPSB
CMPSW
CMPSD
SCAS
SCASB
SCASW
SCASD
LODS
LODSB
LODSW
LODSD
STOS
STOSB
STOSW
STOSD
INS
INSB
INSW
INSD
OUTS
OUTSB
OUTSW
OUTSD
JMP
JC
JNC
JZ
JE
JNZ
JNE
JS
JNS
JO
JNO
JP
JPE
JNP
JPO
JA
JNEB
JAE
JNB
JB
JNAE
JBE
JNA
JG
JNLE
JGE
JNL
JL
JNGE
JLE
JNG
JCXZ
JECXZ
LOOP
LOOPZ
LOOPE
LOOPNZ
LOOPNE
RET
RETN
RETF
INT
INTO
IRET
IRETD
SET
CLC
STC
CMC
CLD
STD
CLI
STI
HLT
WAIT
ESC
LOCK
NOP
BT
BTC
BTR
BTS
BSF
BSR
BOUND
ENTER
LEAVE
LAR
LSL
LGDT
LIDT
SGDT
SIDT
LTR
STR
LMSW
SMSW
LLDT
SLDT
ARPL
CLTS
VERR
VERW

#KEYWORD=Register
flat
stdcall
casemap
none
dup
proto
call
local
invoke
eax
ax
ah
al
ebx
bh
bl
bx
ecx
cx
ch
cl
edx
dx
dh
dl
esi
si
edi
di
ebp
bp
esp
sp
carry
overflow
parity
sign
zero
true
false
FLAT
STDCALL
CASEMAP
NONE
DUP
PROTO
CALL
LOCAL
INVOKE
EAX
AX
AH
AL
EBX
BH
BL
BX
ECX
CX
CH
CL
EDX
DX
DH
DL
ESI
SI
EDI
DI
EBP
BP
ESP
SP
CARRY
OVERFLOW
PARITY
SIGN
ZERO
TRUE
FALSE
#KEYWORD=Statements
386
model
option
data
const
stack
code
proc
endp
struc
ends
end
include
includelib
if
else
elseif
endif
while
endw
repeat
break
continue
until
null
386
MODEL
OPTION
DATA
CONST
STACK
CODE
PROC
ENDP
STRUC
ENDS
END
INCLUDE
INCLUDELIB
IF
ELSE
ELSEIF
ENDIF
WHILE
ENDW
REPEAT
BREAK
CONTINUE
UNTIL
NULL
#KEYWORD=Description
ds
cs
es
ss
fs
gs
addr
offset
byte
word
dword
ptr
DS
CS
ES
SS
FS
GS
ADDR
OFFSET
BYTE
WORD
DWORD
PTR
#KEYWORD=Symbols
=
:
@
(
)
,
.
;
/
+
-
*
%
#
01 maggio

今天才知道,一个磁盘的扇区大小为512B

软盘、硬盘都是这样,那么来说,处理起来是一样的。

不过又看到一则新闻,稍微老一些,2006-3-26。

德国IT新闻网站Golem.de报道,“国际磁盘驱动器、器材及原料协会”(IDEMA)已经同意将硬盘扇区大小由目前的512Byte增加到4096Byte,这将导致硬盘存储密度和容量的进一步提升。

该报道称更大的扇区可以实现更健全的错误修正机制。采用4KB扇区的硬盘将在今年晚些时候上市,但目前我们还没有哪些厂商将推出这种硬盘及其容量大小的消息。

世界变化就是快啊。

29 aprile

Petri网简介(转载)

    Petri网是对离散并行系统的数学表示。Petri网是1960年代由卡尔·A·佩特里发明的,适合于描述异步的、并发的计算机系统模型。Petri网既有严格的数学表述方式,也有直观的图形表达方式,既有丰富的系统描述手段和系统行为分析技术,又为计算机科学提供坚实的概念基础。
    由于Petri网能够表达并发的事件,被认为是自动化理论的一种。研究领域趋向认为Petri网是所有流程定义语言之母。

  • 经典Petri网
    经典的Petri网是简单的过程模型,由两种节点:库所和变迁,有向弧,以及令牌等元素组成的。

    • Petri网的结构
      (1) Petri网的元素:
      • 库所(Place)圆形节点
      • 变迁(Transition)方形节点
      • 有向弧(Connection)是库所和变迁之间的有向弧
      • 令牌(Token)是库所中的动态对象,可以从一个库所移动到另一个库所。
        (2) Petri网的规则是:
      • 有向弧是有方向的
      • 两个库所或变迁之间不允许有弧
      • 库所可以拥有任意数量的令牌
    • 行为
      如果一个变迁的每个输入库所(input place)都拥有令牌,该变迁即为被允许(enable)。一个变迁被允许时,变迁将发生(fire),输入库所(input place)的令牌被消耗,同时为输出库所(output place)产生令牌。
      • 变迁的发生是原子的
      • 有两个变迁都被允许的可能,但是一次只能发生一个变迁
      • 如果出现一个变迁,其输入库所的个数与输出库所的个数不相等,令牌的个数将发生变化
      • Petri网络是静态的
      • Petri网的状态由令牌在库所的分布决定
    • Petri网的形式化定义
      一个经典的Petri网由四元组(库所,变迁,输入函数,输出函数)组成。任何图都可以映射到这样一个四元组上,反之亦然。
    • Petri网流程建模
      一个流程的状态是由在场所中的令牌建模的,状态的变迁是由变迁建模的。令牌表示事物(人,货物,机器),信息,条件,或对象的状态; 库所代表库所,通道或地理位置;变迁代表事件,转化或传输。
      一个流程有当前状态,可达状态,不可达状态。
    • 经典Petri网的局限性
      • 没有测试库所中零令牌的能力
      • 模型容易变得很庞大
      • 模型不能反映时间方面的内容
      • 不支持构造大规模模型,如自顶向下或自底向上

  • 高级Petri网
    为了解决经典Petri网中的问题,研究出了高级Petri网,在以下方面进行了扩展:

    • 令牌着色
      一个令牌通常代表具有各种属性的对象,因此令牌拥有值(颜色)代表由令牌建模的对象的具体特征,如一个令牌代表一个工人(张三,28岁,经验3级)。
    • 时间
      为了进行分析,我们需要建模期间,延迟等,因此每一个令牌拥有一个时间戳,变迁决定生产出的令牌的延迟。
    • 层次化
      构造一个复杂性与数据流图相当的Petri网的机制。子网是由库所,变迁和子网构成的网络。
    • 时序
      增加时序逻辑的定义,更好的描述行为过程。

  • Petri网的应用
    Petri网的应用非常广泛,以下是Petri网比较常用的几种应用:

    • 软件设计
    • 工作流管理
    • 工作流模式
    • 数据分析
    • 并行程序设计
    • 协议验证

彩信之我见

-- 2006.4.28 By A.TNG


1、废话。
搞IT总是让人感觉没有前途,来公司实习,越来越觉得前途渺茫,直到突然有一天,醒悟过来,我原来不是属于IT行业的,而是属于通信行业。看看sina上科技版首页,俨然的把IT业界与电信分开来,于是觉得很欣慰,自己原来也不是那么“挨踢”。

爱立信、诺基亚、摩托罗拉、西门子、阿尔卡特、朗讯、北电、华为和中兴,哪家不是世界巨头,哪家不令各大学高材生趋之若鹜。她们都是属于电信行业。因此,就算是进了排名最后的中兴,“挨起踢”来,都让人觉得舒服,或者说,人家放个屁都是香的。

完了,跑题了。最后跑一句:珍爱生命,远离··。

2、关于彩信。
其实跟彩信结缘也是在去了上海之后,本科毕业以前,不了解什么是彩信,就听说过SMS/MMS,知道肯定是两个不同的东西,听说过发彩信巨爽,图片声音样样都有,还有就是发一条,要花1RMB,掐指头算了一下,合10条短信呢,算了,还是省省吧。直到二月份去了上海,知道自己做的email协议栈只是一个很小的组件,还是依附于彩信的,于是开始对彩信有种莫名的崇拜。

还有一点,就是觉得彩信组的人都是大牛,人也特别好,感觉好亲切:)

3、说说彩信
其实到现在为止,发彩信的人还是很少,首先是终端设备支持不够。中国人穷,对于手机是能用就用,只有极少数人看到出新手机,不管多贵都去买一个尝鲜,大多数人用的还是那种比较老式,对彩信支持都不够好的手机。

然后是网络支持不够,凭借现在2G的网络或者是CDMA的网络,对于发送小的彩信,还是不错的,来个小图片,来段短声音,都还凑活。但是据彩信组的大牛们说,在现在GPRS的网络上,超过80K的彩信,就极有可能发送不成功;移动的兄弟们说,我们的GSM网络就支持100K的数据,超过这个量,说断就断,不会事先跟你打招呼的。其实啊,各大厂商也就是把宝压在了3G的网络上头,在那个上头发彩信,感觉爽极了。

说实话,彩信还有有她的缺陷,在能发送的情况下,彩信中心是不会保存发送的彩信的,因为她不是一个邮件服务器,不过她也有很有趣的地方,彩信可以通过网关,并且重新编码,发到用户的邮箱里头。

彩信发到邮箱,需要各个方面的支持,网关需要支持,能对彩信进行解码和MIME封装,需要支持SMTP把彩信发到邮件服务器上,用户的客户端(Outlook和Foxmail)把彩信型邮件接收下来,也需要显示上的支持,不过我试过Outlook Express 6.00.2800.1807和Foxmail 5.0.800.0都不支持对SMIL的解析,所以也完全没有达到显示彩信的效果。唉,其实也很惭愧,我觉得既然OE和Foxmail都不支持,我们做的东西也不需要支持那么好,照着葫芦画瓢就好了。

4、个人感觉
虽然现在手机上的邮件用的人还是很少,不过我仍然希望手机邮件有一个美好的未来。从功能上说,彩信和邮件都能提供很完善的多媒体服务,而且邮件的历史远比彩信要长,使用的人更多,如果能把手机和邮件完美结合,潜在的客户群体肯定很大。邮件也有她的缺点,她属于被动式,用户需要主动去收取,才能够知道是否有新邮件,而彩信属于PUSH型,能主动通知用户是否有彩信到。不过在这一点上,加拿大有一家公司联合电信运营商推出了“Push Mail-黑莓”服务,来弥补邮件被动式的缺点,“Push Mail”是建立与现有的POP机制之上的,应该是更加的完善。令人高兴的是联通公司也提出了“红草莓”服务,跟那个“Push Mail”类似。

个人感觉,邮件这个东西,发展到现在,很多方面都很完善了,ESMTP、POP3、IMAP4和MIME这些协议都有比较稳定的代码支撑着,可是邮件和手机结合的同时,要充分考虑手机的特点,手机在控制性和显示方面都有天生的缺陷,因此在显示邮件时,如何充分的利用MIME协议的特点,并且最大限度的满足用户的需求,才是难点。其实显示邮件方面Lotus Notes感觉是做得最好的,而OE和Foxmail要差一些。


28 aprile

十八点(转载)

  
一、读大学,究竟读什么?
    大学生和非大学生最主要的区别绝对不在于是否掌握了一门专业技能……一个经过独立思考而坚持错误观点的人比一个不假思索而接受正确观点的人更值得肯定……草木可以在校园年复一年地生长,而我们却注定要很快被另外一群人替代……尽管每次网到鱼的不过是一个网眼,但要想捕到鱼,就必须要编织一张网……
      
二、人生规划:三岔路口的抉择
    不走弯路就是捷径……仕途,商界,学术。在这人生的三岔路口,你将何去何从……与其跟一百个人去竞争五个职位,不如跟一个人去竞争一个职位……学术精神天然的应当与尘嚣和喧哗保持足够的距离……商场不忌讳任何神话。你也完全可能成为下一个传奇……
      
三、专业无冷热,学校无高低
    没有哪个用人单位会认为你代表了你的学校或者你的专业……既然是概率,就存在不止一种可能性……如果是选择学术,冷门专业比热门专业更容易获得成就……跨专业几乎早已成为一种流行一种时尚……大学之间的实力之争到了考研考场和人才市场原来是那样的微不足道……
      
四、不可一业不专,不可只专一业
    千招会,不如一招熟……十个百分之十并不是百分之百,而是零……在这个现实的社会,真正实现个人价值才是最体面最有面子最有尊严的事情……要想知道需要学什么,最好的方式就是留意招聘信息……很多专业因为不具备专长的有效性,所以成为了屠龙之术……为什么不将“买一送一”的促销思维运用到求职应聘的过程中来呢……
      
五、不逃课的学生不是好学生
    什么课都不逃,跟什么课都逃掉没什么两样……读大学,关键是学会思考问题的方法……逃课没有错,但是不要逃错课……英语角绝对不是学英语的地方……为了英语丢了专业,那就舍本逐末了……招聘单位是用人才的地方,而不是培养人才的地方……既要逃课,又要让老师给高分……
      
六、勤工俭学的辩证法
    对于贫困生来说,首先要做的不是挣钱,而是省钱……大部分女生将电脑当成了影碟机,大部分男生将电脑当成了游戏机……在这个处女膜都可以随意伪造的年代,还有什么值得轻易相信……态度决定一切……当学习下降到次要的地位,大学生就只能说是兼职的学生了……
      
七、做事不如做人,人脉决定成败
    学问好不如做事好,做事好不如做人好……会说话,就能减少奋斗三十年……一个人有多少钱并不是指他拥有多少钱的所有权,而是指他拥有多少钱的使用权……一个人赚的钱,12.5%是靠自身的知识,87.5%则来自人脉关系……三十岁以前靠专业赚钱,三十岁以后拿人脉赚钱……你和世界上的任何一个人之间只隔着四个人……
      
八、互联网:倚天剑与达摩克利斯之剑
    花两个小时就写出一篇天衣无缝的优秀毕业论文……在互联网领域创业的技术门槛并不高,关键的是市场眼光和营销能力……轻舞飞扬已经红颜薄命了,而痞子蔡却继续跟别的女孩发生着一次又一次的亲密接触……很多大学生的网友遍布祖国大江南北,可他们却从未主动向周围的人说一声:你好,我们可以聊聊吗……
      
九、恋爱:花开堪折方须折
    爱情是不期而至的,可以期待,但不可以制造……越是寂寞,越要警惕爱情……既然单身是可耻的,那西门庆是不是应该被评为宋朝十大杰出青年……花开堪折方须折,莫让鲜花败残枝……一个有一万块钱的人为你花掉一百元,你只占了他的百分之一;而一个只有十块钱的人为你花掉十块,你就成了他的全部……
      
十、性:上帝死了,众神在堕落
    爱要说,爱要做……我只有在肉体一下一下的撞击中才感到快乐。经过之后,将是更大的寂寞更大的空虚……为何要让别人的虚荣成为对自己的伤害……当她机械地躺在床上张开双腿,她的父母正在憧憬着女儿的未来……一朝春尽红颜老,花落人亡两不知……
      
十一、考研:痛苦的安乐死
    没有比浪费青春更失败的事情了……研究生扩招的速度是30%,也就意味着硕士学历贬值的速度是30%……同样是付出三年的努力,你可以让E1的值增加1,也可以让E2的值增加2甚至增加3……读完硕士或博士并不等于工作能力更强……面对13.54万的成本,你还会毫不犹豫地投资读研究生吗……努力就会有结果,但不一定是好结果……
      
十二、留学:“海龟”变“海带”
    月薪2500元的工作,居然引得三个“海归”硕士争相竞聘……对于某些专业而言,去美国留学和去埃塞俄比亚留学没什么两样……既然全世界的公司都想到中国的市场上来瓜分蛋糕,为什么中国人还要一门心思到国外去留学然后给外国人打工……
      
十三、非统招:养卑照样处优
    她在中国信息产业界创下了几项纪录。她被称为中国的“打工皇后”。而她不过是一名自考大专生……要想把曾经输掉的东西赢回来,就必须把自己比别人少付出的努力补上来……非统招生不但要有一定的实力,而且必须掌握一定的技巧,做到扬长避短出奇制胜……路在脚下。好走,走好……
      
十四、毕业:十面埋伏的陷阱
    母校不把自己当母亲,你又何必把自己当儿女……听辅导班不过是花钱买踏实……人才市场就是一个地雷阵……通过多种方式求职固然没有错,但是千万不要饥不择食……只要用人单位一说要你交钱,你掉头就走便是了……这年头立字尚且不足以为据,更何况一个口头约定……
      
十五、求职:做人不要太厚道
    求职简历必须突出自己的核心竞争力……求职的时候大可不必像严守一那样“有一说一”……一个人说假话并不难,难的是把假话说到底,并且不露一丝破绽……在填写自己的特长时,一定要尽可能详细……一份求职简历只要用一张A4纸做个表格就足够了……面试其实是有规律的,每次面试的时候只要背标准答案就行了……
      
十六、骑一头能找千里马的驴
    美国铁路两条铁轨之间的标准距离是4英尺8.5英寸,为什么呢?因为两匹马臀部之间的宽度是4英尺8.5英寸……垃圾是放错位置的人才……世界上最大的悲剧莫过于有太多的年轻人从来没有发现自己真正想做什么……中小型企业或许能够让你得到更充分的锻炼……从基层做起并不意味着可以从基层的每一个职位做起……要“钱途”,更要前途……
      
十七、写字楼政治:白领必修课
    大公司是做人,小公司是做事……职员能否得到提升,很大程度不在于是否努力,而在于老板对你的赏识程度……公司的事情和秘密永远比你想象的还要复杂和深奥……在适当的时候装糊涂不但是必要的,而且是睿智的……就把你的同事当成一群你可以叫得出名字的陌生人好了……
      
十八、创业:29岁以前做富翁
    瘦死的骆驼比马大……撑死胆大的,饿死胆小的……不再是“大鱼吃小鱼”,而是“快鱼吃慢鱼”……对于趋势的把握是一个创业者最重要的能力……高科技行业留给毕业生的空间已经很小……欲速则不达。在创业以前通过给别人打工而积累经验是非常必要的……市场永远比产品更重要……钱不够花,怎么办?第一,看菜吃饭;第二,借鸡生蛋……
  
27 marzo

2006-3-25 太湖鼋头渚

    真是天公作美,知道我们要去仙界和诸仙交流,所以太阳特别的好。过了会仙桥,各位都成了神仙了,一路游山玩水,好不快活。
    不过就是时间太短了,还有就是其他人手艺不太好,把我照得比较搓,见谅,见谅。
27 febbraio

风雨夜上海

----2006.2.26
 
偷偷的写这个日志,身边都是领导,最大的领导就在我旁边。所以尽量废话少说。
 
昨天晚上去游了夜上海,在老大的带领下。不过很多的不凑巧,比较忙,导致精神不振;风大,导致形象不佳;人多,上海太大了,简直就不能算是一个城市了。不过总归来说,玩得还是很高兴得,到处都看了,终于知道,什么是国际型大都市了。大家凑活着看吧。
09 febbraio

人生规划:20岁到60岁我该干什么(zz)

  一个人的职业生涯,贯穿一生,是一个漫长的过程。科学地将其划分为不同的阶段,明确每个阶段的特征和任务,做好规划,对更好地从事自己的职业,实现确立的人生目标,非常重要。
  职业生涯阶段如何划分,各国专家学者有不同的划分理论和方法,主要可分为按年龄层次划分、按专业层次划分和按管理层次划分三种类型。
我国从事职业生涯规划研究的人事人才科学研究所副研究员罗双平认为:以年龄为依据,每十年作为一个阶段比较合适,即二十岁至三十岁为一个阶段,三十岁至四十岁为一个阶段,依次类推。我们每个人都要经历这几个阶段,那么你现在该做些什么呢?罗双平谈了他的看法,供读者朋友参考。
  二十岁至三十岁:走好第一步
  这一阶段的主要特征,是从学校走上工作岗位,是人生事业发展的起点。如何起步,直接关系到今后的成败。
  这一阶段的主要任务之一,就是选择职业。在充分做好自我分析和内外环境分析的基础上,选择适合自己的职业,设定人生目标,制定人生计划。再一个任务,就是要树立自己良好的形象。年轻人步入职业世界,表现如何,对未来的发展影响极大。有些年轻人,特别是刚毕业的大学生,总认为自己有知识,有文化,到单位工作后不屑于做零星小事,不能给同事们留下良好的印象,这对一个年轻人的发展而言,可以说是一个危机。还有一个重要任务,就是要坚持学习。根据日本科学家研究发现,人一生工作所需的知识,90%是工作后学习的。这个数据足以说明参加工作后学习的重要性。
  三十岁至四十岁:不可忽视修订目标
  这个时期是一个人风华正茂之时,是充分展现自己才能、获得晋升、事业得到迅速发展之时。此时的任务,除发奋努力,展示才能,拓展事业以外,对很多人来说,还有一个调整职业、修订目标的任务。人到30多岁,应当对自己、对环境有了更清楚的了解。看一看自己选择的职业、所选择的生涯路线、所确定的人生目标是否符合现实,如有出入,应尽快调整。
  四十岁至五十岁:及时充电
  这一阶段,是人生的收获季节,也是事业上获得成功的人大显身手的时期。对于到了这个年龄仍一无所得、事业无成的人应深刻反省一下原因何在?重点在自身上找原因,对环境因素也要做客观分析,切勿将一切原因都归咎于外界因素,他人之过。只有正确认识自己,找出客观原因,才能解决人生发展的困阻,把握今后的努力方向。
  此阶段的另一个任务是继续充电。很多人在此阶段都会遇到知识更新问题,特别是近年来科学技术高速发展,知识更新的周期日趋缩短,如不及时充电,将难以满足工作需要,甚至影响事业的发展。
  五十岁至六十岁:做好晚年生涯规划
  此阶段是人生的转折期,无论是在事业上继续发展,还是准备退休,都面临转折问题。由于医学的进步,生活水平的提高,很多人此时乃至以后的十几年,都能身体健康,照样工作,所以做好晚年生涯规划十分重要。日本的职工一般是45岁时,开始做晚年生涯规划;美国是50岁时做晚年生涯规划。我国的职工按退休年龄提前5年做晚年生涯规划即可。
  主要内容应包括以下几个方面:一是确定退化后的二三十年内,你准备干点什么事情,然后根据目标,制定行动方案;二是学习退休后的工作技能,最好是在退休前三年开始着手学习;三是了解退休后再就业的有关政策;四是寻找工作机会。目前我国已有离退休人员的人才职业介绍所,可提前与这些部门联系,取得他们的帮助。

人生规划,一个研究生的回顾(zz)

  我今年39岁了, 25岁研究生毕业,工作14年,回头看看,应该说走了不少的弯路,有一些经验和教训。现在开一个小公司,赚的钱刚够养家糊口的。看看这些刚毕业的学生,对前景也很迷茫,想抛砖引玉,谈谈自己的看法,局限于理工科的学生,我对文科的不懂,身边的朋友也没有这一类型的。
  91年研究生毕业,那时出路就是1种:留在北京的国营单位,搞一个北京户口,这是最好的选择。到后来的2~3年内,户口落定了,又分成4条出路:
  1、 上国内的大企业,如:华为
  2、 自己做公司,做产品开发;
  3、 上外企,比如:爱立信、诺基亚
  4、 自己做公司,做买卖;
  5、 移民加拿大
  我想,首先要看自己适合做什么?做技术还是做买卖。
  做技术,需要你对技术感兴趣。我掰着数了一遍,我们研究生班的30来号人,实际上 ,适合做技术的,大概只有3、4个人,这几个人,1个现在还在华为,3个移民加拿大了,现在这4个人混的还可以,在华为的同学也移民加拿大了,他在华为呆了6年,在华为奖金工资加起来大概30万吧,还有华为的股票,再过几年,华为的股票一上市,也能值个100~200万。要是一毕业就去华为,那现在就绝对不是这个数字了。
  要是做技术,最好的就是上大公司,国内的大型企业,象华为中兴肯定是首选,能学到很多东西。华为虽然累,但是,年轻人不能怕累,要是到老了,还需要去打拼,那才是真的累啊。
  在外企,我想他们主要就是技术支持和销售,但是技术是学不到的,当然不能一概而论,我指的是象爱立信和诺基亚,真正的研发不会在中国做的,学到的也不如在华为多,其它的中兴我不是很了解,我想应该也不错啊。
一个人都有一技之长,有傍身之技,那是最好的,走到哪里,都能有一口饭吃,还吃的不错,这是传统的观点。
  任何技术都是要在某个行业去应用,这个行业市场越大当然越好;要在一个领域之内,做深做精,成为绝对的专家,这是走技术道路的人的选择。不要跳来跳去,在中国,再小的行业你要做精深了,都可以产生很大的利润。
  研究生刚毕业的时候,做产品开发的有不少人,都是自己拍拍脑子,觉得这个产品有市场,就自己出来做。现在看来,我的这些同学,做产品开发的成功的没有一例,为什么?资源不足。
  1. 资金,刚毕业的学生啊,就是没钱;没钱,也意味着你开发的东西都是小产品;而且只能哥几个自己上,研发、生产、销售都是一个人或者几个人自己来,没有积累,什么都是重新来过。
  2. 人脉,任何一个行业,要想进去,需要有很深的人脉,否则,谁会用你的东西啊?谁敢用你的东西啊?
  我看到的,我这个班上开发产品的,自己还在坚持的,只剩下一个人了,说实在的,到现在,没有自己的汽车,也没有自己的房子,混的挺惨的。现在出国的不说了,在外企、在华为,至少都是几十万的年薪了,还有各种福利,就是产品开发成功了,又能如何?也就是这样了,但是以前那些年,都没有金钱的积累,等于白干。
  我身边的一个自动化系的研究生班的同学,能靠自己开发产品活得还可以的,也只有2个人。说明这条路不是那么好走的啊。
  其次就是上外企。我的2个同学,一个上了爱立信,一个先到爱立信后到诺基亚,都混的不错。到诺基亚的后来利用在诺基亚结识的人脉(就是哪些电信的头头脑脑),自己开了公司,也赚了不少的钱。
  外企最大的好处就是除了能学到比较规范的管理外,还能给你的职业生涯镀金。到了一个外企外,再到同行业的外企我想就很容易了。而且外企的收入高啊。
  自己做公司,做买卖,一开始有3~4个人走这条路,但是真正发财的只有一个人,其 他人后来上外企了。做买卖,还是要有一定的天赋,还有机遇。要有对金钱的赤裸裸的欲望,要有商业上的头脑。后来我们同学在一起谈,说,我们即使给自己这个机遇,也未必能做的好。何况当时那个同学看好的产品(做一个台湾产品的代理),我们大家都没有看好,说明,真理还是掌握在少数人手里。
  到后来,同学们纷纷移民移民加拿大。
  移民加拿大对搞技术的人来说,还是一个不错的选择,但是要尽早,练了几年的技术,就赶紧出去,大概是在1996年走了不到10个,现在都还可以,买了房子和车了。要是晚了,语言再学也难了,而且在国内都混的还可以了,也就没有必要出去了。
我自己呢,先是在国营的研究所混了4年,后来到一家公司干了6年,2002年出来自己做公司,现在也就是混了一个温饱吧,算是有房有车,有点积蓄,但是不多,还有一个可爱的女儿。回首这10来年,有一些经验和教训。
  1. 要有一个职业生涯的规划。首先需要定位自己做什么合适,是做买卖还是做技术,一条路走到黑;当然,做了技术,后来改行也行;
  2. 做技术,就是要做精做深,成为这个行业的这个技术的专家;最好就是去国内的大公司,才能全面学到东西,能够给你培训的机会;如果大公司进不去,先到小公司练技术,找机会再到大公司去镀金,学高深的技术。千万不要自己做产品,要做也是对这个行业熟悉了,再去做。
  3. 积极争取机会。积极争取学习和进步的机会。比如,做技术,就需要多锻炼,多学习,来提高自己的水平。一门技术,只要有机会去学习,都能学的会;要是没有机会,天才也没有办法学到这个技术。柳传志就说,杨元庆就是"哭着喊着要进步",实际上,就是争取自己的机会;当然,这种强烈的进步欲望,也是领导看重的地方。每一步都走在前面,积累10年,你就有了比其他人更多的机会了。
  4. 积累个人的信誉。从你的职业生涯的第一天,就要按照诚信的原则办事。要做到,当人们提起你的名字的时候,说,这哥们还不错,做事还行。
  5. 注意利用资源。如果你有有钱的亲戚、成功的长辈或者朋友,可以充分利用这些机会,得到更加顺利的发展前景。
  6. 注意财富的不断积累。人生要想得到自由,财富是很关键的。否则,永远仰人鼻息,永远看人脸色。人都是势利眼。今后的家庭、职业生涯,金钱的积累很重要,没有钱,永远不能开张自己的事业,得到更多的机会;财富要做到逐年积累,你才能家庭生活幸福。没有钱是不可能有幸福的家庭的。
  7. 注意人脉的积累。最终,事业要靠在社会上的人脉的资源。要注意认识在你这个行业的人,结交他们,最终他们会成为你事业上的助力。
  8. 寻求贵人相助。要找大老板来帮助你,得到大老板的赏识。想想看,大蛋糕,切一点就够了,小蛋糕,都给你也吃不饱啊。
  9. 多听听成功的前辈和成功的朋友的意见。注意少听家里长辈的意见,尤其是都已经退休的长辈,他们对社会的认识还停留在很久以前,而这个社会已经发生很大的变化呢。最重要的是,长辈有时候会强求你做一些事情,但是,最终的结果他们是不负责的。只有你才能对自己负责。
  欢迎朋友们都能提出自己的看法。

如何正确的进行人生规划(zz)

  20岁以前,大部份的人是相同的,升学读书升学读书...,建立自己基础。在父母亲友,社会价值观影响及误打误撞的情况下完成基本教育。
  选择读书,应该一鼓作气,在您尚未进入产业时,能读多高就多高,毕竟何时进入产业,您都是社会新鲜人。
  但是一旦您已经有工作经验而又有心进修,当然管道很多,相对的挣扎也多。
  因为您不知现在的年纪、条件、资历……再去做进修这样的投资是否值得?
  如果,您认定一辈子要当上班族,学历对您而言相信是很重要的,否则,时间宝贵,不容许您再走错路。
  20 ~ 25岁,您要懂得掌握与规划自己的未来,决定了就是一条无悔的不归路。
  刚得到法律付予您的种种权力,相对的您要尽您的义务及学习面对责任的承担。
  这时候的您,是喜悦、矛盾与痛苦交战,喜悦来自于开始被赋予一些自主权,矛盾来自于与父母割不断的脐带关系,痛苦的是开始要尝试错误。
  您要开始为自己的未来规划,如升学、就业、感情……拿回自己对人生的主控权,而非一直受人左右影响的去摇摆自己的未来。


【学会人际关系,多认识积极的朋友,十年后这些朋友都将是产业的中坚】

  25 ~ 30岁,您像一块海绵,努力吸收也甘心被压榨,为的只是自我的成长。
  这时候的您,应是工作取向,薪水待遇。升迁调职您应该是斤斤计较。因为唯有努力付出,相对的您才敢积极争取,社会新鲜人的动力应该让您冲出自己的一片天,也因为没有经验,所以不懂挫折。
  因为资源不多,所以一切尽人事,听天命。现在的您:领取别人的薪水,学习别人的经验,付出自己的青春,建构自己的未来。


【学会累积经验,接触机会,良师益友的提携更是提升您成长的大利器】

  30 ~ 35岁,您要学习判断机会、掌握机会,不能再有尝试错误的心态。
  这时候的您,应是事业取向和家庭取向,工作应该从体力转换为脑力。
  您应该看到的是远景,而非现况,面对的是宽广人生,而非局限于自我。
  结婚是许多人面临人生第一次的重大抉择,面对婚姻,很多人以为结婚就是一个责任的结束,殊不知正是学习的开始。
  就像一些刚上市上柜公司,以为目标达成了,忘了自己的企业责任,忽略本业。反而是一个恶梦的开始。
  人的本业不就是经营自己的家庭,赚钱的目的不就希望给家人更好的生活,但这可不能成为忽略家人的借口,一个经营不好家庭的人,纵使赚到全世界,他得到的只是表面的掌声,在他人生的这个圆,永远有一个缺口。
  家应该是您最大的精神支柱。动力来源和坚强后盾!


【时间管理,转化心境;转化用头脑去工作,不要用身体去工作】

  35 ~ 40岁,您要享受给人希望,功德无量的格局。
  这时候的您,应是企业取向,工作只是一种休闲,更可转化为对他人的责任。如果您专注于研究,您应该不只穷毕生之力。
  24小时不眠不休的去做苦力您应该有成立研究机构,带领一群人做更多研发的雄心壮志。
  如果您是企业主管,您应该不只停留在汲汲营营,斤斤计较,您应该有能力担负主导周遭的员工、家人,带领他们享受更好的生活。
  格局的大小,会影响您成就的多少,做一个有影响力的人,而非被影响的人。


【不论目前您多风光。多有成就,在您心中是否画得出十年后的你?】

  静心思考!我们现在所有努力的目的不就是为了父母、另一半、小孩……?
  工作,不应该等于是人生,更不应该是需要经营一辈子的事。
  试问健康、财富、自我成长、人际关系和时间自由,什么是您努力工作的动力?
  我相信没有人愿意放弃任何一点。
  这些正是促使我们年轻人前进的动力。
  十年后,您是提早完成它?还是提早放弃它?


【宁可因梦想而忙碌,不要因忙碌而失去梦想】

  我看周遭有太多优秀甚于我数倍的朋友,可惜的是终日汲汲营营,投入更多的时间、精神、资源,却没有享受到应得的代价,原因无他,努力错方向,找错机会,拒绝机会而己。
  Jordan 打了一辈子的篮球,是很难在棒球场上找到自己的舞台。不要让忙碌蒙蔽了您的双眼再回头:廉颇老矣,尚能饭否?


【你的时间在那里,成就就在那里】

  当您一个人成功,您只享受到一个人的快乐!
  懂得分享与付出,真正的快乐来自于:周遭的亲友因您的成长而提升,不论是精神或物质。
  真正的成功来自于:周遭的亲友因您付出获得改善,给人希望功德无量。
  我们不是在做慈善事业,尚没有能力普渡众生,但是,我们可以发挥一己之力,对亲友,对那些有缘相遇的陌生朋友。
  伸出您的手,在他们需要的时候!
  太多人在等生命中的贵人,聪明如您,何不先从帮助他人开始?
  “有人4、50岁了,还喜欢说出自某某名校,我觉得奇怪,他们把学校的那几年当成生命的巅峰,其实他出了校门便已开始走下坡路了。 ”
25 gennaio

年前最后一笔


----2006.1.25

年前最后一笔了,明天还有半天的班,估计是没有时间写了,放了假,27号中午的飞机回家。我的2005,再见,我的2006,你好。

最近变化好大,有点压得透过气来。世间的事情就是无法预料,YanPeng因为导师的原因,回了学校再也不来了,小马哥被调离了项目组。这个消息太突然,而且我当时正在看《金刚》的结局,一下子震得我低落极了。

事情已经是定局,我这种小角色肯定是无法扭转的,只能默默地承受,他俩的工作落到了我的手上,我要从零开始,N多莫名其妙的软件,狂负责的编译调试环境,我都得花时间去熟悉,上海那边一个劲的提故障,可是我连测试手机都没有摸热,脑子都快炸了。

通讯来了一个老员工帮我忙,很感谢他,我着急上火的时候,他一个劲安慰我,唉,没有过不去的坎。心急的时候,就无法正常思考,其实很多时候静心想想,也就好了。

来实习这么长时间了,第一次感觉到了压力,可能刚开始的时候,对小马哥依赖太多了,人还是要学着独立的。

祝所有的人,春节快乐。

BTW: 2006是我的本命年。

24 gennaio

BREW平台Callback(回调)机制的最终分析

----2006.1.22
 
摘要:回调机制是BREW平台中最关键的机制之一,很多接口类都是通过回调机制呈现在开发人员的面前,《对AEECallback结构体及其基础函数的分析》中队brew/src/thrdutil中的一些源代码进行分析,初略的理解了AEECallback结构体及其相关函数的实现,对开发能有一定的帮助,本文则更加深入的分析了该机制的特点,以及高通工作人员给出的一些答案。
 
注:非常感谢手机之家_开发联盟的superant, 东方欲晓, record等人,是他们激烈的讨论才引出了最后的答案。本文很多内容都摘自他们的帖子。
 
BREW平台的回调机制,通常是异步的,举例来说,在网络通信的时候,如果是发送数据,普通的方式是发送一快数据,判断是否发送成功,如果发送不成功,则空闲一段时间,在调用发送数据的函数,如此往复,但是如果在BREW平台上,调用发送函数发送数据,如果发送成功,函数会返回发送成功的字节数,如果返回的是-2,表示当前发送不成功,接下来,需要通过_Writeable注册一个回调函数,BREW会根据网络状况调用你注册的回调函数,继续发送需要发送的数据,直到所有数据发送成功。同样的道理,在接收数据的时候,如果数据量比较大,不是一次能够接收完,通常来说就是通过_Readable函数来注册接收回调函数,当有数据到的时候就会调用你注册的回调函数接收数据,直到所有数据接收成功。
 
BREW平台回调机制的实现有几个关键的部分下面一一介绍。
Callback机制中的核心数据结构:
struct _AEECallback
{
    AEECallback *pNext;
    void        *pmc;
    PFNCBCANCEL pfnCancel;
    void        *pCancelData;
    PFNNOTIFY    pfnNotify;
    void        *pNotifyData;
    void        *pReserved;
};
该结构体的每一个成员在《对AEECallback结构体及其基础函数的分析》中有介绍,在此略过。
从结构的命名中可以猜测,其实AEECallback结构体是以链表形式存在,其中的pNext成员就是起到遍历链表的作用,该链表由系统维护。
CALLBACK_Init和CALLBACK_Cancel是两个关键函数。CALLBACK_Init负责对AEECallback结构体进行初始化,该函数的参数列表第二个参数是PFNNOTIFY类型,就是需要回调的函数的函数指针类型,第三个参数是传递给该回调函数的参数指针,类型是void型指针。CALLBACK_Cancel函数是取消回调函数,也就是调用AEECallback结构体中的pfnCancel指针指向的函数来取消回调。
ISHELL_Resume()函数允许向 AEE 外壳注册回调。 它可以向 AEE 外壳的待处理操作列表中添加回调。 AEE 外壳将在下一次调用事件循环时调用此回调函数,以使应用程序或对象协同处理多任务。如果已注册回调,则会先将其注销,然后再重新注册。
 
举例来说,我们在APPLET里中定义m_pCB为AEECallback类型,调用CALLBACK_Init(&pme->m_pCB,FUNC1,pme),意在初始化m_pCB,将函数FUNC1注册到这个结构体中,这时候,m_pCB中的pfnNotify就指向了FUNC1,任何时候,不管是开发人员自己写代码调用ISHELL_Resume()函数,还是通过调用其它函数(BREW平台的APIs),在这些函数中有隐含的调用ISHELL_Resume(),该函数都会在BREW系统的回调链表中添加一个节点,该节点就是m_pCB,那么在适当的时候,BREW就会调用你的FUNC1函数。其实在很多BREW APIs中都会有调用ISHELL_Resume(),例如ISHELL_SetTimerEx()函数,不过这个只是猜测,因为根本无法看到该这些函数的原代码。
 
归纳起来,如过要使用BREW平台的回调机制,则只要初始化适当的AEECallback结构体,再在需要回调的时候调用ISHELL_Resume来启动回调函数 。
 
下面来叙述一下BREW平台的消息机制和回调机制的大概流程:
 
我们假设BREW运行于Ui Task,特定于BREW的Sig为AEE_APP_SIG。
 
For(;;)
{
  waitfor sig         // 无限循环,捕捉到相应的Sig后,作相应的处理
}
 
if (receive aee app sig)
{
  AEE_Dispatch         // 如果收到AEE_APP_SIG 说明BREW需要执行,调用AEE_Dispatch进行消息循环分发。
}
 
其中AEE_Dispathc是最关键的函数,他会检查BREW事件队列中的所有事件一一分发(调用APP->HandleEvent函数),同时也会检查Callback队列中注册的Callback,一一回调。所有都处理完后,表示本次BREW循环结束,将运行环境切换回UI Task中的其他非BREW程序,或者切换至其他Task。当下次BREW再需要执行时,BREW将再次设置AEE_APP_SIG,从而导致当系统任务调度到UI Task时,AEE_Dispatch再次被调用,以此往复。
 
关于在ISHELL_Resume注册完回调函数之后,具体会在什么时候调用回调函数,这个并没有深究的价值,在Callback对列中,BREW根据自身的算法选择回调,当一个时间片不够回调所有的Callback的话,则会把未完成的Callback放到下一个或者下几个时间片执行。所以,对于正常的概念来说,只需要知道,当执行了ISHELL_Resume之后,很短的时间内BREW会调用完成Callback的调用。

22 gennaio

对AEECallback结构体及其基础函数的分析

----2006.1.22

typedef struct _AEECallback AEECallback;
struct _AEECallback
{
 AEECallback *pNext;  //保留,并且调用程序不得修改此成员
 void        *pmc;  //保留,并且调用程序不得修改此成员
 PFNCBCANCEL  pfnCancel;  //回调被取消时,指向回调处理程序所调用函数的指针。调用程序必须将此指针设置为 NULL。
 void        *pCancelData; //传递给 pfnCancel 的数据。 调用程序不得修改此成员。
 PFNNOTIFY    pfnNotify;  //AEE 调用的回调函数。调用程序必须将此指针设为指向 AEE 回调处理程序所调用的函数。
 void        *pNotifyData; //传递给 pfnNotify 的数据,调用程序必须将此指针设为指向须传递给 pfnNotify函数的数据。
 void        *pReserved;  //保留,并由回调处理程序使用此成员
};
该结构体,前两个参数pNext和pmc都是不可修改的,是系统维护其值;pfnCancel和pCancelData是在取消回调时使用的;pfnNotify和pNotifyData是在回调时使用的;最后一个pReserved是保留参数,可以存储任何数据。

AEECallback_Setup函数:
1、通过CALLBACK_Cancel宏调用了pcb中的pfnCancel函数。
2、给pfnCancel赋值,给pCancelData赋值
3、给pReserved赋值,其值是pfnCancelNotify
备注:猜想该函数的功能就是为pcb结构体赋值,按次序分别是pfnCancel、pCancelData、pReserved

static void AEECallback_Setup(AEECallback *pcb, PFNCBCANCEL pfnCancel,
                              void *pvCancel, PFNSCHEDNOTIFY pfnCancelNotify)
{
   CALLBACK_Cancel(pcb);
   pcb->pfnCancel   = pfnCancel;
   pcb->pCancelData = pvCancel;
   pcb->pReserved   = (void *)pfnCancelNotify;
}

AEECallback_Fire函数:
1、把参数强制类型转换为AEECallback类型
2、把pfnCancel赋值为0
3、调用pfnNotify函数
备注:猜想该函数的功能就是调用notify函数

static void AEECallback_Fire(void *pvCxt)
{
   AEECallback *pcb = (AEECallback *)pvCxt;

   pcb->pfnCancel = 0;
   pcb->pfnNotify(pcb->pNotifyData);
}

AEECallback_CancelNotify函数:
1、从pReserved指针中取出保存的pfnCancelNofity,并且强制类型转换,猜想该成员的职责就是保存一些需要保存的指针
2、把pfnCancel置为0
3、调用pfnCancelNotify函数
备注:该函数其实就是要调用其中的CancelNotify函数,只是该函数指针保存在了void类型的pReserved成员里头,需要进行强制类型转换,注意的一点是在调用notify类函数的时候都把pfcCancel置为了0,不知道是何用意

static void AEECallback_CancelNotify(AEECallback *pcb)
{
   PFNSCHEDNOTIFY pfnCancelNotify = (PFNSCHEDNOTIFY)pcb->pReserved;

   pcb->pfnCancel = 0;
   pfnCancelNotify(pcb->pCancelData, AEECallback_Fire, pcb);
}

AEECallback_SchedNotifyWait函数:
1、先通过AEECallback_Setup函数来填充pcb结构体
2、再调用pfnSched函数
备注:不明白这个函数什么意思

void AEECallback_SchedNotifyWait(AEECallback *pcb, void *pSchedNotifyObj,
                                 PFNSCHEDNOTIFY pfnSched,
                                 PFNSCHEDNOTIFY pfnCancel)
{
   AEECallback_Setup(pcb, AEECallback_CancelNotify, pSchedNotifyObj, pfnCancel);
   pfnSched(pSchedNotifyObj, AEECallback_Fire, pcb);
}

20 gennaio

关于BREW多线程雏形的想法

总感觉不舒服,这个多线程,什么东西都需要自己控制。
以前也没有怎么接触过多线程这个东西,没什么经验,大家看看还有没有其他什么想法。

大概想法就是在i=2500或者i=5000或者i=7500的实现线程休眠 500微秒,然后可以把时间片分给其他线程。
大家看输出,可以看出来,基本上是这样的。

/* From src/thrdutil */
void IThread_Sleep(IThread *me,IShell *piShell, int nMS)
{
    AEECallback *pcb = ITHREAD_GetResumeCBK(me);
    ISHELL_SetTimerEx(piShell,nMS,pcb);
    ITHREAD_Suspend(me);
}

/*
*    pIThread1的回调
*/
void Cbk_Runnable1(IThread * pIThread, void *po)
{
    int i;
    TestFor * pMe = (TestFor *)po;
    if(NULL != pIThread)
    {
        DBGPRINTF("thread 1 started");
        for (i=0; i<10000; i++)
        {
            if ((2500 == i) || (5000 == i) || (7500 == i))
            {
                IThread_Sleep(pIThread, pMe->pIShell, 500);
            }
            DBGPRINTF("Thread 1 Count = %d",i);
        }
    } else
    {
        DBGPRINTF("Thread 1 start fail");
    }
}
/*
*    pIThread2的回调
*/
void Cbk_Runnable2(IThread * pIThread, void *po)
{
    int i;
    TestFor * pMe = (TestFor *)po;
    if(NULL != pIThread)
    {
        DBGPRINTF("thread 2 started");
        for (i=0; i<10000; i++)
        {
            if ((2500 == i) || (5000 == i) || (7500 == i))
            {
                IThread_Sleep(pIThread, pMe->pIShell, 500);
            }
            DBGPRINTF("Thread 2 Count = %d",i);
        }
    } else
    {
        DBGPRINTF("Thread 2 start fail");
    }
}
/*
*    pIThread3的回调
*/
void Cbk_Runnable3(IThread * pIThread, void *po)
{
    int i;
    TestFor * pMe = (TestFor *)po;
    if(NULL != pIThread)
    {
        DBGPRINTF("thread started3");
        for (i=0; i<10000; i++)
        {
            if ((2500 == i) || (5000 == i) || (7500 == i))
            {
                IThread_Sleep(pIThread, pMe->pIShell, 500);
            }
            DBGPRINTF("Thread 3 Count = %d",i);
        }
    } else
    {
        DBGPRINTF("Thread 3 start fail");
    }
}

XXX_HandleEvent中添加的部分

case EVT_APP_START:                        
    // Add your code here...
    {
        int nRtr;
        nRtr = ISHELL_CreateInstance(pMe->pIShell, AEECLSID_THREAD,
            (void **)&pMe->pIThread1);
        if (SUCCESS != nRtr)
            return FALSE;
        nRtr = ISHELL_CreateInstance(pMe->pIShell, AEECLSID_THREAD,
            (void **)&pMe->pIThread2);
        if (SUCCESS != nRtr)
            return FALSE;
        nRtr = ISHELL_CreateInstance(pMe->pIShell, AEECLSID_THREAD,
            (void **)&pMe->pIThread3);
        if (SUCCESS != nRtr)
            return FALSE;

        nRtr = ITHREAD_Start(pMe->pIThread1, 100,
            (PFNTHREAD)Cbk_Runnable1, (void *)pMe);
        nRtr = ITHREAD_Start(pMe->pIThread2, 100,
            (PFNTHREAD)Cbk_Runnable2, (void *)pMe);
        nRtr = ITHREAD_Start(pMe->pIThread3, 100,
            (PFNTHREAD)Cbk_Runnable3, (void *)pMe);
    }
    return(TRUE);

16 gennaio

关于AEEClsCreateInstance中nSize的奇怪问题

AEEClsCreateInstance函数的功能是用来创建接口类,具体可以见Blog中《开发BREW扩展类》一文。在该函数的实现代码中,有一个奇怪的nSize,他是需要创建的接口类申请空间的大小,但是代码中对于该nSize的赋值十分有意思,对于以下的分析比较合理,贴出来分享一下。

转自:http://expert.imobile.com.cn/bbs

作者:东方欲晓

if(nSize < sizeof(ExtensionCls))

nSize += sizeof(ExtensionCls);
if((pMe = (ExtensionCls *)MALLOC(nSize +
sizeof(VTBL(IExtensionCls)))) == NULL )
return ENOMEMORY;

ExtensionCls是用户定义的class结构,所以创建的内存nsize至少应该等于这个,
if(nSize < sizeof(ExtensionCls))

nSize += sizeof(ExtensionCls);
是进行合理性检查,防止传入的size不合理(小于class的大小),如果不合理则在nsize的基础上加一个完整的class的内存大小,保证能正常创建class(通常传入的nsize就是sizeof(appclass),所以这一句不会走)
pMe = (ExtensionCls *)MALLOC(nSize +
sizeof(VTBL(IExtensionCls))))

进行真正的内存分配,此时除了给class分配内存外,还要为vtbl分配内存,所以分配的大小为nSize +
sizeof(VTBL(IExtensionCls))

这样分配完后,在内存中,class之后就紧接着是一块vtbl区域,接着代码中会为这块vtbl区域初始化,即将其函数指针指向确切的函数地址,最后再将class中的pvt指针指向该vtbl,从而完成了整个class的初始化。

14 gennaio

开发BREW扩展类

看到一篇文章,觉得比较有指导意义,译过来,希望给大家有些帮助,能力有限,有错误的地方还望大家指出来。共同学习,共同进步。

原文地址:
https://brewx.qualcomm.com/bws/content/gi/common/appseng/en/knowledgebase/docs/extensions.htm

1 简介:
扩展类可以扩充BREW的功能,通常来说,扩展类都是以动态的形式存在并且以OTA方式提供下载,然而在某些情况下,OEM制造商会在他们的设备中添加了一些静态的扩展类,例如:OEM制造商经常会给BREW标准接口类IAddrBook添加许多额外的功能,他们通常会创建自己的IAddrBook接口,并且把新增的额外功能提供给开发者。

2 静态 VS 动态
静态扩展类通常是被包含在OEM制造商的SW版本中,不由第三方开发者提供。从另外一个角度看来,动态扩展则是由OTA方式下载。移动应用和它所需要的扩展类是同时被下载到手机上的(除非手机上已经存在了所需的扩展类),通过MIF文件提供的依存列表信息把所需要的扩展类下载到手机当中。每一个动态扩展都会有一个相关联的引用计数,该引用计数负责监视手机上所有应用对于该扩展类的依赖关系,如果引用计数减少到0,则该扩展类将被从手机内存中移除。静态扩展则不是,它没有引用计数,也永远不会被从手机上删除。

3 特性
扩展类不包含权限设置,它的权限级别继承于父应用,例如:如果某应用使用了一个具有文件操作功能的扩展类,那么该应用就是扩展类的父应用,除非父应用的MIF文件中设置了允许文件操作的权限,否则该扩展类的所有文件操作都会失败,这一点对于扩展来开发人员来说十分重要。
与权限级别相似的是,扩展类中所有对于文件的操作都是通过父应用的上下文来执行的,如此来看,对于父应用不可见的文件,扩展类也是无法操作的。
扩展类即可以是保护类型也可以是未保护类型。对于保护类型来说,如果父应用的MIF文件中的依存关系列表中有显示罗列出该扩展类,那么父应用就可以创建并使用它,反之,则不行;对于非保护类型则可以被所有应用使用,不用考虑该应用是否明确声明了依存关系,这样的扩展类通常被加上一个MIME类型的标记,应用只需要通过外壳查找目标MIME类型的句柄,就可以使用它的功能了。

4 MIF文件
扩展类和应用一样需要MIF文件,然而,他们却在根本上有所不同。首先,一个扩展类的MIF文件不需要定义一个applet,只需要在MIF编辑器的"Externsions"标签栏中的"Exported Classes"中设置其class ID即可。保护类型的属性也在该标签栏中设置。
如果一个应用的运行以来与一个扩展类,那么在该应用的MIF文件中必须把扩展类的class ID设置为依赖,如果没有此类依赖关系,该扩展类将不会在下载应用的时候被一同下载,当该应用运行的时候就会失败。如果扩展类是可选的,就可以不用添加依存关系,然而应用必须能够处理扩展类不存在的情况。

5 声明扩展类的结构体
第一步,开发扩展类首先需要声明虚函数表。
typedef struct IDemoExtension IDemoExtension;
AEEINTERFACE(IDemoExtension)
{
        INHERIT_IQueryInterface(IDemoExtension);
        int (*DisplayTime)(IDemoExtension *po);
        int (*DisplayDate)(IDemoExtension *po);
};

第一行声明了一个IDemoExtension的结构体,该结构体是一个apple结构体,将会返回给正在初始化的应用,其中的宏AEEINTERFACE声明了一个扩展类的虚函数表,从上面的声明看出,虚函数表中有5个函数。BREW的每一个接口类都定义了一个类似于上面定义中的宏INHERIT_XXX,用它来把已继承的函数添加到已存在的函数表中,至少,BREW强烈推荐如上例所示,从IQueryInterface派生而来。宏INHERIT_IQueryInterface添加三个函数到函数表中:AddRef, Release, QueryInterface。我们的例子中添加了DisplayTime和DisplayData两个函数。
下一步我们定义宏来索引我们函数表中的函数
#define IDEMOEXTENSION_AddRef(p)
 AEEGETPVTBL((p),IExtensionCls)->AddRef((p))
#define IDEMOEXTENSION_Release(p)
    AEEGETPVTBL((p),IExtensionCls)->Release((p))
#define IDEMOEXTENSION_QueryInterface(p)
    AEEGETPVTBL((p),IExtensionCls)-> QueryInterface((p),(clsid),(pp))
#define IDEMOEXTENSION_DisplayTime(p)
 AEEGETPVTBL((p),IDemoExtension)->DisplayTime((p))
#define IDEMOEXTENSION_DisplayDate(p)
 AEEGETPVTBL((p),IDemoExtension)->DisplayDate((p))
上面的宏可以供父应用来调用扩展类的函数,宏AEEGETPVTBL能准确地指向函数表,并能方便的调用到函数表中的函数,需要注意的是AEEGETPVTBL是同AEEINTERFACE配合起来使用的,我们一旦声明好虚函数表,那么就应该定义扩展类结构体了。

以上代码的第一行把虚函数表插入了扩展类的结构体定义之中,虚函数表必须总是被定义成为结构体的第一个成员,m_nRefs变量也必须在结构体定义中声明,一个扩展类在应用中将会数次被引用,这个变量用来保存在应用中被引用的总次数,AddRef和Release函数是用来对该变量进行增减操作的,一旦m_nRefs变为0,该扩展类就会释放所有占用的资源。结构体中接下来的三个成员是扩展类的可选成员,可以在扩展类中添加任何所需要的成员变量。
6 定义函数
定义的第一个函数必须是AEEClsCreateInstance,REW总是调用该函数来创建该class的接口,下面的代码表明了普遍实现的方法。
int AEEClsCreateInstance(AEECLSID ClsId, IShell *pIShell,
       IModule *po, void **ppObj)
{
   *ppObj = NULL;
   if( ClsId == AEECLSID_MYEXTENSION) {
  return MyExtension_New(sizeof(IDemoExtension), pIShell, po,
           (IModule **)ppObj);
   }
   return EFAILED;
}
我们的函数首先会检查由BREW传入的参数class ID,如果检查通过了,函数就尝试创建和初始化需要的资源。

int MyExtension_New(int16 nSize, IShell *pIShell, IModule* pIModule,
                                                   IModule ** ppMod) {
   IDemoExtension*            pMe = NULL;
   VTBL(IDemoExtension) *     modFuncs;
   if( !ppMod || !pIShell || !pIModule )
      return EFAILED;
   *ppMod = NULL;
   // Allocate memory for the ExtensionCls object
   if( nSize < sizeof(IDemoExtension) )
      nSize += sizeof(IDemoExtension);
   // Allocate the module's struct and initialize it. Note that the
   // modules and apps must not have any static data.
   // Hence, we need to allocate the vtbl as well.
   if( (pMe = (IDemoExtension*)MALLOC(nSize +                                      
                              sizeof(VTBL(IDemoExtension)))) == NULL )
      return ENOMEMORY;
   modFuncs = (VTBL(IDemoExtension)*)((byte *)pMe + nSize);
   //Initialize individual entries in the VTBL
   modFuncs->AddRef              = MyExtension_AddRef;
   modFuncs->Release             = MyExtension_Release;
   modFuncs->QueryInterface      = MyExtension_QueryInterace;
   modFuncs->DisplayTime         = MyExtension_DisplayTime;
   modFuncs->DisplayDate         = MyExtension_DisplayDate;
   // initialize the vtable
   INIT_VTBL(pMe, IModule, *modFuncs);
   // initialize the data members
   pMe->m_nRefs      = 1;
   pMe->m_pIShell    = pIShell;
   pMe->m_pIModule   = pIModule;
   // Add References and get IDisplay
   ISHELL_AddRef(pIShell);
   IMODULE_AddRef(pIModule);
   // Create an instance of the IDisplay
   if( ISHELL_CreateInstance(pIShell, AEECLSID_DISPLAY,
                         (void **)&pMe->m_pIDisplay) != SUCCESS )
      return EFAILED;
   // Set the pointer in the parameter
   *ppMod = (IModule*)pMe;
   return AEE_SUCCESS;
}

AddRef, Release和QueryInterface是从IQI接口继承过来的,AddRef负责增加引用计数的数值。

static uint32 MyExtension_AddRef(IDemoExtension* pMe) {
   // Increment reference count
   return ++(pMe->m_nRefs);
}

Release函数负责减少引用计数的数值,当引用计数的值变为0的时候,该实例就会从内存中被释放掉。

static uint32 MyExtension_Release(IDemoExtension* pMe) {
   // Decrement reference count
   if( --pMe->m_nRefs != 0 )
      return pMe->m_nRefs;

   // Ref count is zero.  Release memory associated with this object.
   if( pMe->m_pIDisplay )
      IDISPLAY_Release(pMe->m_pIDisplay);

   // Release interfaces
   ISHELL_Release(pMe->m_pIShell);
   IMODULE_Release(pMe->m_pIModule);
   //Free the object itself
   FREE_VTBL(pMe, IModule);
   FREE( pMe );
   return 0;
}

我们最后一个需要定义的函数就是QueryInterface,这个函数返回一个与扩展类相关联的接口,IWeb接口就是一个例子,当一个应用使用该接口完成一个网页请求的时候,底层的一个TCP套接字被创建,为了能够直接控制这个套接字,应用可以通过调用IWeb_QueryInterface函数,传递参数AEECLSID_SOCKET(AEECLSID_SOCKPORT在BREW3.x中),来获得这个接口,该函数将返回一个指针,下面是QueryInterface的一个具体实现。

static int MyExtension_QueryInterface(IDemoExtension* me,
                               AEECLSID class, void** ppo) {
   switch (class) {
      case AEECLSID_QUERYINTERFACE:
      case AEECLSID_MYEXTENSION:
      case AEECLSID_BASE:
         *ppo = me;
         MyExtension_AddRef(me);
         return SUCCESS;
      case AEECLSID_DISPLAY:
         *ppo = me->m_pIDisplay;
          return SUCCESS;
      default:
         *ppo = NULL;
         return ECLASSNOTSUPPORT;
   }
}

7 静态扩展类

上面介绍的这个例子也可以用来创建静态扩展类,然而静态扩展类可以作为全局变量,有了这个特性,下面的代码可以为静态扩展类构造虚函数表。

static const AEEVTBL(IDemoExtension) gvtIDemoExtension = {
   MyExtension_AddRef,
   MyExtension_Release,
   MyExtension_QueryInterface,
   MyExtension_DisplayTime,
   MyExtension_DisplayDate,
};

以上的代码被放置在函数之外,通常是在一个头文件中,初始化扩展类可以简单的用一行代码搞定。

pMe->pvt = &gvtIDemoExtension;

上面的代码初始化了整个虚函数表,不必对于虚函数表中每个函数都初始化一次,然而上面的代码仅对静态扩展类是有效的,一个动态扩展类的地址是在运行时确定的(于之不同的是,静态扩展类的地址是在编译时确定的),因此,虚函数表必须在运行时声明。

8 关于宏
强烈推荐在以上例子中所使用到的宏,然而在BREW中还有许多其他的可以用来创建扩展类的宏。其中很多宏被包含近来是为了向后的兼容性,一些宏例如:QINTERFACE, GET_PVTBL和DECLARE_IBASE都是十分令人气馁的,他们应该分别被AEEINTERFACE, AEEGETPVTBL和INHERIT_IQueryInterface替代。

9 使用扩展类
扩展类可以像标准的BREW APIs一样被创建和释放,每个函数都可以通过头文件的宏定义来调用,同BREW APIs一样,在编译调用扩展类函数的应用的时候需要包含其头文件,下面的例子叙述了如何在应用中调用扩展类的函数。

if(ISHELL_CreateInstance(pMe->a.m_pIShell, AEECLSID_MYEXTENSION,
              (void **) (&(pMe->pIDemoExtension))) != SUCCESS ) {  
   return FALSE;
}

IDEMOEXTENSION_DisplayTime(pMe->pIDemoExtension);
if(pMe->pIDemoExtension) {
   IDEMOEXTENSION_Release(pMe->pIDemoExtension);
   pMe->pIDemoExtension = NULL;
}

10 通过MIME类型使用扩展类
扩展类可以把其自身注册为一个MIME类型的句柄,它的注册最好通过MIF编辑器来完成。

上面提及的例子展示了一个扩展类注册为MYTIME句柄,MIME类型的注册允许在运行时解决class ID的问题,应用可以通过ISHELL_GetHandler函数找到一个MIME类型的class ID,整个函数会遍历所有的MIF文件,寻找以MIME类型方式注册的class ID。
下面这个例子阐述了一段能创建和显示多种不同格式文件的代码,在此例中,有一个抽象类IReader,一个扩展类可以扩充它的接口来显示多种格式的文件。

// An Application will call this function when it intends to
// open a file
void DisplayFile(MyApp* pMe, char* mime, char* file) {
   AEECLSID myClass;
   // This call will create a handler for MYPDF, MYDOC, or
   // MYXML depending on mime
   myClass = ISHELL_GetHandler(pMe->pIShell, AEECLSID_APP, mime);
   // Create necessary Interface
   ISHELL_CreateInstance(pMe->pIShell, myClass, &pMe->pIReader);

   // Ishell Createinstance
   // Each extension implements DisplayFile, The virtual
   // table will point to the correct corresponding function
   // call.  We are ignoring error checking
   IREADER_DisplayFile(pMe->pIReader, file);
}

DisplayFile是一个被所有扩展类实现的扩充IReader的接口,虚函数表在创建扩展类的时候被初始化,这样就使IREADER_DisplayFile可以索引正确的函数来显示文件。

11 更多信息
BREW 2.x 察看AEE.h
BREW 3.x 察看AEEInterface.h