标题: 8086 上的多任务 GUI 开发 问题集, 欢迎赐教!
[打印本页]
作者: jawbin
时间: 2006-5-26 11:37
标题: 8086 上的多任务 GUI 开发 问题集, 欢迎赐教!
首先有一个额外的想法, 如果一个机器(真实或虚拟), 若是能限制能力就好了(例如, 限制为只能使用 8086 指令, 只使用一兆内存等等)
8086 意味着只能有实模式, 常规内存 640 K, 任务切换需要自己实现.
目前对我来说, 任务切换是难点,我也不想实现得很彻底,或者说我仅仅是"假的"多任务,或者模拟多任务,因为我是想让各个任务按时间片轮换的,每个任务都在规定的时间内必须结束(自己结束). UC/OS-II 应该实现了任务切换,一来我还不是很懂,二来我也有点恐惧,因为我对被强行切换掉的任务继续执行总感觉有点不舒服.我希望各任务是协作式的,而不是强行剥夺.当然,我这里实际上也类似强行剥夺,因为我在每个任务中会判断自己的剩余时间,到了的话自己结束(这和被强行结束不同的是,这个任务的一次调度被完成了,而不是被"粗暴地"中断了).但是显然,这样增加了各个任务的书写复杂度,因为每个任务自己都要盯自己的表,而且下次被调度的时候必须考虑能接着执行,这样,一个大的任务可能需要分片,类似重入.我对多任务懂得不多,所以,这样的想法应该不是很好.也许是最糟的多任务处理方法之一!
然后说说 GUI, 我希望是一个"跨平台"的层,可能有点象 SDL, 但是, 我希望图形对象都是用矢量描述的, 而且希望借鉴 *nix 的 c/s 模式, 这样, 实现起图形终端可能比 vnc 以及 rdp 等可能更少占用网络以及其他资源, 初步考虑还是使用比较标准一点的 xml 样的东西. 另外这样可以使 server 端不必和 client 端同等的显示能力, 甚至如果可能的话, 各个 clients 的图形对象也根据显示设备而重新布局过, 当然随之改变的还可能有颜色, 甚至风格(观感)等(这些可能属于 client 端的配置, 但"不强求", 也可以有一个选项使各 clients 的显示结果尽可能相同, 为了一致性). 但是这要求矢量描述中可能会加入事件, 还要考虑是只传送剪切域, 还是"全部"传送, 这里的全部有可能有多个虚屏. 这里可能可以参考 mozilla. 另外, 我想为了效率, remote clients 和 local clients 的实现可以不同.
在这里可能会提供一些"内置的"基本组件,例如 button, lable 甚至更高级的组件等等, 也可能允许安装自定义组件.
关于显示的另一个问题就是设备驱动, 通常使用的类似 BGI 的驱动, 但是有些设备并不提供类似的驱动, 而且更糟的是, 它完全不是 VGA 兼容的, 它也许提供一个库让你使用, 所以这时候为了可重用,可能还得加一层,或者有可能为之写一个 BGI 驱动么? 另外, 我希望这个层之上的应用是尽可能跨平台的. 看来它只好在各个平台下有不同的实现了,因为,BGI并不能应用在 linux 上. 虽然 linux 不能运行在 8086 上, 但是, 我总想尽可能地复用.
关于观感, 我想已经有很多可以借鉴的了, 但是面对极其有限的资源, 必须使代码尽可能地紧缩. 我觉得 windows 系统的已经比较可以了(其实这里可能缩小范围到显示风格或方案了), 它提供了有限的改变, 例如按钮各个部位的颜色等等.
为了可移植, 我想不用 c++, 而用 c, 而且觉得 c 的目标代码可能更精巧, 当然了, 我对 c++ 的理解也不是很多.
快吃饭了, 我先捡要紧的说. 其他的时候再补充!
最重要的是, 我希望搞个 RAD 工具来, 一个目标系统很好, 但是开发工具很差的东西, 并不完美, 不是吗?
好了, 午饭过后再说!
Last edited by jawbin on 2006-5-30 at 10:36 ]
作者: jawbin
时间: 2006-5-26 11:44
我把假设的这个东西称为 A, 那么, 按我所说, 可能事件都要自己处理(重要的是,比如按了某个 button, 不是由 os 告诉你, 而是自己判断得到, 而且这些组件都不用 native 的), 这其实仅仅有一个可能微乎其微的"好处":"anti-crack". 但是却大大增加了复杂性, 也许可能多个应用共享一个虚拟机.
Last edited by jawbin on 2006-5-26 at 11:57 ]
作者: jawbin
时间: 2006-5-26 11:56
回来了:)
另外还有字体问题, 通常是用 GB-2312, 有无必要使用 unicode 呢? 用点阵字还是矢量字, 全角半角混排....
作者: jawbin
时间: 2006-5-26 12:08
对了, 对于"快速开发"(而不是长远考虑), 或者无论如何, 有无现有的系统可以达到或者超过类似的目的?
可以比较一下 windows 286 - (windows 286 从名字就可以看出, 应该是不能运行在 8086 上的), UC/OS-II, 以及其他的 os'es 或者 platforms 或者 frameworks.
当然一个系统通常还需要网络,这个,在 dos 下倒好解决(我上面所说的都是基于 DOS 或者另一个具有比 BIOS 更高级一点功能的系统的, 否则只能使用 BIOS 功能的话就增加了很多难度, 使用 Packet driver 就可以了, 当然上层一点的使用什么为好了, WATTCP? 如果能和伯克里 socket 兼容的话, 也能增加点移植性, 但是它又太基本了, 应该有更高层一点封装, 以免每次都做轮子:)
Last edited by jawbin on 2006-5-27 at 08:57 ]
作者: jawbin
时间: 2006-5-26 12:19
另外我考虑了一点关于类似 MVC 样的东西, 举例来说, 诊断调试的输出对象不是早绑定的, 它可以随时根据需要改变. 例如字符模式,图形模式(图形模式还有各种不同的显示设备,但那由 client 端的具体设备决定),以及输出到文件,等等,甚至可以重定向,例如重定向到短信.
其他有关输出的也类似.
Last edited by jawbin on 2006-5-26 at 13:38 ]
作者: jawbin
时间: 2006-5-26 12:26
具体在内部,可能还有缓冲区的实现(用链表则增加空间,因为每一项多一个指针,甚至指针的长度比实际用的数据的长度还长,而用条件判断使成环形则似乎每次都需要判断,"降低性能“),如果再复杂一点可能还需要为常用数据缓冲.数据迟载入(Data Late Loading, DLL *^_^*), 分成几块是否可以避免远指针的开销.
作者: zyl910
时间: 2006-5-26 14:18
任务切换不难,在计时器中断中保存/恢复关键寄存器就行了
关键在于DOS下所有中断服务程序都是堵塞执行的,严重浪费CPU资源,而且处于DOS忙状态时不能切换(要不然会发生重入导致系统崩溃)
所以最好是实现一个API接口层,代理一切中断调用,并使用消息机制,这样的多任务才有实际价值
编写图形界面系统是很复杂的(我一直不知道怎么实现高效的窗口剪裁算法,好恨啊),特别是还需支持MVC模式,感觉不是一般的复杂
编写图形系统最基础的是需要一套设备无关的图形设备接口函数库,如windows中的GDI函数库,可这套函数库很难设计
这是因为图片数据量很大,640KB的基本内存不够用
用XMS?XMS是靠80386保护模式实现的,调用XMS功能时会切换到保护模式执行操作,效率非常低
实模式下访问显存是很痛苦的,不是位平面就是分页。特别是内存不足,一般是逐行的处理图像数据,处理好一扫描行后提交,再处理下一扫描行。
而进了32位保护模式,是线性地址,32位平面内存,完全可以直接对整幅图片进行处理。如果再采用扫描行方式会存在多余的内存移动操作。
绘图还不是最难的,最难的是文本绘制
字符编码我觉得使用UTF-8最好,GB2312早就过时了
然后得提供一套函数集,将不同编码的字符串 与 UTF-8字符串 进行转换
点阵字库有太多缺点了,所以最好使用像TTF那样的轮廓字库。但是TTF是使用贝塞尔曲线描述字符轮廓,而实时计算贝塞尔曲线貌似很难。
为了unicode排版,TTF是不够的。所以Microsoft现在使用OpenType字库,OpenType字库存放了许多unicode排版细节信息。
看看Windows系统提供了多少
种多语言处理API吧(严重打击我的智商,特别是Uniscribe):
- National Language Support: 本地语言支持。提出了Primary Language、LanguageID、LocaleID等概念提供本地化支持。Windows 95、Windows NT 3.5
- Unicode and Character Sets: Unicode字符集函数。使用CodePage概念将不同编码的多字节字符串与Unicode字符串进行转换。Windows 95、Windows NT 3.5
- Fonts and Text: (GDI)字体与文本。提供了基本的字体与文本排版功能。Windows 95、Windows NT 3.5
- Font Embedding: (GDI)字体嵌入。从OpenType字库得到Unicode排版的许多细节信息。Windows 98、Windows 2000
- Uniscribe: Unicode复杂文本排版。组合字符、预构字符的排版处理已经非常麻烦了,再加上还有阿拉伯文那样的双向文本,所以专门提供了Uniscribe函数集来处理复杂文字排版。IE 5.0,Windows 2000内置。
- Keyboard Layout: 键盘布局。处理西方字符,只是一个简单的键盘按键映射机制。Windows 95、Windows NT 4.0
- Input Method Manager: 输入法管理。与输入法沟通一套API,使用该API能使你的程序像微软拼音在Word中的表现一样——与文本编辑器文美融和。Windows 98、Windows 2000
- Text Services Framework:文本服务框架。处理键盘、输入法、手写、语音输入的通用框架。Windows XP内置,但其它平台可以安装TSF支持包。
- Active Input Method Manager: 活动输入法管理。IE提供的的输入法管理增强型解口,比如在简体中文Win98平台下可以在IE中使用仓颉输入法。IE 4.0
- MLang: 多语言。IE中用到的一套多语言编码转换API。IE 4.0
最后,坚决BS RAD 工具,API才是王道
作者: asbai
时间: 2006-5-26 14:36
好长啊,刚看了第一段,jawbin兄说的这个叫协程,Win3.1用的就是这种协作模式。这个模式的主要优点是线程/进程间同步比较容易,大大减少了临界资源争抢和同步访问的问题。不过缺点很多,主要是:
1. 协程间必须严格自律,否则会出现一个协程完全不释放CPU的情况(比如存在缺陷或者恶意代码)
2. 时间片颗粒太粗,协程间切换不够平滑,在很多情况下用户能明显感觉到运行不流畅
3. 操作系统无法控制资源分配,也无法解决死锁等问题。
协程现在基本用在较多用在来源可靠,组合固定并且经过全面测试的场合,比如嵌入式应用和嵌入式角本引擎等等。
作者: jawbin
时间: 2006-5-26 14:37
很兴奋zyl910来相助,我先回再细细体会!
不过我觉得拒绝 RAD 就是自绝于效率,而且如果整个系统都是自己开发的,那么,不用担心封装造成的黑盒子问题!这不是噱头,而是实实在在的生命(时间)啊!
作者: jawbin
时间: 2006-5-26 14:38
哇, asbai 兄也来了, 我 high 疯了快!
作者: jawbin
时间: 2006-5-26 14:46
asbai兄, 我肯定是要各任务严格自律的,因为任何一个循环,必定要给以给极限次数或者时间,每次循环都检查,以防阻塞.
另外,一些延时函数我想利用起来,让别的等待的任务运行,而不是让 CPU 空等.
目前我搞的一个很粗糙的,任务还是很协调的.
另外,还需要加入错误处理,例如截获 int 24h, 然后给用户更友好的信息(Windows 的蓝屏总比没有好,呵呵)
接着消化 zyl910兄 的内容!
Last edited by jawbin on 2006-5-26 at 14:50 ]
作者: asbai
时间: 2006-5-26 14:50
Originally posted by jawbin at 2006-5-26 14:46:
asbai兄, 我肯定是要各任务严格自律的,因为任何一个循环,必定要给以给极限次数或者时间,每次循环都检查,以防堵塞.
另外,一些延时函数我想利用起栮..
这样只能开发诸如嵌入式应用这种封闭式系统,开放式系统允许第三方自由开发应用,这样光严格自律就不够啦,还要想办法律人才行,呵呵
作者: jawbin
时间: 2006-5-26 14:51
Originally posted by asbai at 2006-5-26 02:50 PM:
这样只能开发诸如嵌入式应用这种封闭式系统,开放式系统允许第三方自由开发应用,这样光严格自律就不够啦,还要想办法律人才行,呵呵
千真万确,我前面也说了,这样协作的话,增加各个任务书写的复杂度,当然可能也增加了代码以及降低了效率. 所以这是个必须注意的问题. 只能自己用可以...哎
Last edited by jawbin on 2006-5-26 at 14:53 ]
作者: jawbin
时间: 2006-5-26 14:56
"任务切换不难,在计时器中断中保存/恢复关键寄存器就行了"
我通常是不敢在中断中做事的,怕出问题,一般做的可能就是 ++i 这样我感觉起来高枕无忧的事情..
但我目前其实尽可能少在中断里做事
对了,由于异常提示(例如类似蓝屏)是必须让用户看见的,所以,考虑点 MVC 我觉得还是应该的.
Last edited by jawbin on 2006-5-26 at 14:58 ]
作者: jawbin
时间: 2006-5-26 15:00
消息应该要使用,毕竟已经证明很不错.
作者: jawbin
时间: 2006-5-26 15:09
windows 3.1 的剪裁可能比 windows 3.0 有很大的改进, 如果能研究一下也许不错, 不过只能反汇编? 那还不如不看?找找看 Windows CE 的 source(Windows CE 3.0 的部分 source 网上有). 另外,不知道 Windows CE 的开发工具中可否得到相关的资料. 还有 DDK 可看?
那看看 minigui 如何, 不过可能它借鉴了 wxWindows(后来的 wxWidgets吧),有个国内的什么微逻辑公司好像也有类似的东西,不同的是好像支持多任务(多线程?我记得不清楚了).但是他们的 DC 应该都和 MS 的不同. 另外, 我其实不是很喜欢 GDI, 即使参照, 也应该看看 MS 的 vista 在这方面的发展趋势, 我认为. java 的和 BGI 差不多, 用起来方便一些. 但是如果全体从矢量角度考虑的话,完全可以不管是否与以前有否兼容,否则可能绊脚自己.
Last edited by jawbin on 2006-5-26 at 15:13 ]
作者: asbai
时间: 2006-5-26 15:11
窃以为 zyl 兄所言甚是(前半部分,呵呵)。国际化和本地化确实是大势所趋,unicode核心已经是所有现代OS必备的特征了。语言和文字的处理也确实是非常复杂的工作。现在隆重推荐几款重量级开源跨平台语言文字处理工具:
1. ICU(
http://www-306.ibm.com/software/globalization/icu/index.jsp),IBM出品,应该不算太次。支持编码转换,文字阅读方向处理(RTL-LTR),按本地习惯输出和排序等等很多功能。支持 C/C++/Java等接口
2. GNU libiconv,GNU出品,大半能算做精品。posix/sus 兼容iconv字符集转换接口,支持几乎所有存在的字符集编码转换。跨平台,C接口。
3. FreeType,字体引擎,支持:TureType,OpenType,adobe,pcf,bdf,以及各种点阵字体等等很多格式的字体,呈示效果不输WindowsXP,GNOME(GTK+)和很多著名桌面环境的底层。跨平台,C接口。
另外,关于RAD工具的问题,不知道 zyl 接不接受使用GUI框架的思想,很多RAD工具都是直接生成可编辑的 C/C++ 框架代码,与手写质量基本没有区别。何况修改和调整RAD生成的代码比完全自己写还是要快很多的。RAD应该算是生产力工具了吧,呵呵。
而且RAD一般只用在画界面,这里就有2个问题:
1. GUI本身是一种低速IO,并且,图形算法固有的时空开销就比较大,就算RAD产生的代码稍微低效,这通常也不是问题。除非兄台在为大型游戏编写实时3D引擎中~
2. 除了刚在所说的游戏等特别情况,大部分应用对GUI的效率都是不敏感的。真正对效率敏感的应用如:数据库引擎、网络服务、编译器、密集型分布计算等等应用,不是完全不使用GUI(因此也就没什么机会用RAD,比如SQL Server、DB2......)就是其绝大部分效率因素并不由GUI部分决定(比如用了超低效 .net 代码来开发界面的 VC2002/2003/2005)。
作者: asbai
时间: 2006-5-26 15:13
Originally posted by jawbin at 2006-5-26 15:09:
windows 3.1 的剪裁可能比 windows 3.0 有很大的改进, 如果能研究一下也许不错, 不过只能反汇编? 那还不如不看?
那看看 minigui 如何, 不过可能它借鉴了 wxWi ...
裁减部分可以看 wxWidgets中的 wxUniversal 源码 SciTech 写出来的很难不高效,呵呵~
作者: asbai
时间: 2006-5-26 15:21
个人感觉仅限于 8086 这个条件严苛到没有实际意义了。即使是嵌入是应用,现在起码也是 ARM7 打底了(你去买个智能电饭煲里现在都装这个啦),新型手机等大部分设备里起码是 ARM9 双芯片了,作为参考, ARM7 的计算能力可以与 486 比较,ARM9 则对应 Pentium,浮点运算能力甚至超过这两者(和搭配的DSP有关)。
这一方面是因为用户需求越来越高,另一方面,现在半导体成本急剧下降。生产像8086这样的处理器已经没有实际意义了(也许它只比ARM7便宜几美分)。
作者: jawbin
时间: 2006-5-26 15:21
对了,说到输入,我坦白了吧,我目前是在为嵌入式考虑,没有鼠标不消说,键盘不是标准的,从 GPIO 口采,而且复用,要自己去抖,处理连击...还有些功能要求某个键持续按下一段时间才起作用....目前还没和标准键盘映射起来..
作者: jawbin
时间: 2006-5-26 15:27
Originally posted by asbai at 2006-5-26 03:21 PM:
个人感觉仅限于 8086 这个条件严苛到没有实际意义了。即使是嵌入是应用,现在起码也是 ARM7 打底了(你去买个智能电饭煲里现在都装这个啦),新垠...
一则目前这个板子(
www.baby-pc.com 的 PCM3486, 这是他们现在发行的最好的板子,使用的 80186 级的 CPU, 价格在 1000 以下, 可能是 800 左右)是我选的(虽然现在我暂时不搞这个了). 另一方面, 我对 ARM 还不熟悉, 虽然知道目前它运用的领域好像非常广. 不知道它和摩托罗拉的 CPU 是否有什么保护模式? 我只知道摩托罗拉的端口和内存的访问指令是相同的(可能是统一编址的),另外也知道它的 CPU 和 intel 的字节顺序是不同的.
Last edited by jawbin on 2006-5-26 at 15:40 ]
作者: jawbin
时间: 2006-5-26 15:29
这么多内容我得慢慢消化<img src="images/smilies/face-raspberry.png" align="absmiddle" border="0">
在软件方面,我一向看重 IBM, 它为开源作了不少贡献! 它的网站上有很多非常好的东西, 知识无涯啊!
Last edited by jawbin on 2006-5-26 at 15:32 ]
作者: asbai
时间: 2006-5-26 15:37
====================================
关于多线程多任务的实现,记得GNU PTH(GNU出品的通用 User Mode pthread线程库)中有一篇非常好的文档,详细说明了运行栈切换的技术原理,并且比较了各种切换技术的可移植性及性能。大体流程是:保存当前线程上下文;切换到新的上下文空间(sus:swapcontext,在DOS下可以直接改SS等寄存器);最后重定向指令流(执行长跳转)。
运行栈切换(线程切换)是多线程实现中的一个非常关键的问题,建议仔细阅读。
====================================
时间片计时,x86结构大体有2种计时方式:
1. 系统定时器:它的计时粒度可以在 每秒18.2次(55 毫秒),到 每秒 1193180次 (0.84纳秒)(当然8086上到不了这么高的)。
2. Pentium系列及以上还支持 Performance Counter,这是一个 64 位宽的寄存器,它的值在每CPU基本时钟加一。读取它的汇编指令是:RDTSC。
基本上,实现线程第一种最方便。
====================================
最后,实现多任务还必须实现一系列互斥、同步和通信算法。比如信号量、互斥量、条件变量、管道、共享内存等等。
以上3块就是基本多任务环境的完整组成啦,打的累死了,手酸,下了,呵呵~
作者: jawbin
时间: 2006-5-26 15:38
zyl910 兄说到的图像数据量大的确是一个问题,不过如果我每次都仅仅是一个简单的操作的话,也许问题还不是很大,例如画一个 button, 也就是画几条线, 填充一下矩形. 当然, 面对稍微复杂的情况, 还是比较麻烦的.而且,有些设备假如不提供显存来操作的话(可能有点极端了),也是个问题.
作者: asbai
时间: 2006-5-26 15:39
Originally posted by jawbin at 2006-5-26 15:21:
对了,说到输入,我坦白了吧,我目前是在为嵌入式考虑,没有鼠标不消说,键盘不是标准的,从 GPIO 口采,而且复用,要自己去抖,处理连击...还有些功能要求某 ...
除了手机PDA外,大部分嵌入式开发不需要考虑GUI设计~
作者: asbai
时间: 2006-5-26 15:44
Originally posted by jawbin at 2006-5-26 15:27:
一则目前这个板子(www.baby-pc.com 的 PCM3486, 这是他们现在发行的最好的板子,使用的 80186 级的 CPU, 价格在 1000 以下, 可能是 800 左右)是我选的(虠...
x86兼容的嵌入式CPU也有很多啊,比如:VIA的、SiS的、NS Geode、Transmeta、AMD DX、STPC等等。这些CPU都是低功耗、免风扇的。而且完整系统板的价格比你报的低多啦~
作者: jawbin
时间: 2006-5-26 15:45
Originally posted by asbai at 2006-5-26 03:39 PM:
除了手机PDA外,大部分嵌入式开发不需要考虑GUI设计~
非常感激 asba 热情相助! 我们这个恰恰需要 GUI, 而且要求某些部分显示得必须非常醒目(例如字体要很大,对比度要很强,因为强日光下液晶显示器会看不清楚普通的图形,当然挑选屏幕的时候还得挑宽视角的,不过那就不是我的事情了,呵呵])而且我正在考虑遥控(为了突破现场的输入局限,但是这目前仅仅是个念头).
作者: asbai
时间: 2006-5-26 15:46
呃,我刚刚说要下了~~
作者: jawbin
时间: 2006-5-26 15:47
asbai 兄, PCM3486 就是个完整的 PC/104 板,其实我为了简单,一直在觊觎 linux,可是软硬件目前还都没有什么眉目,了解得太少了.
作者: jawbin
时间: 2006-5-26 15:49
呵呵,下吧,来日方长嘛!再次感谢 asbai 兄的热情!
作者: jawbin
时间: 2006-5-26 15:57
"实时计算贝塞尔曲线貌似很难"
这里有个空想的方法: CPU有预取指令,我们也可以预画字体,在 CPU 空闲的时候就算,保证在使用前算完,然后真正画的时候就可以不用等待很多时间了.
作者: jawbin
时间: 2006-5-26 16:13
Originally posted by asbai at 2006-5-26 03:37 PM:
====================================
关于多线程多任务的实现,记得GNU PTH(GNU出品的通用 User Mode pthrea ...
我还是怕在中断里做事,呵呵,不过据说有工业强度的 UC/OS-II 都用它, 不过书中提到移植要仔细, 否则可能会崩溃, 虽然意味着一旦调试好后,任务管理应该没有问题,但是还是让我有点怕,呵呵.
计时我就用 18.2 秒, 本来可以改, 但是保守的我还是没动它, 怕万一引起不良后果. RDTSC 自然是用不了的...
Last edited by jawbin on 2006-5-27 at 08:56 ]
作者: jawbin
时间: 2006-5-26 16:23
"关键在于DOS下所有中断服务程序都是堵塞执行的"
那么只能尽力避免使用可能阻塞的例程了,是吗?
但是有时候不得不用,例如复制文件,这真是要仔细考虑的问题.也许必须牺牲一些实时性(最坏的情况下,也许通常这并不造成什么问题!)!但是这样的情况应该尽力避免.
作者: zyl910
时间: 2006-5-26 20:20
Originally posted by jawbin at 2006-5-26 15:57:
"实时计算贝塞尔曲线貌似很难"
这里有个空想的方法: CPU有预取指令,我们也可以预画字体,在 CPU 空闲的时候就算,保证在使用前算完,然后真正 ...
光适量轮廓信息就无法完全放入基本内存了
更别说生成位图的存储开销了
而且轮廓字库最大的好处在于可以任意的旋转和缩放,妄图固定尺寸是倒退
Originally posted by jawbin at 2006-5-26 15:38:
zyl910 兄说到的图像数据量大的确是一个问题,不过如果我每次都仅仅是一个简单的操作的话,也许问题还不是很大,例如画一个 button, 也就是画几条线, 塠...
虽然在人眼中,垂直方面的两个像素是贴在一起的
但是它们实际上在不同的扫描行的,所以仍然需要内存搬运操作
除非有矢量绘图设备
PS: 就是由于上诉原因,BIOS的画线功能速度极慢,所以得自己写绘图函数
作者: asbai
时间: 2006-5-27 05:59
Originally posted by jawbin at 2006-5-26 15:57:
"实时计算贝塞尔曲线貌似很难"
这里有个空想的方法: CPU有预取指令,我们也可以预画字体,在 CPU 空闲的时候就算,保证在使用前算完,然后真正 ...
汗,居然想到预取,越来越跑题了。。。。
x86处理器最早提供预取指令的型号是AMD K6,提供了2条指令:prefetch 和 prefetchw。Intel从PIII开始引入预取:prefetchnta和prefetcht0/1/2。这与兄台给出的限制条件完全不符。
其次,预取指令是为程序员可以编程控制 L1/L2 高速缓存子系统与RAM子系统之间的数据交换而设计的,完全没有兄台设想的那种预计算功能(毕竟x86 CPU,除了最近出的双核型号以外只有一条指令流水)。
再次,预取指令的使用需要程序员对数据和cache line之间的对其;cache的联合方式、容量;CPU内部写缓冲区的端口数目及尺寸;BIU和北桥内存控制器队列和仲裁算法;RAM的BANK和Page数目以及其充电周期等等都有深刻的理解,否则使用预取能达到的效率提升可以忽略。
甚至在新型的配置了智能预取算法的CPU上,不佳的手工预取可能对性能产生负面影响。
PS:在兄台这种使用环境和设备都严格受限的环境中,其实不需要使用矢量字体。矢量字体最大的优势是可以自由旋转和缩放。兄台的使用环境中,貌似字体的尺寸和显示环境都是完全可以预先知道的,使用点阵字体就可以了。
而且,矢量字体在显示小字型(9~11pt)的时候,不管用什么算法,效果都会很差。所以大部分矢量子字体都带有专门为小字型优化的点阵数据或hint。例如Windows环境里菜单和按钮。
所以,使用点阵并不丢人,:D
Last edited by asbai on 2006-5-27 at 06:08 ]
作者: jawbin
时间: 2006-5-27 08:22
哇,一大早来就看到两位兄台的热情回复,太幸福了!
作者: jawbin
时间: 2006-5-27 08:53
我测试过 UCDOS 中的轮廓字体,没仔细看原理,但是速度的确非常慢。
另外,若无显存可操作的话,调用 BIOS 功能可能比较好(起码“仿佛“可以移植,之所以说“仿佛“,因为有的设备不是 VGA 兼容的,它自己提供一个库,所以最好在其上再加一层,以便上层代码一致),但是显然速度很慢,不过,若是附带一个库,它通常会提供简单的甚至稍微复杂一点功能,比如,画直线,椭圆,矩形,填充,而这时候不用考虑显存,甚至它们可能有优化版本或者选项以供选择。
“轮廓信息就无法完全放入基本内存”,理论上可以先放到外存,但是这样非常糟糕!确实是纯粹空想!
另外我说的预取其实只是说原理上相似,而当然不可能直接使用 CPU 的那个功能。所以一些字要显示之前必须自己放到预计算队列中去。其它类似可能会提高性能的可能还有分支预测,缓存等等,但是大大增加了复杂性,再一次证明仅仅是空想!
假如以“微内核“为目标,这已经走得太远了。
我认为一个普通的系统应该考虑“微内核“这样的方式,例如在系统刚开始启动的时候,可以尝试运行一个最小的可工作集来测试是否进一步将其他的全部或部分装入内存,类似但可能不是非常恰当的例子有 Windows 的安全模式,以及 Windows 预安装环境. 我想,内核不支持庞大的 Unicode,把它作为外挂样的就行了。这样可以不背负 Unicode 这个沉重的负担了(Unicode 的字库存储于外存自然可以先不考虑,但是相应的功能也是个不小的模块吧。)另外,如 asbai 兄所说,点阵可以不用这样庞大的计算,大大地节省了资源。其实我想说的是,如果认为中文字库都是负担的话,也可以不必加入“内核“,“内核“仅仅支持英文,以保持它苗条。不过,其实尽管是这样想,我还对模块动态载入没有一个清晰的想法,对可行性也无法确定。
Last edited by jawbin on 2006-5-27 at 09:37 ]
作者: jawbin
时间: 2006-5-27 09:05
另外,我不喜欢 windows 程序和一些 c++ 代码的原因是通常看到看起来频繁申请和释放内存的代码,例如在非常频繁需要处理的 WM_PAINT 中,会经常创建和销毁 GDI 对象,虽然也许在内部事实上并没有对内存的申请与释放,或者有变相的行为,但是不会有什么恶劣的后果,但是,还是让我不舒服。而 c++ 的教科书以及 VS 开发环境中的示例代码通常会出现 new。我唯恐内存碎片带来的不利影响,所以,我尽可能地避免这样的情况发生,事实上,这通常是很容易做到的,不用刻意就可实现。我真地认为频繁申请与释放是一个坏习惯。不过,也是垃圾收集技术培养了这样的习惯。
但是,如 asbai 兄所说,如何律人呢?难道最后不过是个讽刺?
作者: jawbin
时间: 2006-5-27 09:10
我避免大量使用内存的另一方法和任务分片相似,就是每次只操作 1 小片区域。例如,我抓图的时候,就每次只抓一行,把这一行写入位图文件。但任务分片却是真正的问题,也许该使用“真正的“任务管理,而不是在任务本身上变形。
Last edited by jawbin on 2006-5-27 at 13:44 ]
作者: jawbin
时间: 2006-5-27 09:29
对,最大的字体都是知道的,是 32 * 56 的数字。
对了,我看 windows 系统上怎么字体小效果也还好,很大也还好,就是不大不小的时候看起来比较差,应该就是 asbai 兄上面所说的原因。
另外 Windows 系统虽然后来声称是抢先多任务,但是我还是发现实际上有被一个任务阻塞的情形,这是非常恼人的。但是,也许不能全怪 Windows, 可能一则是它本身未标为 “实时“, 二则可能当时用户给它的任务比较繁重。
Last edited by jawbin on 2006-5-27 at 09:34 ]
作者: jawbin
时间: 2006-5-27 09:56
另外为了资源紧张问题,我还想过“造句“功能。具体来说,Windows 系统由于几乎不用考虑资源限制,可以看到它在使用字符串资源时并不“节俭“,随便打开一个有字符串资源的 PE 文件,可以看到其中有很多重复的词汇。所以我在想类似这样的问题:共享这些词语。例如,共享如下词汇:“磁盘“,“文件“,“访问“,“打开“,“关闭“,“成功“,“失败“。。。
然后在需要如下两个字符串的时候就不用占用两个字符串了:
“磁盘访问失败“
“文件访问失败“
但是这个句子又如何记录呢?类似原子?好像又有新问题。
另外我对“集合“很感兴趣,一个 GUI 容器可以有一个控件集合,它的元素可以由 index 来索引,也可以由字符串来索引,数据库好像也类似。非常有意思,但是,我不能考虑后者,因为我觉得那将浪费很多资源,可能他使用 hash 实现的吧。集合的优点就是很方便枚举,以及加入和去掉元素,很多情况下这很有用。
作者: asbai
时间: 2006-5-27 13:11
Originally posted by jawbin at 2006-5-27 09:05:
另外,我不喜欢 windows 程序和一些 c++ 代码的原因是通常看到看起来频繁申请和释放内存的代码,例如在非常频繁需要处理的 WM_PAINT 中,会经常创建和 ...
new和malloc的问题确实是经常被提起的。我的回答是:在现代操作系统中,这其实不是个问题。原因如下:
1. 标准库的内存分配函数通常会自己维护一个小堆(small heap或者叫fast heap)。当你频繁申请小型对象时,实际上它们是直接从这个小堆上分配的。“小堆”本身是一块或几块较大的内存区,主要用于避免每次内存分配时系统内核调用造成的开销。
2. 现在OS的内存管理是分页的,完全不会有碎片化的问题!(就连现在的DPMI Server都使用分页管理),这也是为什么我在说小堆作用的时候不提它有助于降低内存碎片,因为不会有碎片。
3. 堆的分配速度虽然比栈慢,但是在执行流处理或者内存复制任务时,堆的速度通常比栈快。因为编译器通常将栈中的数据做双字对其,而堆的边界一般是16字节,对其不同带来对内存总线和cache line利用率的不小差别。
4. 无论在堆中还是在栈中,最完美的内存访问优化就是不使用内存。reference counting、copy-on-write等技术就是典型为 zero-copy 发展出来的。
内存访问优化,确实是程序员说不完的话题。现代处理器最大的瓶颈应该就是内存了,典型情况下的现代PC,一次一切都非常顺利的完全内存访问周期也需要上百个CPU时钟。。。。。。貌似跑题了,呵呵
作者: asbai
时间: 2006-5-27 13:27
Originally posted by jawbin at 2006-5-27 09:56:
另外为了资源紧张问题,我还想过“造句“功能。具体来说,Windows 系统由于几乎不用考虑资源限制,可以看到它在使用字符串资源时并不“节俭“, ...
字典:所有数据压缩算法的基础,呵呵。问题是,即使用了字典,通常也只能节约外存,数据在放入内存做表示时通常还是要展开的。
比如用png算法压缩的图像放到显存中显示时仍然要展看成raw格式。当然也可以仅展开在屏幕区域显示的那部分,不过这样在滚屏等操作时效率很低。
作者: jawbin
时间: 2006-5-27 13:38
我某些时候(例如,可能等同于 MS 的 GDI 堆的部分可能就会这样,此前我的时钟就是类似这样的,总之一些系统堆可能会这样处理以提高可能微乎其微的一点性能)打算使用类似的自己的"小堆", 我也不想使用栈,因为它占用目标文件的空间。即使这样的小堆也得做些工作,因为它自己当然需要在必要的时候重组。另外即使这完全不是问题,我还是不想让这么频繁的动作花费在分配和释放上,这和应该在循环中做可以在循环外做的事有什么区别呢?不过我还不想那么复杂(组织自己的小堆),我只想能安全地使用我所用的内存就够了。如果这些“现代操作系统“能运行在 8086 上的话,我是可以不用这么过虑了,呵呵。我一直不想依赖库函数或操作系统的某些可能让我养成坏习惯的“优点“。
另外,asbai 兄说的字典,其实是我想用来节约内存的,外存我已经不想太多“节俭“了,如果要考虑外存的话,那我是不会考虑 unicode, 我认为,即使汉字也可能仅仅使用个小字库,而不是 GB2312 全集。不过若真是不想节约外存的话,的确可以完全不用压缩的。所以我这个想法可能没什么用。
Last edited by jawbin on 2006-5-27 at 14:03 ]
作者: jawbin
时间: 2006-5-27 13:55
跑题。。。跑远了正好远足呢

作者: asbai
时间: 2006-5-27 16:37
Originally posted by jawbin at 2006-5-27 13:38:
我某些时候(例如,可能等同于 MS 的 GDI 堆的部分可能就会这样,此前我的时钟就是类似这样的,总之一些系统堆可能会这样处理以提高可能微乎其微 ...
jawbin 兄很喜欢跟 MS 的 GDI 接口较劲啊,呵呵。其实打开 DC 的时候只不过是打开了一个句柄,实际的开销不过是 "++reference_count;" 之类的引用计数操作,通常没有什么内存分配的问题。
在栈中分配对象的大小跟最终生成的可执行程序尺寸没有任何关系。绝大部分编译器中,在栈中分配对象仅仅是修改一下相对于 ESP 的一个偏移值而已。不信的话你可以编译以下C代码:
int main(int argc, char* argv)
{
unsigned char gbTEST; // 在栈上分配64MB缓冲区
return 0;
}
看看编译出来的可执行程序会不会有64MB那么大。甚至,不光这个可执行程序会很小,运行时所消耗的内存甚至也不会超过几K,因为gbTEST只是在地址空间上保留了64MB的段落,由于没有真正的使用到它,所以分配给你的仅仅是进程虚地址空间中的一段而不是物理内存。
作者: jawbin
时间: 2006-5-28 09:09
被 asbai 兄看穿了

, 我是很看不惯 GDI
不过依据 MS 的说法, 它加速 DC 访问是用了 5 个 common DCs 中的一个用来缓存最近使用的 DC,而当这 5
个 DCs 不够的时候,就需要增加更多的 DCs 了. 至于别的小对象(笔,刷子等), 还不清楚, 可能也类似, 但是
问题应该与此相比小很多, 可以先不考虑. 当然 CS_OWNDC 也可以使窗体拥有自己的 DC, 但是, Windows 5.x
之前不被提倡, 估计是 Windows 5.x 之后系统本身对硬件资源的要求就高了, 所以可以"奢侈"一些了,无论如
何, DC 这个稍微有点庞大的东西总不能在资源紧张的系统上原样照搬. 每个窗体的 DC 的内容应该是不同的,
所以不可能引用吧. 即使如果某一个固定的窗口的 DC "相同", 也只需要获取一次(在窗体刚创建之后),最后销
毁一次, 不需要在频繁调用的 OnPaint 中开始来个 ++reference_count, 末尾来个 --reference_count 这样
的假动作吧?. 无论如何, 即使我不去"善意地"猜测它怎么在内部优化实现, 我还是对这样的代码很不舒服.
另外, asbai 兄的举例的确指出了我的错误, 应该是我疏忽了, 我指的是需要初始化的数据(由于使用数组初始
化数据很方便, 通常如果数据不需要初始化的话, 就可以用堆了)它不在栈上, 而是在附加段里. 不过这个小小
的例子有些微欠妥, 就是在栈上分配的太多, 在 VC 等可以编译 PE 文件的环境下编译可以通过, 但是运行会
出错. 而且, 显然, 在 TC 等可以编译 DOS 程序的环境下, 编译都不会通过的, 即使将内存模式设置为 HUGE.
作者: jawbin
时间: 2006-5-28 09:13
所以, 除了零星的小东西, 我不会使用栈, 毕竟它还有点点宝贵, 否则, 递归是不用限制的.
不过 GDI 既然是 MS 这样的巨头搞出来的, 应该有它的道理或"苦衷", 而且它也在发展, 比如后来出现的 GDI+, 在 Vista 中可能更有大的发展. 但是显然, 能真正实用的 Windows 不运行在 8086 上.
Last edited by jawbin on 2006-5-28 at 09:18 ]
作者: zyl910
时间: 2006-5-28 10:05
对于栈
Windows系统默认为每个线程
预留(Reserve)2MB的空间
但最开始只
分配(Commit)两页(8KB)
随着递归执行,线程可能越过分配的区域,会访问到保留的区域
这时Windows系统会自动处理该异常,将访问目标那片内存
提交(Commit)
保留区域的最低8KB用于越界检查(报告栈溢出异常),不可使用
注意
在栈中定义变量只是修改esp指针,不会访问实际内存
只有访问栈中变量时才是访问内存
创建线程(CreateThread)时可决定该线程 栈的大小(dwStackSize)参数
在PE文件格式中有设置栈大小的字段(IMAGE_OPTIONAL_HEADER32的SizeOfStackReserve、SizeOfStackCommit)
typedef struct _IMAGE_OPTIONAL_HEADER {
//
// Standard fields.
//
WORD Magic;
BYTE MajorLinkerVersion;
BYTE MinorLinkerVersion;
DWORD SizeOfCode;
DWORD SizeOfInitializedData;
DWORD SizeOfUninitializedData;
DWORD AddressOfEntryPoint;
DWORD BaseOfCode;
DWORD BaseOfData;
//
// NT additional fields.
//
DWORD ImageBase;
DWORD SectionAlignment;
DWORD FileAlignment;
WORD MajorOperatingSystemVersion;
WORD MinorOperatingSystemVersion;
WORD MajorImageVersion;
WORD MinorImageVersion;
WORD MajorSubsystemVersion;
WORD MinorSubsystemVersion;
DWORD Win32VersionValue;
DWORD SizeOfImage;
DWORD SizeOfHeaders;
DWORD CheckSum;
WORD Subsystem;
WORD DllCharacteristics;
DWORD SizeOfStackReserve;
DWORD SizeOfStackCommit;
DWORD SizeOfHeapReserve;
DWORD SizeOfHeapCommit;
DWORD LoaderFlags;
DWORD NumberOfRvaAndSizes;
IMAGE_DATA_DIRECTORY DataDirectory;
} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;
现在虚拟机的垃圾回收机制实际上是模拟栈操作(美其名曰“内存池”)
每次创建对象时,在内存池后面的空闲空间分配一段内存出来,再修改内存池空闲空间指针。即每次都在后面分配
当发现内存池空间不够时,执行垃圾回收算法,跟踪指针,标记空闲区域
为了减少内存碎片,将后面的内存挪动到前面来,补足空闲区域,并修正指针
所以垃圾回收机制占用内存大,且垃圾回收算法很复杂,为了减少碎片还得执行内存搬运,会影响程序性能
为什么现在流行Java和.Net
这是因为现在计算机行业的应用急剧扩展
很多人抱着幻想来学编程
只想简单学一下控件使用
根本没有算法功底,更不知内存管理算法为何物
(实际上垃圾回收算法只是一种高级的内存管理算法,你可以自己编写的)
而对于操作系统
不用内存管理算法简直是笑话(没有内存管理的操作系统还能叫操作系统吗)
所以GDI对象有自己的内存分配器的,效率很高
(其实经历过windows 3.0的人都知道,那时GDI堆是64KB的。如果不注意释放的话,很容易耗尽GDI资源)
现在想学内存管理算法的话
推荐看侯捷的《STL源码解析》(算法理论书籍估计现在没人愿意看了)
STL是很好一个范本
Last edited by zyl910 on 2006-5-28 at 10:09 ]
作者: zyl910
时间: 2006-5-28 10:08
GDI+只是为适应现在的应用开发 而提出来的一个高级类库(GDI才是最根本的底层借口)
真正的图形图像处理函数都是自己《计算图形学》、《数字图像处理》算法写出来的
要不然你当GDI+的高级功能是怎么写出来的
图形程序员名言:只有能让我操作显存,我就能描绘整个世界
作者: jawbin
时间: 2006-5-28 13:44
zyl910 兄也终于来了!
我并没有说 GDI+ 是平地起高楼, 呵呵, 一个高级的系统是有很多层的.
我只是喜欢看上去比较优美的接口, 当然首先它不是在追求低效.而一个发展的系统,总会让它自己越来越接近自己的理想,当逐步被减少硬件上的束缚之后.所以,一个系统越往后发展,就越值得借鉴.它会越来越成熟.但是理想总是无法达到的,否则它就该改名叫现实而不是叫理想了 *^_^*
所以, 即使在 Vista 已经发售(据说已经推迟了), 它其实还没达到预期目标,至少没达到我的预期目标 :), 但是毕竟让人看到希望:这条路走得还不错,可以继续前进.
Last edited by jawbin on 2006-5-28 at 13:55 ]
作者: asbai
时间: 2006-5-28 13:46
Originally posted by jawbin at 2006-5-28 09:09:
被 asbai 兄看穿了:P, 我是很看不惯 GDI
不过依据 MS 的说法, 它加速 DC 访问是用了 5 个 common DCs 中的一个用来缓存最近使用的 DC,而当这 5
个 DCs 不够 ...
引用计数自然是在同一个窗口内的DC上使用的,每个DC有自己的 reference count,一般来说,这不会降低效率。
作者: jawbin
时间: 2006-5-28 13:49
我不极力推崇底层, 因为那可以有点"无止境",最后可能追究到特定的 CPU 指令集是否最优秀了,那时候已经真正"远足"了,可以跑去设计 CPU 了.
对于一个特定追究的目标, 可以竭力去优化. 但是, 有时候复用也非常重要. 否则, 那时候出来的就不是一个通用的 intel 4004, 那目前的计算机世界是什么样子完全不可预料.
Last edited by jawbin on 2006-5-28 at 13:58 ]
作者: asbai
时间: 2006-5-28 13:53
Originally posted by zyl910 at 2006-5-28 10:05:
对于栈
Windows系统默认为每个线程预留(Reserve)2MB的空间
但最开始只分配(Commit)两页(8KB)
随着递归执行,线程可能越过分配的区域@...
zyl910兄正解!
不过STL的allocator由于要考虑对象构造与析构的语义兼容,很多时候效率颇低。SGI STL的 Pool Allocator 效率尚可,但仍然有很大的优化空间。
例如:对没有构造、析构和拷贝构造问题的内置数据类型而言,用realloc的语义改变容器尺寸效率远高于创建新块;逐个拷贝旧块中的对象;释放旧块这个流程。
作者: jawbin
时间: 2006-5-28 13:54
Originally posted by asbai at 2006-5-28 01:46 PM:
引用计数自然是在同一个窗口内的DC上使用的,每个DC有自己的 reference count,一般来说,这不会降低效率。
按 MS 的说法, 应该是或者使用就近的 cache, 或者在 5 个 common DCs 中使用一个, 或者不够用的时候, 增加. 其实内部如何实现, 已经不是很重要了, 重要的是, 当我看到这样一些"矫情的"动作, 我就大倒胃口. 但是 MS 来说, 可能是"不得已", 或者说, "没什么大不了", 或者说, "没什么错". 它没有想运行在资源有限的系统上, 所以, 它完全不会遇到什么问题. 它通常面临的是 Windows 这个产品的用户的问题.
作者: asbai
时间: 2006-5-28 13:59
另外,jawbin兄:
Win32为每个线程的默认保留栈空间是2MB,如果兄台将我的例子用在自己Create的一个Thread里试验,一定会有栈溢出错误。这个需要在Create的时候手动指定保留栈的尺寸。
但是Win32对每个进程的主线程是可以运行时动态调整栈空间尺寸的,应该不至于出现运行时错?
作者: asbai
时间: 2006-5-28 14:04
Originally posted by jawbin at 2006-5-28 13:54:
按 MS 的说法, 应该是或者使用就近的 cache, 或者在 5 个 common DCs 中使用一个, 或者不够用的时候, 增加. 其实内部如何实现, 已经不是很重要了, 重要皠...
5个应该是初始值,超出以后引用计数仍然是有效的。另外,这5个貌似说的是memory dc,否是Window DC,呵呵。
这是典型的空间换时间的算法,就像 cpu 的L1/L2 cache、OS的磁盘访问缓冲等等。个人认为这确实是天经地义,不付出这点空间,换来的时间劣化是无法忍受的。
作者: jawbin
时间: 2006-5-28 14:08
另外,我不是想设计操作系统(至少目前是这样,因为我还没有那个能力), 我所做的, 是基于操作系统之上的. 我前面已经说过, 如果从 BIOS 起做, 对我而言太困难了:)
而且目前我所做的, 也仅仅是我自己用, 可以不用担心第三方对内存的非预期访问. 呵呵. 当然, 对于一个 platform (Java 既然不是一个 OS, 那么就把这些 runtimes 都统称为 platforms 吧), 不管理内存, 它有什么资格成为一个 platform?
作者: jawbin
时间: 2006-5-28 14:11
Originally posted by asbai at 2006-5-28 01:59 PM:
另外,jawbin兄:
Win32为每个线程的默认保留栈空间是2MB,如果兄台将我的例子用在自己Create的一个Thread里试验,一定会有栈溢出错误。这个需要在Cr ...
asbai 兄, these times, we've gone too far...
我们已经跑到"罪恶的" 640K 以外太远了, 我体力不支了<img src="images/smilies/face-raspberry.png" align="absmiddle" border="0">
Last edited by jawbin on 2006-5-28 at 14:14 ]
作者: asbai
时间: 2006-5-28 14:14
Originally posted by jawbin at 2006-5-28 14:08:
另外,我不是想设计操作系统(至少目前是这样,因为我还没有那个能力), 我所做的, 是基于操作系统之上的. 我前面已经说过, 如果从 BIOS 起做, 对我而言 ...
java之类属虚拟机范畴,也就是说,它是跑在一个平台(OS)之上的平台 <img src="images/smilies/face-raspberry.png" align="absmiddle" border="0">
虽然俺也B4 VM,不过高层利用底层提供的既有功能好像是说的过去。即使OS也要利用它的底层(硬件和BIOS)所提供的既有能力嘛,哪有空中楼阁啊?<img src="images/smilies/face-laugh.png" align="absmiddle" border="0">
例如:硬件提供了MMU,OS就应该充分利用起来,而不是用纯软件实现分页;硬件提供了DMA控制器,OS就应该尽量用它做IO,而不应该自己实行用CPU手工搬运数据的算法~
Last edited by asbai on 2006-5-28 at 14:19 ]
作者: jawbin
时间: 2006-5-28 14:25
asbai 兄原来 B4 VM 啊? 可是就 Java 来说, 不管强弱, 这些年来, 也带来了竞争, 何尝不是一件好事! 另外, 很多 VM 不是研究操作系统的有效工具吗, 例如 VPC, VMWare, qemu...
另外, .Net 及 C# 对 java 的模仿(当然青不出于蓝的话, 要它何用)来看, java 本身就有很多值得学习的地方. 另外, java 在嵌入式领域也有所尝试. 我们不应该缩小自己的视野吧

作者: asbai
时间: 2006-5-28 14:36
嘿嘿,此VM非彼VM也~~
俺非常喜欢用硬件VM,像VPC、VMWare等等。俺只B4 Java、C#这种语言环境类滴~
这些东西最大的优势就是可移植而已,这个目标用wxWidgets、QT等等可移植框架或者apr、nspr这种可移植runtime库一样可以高效很多倍地实现。
不过jawbin兄说的有理,拓宽视野没有坏处~
作者: jawbin
时间: 2006-5-28 14:39
Originally posted by asbai at 2006-5-28 02:14 PM:
例如:硬件提供了MMU,OS就应该充分利用起来,而不是用纯软件实现分页;硬件提供了DMA控制器,OS就应该尽量用它做IO,而不应该自己实行用CPU手工搬运数据的算法~...
假如硬件提供环球旅行就好了....哈哈
不过环球旅行果然是硬件提供的(飞机啊什么的)...倒塌
作者: asbai
时间: 2006-5-28 14:40
Originally posted by jawbin at 2006-5-28 14:39:
假如硬件提供环球旅行就好了....哈哈
不过环球旅行果然是硬件提供的(飞机啊什么的)...倒塌
兄台实在幽默 <img src="images/smilies/face-raspberry.png" align="absmiddle" border="0"><img src="images/smilies/face-raspberry.png" align="absmiddle" border="0"><img src="images/smilies/face-smile-big.png" align="absmiddle" border="0"><img src="images/smilies/face-smile-big.png" align="absmiddle" border="0">
作者: jawbin
时间: 2006-5-28 14:44
.Net声势上算是取代 java, 但实际上弱化了 java 的移植倾向, 它在 Windows 平台上移植, 够阴的, 呵呵
虽然有个 Mono, 但是甚至未取得 MS 的授权, 随时可能被打压.
Qt 的使用效果还好, 但是我就介意它是 C++ 的, 哎, 以后得改改我这毛病了, 呵呵
作者: jawbin
时间: 2006-5-28 14:47
对了, 我想到 c/s 实现图形服务的话, 随后又想到 B/S 的"更灵活性", 也许可以有更多的考虑, 必须能静下心来想才可以

作者: jawbin
时间: 2006-5-28 14:50
恩, 对, 我们是这样的:
它也许有用, 但它未必是唯一!
我刚开始烦的就是 java 那些蹩脚的广告, 认为 java 就是极乐世界了..
作者: asbai
时间: 2006-5-28 15:16
关于C++,正确地使用C++编码,对于典型的应用来说,它的效率不会比等效C代码低。
在某些对健壮性要求很高的场合它的效率还可能会高于相应的C代码。在这种场合中,要求对每个调用是否成功进行判断,并恰当地处理相应的错误,这至少对每次调用增加一次比较操作。现代处理器,由于流水线长度的关系,又对分支过于敏感。。。。。。此外,在这种情况下,通常会有 1/2 到 2/3 左右的代码献身与错误处理中。C++的异常可以很好的消除这个问题。这省去了每步之后的错误判断,而且大部分现代编译器对异常处理的效率是很高的。
另外,对于现代编译器产生的代码来说,C++每一个特性都已经被尽可能的优化了,用C实现等效的特性也不大可能更高效。
但是,C++最大的问题就是容易诱导程序员在不恰当的时候使用不恰当的特性。比如:RTTI、虚基类和虚函数实现起来都有一些不可避免的开销。不了解这些特性的底层实现方式及其开销的程序员经常会在不恰当的地方误用这些特性。
广告时间——关于这方面的详细讨论详见小弟拙作:“《C++编码规范与使用指导》-RTTI、虚函数和虚基类的开销分析及使用指导” 一节 <img src="images/smilies/face-smile-big.png" align="absmiddle" border="0">
http://baiy.cn/doc/cpp.zip
作者: jawbin
时间: 2006-5-28 15:22
正在下载中.... 看来asbai 兄 来头大大的大..
作者: jawbin
时间: 2006-5-28 15:24
本来正想出去呢,在这酷热的环境下,现在改变计划了,哈哈
作者: jawbin
时间: 2006-5-28 15:26
其实我恐怖的是类爆炸,有时候仅仅可能需要一个实例,却为之创建一个类,我最关心的就是 size 了... <img src="images/smilies/face-raspberry.png" align="absmiddle" border="0">
Last edited by jawbin on 2006-5-28 at 15:29 ]
作者: jawbin
时间: 2006-5-28 15:27
我有时候妄图使用结构体安慰自己, 后来看到结构体("纯粹的"结构体)和类差不多, 居然潜意识中连结构体都可能排斥了, 太病态了, 哈哈
好了,应该开始拜读了 asbai 兄的大作了.
Last edited by jawbin on 2006-5-28 at 15:29 ]
作者: jawbin
时间: 2006-5-28 15:31
自从那时候我试验过 stream 和 异常处理都各自会很大地增加目标的 size, 我后来见到 c++ 就感到眩晕

作者: jawbin
时间: 2006-5-28 15:34
asbai 兄先提到编译时开销, 这应该通常不算问题, 除非大的惊人(真有这样的可能吗?). 因为一个目标程序的真正价值在执行期间..
接着拜读...
作者: jawbin
时间: 2006-5-28 15:37
另外还有一点, c++ 可以接受 c 代码, 反过去却不行
还有一个现成的样例: win16 及 win32 核心(Kernel, User, Gdi) APIs 均使用了 c, 而且据说为了效率, 还用了 pascal 约定.
作者: jawbin
时间: 2006-5-28 15:39
c++ 仿佛带着护卫和服务员的 c, 可这开支, 我怕消受不起...呵呵
作者: jawbin
时间: 2006-5-28 15:44
c++ 程序员可以一边悠闲地喝着咖啡一边写程序, 而 c 程序员如果这样的话就未免太不敬业了. c++ 增加了程序员的生命, 增加了产品的开发速度, 并且尽一切真实的或"虚伪的"手段减少了出错的可能性. 但是, 任何馅饼都是有价格的. 有时候太穷, 买不起.
作者: asbai
时间: 2006-5-28 15:45
Originally posted by jawbin at 2006-5-28 15:27:
我有时候妄图使用结构体安慰自己, 后来看到结构体("纯粹的"结构体)和类差不多, 居然潜意识中连结构体都可能排斥了, 太病态了, 哈哈
好了 ...
不使用上述高级特性的类与相应的结构体开销等价~
作者: jawbin
时间: 2006-5-28 15:46
另外它那修饰过了的导出符号, 虽然可能会提供更多的信息, 但是看起来"很不人性化":P
我独自跑了怎么远, 呵呵
作者: asbai
时间: 2006-5-28 15:55
Originally posted by jawbin at 2006-5-28 15:31:
自从那时候我试验过 stream 和 异常处理都各自会很大地增加目标的 size, 我后来见到 c++ 就感到眩晕 <img src="images/smilies/face-raspberry.png" align="absmiddle" border="0">
在典型的应用中,异常对目标程序的体积影响完全可以忽略,这种开销只在 Hello World式的程序里可见。stream的情况也是类似的。为什么会这样呢?举个例子:
可以比较以下用C写的 Hello World:
main()
{
printf("Hello Word!\n"<img src="images/smilies/face-wink.png" align="absmiddle" border="0">;
}
和用汇编直接调用OS字符串输出API写的等价代码,你会发现用C写的那个大了几十甚至几百倍!
这并不能说C编译生成的汇编码就是比手写的差几百倍,产生这种状况的原因是linker在连接C代码的时候把printf这个标准c调用的完整实现连接到了目标代码中。而printf实际上是一个功能很强大的输出函数,这个程序其实只用到了它的百分之一不到而已!
而在一个哪怕典型的,那盘就几千行代码的小型应用中,printf中的大部分代码基本都会被用到,这个时候再去比较它们的等效汇编实现就会发现它们的体积已经很接近了~
C和C++的比较同样也是类似的。不要在一个hello world式的程序里比较它们~
作者: jawbin
时间: 2006-5-28 15:58
哎, 我就是对 size 极其过敏, 而且由于前面提到的"移植性"问题, 我对 stream 和 异常处理目前还是敬而远之.
stream 和 异常处理 和 printf 可以比较, 但是, 在我看来, 我需要它们的强烈程度不一样, 就造成了我的取舍.
Last edited by jawbin on 2006-5-28 at 16:07 ]
作者: asbai
时间: 2006-5-28 16:00
Originally posted by jawbin at 2006-5-28 15:46:
另外它那修饰过了的导出符号, 虽然可能会提供更多的信息, 但是看起来"很不人性化"<img src="images/smilies/face-raspberry.png" align="absmiddle" border="0">
我独自跑了怎么远, 呵呵
呵呵,名称粉碎不是给人读的,人类可以通过标准dump工具读出翻译后的函数声明。
例如VC粉碎的:
?_RecvAsync@CSocket@BaiY@@IBEXABV?$CHandle@VTAsyncIoCommand@BaiY@@U?$Deletor@VTAsyncIoCommand@BaiY@@@2@@2@@Z
用Dependency Walker看到就是:
void BaiY::CSocket::_RecvAsync(class BaiY::CHandle<class BaiY::TAsyncIoCommand,struct BaiY:<img src="images/smilies/face-smile-big.png" align="absmiddle" border="0">eletor<class BaiY::TAsyncIoCommand> > const &<img src="images/smilies/face-wink.png" align="absmiddle" border="0">
这甚至比C的Export更友善一些,我们再不用去猜这个export接口的返回值和参数了,对吧~ <img src="images/smilies/face-wink.png" align="absmiddle" border="0">
作者: jawbin
时间: 2006-5-28 16:04
是的, 有更多的信息, 能用程序读出也是真的(例如, 用 depends 可以读出一个 c++ 函数的签名, 但是, 所读出的 c 的就"不完整", 反过来说, 好象相当遗憾), 但是更遗憾的是, c++ 的导出符号的修饰方式没有统一的标准, 各厂商之间可能无法通用. 不过, 这是个小问题. 因为现在库的格式就没有统一, 光导出符号一致也未必有什么意义.
作者: jawbin
时间: 2006-5-28 16:10
所以只能力求在 source 上"移植"了, 虽然给 c++ 外表裹一层 c 接口来达到重用可能可以, 但是未免感觉有点醉糊涂了.
作者: asbai
时间: 2006-5-28 16:10
Originally posted by jawbin at 2006-5-28 15:58:
哎, 我就是对 size 极其过敏, 而且由于前面提到的"移植性"问题, 我对 stream 和 异常处理目前还是敬而远之.
stream 和 异常处理 和 printf 可以比较 ...
嗯,C++标准库的厂商扩充自部分,如:hash_map、hash_set 之类,还有各编译器对不同C++特性的支持程度,例如:模板的嵌套和部分实例化等等,确实给移植带来的一些不大不小的困难。
不过当前不支持异常的编译器不多了,呵呵。而且C本身也是有些移植问题的,比如不同编译器对64位整型的支持(有的不支持,有的是long, 还有的是longlong,另一些是 long long,还有一些是__int64....)等等。
当然这些问题C++里也有,但是大部分情况下,C++可以在不损失时空效率的前提下比较好的封装这些问题,比如:定义一个INT64类,重载所有64位整型的算术和逻运算。在支持int64的环境中,这些重载都直接内联而不损失效率;在不支持int64的编译环境,用两个32位整型和自定义的数逻运算提供支持。
作者: asbai
时间: 2006-5-28 16:14
Originally posted by jawbin at 2006-5-28 15:37:
另外还有一点, c++ 可以接受 c 代码, 反过去却不行
还有一个现成的样例: win16 及 win32 核心(Kernel, User, Gdi) APIs 均使用了 c, 而且据说为了效率, 还用了 ...
实际上,MS声称Win32是使用C++和面向对象体系开发的。但是Win32 API必须是标准C接口,这是为了对C和其它语言(如Pascal,ada、cobol等)提供支持。
仔细看 Win32 API 会发现无处不在的 HANDLE 就是 this 指针,呵呵
作者: asbai
时间: 2006-5-28 16:16
Originally posted by jawbin at 2006-5-28 15:22:
正在下载中.... 看来asbai 兄 来头大大的大..
瀑布汗,,自己瞎写的文档而已,到让 jawbin 兄见笑了~
作者: jawbin
时间: 2006-5-28 16:24
很好, 提到这个 INT64, 我想先说说这些基本类型的"定义". 先不管 .Net 等对这些基本类型提供了相应的类. 仅仅就说基本数据类型. 也不谈运行时泛型, 仅仅谈设计时或"编译时".
WORD 最初可能是认为"泛型", 目前我不清楚, 它是所谓的字长. 可能随着 CPU 的发展而不同. 使用它似乎也是为了代码移植, 而不是为了在运行时能进行什么对泛型的"真正充分利用".
而相应地, "具型"呢, 更清晰, 不会让人觉得含糊不清. 而且不用担心 WORD 型 "移植" 过去之后可能发生的不良结果.
那么, 通常, 基本数据类型, 也许该"具型", 我就是这样用的. 而另一个打算"移植的", 就使用一个"泛型样的", 例如, 可能为颜色专门指定一个类型, 在 Windows 系统之中有个 COLORREF 吧. 不过真正要"移植的"时候, 恐怕已经对颜色有多得多的要求了, 例如不同的颜色空间啊什么的, 不是早先所"泛型"出来的那个类型所能表达, 那么扩展将受阻碍. 所以, 在这种情况下, 有点两难. 望 asbai 兄能有所指点!
在这个时候, 可能类倒是一种希望了,我讽刺我自己,哎..
Last edited by jawbin on 2006-5-28 at 16:34 ]
作者: jawbin
时间: 2006-5-28 16:28
Originally posted by asbai at 2006-5-28 04:14 PM:
无处不在的 HANDLE 就是 this 指针 ...
asbai 兄不是指在 MFC 中吧, 如果是, 那可是不作数的哦.
另外 asbai 兄太过谦了!<img src="images/smilies/face-raspberry.png" align="absmiddle" border="0">
作者: jawbin
时间: 2006-5-28 16:30
事实上, 我在当初还考虑了字节顺序, 所以, 在类型上还"添足"了很多. 只是后来还没使用, 因为一直使用的是 intel 兼容的 CPU.
作者: jawbin
时间: 2006-5-28 16:40
请教了几个问题就成了高级用户, 混得这个开心啊, 哈哈哈
说明很多系统, 都没有真实地反映世界, 哈哈:P
Last edited by jawbin on 2006-5-28 at 16:56 ]
作者: asbai
时间: 2006-5-28 17:24
呵呵,有时候用typedef或者宏定义的类型确实也能算做一种简单的泛型。
不过WORD应该不是这种,实际上WORD在几乎任何平台都是16bit无符号整型。真正和当前平台CPU寄存器(机器字)宽度相当的数据类型,按照 C/C++ 标准的定义应当就是 int。在32位和16位平台上,这都是成立的。
但是到了64位CPU的时代,由于32位的int在绝大多数情况下都够用了,把它扩展到64位显得比较奢侈(特别是在移植广泛使用int数组的老代码时)。于是大家都保留了int为32位的定义。
现在严格保持机器字宽的数据类型,基本只有像 ptrdiff_t 这样跟指针运算相关的类型和 atomic_int 这样跟原子量相关的类型。
==================================
我说的那个 HANDLE 自然不是 MFC 里的,而是各种Create和Open系API返回的那种。
作者: jawbin
时间: 2006-5-28 17:29
对, 是 int. 但是即使 int 不再"生长", 还是摆脱不了含糊不清的状况.
在 Windows 系统中, 我几乎未听说过 handle 是真正的指针(指向一个可以直接 dereference 的内存), asbai 兄 能否示例一下?
作者: asbai
时间: 2006-5-28 17:34
HANDLE 在历代 sdk 里都被定义成 void* 或其等价形式,它本身是抹去了具体类型的对象 this 指针。MS甚至给出了每个Windows版本的C++代码行数,记得2K是1000万行。呵呵。
关于 HANDLE 的精确定义,参见sdk的winnt.h
作者: jawbin
时间: 2006-5-28 18:14
窃以为 SDK 中的定义仅仅是个幌子, 例如 hInstance 据说是个选择符, 不同的情况下实际上是不同的, 它统一"定义"为"HANDLE", 就是“抹煞“这样的差异,为内部实现和外部接口隔离起来,为发展考虑,同时,隔离也起了安全作用. 如果真正是一个简单的可用的数据的指针, 那么很容易被第三方(开发人员)的代码危及到内核,这是不允许的.另外,例如,一个对象也许内存重整而位置变化了,它的 handle 可以不变, 将内存管理向程序员透明化,简化代码的书写.
Last edited by jawbin on 2006-5-30 at 08:49 ]
作者: asbai
时间: 2006-5-28 18:16
Originally posted by jawbin at 2006-5-28 18:14:
窃以为 SDK 中的定义仅仅是个幌子, 例如 hInstance 据说是 DGROUP 的选择符, 不同的情况下实际上是不同的, 它统一"定义"为"HANDLE", 就是“抹 ...
嗯嗯,当然是不同的,不同的HANDLE是指向不同类型对象的this指针,相同的是它们都是指针,而且都是某个对象的this指针 <img src="images/smilies/face-laugh.png" align="absmiddle" border="0">
作者: jawbin
时间: 2006-5-28 18:19
在 Windows 中后来的确有 c++ 代码, 例如比较复杂的 COM 等可能就是用 c++ 的, 但是我说的是 "内核(Kernel, User, Gdi)", 通常外围组件不会象内核那样寸土必争。当然了, 后来这些"内核"中的部分也许仅仅是占位符样的东西了。
作者: jawbin
时间: 2006-5-28 18:25
若如 asbai 兄所说, 那么一个 HWND 就是指向一个窗体对象了, 通过它可以看到窗体的各个成员的. 当然这无法验证. 但是, 正如我前面所说, 如果在 c++ 上裹上 c 接口, 真是醉得不轻.
作者: asbai
时间: 2006-5-28 18:34
呵呵,User32和Gdi是Win32子系统中的组件,不属于 Kernel。
至于Windows 的 Kernel里面到底用了多少汇编、多少C和多少C++,这个问题实在很难说,毕竟Win32的源码是不开放的。上次派到MS总部去看Win32 源码的那些教授们回来也没写出点啥子名堂.......
不过按照MS的说法Kernel也貌似是用了大量C++代码。其实C++的大部分特性跟等价C相比并不损失时空效率。
一个简单的例子:实现一个FILE结构,然后定义一组对 FILE* 操作的接口-fopen、fread、fclose等等,和直接实现一个File类,其数据成员与FILE结构一致,其构造析构和其它操作与fopen、fclose、fread等对应..... 两者的实现效率至少是一样高的。在大多数编译器上,用C++效率要稍微高那么一点点,因为this指针保证是通过寄存器而不是栈传递。
这个针对libc的例子同样适用于OS Kernel

作者: asbai
时间: 2006-5-28 18:43
Originally posted by jawbin at 2006-5-28 18:25:
若如 asbai 兄所说, 那么一个 HWND 就是指向一个窗体对象了, 通过它可以看到窗体的各个成员的. 当然这无法验证. 但是, 正如我前面所说, 如果在 c++ 上裹 ...
为C++实现提供C接口的项目貌似很多。。。。。主要原因:
1. C接口兼容性最好,几乎各种程序设计语言都能够跟C接口互动
2. 大型软件项目,由于其体系结构复杂,参与人员众多,协作沟通困难等原因,有时必须使用C++中的很多特性划分和封装组件,否则项目实施会很困难。实际上在越大的项目中C++越能体现它的价值。
3. 为C++组件提供C接口唯一的代价就是在接口层多了一次函数调用的代价,这基本不会成为什么问题。但是正如前两条所述,C接口带来了广泛的语言兼容性,而C++使项目变得可行并且开发、测试和成本更低。
作者: jawbin
时间: 2006-5-28 18:58
所以,我说的“内核“是和 MS 命名的那个 kernel 模块是不同的,因为它这个 kernel 事实上并不能真正做什么,它的安全模式都需要 User 和 Gdi, 要说 command line 的安全模式,实在觉得和 Windows 距离太远, 还没看过那时候 kernel 在干什么,呵呵.所以我不说 Kernel,而是说“内核”,因为这个是真正能工作的, 而不是那个名不副实的东西.
仅仅为一个类提供基本操作(read, write 等等)和用 c 实现是没什么高下的, 但是通过 RTTI, 异常处理等等保驾护航之后, 以及后来看到的非常方便的 ToString 等等类似的“非常贴心“的小甜饼的装饰时候,就成了资源杀手,令人生畏.
不是说 c++ 提供 c 接口有多大问题, 仅仅是从“自视甚高“的 c++ 屈就出 c 接口来, 想必心中百味陈杂.
Last edited by jawbin on 2006-5-28 at 19:02 ]
作者: zyl910
时间: 2006-5-28 20:11
BIOS?若是做操作系统,我现在只相信硬件IO端口
面向对象技术中最有用的是接口,而不是(类)继承。我一直想在DOS下设计一个类似COM(通用组件模型)的东东,实现能多语言协作的面向对象开发,而且最好能实现实模式与保护模式的透明(太NB了,我设计不出来)。
类 = 结构体 + 虚函数表 + 编译器障眼法(语法)
面向对象是这样发展的:结构体指针 -> 句柄 -> 对象
所以我一直跟别人说Windows系统符合面向对象思想的
程序执行效率与编译器没多大关系(注意不是Java、.Net那样的解释器)
影响速度的主要是算法与数据结构,其次是硬件平台特性
至于汇编。如果没用MMX、SSE等并行指令集的话,一般的程序员写绝对没有优化的编译器产生的机器码好
以前在混一个论坛时看到一个人,他自以为水平很高,经常说XX语言不好
所以我用他最鄙视的VB写了个图像插值缩放程序
开始时他想当然的以为用vc写的肯定比我快,但是编译后发现速度比我的慢许多
然后他反复优化算法,最终决心用汇编重写核心代码,这才与我的差不多(没使用MMX,因为那时我俩都不懂)
自此他彻底服了,承认算法优化的重要性——居然 汇编优化的程序 跟 没有指针的VB差不多
(这件事使我认识到,如果汇编没使用MMX、SSE,千万别跟别人比速度)
我认为C++最有价值的 模板+多重继承,这才是真正现代的编译器技术
Last edited by zyl910 on 2006-5-28 at 20:15 ]
作者: asbai
时间: 2006-5-28 20:24
Originally posted by jawbin at 2006-5-28 18:58:
所以,我说的“内核“是和 MS 命名的那个 kernel 模块是不同的,因为它这个 kernel 事实上并不能真正做什么,它的安全模式都需要 User 和 Gdi, 要说 command l ...
jawbin此言。。。。让人冒汗啊,kernel本来就是完成基本的核心OS操作的,比如内存管理、进程调度等等。任何一个OS都是如此,你说的“真正做什么”的东西实际是基于kernel的shell而已,linux/unix不也是这样吗?unix的kernel照兄台的标准也是“什么都不能做”的,真正跟用户交互的当然是shell拉,只不过linux的shell是bash,很多unix的shell是ksh或csh,而windows的shell是GUI的Win32子系统而已。没有硬件,何来kernel?没有kernel何来shell?
同理,安全模式一样要何用户交互,当然也要有shell拉
作者: zyl910
时间: 2006-5-28 20:47
从Win98开始,windows的shell实际上是IE
作者: jawbin
时间: 2006-5-28 20:47
先来回 asbai 兄, 我说的也不是 shell, 在 windows 4.x 至少可以指定 shell, 缺省的是 explorer. 而是一个 GUI os 的基本服务, 如果把 windows 等同与 non-GUI os, 那我就无话可说了.
作者: asbai
时间: 2006-5-28 20:55
BIOS?若是做操作系统,我现在只相信硬件IO端口
■ 有专门的驱动或者能够自己写针对专门的硬件驱动当然好,否则很多时候也只能用相对低效的BIOS啦。
面向对象技术中最有用的是接口,而不是(类)继承。我一直想在DOS下设计一个类似COM(通用组件模型)的东东,实现能多语言协作的面向对象开发,而且最好能实现实模式与保护模式的透明(太NB了,我设计不出来)。
■ OOP里到底什么最有用,是C++社区中永恒的话题,这个很难说清楚。当然封装和接口是非常有用计数概念。COBRA和COM都有DOS实现的开源项目,而且不止一个,呵呵。
类 = 结构体 + 虚函数表 + 编译器障眼法(语法)
■ 类等于什么应该取决于具体应用才是,最简单的时候等于结构+相关函数;有时等于虚表+结构+相关函数;有时虚表+结构+相关函数+typeinfo信息;有时虚表+虚基表+结构+相关函数+typeinfo;还有时。。。。。。。
面向对象是这样发展的:结构体指针 -> 句柄 -> 对象
所以我一直跟别人说Windows系统符合面向对象思想的
■ 完全同意
程序执行效率与编译器没多大关系(注意不是Java、.Net那样的解释器)
影响速度的主要是算法与数据结构,其次是硬件平台特性
■ 算法和体系结构当然对代码效率有莫大的影响,不过不能否认的是编译器优化也是非常重要的。例如:即使是现代CPU,整数除法也是很慢的,在P4上大概也需要超过60个CPU时钟。但是乘法和位运算却非常快,很多编译器支持用乘法和位运算完成快速除法和取模。
■ 另外由于现代CPU的流水设计,它对分支语句非常敏感。特别是循环中出现的分支,可以轻易地浪费掉上千CPU时钟。很多优化编译器有办法使用特殊的汇编指令有效消除这种分支。
■ 此外还有消除数据相关性、子表达式优化、多分支优化、高速缓存和内存子系统访问优化等等上百种非常有效的编译器优化技术。可见目标代码的质量还是与编译器非常有关系的。
至于汇编。如果没用MMX、SSE等并行指令集的话,一般的程序员写绝对没有优化的编译器产生的机器码好
■ 优秀的汇编程序员确实可以写出比最优秀的优化编译器产生的代码快很多的等效汇编实现,并且仅使用编译器会用到的那些汇编指令。这方面的理论和大量实例比较可以google一下 Kris Kaspersky 和 Agner 这两位大师所写的著作。
以前在混一个论坛时看到一个人,他自以为水平很高,经常说XX语言不好
所以我用他最鄙视的VB写了个图像插值缩放程序
开始时他想当然的以为用vc写的肯定比我快,但是编译后发现速度比我的慢许多
然后他反复优化算法,最终决心用汇编重写核心代码,这才与我的差不多(没使用MMX,因为那时我俩都不懂)
自此他彻底服了,承认算法优化的重要性——居然 汇编优化的程序 跟 没有指针的VB差不多
(这件事使我认识到,如果汇编没使用MMX、SSE,千万别跟别人比速度)
■ 优化的层次应该是:体系结构 > 算法 > 细节优化。
■ 其中细节优化是指消除指令相关、控制相关等影响CPU流水的问题;优化高速缓存和内存访问效率;以及等等类似的细节问题。
■ 用汇编写的冒泡排序也不可能比用Java写的快速排序更快,如果在使用同样算法的前提下,一个汇编程序员写出来的代码比它的C等效还差,那只能说明他不适合做一个汇编程序员
我认为C++最有价值的 模板+多重继承,这才是真正现代的编译器技术
■ 这又是一个C++社区中典型的永无止境型争论话题

作者: jawbin
时间: 2006-5-28 20:55
zyl910 兄 只相信 IO 端口...看来也很相信集成电路引脚...昏迷in....
支持多语言? 为什么要背这个包袱呢? 应该由多语言的应用遵循你的接口规范就可以了吧.
实模式 和 保护模式透明? 那只好下面打底, 才能让上层跨越. 但是"真正的"透明, 可能完全不需要努力,仅仅使用实模式的功能就可以了,在保护模式里可以兼容或者使用实模式虚拟机. 一旦用到保护模式特有的东西, 实模式下无法用, 那就透明不了了.
模板是个编译时泛型, 是个方便的工具, 但是我同样担心无所顾忌的"盲目滥用". 当然咯,我对模板其实不太熟悉,哈哈.
Last edited by jawbin on 2006-5-28 at 21:10 ]
作者: asbai
时间: 2006-5-28 21:00
Originally posted by jawbin at 2006-5-28 20:47:
先来回 asbai 兄, 我说的也不是 shell, 在 windows 4.x 至少可以指定 shell, 缺省的是 explorer. 而是一个 GUI os 的基本服务, 如果把 windows 等同与 non-GUI os, 那我尠...
从OS的角度看,兄台认为Windows和Uinx有什么本质区别吗?一样是基于完全相同的操作系统原理构建的,呵呵。
只不过Windows的GUI是默认shell,而unix上的X+桌面管理器通常不设置成自动启动而已。还有,Windows的GUI确实比基于X11的实现效率高不少,但也没什么本质不同,呵呵。
作者: jawbin
时间: 2006-5-28 21:08
但对它的实际运用来讲, 很有区别.
Windows 仿佛就是 Macintosh 的一个 " clean rooming" 出来的"开放版", 意在"用户友好", 一蹦出来就 GUI, 而 *nix 通常认为是研究人员使用的, 当然后来开发了强大的 GUI (仍有很多问题), 但是即使至尽, 还有很多人以使用 GUI 为耻, 以不使用 GUI 为荣...反正一些高手的意志是坚定的. 当然, 有时候 konsole 是很方便的并且占用很少的资源, 我尤其喜欢虚拟控制台.
为了人民大众,或者为了反对霸权,各linux 厂商终于争相开发 GUI, 然后所有的 OSs 看上去都差不多了。
作者: asbai
时间: 2006-5-28 21:13
还想补充一下,记得 Kris 曾经在他的著名论述中,详细比较过经过完全优化的各种常用算法(诸如:快速排序,遍历和搜索,内存拷贝,快速傅立叶等等)与手工汇编等效代码效率之间的比较。
他使用了当今流行的几种C编译器,最终的比较结果是令人印象深刻的。最接近的性能差距也在50%左右。某些算法甚至会相差几倍。
作者: jawbin
时间: 2006-5-28 21:13
从zyl910 兄的经历来看, RAD 有时候不仅增加开发速度,也不输运行速度,瞧我这中文,听起来别扭..
作者: asbai
时间: 2006-5-28 21:15
Originally posted by jawbin at 2006-5-28 21:08:
但对它的实际运用来讲, 很有区别.
Windows 仿佛就是 Macintosh 的一个 " clean rooming" 出来的"开放版", 意在"用户友好", 一蹦出来就 ...
兄台在说的是用户对OS的感觉差异啊。。。。。小弟一直说的是OS之间在体系结构等技术上的差异。。。完全走岔~ <img src="images/smilies/face-wink.png" align="absmiddle" border="0">
作者: jawbin
时间: 2006-5-28 21:16
当然了,从另一方面讲,如果利用商业开发工具只能开发出糟糕的工具,必然有人写出更好的工具.开放的和平等竞争的市场是不会倒退的.
作者: jawbin
时间: 2006-5-28 21:32
asbai 兄, 就内部结构上, 我记得除了那三个模块最核心之外(可曾见过只有 kernel 的 windows 在运行?),而且,这三个模块之间也并不是优美的层间依赖关系,而是稍微有点点复杂(但是可能比蜘蛛网简单一些,呵呵),甚至互相引入对方的引出符号. 而且后来,一些符号也从一个模块移动到另一个模块了(这之中之一应该有 kernel 模块).
作者: asbai
时间: 2006-5-28 21:36
Originally posted by jawbin at 2006-5-28 21:32:
asbai 兄, 就内部结构上, 我记得除了那三个模块最核心之外(可曾见过只有 kernel 的 windows 在运行?),而且,这三个模块之间也并不是优美的层间依赖关系,耠...
嗯?这么说兄台发现ntoskrnl.exe引用了user32或者gdi模块 export出去的接口?小弟从不曾在那个版本的windows上发现这个问题,呵呵。至于user32和gdi之间相互引用那是很正常的,它们都会需要kernel提供支持这就更正常了,不是吗?
作者: jawbin
时间: 2006-5-28 21:40
不, ntoskrnl.exe 的模块名应该不是 kernel, kernel32.dll 的才是, 所以我说后来他们部分可能仅仅是占位符了, 就是因为 ntoskrnl 来了, 至于 kernel 模块, 可能会有, 我回去查查, 但是, 当然即使有也非常少.
作者: asbai
时间: 2006-5-28 21:51
呵呵,kernel32.dll 只是为了Win32子系统编程方便而封装的系统调用接口,它本身是完全跑在用户模式的,和真正的OS Kernel还差很远,中间隔着 ntdll 和 sspt。
真正的kernel是包在ring 0的,只能通过sspt定义的接口访问到,sspt是真正的user mode(ring 3)到OS Kernel(ring 0)直接的界面,2K上的sspt是通过INT 23H实现,后来据说因为P4对INT指令支持效率太差,XP上换成sysenter指令了。
kernel32.dll只跑在user mode,它是Win32子系统的一个模块,它向下调用ntdll,ntdll负责访问sspt调用真正的kernel service。
兄台这次火星啦

作者: jawbin
时间: 2006-5-28 21:54
在以前的 Windows 中, 据 未公开的 Windows 核心技术 这本书讲, Kernel 模块使用了 User 中的至少下述符号:
IsUserIdle, GetFocus, IsWindow, GetWindowTask,PostMessage.
在这里,可能还遗漏了其他的通过 GetProcAddress 来引入的符号.
当然它也讲了,这不是什么丢人的事,罗马不是一日建成的.
另外我在几年前测试的时候发现了这样一个有趣的现象, 那就是似乎 win16 比较"宽容", 不大小些敏感, 当使用 rundll 的时候, 这个特性更觉得有趣.
Last edited by jawbin on 2006-5-28 at 22:02 ]
作者: jawbin
时间: 2006-5-28 22:01
asbai 兄, 我是从来没把 MS 声称的 kernel 当回事(当成真正的核心), 因为它并不名副其实, 所以我都是用它的官方术语或者字面来描述.
那么显然, 在 Windows 模块中, 如果要找出名字为 "Kernel" 的(具体来说,就是用GetModuleHandle 能取得 handle 的那个), 当然只有 krnl32.dll 了. 多年前, 恐怕 hal.dll 都不存在, 我们不能拿变动的来说事是吧. 例如中国可能已经这么多年是 P. R. China 的简称了, 说到它, 我们未必说, 它在若干年前是战国, 在若干年后又是元朝. 后来又有两部分,未被统一的一部分,和虎视耽耽妄图统一的一部分...
Last edited by jawbin on 2006-5-28 at 22:08 ]
作者: asbai
时间: 2006-5-28 22:13
晕。。。完全没看明白兄台在说什么~
我刚刚说的Windows体系结构从NT时代开始就一直是这样了,难道兄台一直在跟我讨论的Windows是指Win3.x或Win9x ?
PS:能用GetModuleHandle取得handle的那个肯定不可能是OS Kernel,呵呵
Last edited by asbai on 2006-5-28 at 22:15 ]
作者: jawbin
时间: 2006-5-28 22:18
我讨论的正是史前时代,哈哈!
以后的话,可能想大踏步地迈进"共产主义"时代, 到 Vista 梦幻乐园吹吹风...
另外我讲的就是字面, 呵呵, 它这么叫, 名不副实不是我的错, 哈哈!
Last edited by jawbin on 2006-5-28 at 22:19 ]
作者: jawbin
时间: 2006-5-28 22:26
想不到居然这么晚了, 休息咯!
作者: zyl910
时间: 2006-5-29 10:33
//从zyl910 兄的经历来看, RAD 有时候不仅增加开发速度,也不输运行速度,瞧我这中文,听起来别扭..
嘿嘿
那个是图像插值缩放程序
自己编码实现图像插值缩放程序
从VB5开始,VB程序能编译成本机代码,而且编译优化也做得不错
跟C/C++编译器处在同一级数,唯一就是缺乏指针语法
注意不是现在的Java、.Net那样的解释器
我由于用SoftICE跟踪过VB程序
特别是看过Matthcw Curland写的《Advanced Visual Basic》(此人被微软请去开发VB编译器,此书专写VB编译器细节,不懂Win32汇编和底层COM原理的人绝对看不懂。所以我很怀疑此书还算作VB书,简直是编译器实现原理)
了解许多VB编译器细节
在写那个程序时,先将算法优化到极致,尽量减少运算量
在计算坐标时,根据Bresenham算法思想,完全使用整数加减实现坐标计算
在计算插值时,将那些乘法优化成查表,避免了浮点运算,精度高速度快
整个程序基本上是由整数加减与查表实现的,只有少数几个整数乘法
很多人在优化程序时
只注意高层算法优化 及 指令细节优化
完全忽视了整数运算、位运算优化 和 查表优化
我与他的算法已经优化到极致了
但他忽视了整数运算、位运算优化 和 查表优化
乘法指令 绝对比 整数加减、数组查表 慢
就算你考虑了细节优化也一样
只有使用MMX、SSE等并行指令才能摆脱这尴尬局面
可能就是因为现在有MMX、SSE等并行指令集
在MMX等并行指令集 下 无法查表优化
再者那些老外大多从计算机早期搞起的,位运算、查表 已经滚瓜烂熟,觉得再提这些东西没什么意思
在现代的 超流水线、超标量 处理器 中,指令序列会影响速度
所以老外强调这些东西,也就是MMX等并行指令集下的优化技巧
而国内很多人只关注前沿,并没有注意其边界,忽略了很多基础东西
以为 跟上了前沿的自己 水平很高
(如果当年中国没那么快转向Windows平台,而是扎扎实实的在DOS平台下学习技术,有可能软件业不会是现在这个样子)
//在以前的 Windows 中, 据 未公开的 Windows 核心技术 这本书讲, Kernel 模块使用了 User 中的至少下述符号:
//IsUserIdle, GetFocus, IsWindow, GetWindowTask,PostMessage.
//在这里,可能还遗漏了其他的通过 GetProcAddress 来引入的符号.
这个是必须的
我曾经想过自己在DOS下模拟Win32API体系,发现内核无论如何都得使用某些非内核函数
其实最难处理的虚拟内存管理
内存交换文件是存放在磁盘上的
而由于文件系统的多种多样,所以一般做成可动态加载的驱动程序
而动态加载驱动程序又得使用虚拟内存管理
鸡生蛋、蛋生鸡……
// 我是从来没把 MS 声称的 kernel 当回事(当成真正的核心), 因为它并不名副其实, 所以我都是用它的官方术语或者字面来描述.
kernel32.dll(或Windows早期的kernel.exe)只是 内核API 接口,并不是操作系统内核
而单独的 kernel 这个词代表内核,具体的实现细节未知
Last edited by zyl910 on 2006-5-29 at 10:47 ]
作者: jawbin
时间: 2006-5-29 10:50
由于 PC 硬件现在已经很好了, 而且还继续, 所以"开发越来越容易"也就不足见怪了, 通常都不需要特别注意资源的使用和运行效率. 但是开发工具的生产者肯定会多少关注这些.
象 zyl910 兄这样追求完美的人, 现在是很少有了.
另外若真正开发一个 OS 的话, 势必应该分出驱动层, 但是面对众多的硬件, 自己写驱动恐怕都是"不可能的任务", 但是别人写的, 你又信得过吗? 而驱动会影响 OS 的性能甚至稳定性,健壮性等等. 难度不可谓不大.
作者: jawbin
时间: 2006-5-29 10:54
在我前面的上下文中, Kernel 是指 MS Windows 中的名称为 "KERNEL" 的模块, 而中文"内核"或"核心"才是一个普通应用(我通常从统计学的角度来使用普通这个词汇, 具体的说, 就是多数, 甚至绝大多数)所直接依赖必不可少的部分.
这样就很混淆,哈哈.
作者: jawbin
时间: 2006-5-29 10:58
从超星没查到 Advanced Visual Basic, 作者好象就是讲 VB 指针的那位高人. 可惜从没仔细拜读.
作者: jawbin
时间: 2006-5-29 11:06
另外在 dos 下模拟 Win32 仿佛可以先不用 zyl910 兄做了, 已经有了. 而且, 在我后来的想法里, 不如搞一个"跨平台"样的, 而不仅仅象一个 "DOS 下的 Windows 虚拟机". 而且, 事实上, 如果能完全实现 Windows 的功能, 那么和 Windows 有何区别? 使用比 Windows 更少的资源, 优化得更多? Windows 的开发花费了那么多人力, 一个人要完全实现, 实在不可想象. 现成的模拟有 ReactOS, 另外郭恒也搞了个有点点类似的东西, 但是是基于 DOS 的. 在这个版能找到他的帖子, 里面有地址可以下载到, 是主要针对嵌入式系统的.
作者: jawbin
时间: 2006-5-29 11:19
Originally posted by zyl910 at 2006-5-29 10:33 AM:
而由于文件系统的多种多样,所以一般做成可动态加载的驱动程序
而动态加载驱动程序又得使用虚拟内存管理
鸡生蛋、蛋生鸡……
如果要"考虑周全", 有时候系统是非常复杂甚至无法实现的吧.
所以有时候可能只需要考虑"大多数"就可以了. 例如实际情况中,可能所使用的文件系统就那么几种,只要先支持这几种.
早期的 DOS 启动遇到类似的问题好象是指定物理位置来解决. 总得有个开始, 才能继续. 不管是看起来多么糟糕的开始.
作者: asbai
时间: 2006-5-29 11:36
Originally posted by jawbin at 2006-5-29 11:19:
如果要"考虑周全", 有时候系统是非常复杂甚至无法实现的吧.
所以有时候可能只需要考虑"大多数"就可以了. 例如实际情况中,可能所䠮..
一步一步来是没办法的事,但是一开始总要把能想到的问题尽可能想全,留出可扩展的框架便于以后改进。
另外,驱动的问题在PC平台是除了Windows以外所有OS都头痛的问题Windows有一线厂商的全面支持和自己的驱动可靠性认证程序。。。。有钱就是爽,呵呵。
不过linux和freebsd的驱动库稳定性和全面性也都不错,这些OS本身又是开源的,可以考虑做一个linux/freebsd的驱动适配层,直接用它们的驱动。
犹他大学搞得OSKit就是这样的,该kit本身体积也很小巧,还内置了COM接口、pthread库各种功能乃在艰苦环境下开发嵌入式环境的居家旅行、野外打鸟必备佳品 <img src="images/smilies/face-smile-big.png" align="absmiddle" border="0">
作者: jawbin
时间: 2006-5-29 12:11
哇, asbai 兄又带来了好工具,收藏!
但愿最后不要变成只是盲目收藏而不懂欣赏的可悲下场。。。好东西太多了,恐怖了。。
Last edited by jawbin on 2006-5-29 at 12:13 ]
作者: jawbin
时间: 2006-5-29 12:17
我也想过在不同的情况下都有“移民局”,source级,二进制级。。专门负责外来模块的接待和转化工作<img src="images/smilies/face-raspberry.png" align="absmiddle" border="0">
但是支持的种类越多,就越庞大,而且可能与总体设计思想偏离得更多,还可能性能不佳。。。
Last edited by jawbin on 2006-5-29 at 12:18 ]
作者: jawbin
时间: 2006-5-29 12:27
Originally posted by asbai at 2006-5-29 11:36 AM:
一步一步来是没办法的事,但是一开始总要把能想到的问题尽可能想全,留出可扩展的框架便于以后改进。
另外,驱动的问题在PC平台是除了Wind ...
asbai 兄误会了哦,我指的是系统启动的开始,而不是系统设计的开始 :P
作者: jawbin
时间: 2006-5-29 13:25
娱乐一下(来自网上):
今天我接到由三星公司来的信,说我中了一等奖~爱丽舍家庭轿车一辆,要不是我查了网站才知道是骗局差点惨了!我才读中学二年级,他们都骗,这还是人吗?
代骂业务:
业务介绍,通过国际网络电话,采用最先进自动骂街技术,包含50多种方言,1000多条骂人术语。
<img src="images/smilies/face-smile-big.png" align="absmiddle" border="0"><img src="images/smilies/face-smile-big.png" align="absmiddle" border="0"><img src="images/smilies/face-grin.png" align="absmiddle" border="0"><img src="images/smilies/face-grin.png" align="absmiddle" border="0">
Last edited by jawbin on 2006-5-29 at 16:25 ]
作者: zyl910
时间: 2006-5-29 19:19
Originally posted by jawbin at 2006-5-29 10:58:
从超星没查到 Advanced Visual Basic, 作者好象就是讲 VB 指针的那位高人. 可惜从没仔细拜读.
书评:
http://dev.csdn.net/develop/article/13/13308.shtm
VB和COM的宝书
Matthcw Curland著,涂翔云等译,《高级Visual Basic编程》(Advanced Visual Basic)。中国电力出版社,2001.5,¥55.00
《高级Visual Basic编程》(Advanced Visual Basic)
作者 Matthcw Curland著 涂翔云等译
出版社 中国电力出版社
介绍
本书是微软Visual Studio的资深专家Matthew Curland多年VB开发经验的总结,着重于解决VB程序员编程时所遇到的困难。由浅入深,循序渐近地讲解了如何开发标准的VB代码、高级编程技巧。以及如何通过一些新算法的使用提高编码效率和性能。附带的光盘提供了书中内容的相关代码,稍加扩展即可获得用户自定义类型、轻量对象系统、创建定制窗口以及函数重载等功能。盘中还提供了三个功能强大的类型库,对VB中使用的和由VB产生的类型库进行了修正。
本书适合高级程序员阅读,也可供专业计算机人士参考。
正文
目录
第一章 构建块
1.1 虚指针
1.2 活指针
1.3 VBoost对象
第二章 使用数组
2.1 数组描述符
2.2 读取数组变量
2.3 写入到数组变量
2.4 数组选项:超出固定或可变字长
2.5 使用数组的一些小提示
第三章 IUnknown接口:一个未知量
3.1 VB和IUnknown接口
3.2 声明IUnknown并调用它的函数
第四章 绑定函数到对象上
4.1 何时绑定对象
4.2 运行时间的名字绑定
4.3 VTable绑定用户定制控件接口
第五章 对象的设计结构
5.1 使用Implements来实现抽象
5.2 调用代码的可插入性
5.3 实现和实现重用
5.4 聚合
5.5 聚台现存的对象
第六章 循环引用
6.1 中间对象解决方案
6.2 弱引用和集合
6.3 转移对象所有权
6.4 层次化对象模型
第六章 外部对象的创建
7.1 使用类工厂进行对象的创建
7.2 直接加载DLL对象
7.3 自定义加载定制化控件
7.4 定制类对象
第八章 轻量COM对象
8.1 关于轻量的基础知识
8.2 结构终止代码
8.3 LastIID的轻量版本
8.4 ArrayOwner的轻量版本
8.5 接口位于何处
8.6 错误的产生及避免
8.7 从轻量对象返回错误
8.8 聚合轻量对象
8.9 编制Query lnterface函数
第九章 大型多对象系统
9.1 使用定长内存管理器
9.2 Scribble示例
第十章 VB对象和运行对象表
10.1 在ROT中注册VB对象
10.2 ROTHook实现细节
第十一章 函数指针的调用
11.1 示例:调用DLLRegister Server
11.2 示例:QuickSort,一劳永逸
11.3 Alpha中的VB函数指针
11.4 堆栈分配
11.5 产生自己的内联汇编
11.6 类函数指针
11.7 使用CDECL函数
第十二章 重载函数
12.1 协作重定向
12.2 接口封装
12.3 瘦接口封装
12.4 封装中的一些问题
第十三章 VB中的线程
13.1 线程中的局部存储
13.2 能否避免排队开销
13.3 线程化或非线程化
13.4 在客户机EXE中创建线程
13.5 STA单元中Coordiate Gate的崩溃
13.6 在DLL中创建工作线程
第十四章 VB中的字符串
14.1 UNICODE转换
14.2 字符串的分配
14.3 作为数值的字符串
第十五章 类型库和VB
15.1 VB产生的类型库
15.2 VB友好的用户定制类型库
15.3 二进制兼容性
15.4 后期构建类型库的修改
第十六章 控制窗口
16.1 子类化
16.2 自定义窗口的创建
16.3 无窗口的控件
附录 VBoost参考
作者: asbai
时间: 2006-5-30 15:21
Originally posted by jawbin at 2006-5-29 13:25:
娱乐一下(来自网上):
今天我接到由三星公司来的信,说我中了一等奖~爱丽舍家庭轿车一辆,要不是我查了网站才知道是骗局差点惨了!我才读中学二 ...
十分渴望学习一下那1000多条骂人术语~~ <img src="images/smilies/face-wink.png" align="absmiddle" border="0"><img src="images/smilies/face-wink.png" align="absmiddle" border="0">
作者: jawbin
时间: 2006-5-31 18:40
多谢两位仁兄今日相助, 非常感激!
不过最近我可能很少上来了,因为失业了.
后会有期.
顿首再拜,哈哈!
作者: asbai
时间: 2006-5-31 19:46
那太遗憾了,敢问jawbin兄哪里人?
作者: jawbin
时间: 2006-6-1 18:09
asbai 兄, 小弟家在甘肃, 目前在湖北. 今晚还得去赶紧收拾东西, 哎, 老板逼人太甚...
作者: asbai
时间: 2006-6-1 19:13
只希望以后能借东风之便,常听兄台佳音了。
作者: jawbin
时间: 2006-6-8 19:25
谢谢 asbai 兄, 今日回来看看
