Bill's profileBinary boy's lifePhotosBlogLists Tools Help

Bill Xie

Occupation
Location
Interests
Photo 1 of 63

Windows Media Player

Binary boy's life

Computer science is no more about computers than astronomy is about telescopes
9/7/2009

日本西部用心棒和意大利西部牛仔

因为先看过《美元三部曲》中的第一部《荒野大镖客(A fistful of dollars)》,所以再看到《用心棒》的时候,第一感觉就是,不是黑泽明在抄袭就是索吉欧·里昂(Sergio Leone)在抄袭。因为两者的情节几乎完全一样,只是场景和表现的方式上有所不同。

 事实上,是里昂抄袭了黑泽明,而黑泽明则是改编自意大利的民间故事《一仆二主》。

 《用心棒》于1961年发行后,黑泽明并没有赚到太多的钱。当1964年《荒野大镖客》发行后,黑泽明给里昂写了封信,说“这是部好电影,但是,这是我的故事”。里昂得知黑泽明夸他之后,非常高兴,认为得到了大师的认可,但是紧接着黑泽明的律师就来了(囧)。最后,大家达成了庭外和解,黑泽明得到了《荒野大镖客》的远东版权,而不再追究侵权。于是,黑大师赚到了一生中最大的一笔钱,“从此更加心无旁骛,专心投身电影艺术研究”(从此过上幸福的生活?)。

 《用心棒》拍摄的时候还是黑白片,但是黑大师的表现能力并没因此受到限制(事实上,黑泽明成名于黑白片)。浪人走到一个分叉路口,不知道往那边走,笑着拿起一根树枝往天上一扔,树枝指向那边就朝哪方走。然后,音乐响起,整个电影轻松愉快的开场。而浪人向一家村民要水喝时,村民的儿子和父亲争执,要加入黑社会也不愿意当农民只能喝粥,观众开始有不详的预感。当浪人走进小镇,空无一人的街道上,一只小狗叼着一只人手跑过,恐怖的感觉到达了顶点。这是部恐怖片吗?当然不是。浪人挑动小镇两派恶势力决战,在决战之际,清兵卫家的教头临阵脱逃,翻墙时,回头看见浪人,相视会心一笑,跑远了,还相互招招手,让人忍俊不禁。而浪人教育混黑社会的小P孩那个场景,昆汀(Quentin Jerome Tarantino)在多年后的《杀死比尔》中也用乌玛打青蜂组小P孩的PP向其致敬。“这真是一个好地方,杀人能赚钱,而且杀得都是败类”----第一次看到这样“邪恶和精于算计的”三船敏郎。

 《荒野大镖客》拍摄时已经是彩色时代了,天才的里昂,黑泽明的故事,再加上莫里康(Ennio Morricone)的音乐制作,伊斯特伍德(Clint Eastwood,当时只是一个无名小白脸)的主演,近乎顺理成章成为了一部划时代的作品。但是,其所表达的立场仍然没有变化,在影片的结尾,当镇上的居民感谢牛仔惩罚了邪恶,保卫了美国时,牛仔艰难爬上马,说“美国在这边,墨西哥在那边,而我,在中间”。

 这种立场后来大概被称之为“牛仔精神”,也因此出现了大量的拙劣模仿品,也就是好莱坞商业西部片泛滥的时代。但是,里面的牛仔已经堕落为“不吃不喝,金刚不坏,百步穿杨。少女守护者,正义的化身,邪恶的天敌。”

 时至今日,黑泽明、里昂、三船敏郎都已经先后去了天堂,莫里康和伊斯特伍德也都是八旬高龄。其中伊斯特伍德从成功的演员转型为成功的导演,一直保留着这种牛仔精神,在他的《老爷车》中,更是在城市中用生命最后“亦正亦邪”了一把。迟早,上帝会把他们全部都召唤去,这几个老牛仔到时聚在一起,又会鼓捣出什么新的电影出来呢?

9/2/2009

指环王和精灵宝钻

伟大的语言学家和文学家J.R.R.托尔金打算写《指环王》的时候,是因为发现很多种语言都有自己的史诗神话传说,而英语,则没有。于是,他决定要结束这种情况。
 
按照《精灵宝钻》译者邓嘉苑的说法,托尔金参照圣经旧约写下了《精灵宝钻》,参照新约写下了《指环王》(译者本身是一个基督徒)。但是圣经毕竟是“1600多年间,由40多个作者”写成的。即便如此,精通古欧洲语言的托尔金还是自创了20多种语言,而《精灵宝钻》甚至“完全翻译自精灵的语言”,毕竟相当一部分是讲述的人类不能涉足的“蒙福之地”的故事。
 
《精灵宝钻》的时间从宇宙创造开始,到第三纪的结束,而《指环王(包含哈比人历险记)》则是第三纪最后几十年的故事。整个故事架构据说从第一次世界大战,托尔金在战壕中的时候开始构思,在医院里面开始动笔。《指环王》在20世纪50年代中期开始陆续出版。而时间跨度大得多的《精灵宝钻》则直到1977年,托尔金去世后四年,才由他的儿子整理出版,可以毫不夸张的说,“穷尽一生”。如果万能的“唯一之神”伊露维塔赐予托尔金哪怕只有全盛期的努曼诺尔人那样短短几百年的寿命,无疑,那将是一部远远超越人类一切现有文学作品的巨著(包括圣经 )。
 
如果只读《指环王》,而不读《精灵宝钻》,会觉得有很多疑惑。他们怎么来的?谁是伊兰迪尔?“Into the west”?他们究竟去了什么地方,他们以后会不会“幸福的生活下去”?
 
当然,《精灵宝钻》“说的是一则悲伤的堕落故事;美好的事物堕落衰亡,不论是造物者还是旁观者,内心的遗憾与难过都是难以言喻的。---邓嘉苑”。凡是美好到极致的事物最终都堕落了,从埃奴(次神)中最强大的米尔寇开始,然后是极度智慧和美丽的诺多精灵,最后是强盛的努曼诺尔人。在《指环王》中,托尔金最后给出的希望是哈比人,一种很不起眼,并且乐于被忽视的人类,力量小、胸无大志、贪图吃喝,但是善良、忠诚、敢于牺牲、乐观以及永远抱有希望。
 
不管,你只是打算看床头故事,还是去考古故事背后的故事,或者去研究托尔金,这都会给你带来很多的乐趣。
6/26/2009

MJ, Heal The Heaven...!!

MJ, Heal The Heaven...!!
6/11/2009

绿霸背后的裸奔

1、低劣的软件:

-随时导致系统运行错误
-任意封杀网站(包括新华社旗下网站)
-密码系统完全不保护,一秒就可破解
-面子上的色情保护,要么误报,要么无动于衷
-要命的系统更新技术,有导致DNS崩溃的可能

这样的软件一年使用费4170万,这不是腐败,什么才是腐败?

2、不可告人的手段:

-和病毒一样安装全局钩子和驱动保护
-每3秒截屏一次
-封锁翻墙软件的端口
-强制过滤色情暴力无关的关键词 

这就是号称的“不监控”,“符合国际惯例” 

如果这都不算裸奔,那什么才是裸奔?非要搞得和朝鲜一样,才算裸奔?!!!! 

相关链接:

http://bbs.ifeng.com/viewthread.php?tid=3724111### 

传说中的逆天神器“绿坝-花季护航”试用手记

http://www.techweb.com.cn/news/2009-06-11/410544.shtml

“绿坝”过滤频频出错 工信部强制推广遭质疑

http://blog.sina.com.cn/s/blog_4701280b0100dlh2.html

绿坝系统提醒你,以下内容包含不良信息_韩寒_新浪博客

4/23/2009

VC中创建线程分析

文章作者:Bill Xie   自由转载,但请注明作者和原始出处!
================================
1、CreateThread、_beginthreadex、AfxBeginThread的区别和正确使用:
 
CreateThread是一个Windows 的API函数,_beginthreadex是一个微软VC中C运行时库中的线程创建函数,AfxBeginThread则是MFC中的线程创建函数。
其依赖关系为:<--表示被依赖
 
CreateThread <--_beginthreadex
CreateThread <-- AfxBeginThread
 
_beginthreadex为每个使用线程在Heap上创建(用__calloc_crt,相当于calloc)了一个tiddata结构并且设定到动态TLS。这样C运行时库中使用静态变量的几个函数就可以得到只和线程相关的一份“静态”和“全局”变量了,C运行时全局变量也不会互相干扰。假若使用CreateThread创建了这同样的线程,因为没有事先分配这个tiddata结构,那么后果自然就很严重了。
 
但是使用了_beginthreadex也有一个不算麻烦的小麻烦。
 
线程最好的退出方式是从return退出,所有的资源都会正确的释放,如果调用ExitThread退出线程,那么线程堆栈上的内容被自动清除,但是C++对象不能够通过调用析构函数而正确的清除。而和_beginthreadex对应的_endthreadex恰恰就是调用了ExitThread来终止线程。所以,这下_endthreadex也不能调用了。那么在前面分配的tiddata怎么办?没有任何办法,除非去调用_endthreadex才会得到释放,但是ExitThread语句又紧跟在后边。还好,tiddata结构非常小,不超过100个字节,但是要是遇上傻大黑粗的野蛮程序员,在进程生命期中反复创建线程,也挺恐怖。
 
所以最好的做法就是,在线程中,避免使用C运行时函数,用C++运行时库或者Windows API解决问题。这样就可以避免使用_beginthreadex,而用CreateThread创建线程,也不会有tiddata结构的运行时内存泄漏了。(或者,不在线程中使用C++对象,这样就不怕_endthreadex的ExitThread 了。 吐舌 馊主意!不过_beginthreadex本来就是为使用C函数的线程准备的。)
 
至于AfxBeginThread,是MFC中定义的一个函数,如果你使用MFC框架,那么应该使用这个函数,这样更符合框架的设计目的。这个函数内部的实现就是通过CreateThread,因为既然都使用MFC了,还费劲使用C函数库干嘛?
 
 
2、CreateThread的不知道算不算Bug的Bug:
 
CreateThread的定义请参见不同版本的MSDN。
 
该API函数的第2个参数 dwStackSize 以及 第5个参数 dwCreationFlag 分别指定了线程堆栈的大小和创建标志。
 
按照文档中的说明,dwCreationFlag有一个设定值为STACK_SIZE_PARAM_IS_A_RESERVATION。这个标志的含义是,将指定大小的堆栈在虚拟内存中分配,并且初始只映射系统缺省页数的物理内存(大概是两个页),随着堆栈使用的增大,系统根据堆栈物理内存页后方的设定为PAGE_GUARD属性的页触发来把更多的物理内存映射到后续的页。
 
值得注意的是,STACK_SIZE_PARAM_IS_A_RESERVATION这个标志只在Windows XP以及Windows 2003以及更新的系统上才会得到支持。如果没有设定这个标志,并且也让线程一创建就开始运行(大部分时候是这样),那么系统会参照dwStackSize中指定的堆栈大小,并参照内存分配最小粒度以及页对齐原则,把一定大小的物理内存映射给堆栈。
 
看上去一切都很不错,但是如果你尝试创建一个会很快耗光堆栈的线程ThreadProc,并且用下面的语句创建:
CreateThread(NULL, 1024 * 1024 /* 1MB */, ThreadProc, NULL, 0, &threadId);
然后在线程中精心设置好异常捕捉,满心欢喜的等待EXCEPTION_STACK_OVERFLOW的到来。
那么你会震惊的发现:
 
程序因为EXCEPTION_ACCESS_VIOLATION异常而直接退出了。没有EXCEPTION_STACK_OVERFLOW。
 
你重新设置异常条件,决定捕捉全部的异常,但是你会发现你完全无法捕捉。Why?
 
接下来,你把堆栈大小设置为:1024 * 1024 + 1 或者1024 * 1024 - 8092,那么又可以准确的捕捉到溢出异常了。
或者你把dwStackSize设置为0,或者给dwCreationFlag 加上STACK_SIZE_PARAM_IS_A_RESERVATION标志,也可以成功捕捉异常。
 
现在应该知道原因了。运行在x86上的Windows XP,目前内存分配最小粒度为64KB,用户态下页面大小是4KB。
 
当把dwStackSize设置为0时,系统会缺省设置堆栈为1MB大小,但是事实上线程得不到1MB的堆栈,因为系统会在堆栈最后的部分用加上PAGE_GUARD属性的页来检测溢出,并且还要保留用于处理溢出异常发生时的页面,所以可以检测到异常。
 
而给dwCreationFlag 加上STACK_SIZE_PARAM_IS_A_RESERVATION标志时,按照64KB分配粒度的原则,堆栈最后的部分始终有足够的页来检测溢出和处理异常。所以也很安全。
 
而为线程指定1024 * 1024大小堆栈时,堆栈边界正好和64KB以及1MB边界对齐的,当完全提交物理内存时,堆栈的所有页的属性都将是PAGE_READWRITE(事实上应该是一个Windows的Bug,但是考虑性能原因而故意让其存在)。所以系统根本没机会检测到溢出错误,而会在堆栈溢出后直接引发EXCEPTION_ACCESS_VIOLATION。而因为没有预留的异常处理页面,异常处理程序又会引发一个EXCEPTION_ACCESS_VIOLATION异常。天啦!!整个进程就这样崩溃了。吐舌
 
而为什么指定1024 * 1024 + 1 或者1024 * 1024 - 8092就可以捕捉异常了呢?因为堆栈大小在超过1MB后,是按照1MB对齐原则,哪怕多一个字节,系统也会多映射1MB的物理内存。这样足够两页的异常检测和异常处理页面。1024 * 1024 - 8092大小正是这个原因所以能正确引发异常。
 
所以,正确的做法是,如果想在运行CreateThread时指定堆栈大小,并且全部提交物理内存的话,那么大小必须保证在整1MB边界上留出至少两个页面给系统。