23rd August 2007

Nikon 终于动作了

今天Nikon宣布了自己的第一个全幅DSLR D3。 终于赶上了Canon的节奏,当然也给DSLR市场带来了活力,值得进一步观望。当然D3俺是不想了,预售价都是5000大洋。 同时宣布的还有Nikon的D300。这个机型估计对我更有吸引力。

Nikon动作慢是出了名的。D200已经是2年前的事情了。D300我不太清楚,但是D200有很多D70没有的东东确实值得拥有,毕竟D200是中高端的DSLR:D200去掉了那些我从来不用的风景/人像/花鸟模式,而把ISO、WB和QUALITY分开成三个按钮放在了左边,而P,S,A,M模式都放在了右边用dial来选择;D200用的是RGB的histogram,可以看照片的各个色相的图,仔细检查曝光过度程度;当然2.5寸的LCD也很不错,D300已经变成3寸的了,值得期待;当然像素和对焦/测光模式的区别自然是有了;最后就是外表了,都是黑黑的,但是D200是镁合金机身,自然专业不少。

D300在年末推出,据称又比D200进步不少,看来要开始攒银子了。。。。。

posted in 捕风捉影 | 2 Comments

13th August 2007

几个有用的食物拍摄的帖子

《樱的摄影心得之—光线篇》
http://web.wenxuecity.com/BBSView.php?SubID=cooking_best&MsgID=37394

《樱的食物摄影心得之—基础构图篇》
http://web.wenxuecity.com/BBSView.php?SubID=cooking&MsgID=533306

《樱的食物摄影心得之—小道具的不可或缺》
http://web.wenxuecity.com/BBSView.php?SubID=cooking&MsgID=533961

袖珍傻瓜相机使用心路行路历程回顾(跟着淡鸟大师学摄影) by霓娜
http://blog.wenxuecity.com/blogview.php?date=200702&postID=23497

posted in 捕风捉影 | 0 Comments

30th June 2007

one-liner

One-liner本来是形容一个喜欢把程序写在一行的人。但是在以前的公司写了一段代码,就被人成为了one-liner。

if(xxxx)
do it;

几个Senior programmer在review我的code的时候说,以后不能这么写,无论如何要加上括号,否则你就是one-liner。当时就悻悻的加上了括号。

if(xxxx) {
do it;
}

半年以后,我坐在这里成为了代码的维护人员。今天某个one-liner就给我带来了麻烦。本来有一段代码:

for(xxxxxx)
if(yyyyyyy){
return zzzzz;
}

我需要移植一段代码,然后就加了一行代码:

for(xxxxxx)
ASSERT(aaaaaaa);
if(yyyyyyy) {
return zzzzz;
}

虽然当时粘过来的时候每行都仔细检查了,但是这个问题没有注意到。后果可想而知。。。。(bug一个)最后还是boss发现了这个问题,发email告诉我code应该是这样的:

for(xxxxxx) {
ASSERT(aaaaaaa);
if(yyyyyyy) {
return zzzzz;
}
}

我还继续犯晕,还跑去问boss他改了什么地方 。汗。。。。。

当然这个问题以后要多注意。有个tip 就是多利用VS或者emacs的re-indent功能,只要重新format就能马上看出问题。

Don’t be one-liner for your good and for everyone else’s.

posted in 编程珠玑 | 2 Comments

30th June 2007

Unlock Mio C220 GPS System

    今天一冲动就去Staples买了Mio C220。因为现在C310已经没了(ebay上还有几个,但是感觉还是去店里可靠)。现在C220到处都是$180,我加上了一个25 off 100的coupon,$155拿下。

    买它当然是为了unlock它。GPSPassion上的经典讨论贴(http://www.gpspassion.com/forumsen/topic.asp?TOPIC_ID=84734&whichpage=1)已经说得很清楚了,我就简单的用中文复述一遍以及我的操作经历吧。

    整个帖子就围绕着两种unlock的方式:一开始大家都follow一个很长的todo list,基本上就是通过Transfer软件把script放到RAM中,然后稍作修改;后来一位大牛干脆利用了SD卡的AutoRun功能,直接把所有script放到了SD卡上,然后等机器hard reset后,直接插入SD卡,就搞定了。这个帖子本来是在讨论能否用另外一个成功破解C250的方法来破解C220,经过大牛们的努力,终于有了这个方便的unlock包(http://rapidshare.com/files/36705155/MioC220-SY2.rar)。

    我当然图方便,用了后面一种办法,结果一试就灵。正如帖子里说的:第一步,解压这个包到SD卡的根目录;第二步,hard reset C220,等启动完毕了就插入这张SD卡。 随后就会弹出一个对话框“Launch DM Tool?”,点 yes,机器就重启了,然后很快就会听到熟悉的Windows启动声音,随后就进入了我们的桌面。当然这个桌面是customize过的,没有开始按钮,只有一些常用的图标在上面。我替换了那个默认的背景画面,然后调整了一下几个图标的位置。现在还挺满意,不过我还得研究一下如何把开始按钮弄出来,然后多加几个GPS软件上去。    总的来说,Mio的GPS还是很值得玩的,很对得起它的价格。

 

posted in 折腾电脑 | 6 Comments

2nd May 2007

Character Set 三两句

最近在做一些跨平台的东东,这个encoding确实让我琢磨了一会儿,以前只是听说过没当回事,现在必须要认真理解以前那些耳熟能详的概念。

  •  ASCII码已经是古董了。它是一种7-bit的编码,这个就不用我多说了。(历史上是ISO-646-US标准)
  • 然后就是ISO-8859系列。这是一种8-bit的编码(因为是单字节编码,所以又叫Single Byte Character Set/SBCS),添加了ASCII不能表示的控制字符。
  • MBCS/DBCS是多字节编码,是为了弥补上面的编码不能表示东方或者其他非拉丁字符的缺陷。比如说Shift-JIS就是日文的字符集。
  • Unicode:为了实现“天下一统”,Unicode Consortium提出了Unicode 标准。它的目标就是统一世界上的字符集。Unicode的特点就是它只定义了code point(每一个unicode编码就是一个code point)的语义,没有定义它本身的编码形式以及对应字符的字形(glyph)。举个简单的例子:U+05D0就是一个犹太文的字母,但是这个字符在计算机中编码后会是什么样的字节串,不同的编码(encodings)会产生不同的结果。比如说UTF-16就是Windows内部采用的Unicode的编码方式,这个字母就会表示成 05 D0 (占用两个字节)。但是如果采用Linux下的UTF-8编码方式,这个字母就是 D7 90 (也是两个字节,但是值完全不一样)。下面这个表格说明了UTF-8根据code point所在不同的range所采用的编码方式。
Code range (hexadecimal) Scalar value (binary) UTF-8(binary / hexadecimal) Notes
000000–00007F128 codes 00000000 00000000 0zzzzzzz 0zzzzzzz(00-7F) ASCII equivalence range; byte begins with zero
seven z seven z
000080–0007FF1920 codes 00000000 00000yyy yyzzzzzz 110yyyyy(C2-DF) 10zzzzzz(80-BF) first byte begins with 110, the following byte begins with 10.
three y; two y, six z five y; six z
000800–00FFFF63488 codes 00000000 xxxxyyyy yyzzzzzz 1110xxxx(E0-EF) 10yyyyyy 10zzzzzz first byte begins with 1110, the following bytes begin with 10.
four x,four y; two y,six z four x; six y; six z
010000–10FFFF1048576 codes 000wwwxx xxxxyyyy yyzzzzzz 11110www(F0-F4) 10xxxxxx 10yyyyyy 10zzzzzz First byte begins with 11110, the following bytes begin with 10
three w, two x; four x, four y; two y, six z three w; six x; six y; six z

         Unicode下两种主要的编码方式UTF-16和UTF-8各自在不同的平台上发挥其本领。UTF-8的设计很象Huffman编码,也就是变长的编码(从表格中可以看出来)。编码最短的是拉丁字母(1个字节),一共128个code points,而且这个字节的最高位一定是0。这样跟ASCII保持了很好的兼容性,因为ASCII编码的字符序列也是UTF-8的字符序列。变长的编码也节省了空间。
          Windows长期以来都使用UCS-2编码。这是一种双字节的定长编码。但是双字节不能表示Unicode中code point在U+FFFF以上的字符(U+FFFF一下的所有字符构成了Basic Multilingual Plane /BMP),所以从XP开始,UTF-16便开始在Windows内部使用。UTF-16的编码值其实几乎就是Unicode 的值,大多数字符还是在U+FFFF以下(在U+FFFF以上的字符UTF-16采用了surrogate pair方式进行特殊编码,可以参考http://en.wikipedia.org/wiki/UTF-16)。为了更好的表示UTF-16编码,C/C++标准采用了wchar_t这个类型,也就是一个双字节的类型;同时Java里面的char本身就是一个double byte的类型。
           UTF-32也是一种编码,但是它是一种定长的编码,它的编码值就和Unicode中的code point一一对应。UTF-32可以作为UTF-8和UTF-16之间转换的媒介。
           对于Windows程序员来说,TCHAR提供了一定便利。TCHAR是一个宏定义,它可以是Unicode(UTF-16)可以是MBCS/DBCS,也可以是ANSI(任何基于单字节的编码)。这样写程序的时候就尽量使用TCHAR而不用关心底层的编码实现。比如说很多地方就可以使用LPCTSTR (Long Pointer to a Const TCHAR STRing)类型。TCHAR具体代表什么编码类型需要在程序中用宏定义。
            Linux就没有这么麻烦了,因为Linux内部使用的就是UTF-8编码,所以字节流就足够了。但是跨平台的时候就比较麻烦了。

            其实所有的编码都可以表示为字节流。但是,当说到字符串的时候就不太好办了。比如”abc”用UTF-8和UTF-16表示出来是不一样的字节流。UTF-8是2个字节长,而UTF-16是4个字节长。所以为每种编码引入对应的基本数据类型给字符串操作带来了方便。UTF-8的基本编码单位是单字节,所以char[]足以胜任;UTF-16的编码单位是双字节,所以wchar_t[]正好合适对UTF-16编码单位的操作。这样看来,实际UTF-8/16里面的8和16其实是编码单位的大小,而并不是说每个字符只有8个bit或者16个bit(在UTF-8中,一个字符最多可以编码为4个字节,UTF-16中同样也有两个双字节表示的字符)。所以在传输这些编码的时候,作为字节流没有任何问题;但是如果要解释和处理这些编码的时候,使用相应的数据类型方便得多。

posted in 编程珠玑 | 0 Comments

30th April 2007

给电脑配置Playstation2手柄

实况足球是我认为做得最好的足球游戏。以前在游戏厅里那种手柄的感觉一直没有在电脑上找回来。电脑城的USB手柄不是质量问题就是手感不好。最近看到了这个在radioshack卖的东东(12大洋),确实填补了我心中的一片空白。

 http://www.radioshack.com/sm-usb-adapter-for-playstation-2-controller–pi-2348187.html

 这个Playstation-to-USB adapter可以用PS或者PS2手柄上(radioshack的clerk居然理解成了PS/2接口到USB的转换器,分特)。拿到这个adapter以后就可以立刻去amazon寻找Sony标配的dualshock2的手柄了。这个可是如假包换的原配手柄(20大洋)。

http://www.amazon.com/Sony-Playstation-Dualshock-Controller-Black/dp/B00004YRQ9/ref=pd_bbs_sr_1/104-2495826-5862344?ie=UTF8&s=videogames&qid=1177967710&sr=8-1

 好了,Go and enjoy your PES/WE game!!

posted in 折腾电脑 | 0 Comments

25th April 2007

Linux 内核中的内存分配

本文作者:campos 本文出处:http://www.mykernelspace.com  (转载请保留此行,谢谢)

既然说到了内存管理,就顺带说一下内存分配。内存的管理细节以及实现可以看mm部分的源码。暴露给内核程序员的基本内存分配函数是经常要用到的东东。
简单说来,有三种分配内存的方法:

  • kmalloc:这个用到很多。这个函数将会分配一片连续的物理内存。通常分配连续物理内存的好处就是构造页表的时候开销很低(通常线性地址加上一个偏移就是物理地址),同时访问起来效率也高。当然连续的物理内存也是很宝贵的资源。内核中使用的buddy algorithm和slab机制都是为了尽量减少内存碎片,增加连续内存分配成功的几率。kmalloc有很多mode,比如说GFP_KERNEL, GFP_ATOMIC。这些mode其实是一些更细节的flag的组合,比如说 GFP_KERNEL 就是 __GFP_WAIT | __GFP_IO | __GFP_FS ; 而 GFP_ATOMIC 就是 __GFP_HIGH。在一些中断处理中需要内存分配立刻返回,这样就需要不同的kmalloc 模式。这些flag具体的意思可以参考LDD3的第八章。
  • kmem_cache:这个是Linux内核Slab机制提供的特殊的内存分配函数。“slab”直译过来就是“水泥预制板”:) 其实这个名字非常的形象。内核中经常要分配一些常用的struct,比如说filp, task_struct, file等等。Slab是一个lookaside cache机制,在内存中会创建一个memory pool。这个pool里面当然就是这些指定大小的object。这样分配或者释放起来都很高效(省去了内存分配和初始化的过程)。
  •  __get_free_pages:是直接获取整页的内存(页数是2的幂)。其实kmalloc在实现的时候也调用了这个函数。当需要分配大量的内存的时候,使用这个函数能够提高效率。
  • vmalloc:这个函数分配一片连续的“虚拟内存”。也就是说返回的线性地址虽然是连续的,但是映射到的物理内存是不连续的,而且跟物理地址可能不是一一对应的(不同于kmalloc和__get_free_pages)。所以在使用分配到的内存时,页表的查询比较频繁,所以效率相对较低。但是LDD3中提到了Linux内核在create_module的时候,采用的就是vmalloc。我看了看/proc/kallsyms,我load的module里面的symbol确实都分布在不同的内存区域。

posted in Linux Kernel & Driver | 0 Comments

24th April 2007

Linux memory management at a glance

本文作者:campos 本文出处:http://www.mykernelspace.com  (转载请保留此行,谢谢)

接触Linux内核也有一段时间了,在开发kernel module的时候,其实有几样东西是需要牢牢掌握的。

  • 内核基本的struct:其实读Understanding the Linux Kernel (ULK3)的时候很困惑,如果一章一章地读下去,没有什么感觉。其实在读第一遍的时候先掌握内核中重要的struct。这些struct在内核编程中将会频繁用到。
  • 内核同步机制: user mode里面的semaphore和mutex是编程必备的工具。在内核里面,同步不仅是线程间的,而且还要考虑SMP的问题。
  • 内存管理机制: 无论做什么开发,内存管理都是程序的“循环系统”,如果血液不通畅,整个module的性能肯定上不去。内核内存是很宝贵的资源。下面就简要分析一下具体Linux是如何管理内核内存的(其实是读ULK3和LDD3的读书笔记)

Linux采用的是段页式内存模型,也就是既使用了段(segment)又使用了分页(paging)技术。分段技术是历史遗留的产物,它出现的主要原因就是寄存器存储位数和地址空间位数不匹配的结果。分页和分段都是虚拟内存实现的机制。分段技术利用段基址把内存划分成很多互相重叠的内存空间。当进程在自己的段里运行的时候,自己能够访问的就是整个段空间。但是这样的后果就是进程的内存缺乏必要的保护。所以随着保护模式的引入,分页技术得到了使用。在分页技术的优势下,分段技术显得没有什么必要了,但是程序的分段结构依然保留了下来(数据段、代码段、堆栈段等等)。在程序运行的时候,依然需要配备相应的段。所以Linux采用了这种混合的段页式内存。在Linux中,只有几个固定的段可供使用:内核数据/代码段、用户数据/代码段。在所有内核进程中,段寄存器里载入的始终是内核数据/代码段的段基址,而且这个基址是固定的(当然这个基址还不是真实的物理地址)。同样在用户进程中,段寄存器里载入的始终是用户数据/代码段基址。

所有的虚拟内存实现的机制最关键的是内存虚拟地址到物理地址的映射。Linux的段页式内存模型使用了两级地址映射:虚拟地址-〉线性地址-〉物理地址(ULK3和LDD3的命名有所不同)。逻辑地址就是在程序中使用的地址,这个地址首先要经过分段处理,也就是读入相应的用户/内核段基址(已经是常量了,而且就是0×00000000)。随后就得到了一个地址值没有变化的线性地址。这个地址是32位的(这里讨论的是i386 32bit架构)。这个线性地址需要经过分页处理才能得到真正的物理地址。可以看出这里虚拟内存地址实际上在数值上都等于线性地址,只是段寄存器的内容反映了这个地址是属于用户还是内核,数据还是代码。

在Linux中,有一个著名的3Gb界限,也就是在线性地址中0~3GB的地址是留给用户空间的,3GB~4GB的地址是内核使用的。所以当你在程序中看到一个大于等于0xc0000000的地址,这个地址一定是映射到内核的内存中。Linux采用了多级页表技术来实现从线性地址到物理地址的映射。32位的地址的前10位是Page Directory的index(显然这个表最多有1024个entry)。中间10位是Page Table的index。剩下的12位是页内偏移(在这种实现下,每一个内存页的大小是4KB)。使用这种分页技术的好处是节省进程的空间,因为每一个进程都有一个页表。

Linux使用的是4KB的内存页面大小(CPU有4KB或者4MB两种设置)。实际的物理内存被划分成一个一个4KB的内存页帧,每一个页帧在内存中都对应有相应的一个32字节大小的page struct。在Linux看来,一个page struct实际上就代表了一个实际的物理页帧。每个有效的(mapped)线性地址都映射到一个相应的page struct上。但是内核的线性地址空间只有1GB,而真正的物理地址可以大到4GB,所以没有办法做到线性地址和实际的物理地址的一一对应。所以在内核的1GB线性地址空间中有一部分(896MB, Low Memory)是直接映射到物理地址上的,这部分内存叫做内核逻辑地址(根据LDD3的命名)。内核线性空间还剩下128MB (内核虚拟地址)提供给映射896MB以上的内存(High Memory)之用。内核通过三种不同的办法将896MB以上的物理内存映射到这128MB的线性地址空间中(permanent kernel mapping, temporary kernel mapping, and noncontiguous memory allocation),具体的实现方法可以参考ULK3的第八章。

在Linux处理内存的时候,定义了很多逻辑的概念,比如说page,zone, virtual memory area (VMA), mm_struct等等。每一个逻辑的概念都有对应的struct来支持。作为一个kernel module的开发者,最常用的就是在内核中申请/释放/使用内存空间,以及提供设备中的mmap实现以便让设备内存可以被映射。除了这些,还有一些底层的细节,比如说Linux的内存分配释放缓冲的机制(Buddy System, Slab)。这些底层实现是读Linux源码或者进行Linux裁减时必须要了解的东西,所以以后再来分析这些机制。

posted in Linux Kernel & Driver | 0 Comments

14th April 2007

STL 源码研读笔记(3.2)– allocator

本文作者:campos 本文出处:http://www.mykernelspace.com  (转载请保留此行,谢谢)

    最后我们来看看这个被容器类广泛include的bits/allocator.h文件。
    这个文件定义了allocator这个模板类。回想一下上次在new_allocator中看到的:这个类主要定义了几个模板类型的别名,还有重要的函数(allocate, deallocate, construct, destruct)。实际上,这个类没有任何的数据成员,纯粹是一个utility类。现在要分析的类allocator就是从这个类继承来的。下面是代码:

template<typename _Tp>
class allocator: public ___glibcxx_base_allocator<_Tp>
{

   //一些类型别名的定义,与new_allocator里的定义一致
public:
typedef size_t size_type;
typedef ptrdiff_t difference_type;
typedef _Tp* pointer;
typedef const _Tp* const_pointer;
typedef _Tp& reference;
typedef const _Tp& const_reference;
typedef _Tp value_type;

      //功能未知的部分。定义了另一个类型的模板结构,唯一的成员就是另外一个类型allocator的typedef。
template<typename _Tp1>
struct rebind
{ typedef allocator<_Tp1> other; };

      //默认构造函数
allocator() throw() { }

      //拷贝构造函数,调用父类的构造函数
allocator(const allocator& a) throw()
: ___glibcxx_base_allocator<_Tp>(a) { }

  
      //参数为另外一个类型allocator的拷贝构造函数
template<typename _Tp1>
allocator(const allocator<_Tp1>&) throw() { }

      //析构函数
~allocator() throw() { }

      //继承其他所有的定义
    };

    同时在这个类的声明的后面,也同样将==和!=两个操作符定义为永远相等。

  //==操作符永真
template<typename _T1, typename _T2>
inline bool
operator==(const allocator<_T1>&, const allocator<_T2>&)
{ return true; }

  //!=操作符永假
template<typename _T1, typename _T2>
inline bool
operator!=(const allocator<_T1>&, const allocator<_T2>&)
{ return false; }

    在这个allocator的真正声明前面有一个specialization定义:

  //前向定义
template<typename _Tp>
class allocator;

  //对于void类型做特殊处理
template<>
class allocator<void>
{

    //定义几个别名,pointer类型为void*。最重要的区别就是去掉了reference类型,因为void&是没有意义的。当然这个void的value_type我也得想想是什么意思:)
public:
typedef size_t size_type;
typedef ptrdiff_t difference_type;
typedef void* pointer;
typedef const void* const_pointer;
typedef void value_type;

    //同样神秘的rebind,应该会在以后知道它的用途。这里我被迫卖个关子。
template<typename _Tp1>
struct rebind
{ typedef allocator<_Tp1> other; };
};

   好了,其实allocator也挺简单的,至少现在看来是这样。整个这个allocator最重要的allocate和deallocate只是调用了全局的operator new和operator delete。construct和destruct函数调用了全局的new expression和对象的析构函数。根据现在所看到的代码,可以想象的allocator在容器中的用法就是:声明一个allocator对象作为容器的成员,然后任何内存分配的任务都由这个allocator对象来实现。其实经常看到的一个容器声明就是(一般出现在错误中),template<typename _Tp, typename _Alloc = allocator<_Tp> > 。我还没有自己写过自己的allocator,所以一般都用的是系统默认的allocator。

   到现在,分析过的代码有auto_ptr,iterator和allocator。接下来就可以正式看看这些基本的STL元素是如何使用的。下次就打算挑一个non-associative的容器来看看,其他的non-associative的容器依此类推。然后再下次就找个associative的容器来研究研究,尽量达到举一反三的作用。

posted in 编程珠玑 | 0 Comments

13th April 2007

为什么要读源码 (zz)

很难想象钢琴家不用聆听大师的作品;诗人不用揣摩传世的经典;画家不用体会前辈的佳作;拳手不用参详高人的示范。那我们怎么能想象程序员不用仔细学习性感的代码?可惜的是,美妙的代码往往有如像Shrek,乍一看也就是面目丑陋的庞然大物。没有Fionna的聪慧,我们也难欣赏Shrek洋葱一般层次丰富的心灵。再说,代码一旦写成,我们看到的也就是一段神来之笔。再难体会到作者在难题前内心有如困兽般地冲撞,面临多种选择时精神的激荡。我们也再难追溯每个数据结构背后的理念,每段算法成型过程中每一步的由来(顺便说一句。这也是为什么Knuth的书引人入胜的原因。每段算法怎么从无到有,自粗而细,由慢转快,通通脉络清晰)。就算是理解代码本身,想来每人的体会也有深有浅。不知道多少老大因为这些困难没能体会到阅读代码时心头肿胀(乱用冯唐语)的快感?除非,除非有高手引领我们入门,给我们细述经典代码如何玲珑浮屠,如何眼波婉转。

posted in 编程珠玑 | 0 Comments