2007年6月1日星期五

[ZZ]对学生朋友的一点建议

对学生朋友的一点建议

2007年6月1日 上午 11:09:00
发表者:Google(谷歌)中国工程研究院 工程师 方坤

自去年春天加入谷歌,我曾多次随公司校园招聘团队一起走访各地院校,帮助公司发掘人才。利用这样的出差机会到处走走看看,饱览祖国大好河山,是我一点小小的私心。但更具挑战性、更激动人心的,还是和我未来的同事们过招,不打不相识嘛。一想到马上就要和这样优秀的人才一起工作,我就兴奋不已,真恨不得现在就买一张单程机票把他/她拽回北京。

然而,在面试过程中,我也相当惋惜地看到,由于种种主客观原因的限制,很多青春韶华的学子未能充分发挥出他们应有的潜力。这里我不讲成绩,只把我遇到过的一些普遍的问题归纳一下,希望对即将踏入社会的学生朋友们能有些许帮助。

实战!实战!

纸上得来终觉浅,绝知此事要躬行。对于一个未来的软件工程师来说,实际编程经验是相当重要的。我们会要求应聘者在紧凑的时间内编写大量的代码,从中考察应聘者的分析能力,编码速度,代码可读性,对所用编程语言的掌握程度,对边界条件与异常状况的处理,数据结构与函数接口的定义,程序运行的效率和应聘者查错纠错的能力等等。一口气列出这么多要求,听上去似乎过于苛刻,其实只要积累了足够的实战经验,每一个应聘者都完全可以满足这些最低限度的要求。

我记得开复对于青年学子们有一个建议,大学四年,至少要编写 10 万行代码。不是每一个人都期望进入谷歌这样的顶级技术公司,但即使对开复的建议打个三折,也还有 3 万行呢,不努力,能行嘛。前来应聘的学生们在编写代码时暴露出这样那样的问题,大都可以归结到同一个原因:锻炼太少。比如"for (int i = 0; i < strlen(s); ++i)",没有实战经验的人,怎么可能意识到暗藏其间的效率陷阱。再比如内存泄漏,就和初恋一样,没有亲身经历过的人,不会有刻骨铭心的感受,而一旦经历,终身难忘,根本用不着你有多聪明。遗憾的是,我看到许多相当聪颖的学生写出来的代码只能用惨不忍睹来形容,真让我怀疑这是不是就是他们的第一次。大家不要怪谷歌要求高,恕我直言,如果不能持之以恒,下点儿苦功夫,不光谷歌一家,其它公司恐怕也是进不去的。

北京、上海等地的学生,往往有更多实习、兼职机会,接受锻炼之余,还能攒下一笔小钱作零用之资。其它一些地区的学生也许就没有这么幸运,这就尤其需要积极主动,寻找机会,创造机会,把握机会。世界正在变平,互联网的发展改变着地球的面貌,让不发达地区的学生也能相对容易地接触到发达地区的资源。前不久我在西安遇到一个学生,他半年来一直坚持在北京大学的 ACM 网站上参赛、做题,我看他写出来的程序就确实比他大多数同学都要好一些。我相信,只要他能够持之以恒,还会取得更大的进步。我听说浙江大学的 ACM 网站、TopCoder 网站也都是不错的教育资源,感兴趣的同学不妨一看;虽说做竞赛题距离真正的软件开发还有着显著差别,但也不失为一个不错的出发点。

在积累一定编码量的基础上,我建议大家总结经验教训,不断提高自我。如果几万行代码写下来,学 C++ 的不知道 const 怎么用,学 python 的没听说过 GIL,我看也没什么意思。到了这个阶段,我推荐大家读一些经典的进阶书籍,例如《Effective C++》、《Effective Java》等,即使地处偏远地区,也可通过网上书店买到。(我建议编码量太少的同学就先不要读了,会走火入魔的。我见过有学生连引用和指针都没搞清楚,就在那里重载操作符的。)交流也很重要,如果能够与网上网下志同道合的朋友互相帮助,共同进步,当收事半功倍之效。在这一方面,各校的 Google Camp 也许能够起到一定作用。

"我会写代码呀,为什么谷歌不要我?"

一家外包公司或许会满足于雇佣仅能从事简单的、机械性的重复劳动的软件蓝领,而让谷歌苦苦寻觅的,乃是最优秀的软件工程师兼计算机科学家——是的,在谷歌,研究与开发融为一体,软件工程师与计算机科学家当然也合二为一。计算机这几年算是比较热的,能够得偿所愿挤进计算机科学相关专业的学生都是全中国顶尖的人才,如果大学四年荒芜学业,未能取到真经,浪费了这一来之不易的学习机会,是不是太可惜了呢。

遍历一个数组或链表的时间复杂度是多少?对于这样一个不是问题的问题,竟然各地都有相当数量的学生回答说是 O(logN)!有一次我实在忍不住了,提示应聘的学生说:"你是如何理解'遍历'一词的涵义呢?"他立刻做恍然大悟状,回答说:"哦,对,应该是 O(NlogN)"。我当时失望得一句话都说不出来。类似的例子还有很多。坊间一直传说谷歌的面试题有多难,其实大多数学生都卡在最基础的问题上。少数人费尽气力要收集谷歌曾经用过的面试题,其实我们大多数题目都来自或改编自经典计算机科学教材的习题。

顺便提一句,上面提到的那个答错的学生乃是一流大学中成绩名列前茅的优秀生,门门功课(包括所有计算机专业课)均在 90 分以上。反差何其大也!

诚信为本

我曾服务于多家公司,注意到不同的公司之间,乃至同一公司内不同的面试官之间,对应聘者的要求都会有所差别,或看重潜质,或偏好经验,或强调态度。但有一点大家是共同的:如果应聘者在诚信上有疑问,谁也不敢要。

总体而言,现在的学生其诚信还是相当不错的,大大超出了我们的期望。然而,不和谐音也还是有的。我们曾在某著名高校进行笔试,就发现有学生严重作弊,他们毁掉自己的机会不说,整个学校的名声也受到拖累。面试中偶尔也能遇到诚信堪忧的学生。有一次我出了一道题,前来应聘的学生明明以前见过这道题,却告诉我说没见过,自以为得计,可他那一纵即逝的狡黠一笑哪里逃得过我洞若观火的锐利眼神。大哥,你又不是专业演员,为什么要玩这样的花招?我每年面试的求职者在百人以上,捣鬼是过不了我这一关的。两点之间直线最,说真话最简单。

做最好的自己

不是每一个人都会加入某一家具体的公司,但每一个人都可以成功,成为最好的自己。我这里所谈,既不系统亦欠简练,但确实是我在几次校园招聘之旅中发现的一些具有共性的问题,故不避好为人师之讥,罗列于此。我当然知道,本文的首要目标读者——大一新生是不听劝的,他们更愿意把所有的错误亲自重犯一遍。话虽如此,只要能够对一个学生有所启迪,我的时间就不算白费。

2007年5月23日星期三

[ZZ]linux下的proc文件系统解析

在追踪Larbin的config.h文件时,发现原来它是从configure中产生的:
configure文件中有如下语句:
if [ -e /proc/self/status ]; then
echo "#define HAS_PROC_SELF_STATUS" >> config.h

/proc/self/status是什么?于是搜到了下面这篇文章。

linux下的proc文件系统解析
proc文件系统是一个伪文件系统,它只存在内存当中,而不占用外存空间。它以文件系统的方式为访问系统内核数据的操作提供接口。用户和应用程序可以通过 proc得到系统的信息,并可以改变内核的某些参数。由于系统的信息,如进程,是动态改变的,所以用户或应用程序读取proc文件时,proc文件系统是动态从系统内核读出所需信息并提交的。它的目录结构如下:

目录名称 目录内容
apm 高级电源管理信息
cmdline 内核命令行
Cpuinfo 关于Cpu信息
Devices 可以用到的设备(块设备/字符设备)
Dma 使用的DMA通道
Filesystems 支持的文件系统
Interrupts 中断的使用
Ioports I/O端口的使用
Kcore 内核核心印象
Kmsg 内核消息
Ksyms 内核符号表
Loadavg 负载均衡
Locks 内核锁
Meminfo 内存信息
Misc 杂项
Modules 加载模块列表
Mounts 加载的文件系统
Partitions 系统识别的分区表
Rtc 实时时钟
Slabinfo Slab池信息
Stat 全面统计状态表
Swaps 对换空间的利用情况
Version 内核版本
Uptime 系统正常运行时间

并不是所有这些目录在你的系统中都有,这取决于你的内核配置和装载的模块。另外,在/proc下还有三个很重要的目录:net,scsi和sys。Sys目录是可写的,可以通过它来访问或修改内核的参数(见下一部分),而net和scsi则依赖于内核配置。例如,如果系统不支持scsi,则scsi目录不存在。

除了以上介绍的这些,还有的是一些以数字命名的目录,它们是进程目录。系统中当前运行的每一个进程都有对应的一个目录在 /proc下,以进程的PID号为目录名,它们是读取进程信息的接口。而self目录则是读取进程本身的信息接口,是一个link。Proc文件系统的名字就是由之而起。进程目录的结构如下:

目录名称 目录内容
Cmdline 命令行参数
Environ 环境变量值
Fd 一个包含所有文件描述符的目录
Mem 进程的内存被利用情况
Stat 进程状态
Status 进程当前状态,以可读的方式显示出来
Cwd 当前工作目录的链接
Exe 指向该进程的执行命令文件
Maps 内存映象
Statm 进程内存状态信息
Root 链接此进程的root目录

用户如果要查看系统信息,可以用cat命令。例如:

# cat /proc/interrupts
CPU0
0: 8728810 XT-PIC timer
1: 895 XT-PIC keyboard
2: 0 XT-PIC cascade
3: 531695 XT-PIC aha152x
4: 2014133 XT-PIC serial
5: 44401 XT-PIC pcnet_cs
8: 2 XT-PIC rtc
11: 8 XT-PIC i82365
12: 182918 XT-PIC Mouse
13: 1 XT-PIC fpu PS/2
14: 1232265 XT-PIC ide0
15: 7 XT-PIC ide1
NMI: 0

用户还可以实现修改内核参数。在/proc文件系统中有一个有趣的目录:/proc/sys。它不仅提供了内核信息,而且可以通过它修改内核参数,来优化你的系统。但是你必须很小心,因为可能会造成系统崩溃。最好是先找一台无关紧要的机子,调试成功后再应用到你的系统上。

要改变内核的参数,只要用vi编辑或echo参数重定向到文件中即可。下面有一个例子:

# cat /proc/sys/fs/file-max
4096
# echo 8192 > /proc/sys/fs/file-max
# cat /proc/sys/fs/file-max
8192

如果你优化了参数,则可以把它们写成添加到文件rc.local中,使它在系统启动时自动完成修改。

/proc/scsi
/proc/scsi/scsi
作为系统管理员,需要了解的最有用内容是,在有热交换驱动器情况下,如何不重启系统就可以添加更多磁盘空间。假使不使用 /proc,您可以插入驱动器,但为了使系统识别新磁盘,必须随即重新引导系统。这里,可以用以下命令来使系统识别新的驱动器:

echo "scsi add-single-device w x y z" > /proc/scsi/scsi

为使该命令正常运行,必须指定正确的参数值 w、x、y 和 z,如下所示:
w 是主机适配器标识,第一个适配器为零(0)
x 是主机适配器上的 SCSI 通道,第一个通道为零(0)
y 是设备的 SCSI 标识
z 是 LUN 号,第一个 LUN 为零(0)

一旦将磁盘添加到系统中之后,可以挂装任何先前已格式化的文件系统,也可以开始对它进行格式化等。例如,如果不确定磁盘是什么设备,或者想检查任何先前已有的分区,则可以用如 fdisk -l 这样的命令来向您报告这方面的信息。

相反的,在不重新引导系统的情况下将设备从系统中除去的命令是:

echo "scsi remove-single-device w x y z" > /proc/scsi/scsi

在输入这条命令并将热交换 SCSI 磁盘从系统中除去之前,请确保首先卸下已从该磁盘安装的任何文件系统。

/proc/sys/fs/
/proc/sys/fs/file-max
该文件指定了可以分配的文件句柄的最大数目。如果用户得到的错误消息声明由于打开文件数已经达到了最大值,从而他们不能打开更多文件,则可能需要增加该值。可将这个值设置成有任意多个文件,并且能通过将一个新数字值写入该文件来更改该值。

缺省设置:4096

/proc/sys/fs/file-nr
该文件与 file-max 相关,它有三个值:
已分配文件句柄的数目
已使用文件句柄的数目
文件句柄的最大数目
该文件是只读的,仅用于显示信息。

/proc/sys/fs/inode-*
任何以名称“inode”开头的文件所执行的操作与上面那些以名称“file”开头的文件所执行的操作一样,但所执行的操作与索引节点有关,而与文件句柄无关。

/proc/sys/fs/overflowuid 和 /proc/sys/fs/overflowgid
这两个文件分别保存那些支持 16 位用户标识和组标识的任何文件系统的用户标识(UID)和组标识(GID)。可以更改这些值,但如果您确实觉得需要这样做,那么您可能会发现更改组和密码文件项更容易些。

缺省设置:65534

/proc/sys/fs/super-max
该文件指定超级块处理程序的最大数目。挂装的任何文件系统需要使用超级块,所以如果挂装了大量文件系统,则可能会用尽超级块处理程序。

缺省设置:256

/proc/sys/fs/super-nr
该文件显示当前已分配超级块的数目。该文件是只读的,仅用于显示信息。

/proc/sys/kernel
/proc/sys/kernel/acct
该文件有三个可配置值,根据包含日志的文件系统上可用空间的数量(以百分比表示),这些值控制何时开始进行进程记帐:
如果可用空间低于这个百分比值,则停止进程记帐
如果可用空间高于这个百分比值,则开始进程记帐
检查上面两个值的频率(以秒为单位)
要更改这个文件的某个值,应该回送用空格分隔开的一串数字。

缺省设置:2 4 30

如果包含日志的文件系统上只有少于 2% 的可用空间,则这些值会使记帐停止,如果有 4% 或更多可用空间,则再次启动记帐。每 30 秒做一次检查。

/proc/sys/kernel/ctrl-alt-del
该文件有一个二进制值,该值控制系统在接收到 ctrl+alt+delete 按键组合时如何反应。这两个值表示:
零(0)值表示捕获 ctrl+alt+delete,并将其送至 init 程序。这将允许系统可以完美地关闭和重启,就好象您输入 shutdown 命令一样。
壹(1)值表示不捕获 ctrl+alt+delete,将执行非干净的关闭,就好象直接关闭电源一样。

缺省设置:0

/proc/sys/kernel/domainname
该文件允许您配置网络域名。它没有缺省值,也许已经设置了域名,也许没有设置。

/proc/sys/kernel/hostname
该文件允许您配置网络主机名。它没有缺省值,也许已经设置了主机名,也许没有设置。

/proc/sys/kernel/msgmax
该文件指定了从一个进程发送到另一个进程的消息的最大长度。进程间的消息传递是在内核的内存中进行,不会交换到磁盘上,所以如果增加该值,则将增加操作系统所使用的内存数量。

缺省设置:8192

/proc/sys/kernel/msgmnb
该文件指定在一个消息队列中最大的字节数。

缺省设置:16384

/proc/sys/kernel/msgmni
该文件指定消息队列标识的最大数目。

缺省设置:16

/proc/sys/kernel/panic
该文件表示如果发生“内核严重错误(kernel panic)”,则内核在重新引导之前等待的时间(以秒为单位)。零(0)秒设置在发生内核严重错误时将禁止重新引导。

缺省设置:0

/proc/sys/kernel/printk
该文件有四个数字值,它们根据日志记录消息的重要性,定义将其发送到何处。关于不同日志级别的更多信息,请阅读 syslog(2) 联机帮助页。该文件的四个值为:
控制台日志级别:优先级高于该值的消息将被打印至控制台
缺省的消息日志级别:将用该优先级来打印没有优先级的消息
最低的控制台日志级别:控制台日志级别可被设置的最小值(最高优先级)
缺省的控制台日志级别:控制台日志级别的缺省值

缺省设置:6 4 1 7

/proc/sys/kernel/shmall
该文件是在任何给定时刻系统上可以使用的共享内存的总量(以字节为单位)。

缺省设置:2097152

/proc/sys/kernel/shmax
该文件指定内核所允许的最大共享内存段的大小(以字节为单位)。

缺省设置:33554432

/proc/sys/kernel/shmmni
该文件表示用于整个系统共享内存段的最大数目。

缺省设置:4096

/proc/sys/kernel/sysrq
如果该文件指定的值为非零,则激活 System Request Key。

缺省设置:0

/proc/sys/kernel/threads-max
该文件指定内核所能使用的线程的最大数目。

缺省设置:2048

/proc/sys/net
/proc/sys/net/core/message_burst
写新的警告消息所需的时间(以 1/10 秒为单位);在这个时间内所接收到的其它警告消息会被丢弃。这用于防止某些企图用消息“淹没”您系统的人所使用的拒绝服务(Denial of Service)攻击。

缺省设置:50(5 秒)

/proc/sys/net/core/message_cost
该文件存有与每个警告消息相关的成本值。该值越大,越有可能忽略警告消息。

缺省设置:5

/proc/sys/net/core/netdev_max_backlog
该文件指定了,在接口接收数据包的速率比内核处理这些包的速率快时,允许送到队列的数据包的最大数目。

缺省设置:300

/proc/sys/net/core/optmem_max
该文件指定了每个套接字所允许的最大缓冲区的大小。

/proc/sys/net/core/rmem_default
该文件指定了接收套接字缓冲区大小的缺省值(以字节为单位)。

/proc/sys/net/core/rmem_max
该文件指定了接收套接字缓冲区大小的最大值(以字节为单位)。

/proc/sys/net/core/wmem_default
该文件指定了发送套接字缓冲区大小的缺省值(以字节为单位)。

/proc/sys/net/core/wmem_max
该文件指定了发送套接字缓冲区大小的最大值(以字节为单位)。

/proc/sys/net/ipv4
所有 IPv4 和 IPv6 的参数都被记录在内核源代码文档中。请参阅文件 /usr/src/linux/Documentation/networking/ip-sysctl.txt。

/proc/sys/net/ipv6
同 IPv4。

/proc/sys/vm
/proc/sys/vm/buffermem
该文件控制用于缓冲区内存的整个系统内存的数量(以百分比表示)。它有三个值,通过把用空格相隔的一串数字写入该文件来设置这三个值。
用于缓冲区的内存的最低百分比
如果发生所剩系统内存不多,而且系统内存正在减少这种情况,系统将试图维护缓冲区内存的数量。
用于缓冲区的内存的最高百分比

缺省设置:2 10 60

/proc/sys/vm/freepages
该文件控制系统如何应对各种级别的可用内存。它有三个值,通过把用空格相隔的一串数字写入该文件来设置这三个值。
如果系统中可用页面的数目达到了最低限制,则只允许内核分配一些内存。
如果系统中可用页面的数目低于这一限制,则内核将以较积极的方式启动交换,以释放内存,从而维持系统性能。
内核将试图保持这个数量的系统内存可用。低于这个值将启动内核交换。

缺省设置:512 768 1024

/proc/sys/vm/kswapd
该文件控制允许内核如何交换内存。它有三个值,通过把用空格相隔的一串数字写入该文件来设置这三个值:
内核试图一次释放的最大页面数目。如果想增加内存交换过程中的带宽,则需要增加该值。
内核在每次交换中试图释放页面的最少次数。
内核在一次交换中所写页面的数目。这对系统性能影响最大。这个值越大,交换的数据越多,花在磁盘寻道上的时间越少。然而,这个值太大会因“淹没”请求队列而反过来影响系统性能。

缺省设置:512 32 8

/proc/sys/vm/pagecache
该文件与 /proc/sys/vm/buffermem 的工作内容一样,但它是针对文件的内存映射和一般高速缓存。

使内核设置具有持久性
这里提供了一个方便的实用程序,用于更改 /proc/sys 目录下的任何内核参数。它使您可以更改运行中的内核(类似于上面用到的 echo 和重定向方法),但它还有一个在系统引导时执行的配置文件。这使您可以更改运行中的内核,并将这些更改添加到配置文件,以便于在系统重新引导之后,这些更改仍然生效。

该实用程序称为 sysctl,在 sysctl(8) 的联机帮助页中,对这个实用程序进行了完整的文档说明。sysctl 的配置文件是 /etc/sysctl.conf,可以编辑该文件,并在 sysctl.conf(8) 下记录了该文件。sysctl 将 /proc/sys 下的文件视为可以更改的单个变量。所以,以 /proc/sys 下的文件 /proc/sys/fs/file-max 为例,它表示系统中所允许的文件句柄的最大数目,这个文件被表示成 fs.file-max。

这个示例揭示了 sysctl 表示法中的一些奇妙事情。由于 sysctl 只能更改 /proc/sys 目录下的变量,并且人们始终认为变量是在这个目录下,因此省略了变量名的那一部分(/proc/sys)。另一个要说明的更改是,将目录分隔符(正斜杠 /)换成了英文中的句号(点 .)。

将 /proc/sys 中的文件转换成 sysctl 中的变量有两个简单的规则:
去掉前面部分 /proc/sys。
将文件名中的正斜杠变为点。

这两条规则使您能将 /proc/sys 中的任一文件名转换成 sysctl 中的任一变量名。一般文件到变量的转换为:

/proc/sys/dir/file --> dir.file
dir1.dir2.file --> /proc/sys/dir1/dir2/file

可以使用命令 sysctl -a 查看所有可以更改的变量和其当前设置。

用 sysctl 还可以更改变量,它所做的工作与上面所用的 echo 方法完全一样。其表示法为:

sysctl -w dir.file="value"

还是用 file-max 作为示例,使用下面两种方法中的一种将该值更改为 16384:

sysctl -w fs.file-max="16384"

或者:

echo "16384" > /proc/sys/fs/file-max


/proc文件系统中网络参数

在/proc/sys/net/ipv4/目录下,包含的是和tcp/ip协议相关的各种参数,下面我们就对这些网络参数加以详细的说明。

ip_forward 参数类型:BOOLEAN
0 - 关闭(默认值)
not 0 - 打开ip转发

在网络本地接口之间转发数据报。该参数非常特殊,对该参数的修改将导致其它所有相关配置参数恢复其默认值(对于主机参阅RFC1122,对于路由器参见RFC1812)

ip_default_ttl 参数类型:INTEGER
默认值为 64 。表示IP数据报的Time To Live值。

ip_no_pmtu_disc 参数类型:BOOLEAN
关闭路径MTU探测,默认值为FALSE

ipfrag_high_thresh 参数类型:整型
用来组装分段的IP包的最大内存量。当ipfrag_high_thresh数量的内存被分配来用来组装IP包,则IP分片处理器将丢弃数据报直到ipfrag_low_thresh数量的内存被用来组装IP包。

ipfrag_low_thresh 参数类型:整型
参见ipfrag_high_thresh。

ipfrag_time 参数类型:整型
保存一个IP分片在内存中的时间。

inet_peer_threshold 参数类型:整型
INET对端存储器某个合适值,当超过该阀值条目将被丢弃。该阀值同样决定生存时间以及废物收集通过的时间间隔。条目越多﹐存活期越低﹐GC 间隔越短

inet_peer_minttl 参数类型:整型
条目的最低存活期。在重组端必须要有足够的碎片(fragment)存活期。这个最低存活期必须保证缓冲池容积是否少于 inet_peer_threshold。该值以 jiffies为单位测量。

inet_peer_maxttl 参数类型:整型
条目的最大存活期。在此期限到达之后﹐如果缓冲池没有耗尽压力的话(例如﹐缓冲池中的条目数目非常少)﹐不使用的条目将会超时。该值以 jiffies为单位测量。

inet_peer_gc_mintime 参数类型:整型
废物收集(GC)通过的最短间隔。这个间隔会影响到缓冲池中内存的高压力。 该值以 jiffies为单位测量。

inet_peer_gc_maxtime 参数类型:整型
废物收集(GC)通过的最大间隔,这个间隔会影响到缓冲池中内存的低压力。 该值以 jiffies为单位测量。

tcp_syn_retries 参数类型:整型
对于一个新建连接,内核要发送多少个 SYN 连接请求才决定放弃。不应该大于255,默认值是5,对应于180秒左右。

tcp_synack_retries 参数类型:整型
对于远端的连接请求SYN,内核会发送SYN + ACK数据报,以确认收到上一个 SYN连接请求包。这是所谓的三次握手( threeway handshake)机制的第二个步骤。这里决定内核在放弃连接之前所送出的 SYN+ACK 数目。

tcp_keepalive_time 参数类型:整型
当keepalive打开的情况下,TCP发送keepalive消息的频率,默认值是2个小时。

tcp_keepalive_probes 参数类型:整型
TCP发送keepalive探测以确定该连接已经断开的次数,默认值是9。

tcp_keepalive_interval 参数类型:整型
探测消息发送的频率,乘以tcp_keepalive_probes就得到对于从开始探测以来没有响应的连接杀除的时间。默认值为75秒,也就是没有活动的连接将在大约11分钟以后将被丢弃。

tcp_retries1 参数类型:整型
当出现可疑情况而必须向网络层报告这个可疑状况之前﹐需要进行多少次重试。最低的 RFC 数值是 3 ﹐这也是默认值﹐根据RTO的值大约在3秒 - 8分钟之间。

tcp_retries2 参数类型:整型
在丢弃激活的TCP连接之前﹐需要进行多少次重试。RFC1122规定,该值必须大于100秒。默认值为15,根据RTO的值来决定,相当于13-30分钟,

tcp_orphan_retries 参数类型:整型
在近端丢弃TCP连接之前﹐要进行多少次重试。默认值是 7 个﹐相当于 50秒 - 16分钟﹐视 RTO 而定。如果您的系统是负载很大的web服务器﹐那么也许需要降低该值﹐这类 sockets 可能会耗费大量的资源。另外参的考 tcp_max_orphans 。

tcp_fin_timeout 参数类型:整型
对于本端断开的socket连接, TCP保持在FIN-WAIT-2状态的时间。对方可能会断开连接或一直不结束连接或不可预料的进程死亡。默认值为 60 秒。过去在2.2版本的内核中是 180 秒。您可以设置该值﹐但需要注意﹐如果您的机器为负载很重的web服务器﹐您可能要冒内存被大量无效数据报填满的风险﹐FIN-WAIT-2 sockets 的危险性低于 FIN-WAIT-1 ﹐因为它们最多只吃 1.5K 的内存﹐但是它们存在时间更长。另外参考 tcp_max_orphans。

tcp_max_tw_buckets 参数类型:整型
系统在同时所处理的最大 timewait sockets 数目。如果超过此数的话﹐time-wait socket 会被立即砍除并且显示警告信息。之所以要设定这个限制﹐纯粹为了抵御那些简单的 DoS 攻击﹐千万不要人为的降低这个限制﹐不过﹐如果网络条件需要比默认值更多﹐则可以提高它(或许还要增加内存)。

tcp_tw_recycle 参数类型:布尔
打开快速 TIME-WAIT sockets 回收。默认值是1。除非得到技术专家的建议或要求﹐请不要随意修改这个值。

tcp_max_orphans 参数类型:整型
系统所能处理不属于任何进程的TCP sockets最大数量。假如超过这个数量﹐那么不属于任何进程的连接会被立即reset,并同时显示警告信息。之所以要设定这个限制﹐纯粹为了抵御那些简单的 DoS 攻击﹐千万不要依赖这个或是人为的降低这个限制

tcp_abort_on_overflow 参数类型:布尔
当守护进程太忙而不能接受新的连接,就象对方发送reset消息,默认值是false。这意味着当溢出的原因是因为一个偶然的猝发,那么连接将恢复状态。只有在你确信守护进程真的不能完成连接请求时才打开该选项,该选项会影响客户的使用。

tcp_syncookies 参数类型:整型
只有在内核编译时选择了CONFIG_SYNCOOKIES时才会发生作用。当出现syn等候队列出现溢出时象对方发送syncookies。目的是为了防止syn flood攻击。默认值是false。

注意:该选项千万不能用于那些没有收到攻击的高负载服务器,如果在日志中出现synflood消息,但是调查发现没有收到synflood攻击,而是合法用户的连接负载过高的原因,你应该调整其它参数来提高服务器性能。参考: tcp_max_syn_backlog, tcp_synack_retries, tcp_abort_on_overflow.

syncookie严重的违背TCP协议,不允许使用TCP扩展,可能对某些服务导致严重的性能影响(如SMTP转发)。

tcp_stdurg 参数类型:整型
使用 TCP urg pointer 字段中的主机请求解释功能。大部份的主机都使用老旧的 BSD解释,因此如果您在 Linux 打开它﹐或会导致不能和它们正确沟通。默认值为为﹕FALSE

tcp_max_syn_backlog 参数类型:整型
对于那些依然还未获得客户端确认的连接请求﹐需要保存在队列中最大数目。对于超过 128Mb 内存的系统﹐默认值是 1024 ﹐低于 128Mb 的则为 128。如果服务器经常出现过载﹐可以尝试增加这个数字。警告﹗假如您将此值设为大于 1024﹐最好修改 include/net/tcp.h 里面的 TCP_SYNQ_HSIZE ﹐以保持 TCP_SYNQ_HSIZE*16 0)或者bytes-bytes/2^(-tcp_adv_win_scale)(如果tcp_adv_win_scale 128Mb 32768-61000
0)则系统将忽略所有发送给自己的ICMP ECHO请求或那些广播地址的请求。

icmp_destunreach_rate - 整数
icmp_paramprob_rate - 整数
icmp_timeexceed_rate - 整数
icmp_echoreply_rate - 整数(not enabled per default)
限制发向特定目标的ICMP数据报的最大速率。0表示没有任何限制,否则表示jiffies数据单位中允许发送的个数。

icmp_ignore_bogus_error_responses - 布尔类型
某些路由器违背RFC1122标准,其对广播帧发送伪造的响应来应答。这种违背行为通常会被以告警的方式记录在系统日志中。如果该选项设置为True,内核不会记录这种警告信息。默认值为False。

(1) Jiffie: 内核使用的内部时间单位,在i386系统上大小为1/100s,在Alpha中为1/1024S。在/usr/include/asm/param.h中的HZ定义有特定系统的值。

conf/interface/*:
conf/all/*是特定的,用来修改所有接口的设置,is special and changes the settings for all interfaces.
Change special settings per interface.

log_martians - 布尔类型
记录带有不允许的地址的数据报到内核日志中。

accept_redirects - 布尔类型
收发接收ICMP重定向消息。对于主机来说默认为True,对于用作路由器时默认值为False。

forwarding - 布尔类型
在该接口打开转发功能

mc_forwarding - 布尔类型
是否进行多播路由。只有内核编译有CONFIG_MROUTE并且有路由服务程序在运行该参数才有效。

proxy_arp - 布尔类型
打开proxy arp功能。

shared_media - 布尔类型
发送(路由器)或接收(主机) RFC1620 共享媒体重定向。覆盖ip_secure_redirects的值。默认为True。

secure_redirects - 布尔类型
仅仅接收发给默认网关列表中网关的ICMP重定向消息,默认值是TRUE。

send_redirects - 布尔类型
如果是router,发送重定向消息,默认值是TRUE

bootp_relay - 布尔类型
接收源地址为0.b.c.d,目的地址不是本机的数据报。用来支持BOOTP转发服务进程,该进程将捕获并转发该包。默认为False,目前还没有实现。

accept_source_route - 布尔类型
接收带有SRR选项的数据报。对于主机来说默认为False,对于用作路由器时默认值为True。

rp_filter 参数类型
1 - 通过反向路径回溯进行源地址验证(在RFC1812中定义)。对于单穴主机和stub网络路由器推荐使用该选项。
0 - 不通过反向路径回溯进行源地址验证。
默认值为0。某些发布在启动时自动将其打开。’

/proc --- 一个虚拟文件系统
/proc 文件系统是一种内核和内核模块用来向进程 (process) 发送信息的机制 (所以叫做 /proc)。这个伪文件系统让你可以和内核内部数据结构进行交互,获取 有关进程的有用信息,在运行中 (on the fly) 改变设置 (通过改变内核参数)。 与其他文件系统不同,/proc 存在于内存之中而不是硬盘上。如果你察看文件 /proc/mounts (和 mount 命令一样列出所有已经加载的文件系统),你会看到其中 一行是这样的:



grep proc /proc/mounts
/proc /proc proc rw 0 0

/proc 由内核控制,没有承载 /proc 的设备。因为 /proc 主要存放由内核控制的状态信息,所以大部分这些信息的逻辑位置位于内核控制的内存。对 /proc 进行一次 'ls -l' 可以看到大部分文件都是 0 字节大的;不过察看这些文件的时候,确实可以看到一些信息。这怎么可能?这是因为 /proc 文件系统和其他常规的文件系统一样把自己注册到虚拟文件系统层 (VFS) 了。然而,直到当 VFS 调用它,请求文件、目录的 i-node 的时候,/proc 文件系统才根据内核中的信息建立相应的文件和目录。


加载 proc 文件系统
如果系统中还没有加载 proc 文件系统,可以通过如下命令加载 proc 文件系统:


mount -t proc proc /proc

上述命令将成功加载你的 proc 文件系统。更多细节请阅读 mount 命令的 man page。


察看 /proc 的文件
/proc 的文件可以用于访问有关内核的状态、计算机的属性、正在运行的进程的状态等信息。大部分 /proc 中的文件和目录提供系统物理环境最新的信息。尽管 /proc 中的文件是虚拟的,但它们仍可以使用任何文件编辑器或像'more', 'less'或 'cat'这样的程序来查看。当编辑程序试图打开一个虚拟文件时,这个文件就通过内核中的信息被凭空地 (on the fly) 创建了。这是一些我从我的系统中得到的一些有趣结果:

$ ls -l /proc/cpuinfo
-r--r--r-- 1 root root 0 Dec 25 11:01 /proc/cpuinfo

$ file /proc/cpuinfo
/proc/cpuinfo: empty

$ cat /proc/cpuinfo

processor : 0
vendor_id : GenuineIntel
cpu family : 6
model : 8
model name : Pentium III (Coppermine)
stepping : 6
cpu MHz : 1000.119
cache size : 256 KB
fdiv_bug : no
hlt_bug : no
sep_bug : no
f00f_bug : no
coma_bug : no
fpu : yes
fpu_exception : yes
cpuid level : 2
wp : yes
flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca
cmov pat pse36 mmx fxsr xmm
bogomips : 1998.85

processor : 3
vendor_id : GenuineIntel
cpu family : 6
model : 8
model name : Pentium III (Coppermine)
stepping : 6
cpu MHz : 1000.119
cache size : 256 KB
fdiv_bug : no
hlt_bug : no
sep_bug : no
f00f_bug : no
coma_bug : no
fpu : yes
fpu_exception : yes
cpuid level : 2
wp : yes
flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca
cmov pat pse36 mmx fxsr xmm
bogomips : 1992.29


这是一个从双 CPU 的系统中得到的结果,上述大部分的信息十分清楚地给出了这个系统的有用的硬件信息。有些 /proc 的文件是经过编码的,不同的工具可以被用来解释这些编码过的信息并输出成可读的形式。这样的工具包括:'top', 'ps', 'apm' 等。



得到有用的系统/内核信息

proc 文件系统可以被用于收集有用的关于系统和运行中的内核的信息。下面是一些重要的文件:

/proc/cpuinfo - CPU 的信息 (型号, 家族, 缓存大小等)
/proc/meminfo - 物理内存、交换空间等的信息
/proc/mounts - 已加载的文件系统的列表
/proc/devices - 可用设备的列表
/proc/filesystems - 被支持的文件系统
/proc/modules - 已加载的模块
/proc/version - 内核版本
/proc/cmdline - 系统启动时输入的内核命令行参数
proc 中的文件远不止上面列出的这么多。想要进一步了解的读者可以对 /proc 的每一个文件都'more'一下或读参考文献[1]获取更多的有关 /proc 目录中的文件的信息。我建议使用'more'而不是'cat',除非你知道这个文件很小,因为有些文件 (比如 kcore) 可能会非常长。


有关运行中的进程的信息
/proc 文件系统可以用于获取运行中的进程的信息。在 /proc 中有一些编号的子目录。每个编号的目录对应一个进程 id (PID)。这样,每一个运行中的进程 /proc 中都有一个用它的 PID 命名的目录。这些子目录中包含可以提供有关进程的状态和环境的重要细节信息的文件。让我们试着查找一个运行中的进程。

$ ps -aef | grep mozilla
root 32558 32425 8 22:53 pts/1 00:01:23 /usr/bin/mozilla

上述命令显示有一个正在运行的 mozilla 进程的 PID 是 32558。相对应的,/proc 中应该有一个名叫 32558 的目录


$ ls -l /proc/32558
total 0
-r--r--r-- 1 root root 0 Dec 25 22:59 cmdline
-r--r--r-- 1 root root 0 Dec 25 22:59 cpu
lrwxrwxrwx 1 root root 0 Dec 25 22:59 cwd -> /proc/
-r-------- 1 root root 0 Dec 25 22:59 environ
lrwxrwxrwx 1 root root 0 Dec 25 22:59 exe -> /usr/bin/mozilla*
dr-x------ 2 root root 0 Dec 25 22:59 fd/
-r--r--r-- 1 root root 0 Dec 25 22:59 maps
-rw------- 1 root root 0 Dec 25 22:59 mem
-r--r--r-- 1 root root 0 Dec 25 22:59 mounts
lrwxrwxrwx 1 root root 0 Dec 25 22:59 root -> //
-r--r--r-- 1 root root 0 Dec 25 22:59 stat
-r--r--r-- 1 root root 0 Dec 25 22:59 statm
-r--r--r-- 1 root root 0 Dec 25 22:59 status

文件 "cmdline" 包含启动进程时调用的命令行。"envir" 进程的环境变两。 "status" 是进程的状态信息,包括启动进程的用户的用户ID (UID) 和组ID(GID) ,父进程ID (PPID),还有进程当前的状态,比如"Sleelping"和"Running"。每个进程的目录都有几个符号链接,"cwd"是指向进程当前工作目录的符号链接,"exe"指向运行的进程的可执行程序,"root"指向被这个进程看作是根目录的目录 (通常是"/")。目录"fd"包含指向进程使用的文件描述符的链接。 "cpu"仅在运行 SMP 内核时出现,里面是按 CPU 划分的进程时间。

/proc/self 是一个有趣的子目录,它使得程序可以方便地使用 /proc 查找本进程地信息。/proc/self 是一个链接到 /proc 中访问 /proc 的进程所对应的 PID 的目录的符号链接。



通过 /proc 与内核交互

上面讨论的大部分 /proc 的文件是只读的。而实际上 /proc 文件系统通过 /proc 中可读写的文件提供了对内核的交互机制。写这些文件可以改变内核的状态,因而要慎重改动这些文件。/proc/sys 目录存放所有可读写的文件的目录,可以被用于改变内核行为。

/proc/sys/kernel - 这个目录包含反通用内核行为的信息。 /proc/sys/kernel/{domainname, hostname} 存放着机器/网络的域名和主机名。这些文件可以用于修改这些名字。



$ hostname
machinename.domainname.com

$ cat /proc/sys/kernel/domainname
domainname.com

$ cat /proc/sys/kernel/hostname
machinename

$ echo "new-machinename" > /proc/sys/kernel/hostname

$ hostname
new-machinename.domainname.com



这样,通过修改 /proc 文件系统中的文件,我们可以修改主机名。很多其他可配置的文件存在于 /proc/sys/kernel/。这里不可能列出所有这些文件,读者可以自己去这个目录查看以得到更多细节信息。
另一个可配置的目录是 /proc/sys/net。这个目录中的文件可以用于修改机器/网络的网络属性。比如,简单修改一个文件,你可以在网络上瘾藏匿的计算机。


$ echo 1 > /proc/sys/net/ipv4/icmp_echo_ignore_all

这将在网络上瘾藏你的机器,因为它不响应 icmp_echo。主机将不会响应其他主机发出的 ping 查询。



$ ping machinename.domainname.com
no answer from machinename.domainname.com

要改回缺省设置,只要

$ echo 0 > /proc/sys/net/ipv4/icmp_echo_ignore_all

/proc/sys 下还有许多其它可以用于改变内核属性。读者可以通过参考文献 [1], [2] 获取更多信息。


结论
/proc 文件系统提供了一个基于文件的 Linux 内部接口。它可以用于确定系统的各种不同设备和进程的状态。对他们进行配置。因而,理解和应用有关这个文件系统的知识是理解你的 Linux 系统的关键。

意外的eBay 4面

为什么说“意外”?
原因有俩:
一 我已经拿到了eBay的暑假实习offer;二 没想到这次长达一个小时的电面是目前为止最为刻骨铭心的。(如果喝葛P泉生病那天能坚持微软亚洲研究院的电面的话可能也是今天这样的心情)。
郁闷但是很高兴:因为我的面经已经相当丰富了,哈哈。

本来接到电话已经是下午4点了,我开始犹豫了一下该不该接上海的这个电话。因为我明天就要去ASK.com实习了,eBay就算了吧。老大也是这么和我说的,他说干脆直接拒了那边,就说去微软了。赫赫,我觉得还是应该礼貌点好,人家这次过来的是eBay产品搜索部的头头了,就当再拿一次面经吧。其实无论是去参加google的笔试还是去拿eBay的offer,或者是投微软和百度,无非就是为了面经,为了给10月份的求职加码,说到底我根本不会去这些地方工作也不会离开杭州去生活个几年的。所以就是面经!

好了,那么就面吧。整整一个小时,我经历了非常刺激的电话面试,那边的那个男人的声音非常低沉,感觉是有些岁数了。他主要还是针对我在搜索领域的知识,非常非常的细节。细节到每一个实现是怎么做的,如果“出现XXX情况又该如何解决”。第二部分是英语,要求我把前面涉及到的一些实现细节用英语描述。感觉很吃力,因为一些专业用语毕竟只停留在阅读水平,而不像自我介绍时那么easy的了。然后,继续用英语说到了算法,我希望把他引向我比较了解的哈希表而不是往B+树走。但是哈希表我真的很了解吗?也许很了解,但是也就停留在书本和简单实现的层次上,对于一些意外的情况也许只能通过项目来提升自己的知识面了,比如亿级的数据量,比如哈希表满了,再比如哈希表到底开多少大是合适的,很多很多。后面讲到了快速排序,虽然用英语和他交流,但是还是以为可以easy过去,因为eBay二面的时候已经有人问过了,不就是O(nlogn)然后递归什么的。这次不同,他的出发点就是考察我对算法的理解和复杂度的分析的理解上,不单纯是一个排序算法了。其间我试图用递归树的来解释时间复杂度,算法导论上的几种方法之一,但是他似乎要我解释为什么可以用递归树来解决算法复杂度的计算。莫非是要我给出证明?太夸张了吧,这不由的让我想到了前些日子的google的一道笔试题目。一身汗。最后又对快速排序的最好最佳情况分别进行了讨论,特别让人感觉恶心的是pivot的选择上面,在这个问题上我们足足讨论了有10分钟,因为它决定了快速排序的worst case。

这个电面飞快地过去,其间老大还洗了个澡,我感觉他怎么洗的那么快。对方给我了一个邮件地址,让我发一份电子版的email给他。就这么结束了。后来还和老大交流了很多细节东西,收获颇多啊,看来没有白费我的一个小时,赫赫。

2007年5月14日星期一

葛P泉害人不浅!

长这么大还第一次上吐下泻!
都是学校后勤部门黑心啊!利益集团重重保护,害的我们学生喝大肠杆菌超标40倍的脏水!这是人干的事情么?!!!

2007年5月9日星期三

Linux系统调用列表

以下是Linux系统调用的一个列表,包含了大部分常用系统调用和由系统调用派生出的的函数。这可能是你在互联网上所能看到的唯一一篇中文注释的Linux系统调用列表,即使是简单的字母序英文列表,能做到这么完全也是很罕见的。

按照惯例,这个列表以man pages第2节,即系统调用节为蓝本。按照笔者的理解,对其作了大致的分类,同时也作了一些小小的修改,删去了几个仅供内核使用,不允许用户调用的系统调用,对个别本人稍觉不妥的地方作了一些小的修改,并对所有列出的系统调用附上简要注释。

其中有一些函数的作用完全相同,只是参数不同。(可能很多熟悉C++朋友马上就能联想起函数重载,但是别忘了Linux核心是用C语言写的,所以只能取成不同的函数名)。还有一些函数已经过时,被新的更好的函数所代替了(gcc在链接这些函数时会发出警告),但因为兼容的原因还保留着,这些函数会在前面标上“*”号以示区别。

一、进程控制:

fork 创建一个新进程

clone 按指定条件创建子进程

execve 运行可执行文件

exit 中止进程

_exit 立即中止当前进程

getdtablesize 进程所能打开的最大文件数

getpgid 获取指定进程组标识号

setpgid 设置指定进程组标志号

getpgrp 获取当前进程组标识号

setpgrp 设置当前进程组标志号

getpid 获取进程标识号

getppid 获取父进程标识号

getpriority 获取调度优先级

setpriority 设置调度优先级

modify_ldt 读写进程的本地描述表

nanosleep 使进程睡眠指定的时间

nice 改变分时进程的优先级

pause 挂起进程,等待信号

personality 设置进程运行域

prctl 对进程进行特定操作

ptrace 进程跟踪

sched_get_priority_max 取得静态优先级的上限

sched_get_priority_min 取得静态优先级的下限

sched_getparam 取得进程的调度参数

sched_getscheduler 取得指定进程的调度策略

sched_rr_get_interval 取得按RR算法调度的实时进程的时间片长度

sched_setparam 设置进程的调度参数

sched_setscheduler 设置指定进程的调度策略和参数

sched_yield 进程主动让出处理器,并将自己等候调度队列队尾

vfork 创建一个子进程,以供执行新程序,常与execve等同时使用

wait 等待子进程终止

wait3 参见wait

waitpid 等待指定子进程终止

wait4 参见waitpid

capget 获取进程权限

capset 设置进程权限

getsid 获取会晤标识号

setsid 设置会晤标识号

二、文件系统控制

1、文件读写操作

fcntl 文件控制

open 打开文件

creat 创建新文件

close 关闭文件描述字

read 读文件

write 写文件

readv 从文件读入数据到缓冲数组中

writev 将缓冲数组里的数据写入文件

pread 对文件随机读

pwrite 对文件随机写

lseek 移动文件指针

_llseek 在64位地址空间里移动文件指针

dup 复制已打开的文件描述字

dup2 按指定条件复制文件描述字

flock 文件加/解锁

poll I/O 多路转换

truncate 截断文件

ftruncate 参见truncate

umask 设置文件权限掩码

fsync 把文件在内存中的部分写回磁盘

2、文件系统操作

access 确定文件的可存取性

chdir 改变当前工作目录

fchdir 参见chdir

chmod 改变文件方式

fchmod 参见chmod

chown 改变文件的属主或用户组

fchown 参见chown

lchown 参见chown

chroot 改变根目录

stat 取文件状态信息

lstat 参见stat

fstat 参见stat

statfs 取文件系统信息

fstatfs 参见statfs

readdir 读取目录项

getdents 读取目录项

mkdir 创建目录

mknod 创建索引节点

rmdir 删除目录

rename 文件改名

link 创建链接

symlink 创建符号链接

unlink 删除链接

readlink 读符号链接的值

mount 安装文件系统

umount 卸下文件系统

ustat 取文件系统信息

utime 改变文件的访问修改时间

utimes 参见utime

quotactl 控制磁盘配额

三、系统控制

ioctl I/O总控制函数

_sysctl 读/写系统参数

acct 启用或禁止进程记账

getrlimit 获取系统资源上限

setrlimit 设置系统资源上限

getrusage 获取系统资源使用情况

uselib 选择要使用的二进制函数库

ioperm 设置端口I/O权限

iopl 改变进程I/O权限级别

outb 低级端口操作

reboot 重新启动

swapon 打开交换文件和设备

swapoff 关闭交换文件和设备

bdflush 控制bdflush守护进程

sysfs 取核心支持的文件系统类型

sysinfo 取得系统信息

adjtimex 调整系统时钟

alarm 设置进程的闹钟

getitimer 获取计时器值

setitimer 设置计时器值

gettimeofday 取时间和时区

settimeofday 设置时间和时区

stime 设置系统日期和时间

time 取得系统时间

times 取进程运行时间

uname 获取当前UNIX系统的名称、版本和主机等信息

vhangup 挂起当前终端

nfsservctl 对NFS守护进程进行控制

vm86 进入模拟8086模式

create_module 创建可装载的模块项

delete_module 删除可装载的模块项

init_module 初始化模块

query_module 查询模块信息

*get_kernel_syms 取得核心符号,已被query_module代替

四、内存管理

brk 改变数据段空间的分配

sbrk 参见brk

mlock 内存页面加锁

munlock 内存页面解锁

mlockall 调用进程所有内存页面加锁

munlockall 调用进程所有内存页面解锁

mmap 映射虚拟内存页

munmap 去除内存页映射

mremap 重新映射虚拟内存地址

msync 将映射内存中的数据写回磁盘

mprotect 设置内存映像保护

getpagesize 获取页面大小

sync 将内存缓冲区数据写回硬盘

cacheflush 将指定缓冲区中的内容写回磁盘

五、网络管理

getdomainname 取域名

setdomainname 设置域名

gethostid 获取主机标识号

sethostid 设置主机标识号

gethostname 获取本主机名称

sethostname 设置主机名称

六、socket控制

socketcall socket系统调用

socket 建立socket

bind 绑定socket到端口

connect 连接远程主机

accept 响应socket连接请求

send 通过socket发送信息

sendto 发送UDP信息

sendmsg 参见send

recv 通过socket接收信息

recvfrom 接收UDP信息

recvmsg 参见recv

listen 监听socket端口

select 对多路同步I/O进行轮询

shutdown 关闭socket上的连接

getsockname 取得本地socket名字

getpeername 获取通信对方的socket名字

getsockopt 取端口设置

setsockopt 设置端口参数

sendfile 在文件或端口间传输数据

socketpair 创建一对已联接的无名socket

七、用户管理

getuid 获取用户标识号

setuid 设置用户标志号

getgid 获取组标识号

setgid 设置组标志号

getegid 获取有效组标识号

setegid 设置有效组标识号

geteuid 获取有效用户标识号

seteuid 设置有效用户标识号

setregid 分别设置真实和有效的的组标识号

setreuid 分别设置真实和有效的用户标识号

getresgid 分别获取真实的,有效的和保存过的组标识号

setresgid 分别设置真实的,有效的和保存过的组标识号

getresuid 分别获取真实的,有效的和保存过的用户标识号

setresuid 分别设置真实的,有效的和保存过的用户标识号

setfsgid 设置文件系统检查时使用的组标识号

setfsuid 设置文件系统检查时使用的用户标识号

getgroups 获取后补组标志清单

setgroups 设置后补组标志清单

八、进程间通信

ipc 进程间通信总控制调用

1、信号

sigaction 设置对指定信号的处理方法

sigprocmask 根据参数对信号集中的信号执行阻塞/解除阻塞等操作

sigpending 为指定的被阻塞信号设置队列

sigsuspend 挂起进程等待特定信号

signal 参见signal

kill 向进程或进程组发信号

*sigblock 向被阻塞信号掩码中添加信号,已被sigprocmask代替

*siggetmask 取得现有阻塞信号掩码,已被sigprocmask代替

*sigsetmask 用给定信号掩码替换现有阻塞信号掩码,已被sigprocmask代替

*sigmask 将给定的信号转化为掩码,已被sigprocmask代替

*sigpause 作用同sigsuspend,已被sigsuspend代替

sigvec 为兼容BSD而设的信号处理函数,作用类似sigaction

ssetmask ANSI C的信号处理函数,作用类似sigaction

2、消息

msgctl 消息控制操作

msgget 获取消息队列

msgsnd 发消息

msgrcv 取消息

3、管道

pipe 创建管道

4、信号量

semctl 信号量控制

semget 获取一组信号量

semop 信号量操作

5、共享内存

shmctl 控制共享内存

shmget 获取共享内存

shmat 连接共享内存

shmdt 拆卸共享内存

[ZZ]我对const的总结(整理)

发信人: olddog (汪汪汪), 信区: C++
标 题: 我对const的总结(整理)
发信站: 飘渺水云间 (Thu Jul 4 21:04:12 2002), 转信

1. Const 推出的初始目的,正是为了取代预编译指令,消除它的缺点,同时继承它的优点。
注意一点:const可以避免不必要的内存分配
#define STRING "abcdefghijklmn\n"
const char string[]="abcdefghijklm\n";
...
printf(STRING); //为STRING分配了第一次内存
printf(string); //为string一次分配了内存,以后不再分配
...
printf(STRING); //为STRING分配了第二次内存
printf(string);
...
由于const定义常量从汇编的角度来看,只是给出了对应的内存地址,
而不是象#define一样给出的是立即数,所以,const定义的常量在
程序运行过程中只有一份拷贝,而#define定义的常量在内存中有
若干个拷贝。

另外:const可以限定符声明变量只能被读 ,可以便于进行类型检查 , 必须初始化;


2.const的重要特点:
C++的编译器通常不为普通const常量分配存储空间,而是将它们保存在符号表中,这使得它成为一个编译期间的常量,没有了存储与读内存的操作,使得它的效率也很高,同时,这也是它取代预定义语句的重要基础。这是因为编译器不会去读存储的内容,如果编译器为const分配了存储空间,它就不能够成为一个编译期间的常量了。 另外:const会由编译器对它进行类型的检测。其实,最主要记住一点 const是向左结合的!!!

const出现位置(example)
<1>
const int i = 0;
const char * a;
char * const cp;
<2>
const T1 func(T2)
const char * func(T2)
char * const func(T2)
T1 func(T2) const
T1 func(const char *)
T1 func(char * const T2)


意义
<1>
char * const cp ;//const pointer to char
char const * cp; //pointer to const char
const char * pc; //pointer to const char
从右往左读


3.const的应用
a. const指针和指针const: 注意const是左结合的;
b. const限定了函数的传递值(引用)参数;
c. const限定函数的返回值;



4.与类有关的const;

a.const成员函数;
class MyClass
{
public:
  int Fun() const;
}
const 后置的形式是一种规定,亦为了不引起混淆。在此函数的声明中和定义中均要使用const,因为const已经成为类型信息的一部分。获得能力:可以操作常量对象。失去能力:不能修改类的数据成员,不能在函数中调用其他不是const的函数。
1)类成员函数后加const不能修改类的除了mutable以外的成员变量
2)非const对象 调用 非const函数, 若不存在则调用相应的const版本
3)const对象只能调用const成员函数

const char * func(T2) 这是返回值是const对象的非const函数;


b. const成员变量;
  类型修饰符const不仅可以说明成员函数,也可以说明数据成员。由于const类型对象必须被初始化,并且不能更新,因此,在类中说明了const数据成员时,只能通过成员初始化列表的方式来生成构造函数对数据成员初始化。
例如:
class A
{
public:
A(int i);
const int &r;
private:
const int a;
static const int b;
};

const int A::b=10; //static const init
A::A(int i):a(i), r(a) //const init in constructor
{
}

例如下面有这样一个例子:
class A
{
void func1() const //NO.1
{}
void func1() //NO.2
{}
void func2() const //NO.3
{}
void func3() //NO.4
{}
}

const A ca;
A a;

a.func1();//call NO.2
a.func2();//call NO.3
a.func3();//call NO.4

ca.func1();//call NO.1
ca.func2();//call NO.3
ca.func3();//error

是不是const的常量值一定不可以被修改呢?
观察以下一段代码:
const int i=0;
int *p=(int*)&i;
p=100;
通过强制类型转换,将地址赋给变量,再作修改即可以改变const常量值。

--

2007年5月8日星期二

以高市盈率筑起中国股市的长城

纵观中国15年的证券市场发展史,凡行情“牛”转“熊”或“熊”转“牛”,无非以下三因素:管理层打压或扶持、主力做空或做多、股民看空或看多。其中,最值得重视的是管理层态度。1999年“5.19”行情的发动、2001年从2245点跌至998点就是典型案例。

此次人代会上温总理的工作报告强调,要稳定发展证券市场。中央要的是一个配合国营大、中型企业改制、有利宏观经济调控的股市。

让我们来体会一下最高管理层的良苦用心:

2月份开始的有关中国股市“泡沫”的大争论为什么嘎然而止?“泡沫论”的主导人之一的全国人大副委员长成思危为什么会改口?什么人、什么力量才能使成思危改口?

证监会面对当前股市的强劲上涨,除了对市场违规操作行为进行监管以外,一概不发声音,并且证监会批准发行的许多新股市盈率允许高达50倍以上。这是为什么?

为什么《人民日报》对市盈率泡沫的评价则是总体健康?

为什么以左小蕾为代表的有海外资金背景的所谓经济学家、专家,从1300点就开始唱空中国股市,到现在已经2800点,股市仍不回调,于是便要求最高管理层对股市进行政策干预。但管理层对其似乎并不理会?

QFII(境外合格投资者)要求增加证券投资额度,但管理层为什么至今未松口子?

国家为什么从明年起要统一内、外资企业所得税率为25%(目前内资企业一般为33%,外资企业为15--18%)?

国家为什么决定由国务院牵头成立对外投资公司,将目前过多的外汇储备引导到去国外投资?

近日国家发改委经济研究院的研究报告向中央建议:对当前我国的资本流动性过剩的现象,要采取利用流动性的方针政策,通过资本市场将过剩的资金配置到急需产业升级的重工化基础制造业。国家发改委是在什么背景下?为什么会提出这样的报告?

答案是:管理层希望股市市盈率维持在高位,不希望我国金融开放(以于2006年12月11日正式开放)后,外资有在低位进入股市而在高位套现的机会,从而避免中国出现类似1997年的东南亚金融风波和90年代初期日本证券市场遭遇国际游资抽资的风险。

市盈率60倍高不高?在2001年6月上证指数达2245点时,股市由此开始变盘下行,进入漫漫熊市。当时,其相应的整体市盈率为62倍,一年期存款利率同现在差不多,而企业的平均盈利水平只有2006年的60%左右。而现在,中国经济已是今非昔比,上市公司平均年复合增长30%以上,市盈率60倍明显不算高。平均市盈率60倍所对应的上证指数为4320点左右。

上市企业利润按25%复合增长(2006年末已经超过25%)计算,到2007年末,涨到5400点的市盈率还是60倍,到2008年末涨到6750点还是60倍,到2009年末涨到8450点还是60倍,到2010年末涨到10550点,市盈率还是60倍......

有理由认为,这就是管理层认可60倍市盈率的理由!

管理层之所以对股市加以呵护,应该是出于以下几点考虑:

首先,可以分流过剩的流动性(见著名经济学家、复旦大学谢百三教授的观点);

其次,为企业特别是产业升级的重工化基础行业的龙头企业发展筹措资金;

第三,可以避免承担巨额的银行坏帐损失。

第四,管理层通过股市要实现更多的国有企业改制和企业投资主体多元化。

发改委研究院王小广说的很透彻:

“股市热一点要比房市热一点更好。可以考虑利用流动性过剩的机会,通过股市的发展来促进产业结构升级,通过产业政策与资本市场结合,大力发展代表产业升级方向的重化工业、装备制造业和现代制造业,促进国民经济竞争力的提高。”

我国前几年搞的中、小国企业改制很不理想,很多国有资产被低于净资产值贱卖,甚至几乎是送给了私营企业的老板。而其间,滋生许多腐败。目前,国家手里所剩企业多为规模大、效益好的企业,如果再采取这样的方式改制,势必会给国家造成巨大的经济损失,也不利于社会稳定。而采取在股市上出让,无疑是一个很好的办法,不仅可以卖个更好的价钱,增加国家财政收入,而且可以实现参与证券投资的广大民众利益均沾,相对比较合理。

上述理由,便是管理层认可高市盈率的原因。种种政策迹象表明,中国管理层不希望外资大量流入股市!

股市的高市盈率将逼使国际游资面对中国股市望而却步,只好向低市盈率的其它国家的市场流动!日本经济十年大萧条的一个最主要因素,就是国际资本在东京证券交易所疯狂掠夺日本经济高速增长的成果后一走了之。

中国现在和以前大不一样,高额的外汇储备和庞大的过剩流动资金清楚地表明,引进外资已经没有太多的必要。我们没有必要再让外资来分享我国经济高速增长的蛋糕。甚至,中央已经决定在境外上市的公司,要象交通银行一样逐步回国内上市,不能轻易地让国际货币资本占有我中国人民创造的劳动财富。所以管理层最近出台一系列政策,限制外资入市、打击地下钱庄。

国家已经公开表明,今年要重点推动国有大型企业整体上市。今后一段时期,国有上市公司将有3万多亿的优质资产注入。而每一笔国有资产走向股市,就是一次给全民分享福利的机会。我们民族的财富自然只能属于我们。

这样分析,就不难理解为什么管理层不愿大幅增加QFII的额度了。也不难理解为什么QFII老是唱空中国股市。

要构建和谐社会,真正最直接代表广大中国人民利益的事情,就是要将党的11届3中全会以来,中国改革开放30年的经济成果,通过股市这个平台,让我们中国人民自己来分享,而不是让外国人来分享!全民持股,就是国民共享我国经济高速增长30年来的经济成果!就是有中国特色的社会主义的本质体现!

对此,老外们看不懂,证券洋帮办也看不懂。因为他们只懂“经济学”不知道“政治经济学”为何物!他们习惯于用静态市盈率论事,而不愿意用动态市盈率看问题;在理论方面,他们只知道科技创新是革命,而不知道制度创新是最伟大的革命。

在这场没有硝烟的战争中,美国想干什么?美国在干什么?现在已经非常清楚,他们就是要通过美圆对人民币的贬值和促使人民币在资本项目下的自由兑换,来达到鲸吞中国人民创造的巨额物质财富的目的。

这是一场新形势下两种社会形态及其价值理念的较量!为了中华民族的利益,管理层应该也必将用高市盈率筑起中国证券市场的长城,以奋起抵抗国际资本的入侵!

我们正面临一个百年的历史性大机遇。这个机遇就是要创造一个高市盈率的证券市场。任何犹豫不决和小富即安的思想都会使你感到遗憾!

[ZZ]随机垃圾收集

http://linux.chinaunix.net/bbs/archiver/tid-893113.html
随机垃圾收集

内存管理作为资源管理的一种形式一直以来就是软件开发中的难题,大部分编程错误都出现在和内存操作有关的代码中,几十年来内核程序员一直在寻求更好的内存管理机制。事实证明,有效的资源管理技术是绝大多数软件开发从业人员所难以掌握的。因此,早在20世纪60年代垃圾收集就已经随着LISP的发明而被提出并广泛关注,以试图提供一种自动化的内存管理机制。虽然历经了长期发展,在解释型语言中成功的广泛应用,并且不断以程序库的方式向C/C++这种系统级开发语言中渗透,但是作为一种自动化资源管理机制还远未达到广泛适用,尤其是系统及软件的要求。因此在前人的研究基础上,以节点复制为基本框架,结合系统开发中的内存管理的实际,采用随机收集算法,提出一种适用度广泛的新型垃圾收集算法,并可应用于编译器和操作系统的内存管理、闪存文件系统等方面开发之中。
David在Lcc开发中大量使用的一种基于对象生存周期的内存管理方式Arena。Arena是先分配一个内存块,然后再从该内存块中分配内存数据,在整块内的节点都死亡后一次释放整个内存块,从而大大简化了代码,消除了存储漏洞,并避免了内存泄露和重复释放的问题。而其缺点就是可能需要更多的内存空间,并且可能出现悬挂引用,更重要的是这种生存周期确定并一致特例在现实中很少存在。节点复制是一种高效,接口、模型最为简洁的收集方法,唯一的诟病就是庞大的内存需求。结合节点复制的优点,改进这个内存管理模型,仍然是先分配一个内存块,然后再从该内存块中分配内存数据。然而由于对象生存周期的不确定性,在一段时间后不是释放整个内存块,而是扫描整个块看其中还有没有存活的数据,将存活数据复制到一个新块中去再释放该块。
由于是将内存分块分配和收集,而不是分成两个半区,只是一个块一个块的收集,减小了收集的范围,缩短了因收集而产生的中断。如果程序总共用N个块的话用于垃圾收集的内存为1/N,而不是1/2,所以又降低了节点复制方法的内存占用率。因为节点复制算法是搬迁式的,所以同时还消除了内存碎片,从而又降低了分配对象的代价。
为进一步提高性能,就要考虑收集的顺序。大多数商用系统会采用类似“中断”的方式进行收集,然而和外设不同的是,内存没有计算能力,所有关于内存的计算都是靠CPU来完成的,在这里,“中断”方式仍然是线性的if-else形式。因此,采用“中断”收集方式并不比“轮询”收集方式要好。
简单的自加1或自减1的偏序操作的代价是非常低的,那么是顺序收集还是逆序收集呢?一般不会考虑逆序收集,因为很多操作系统都会尽可能的从低地址连续分配内存,逆序收集无疑会扫描很多空白区域,所以顺序收集是个不错的方法。然而访问内存总不像访问寄存器那样直接,况且不同的机器的内存的容量和数量又是不尽相同的,不同的内存的硬件访问方式也是不同的,再加上与cache的关系,用随机访问来掩盖差异是在合适不过的了。
采用随机的收集顺序,一个random()并不会带来多高的代价但却会有很好的效果。收集正在使用的块是一件很麻烦的事情,会造成很大的性能损失,如果总共有N块内存的话,当前使用率为n/N(通常n不会很大),收集率为1/N,收集到当前使用块的概率为n/N^2,机会还是很小的。而每块被收集到的概率是相等的,又不会出现某些块总被收集到某些块却总也收集不到的现象。
充分利用内存会得到很好的性能,就不一定会固守尽可能从低地址连续分配内存的主观教条,从而整个内存空间的使用状况是离散的,这样就会更加适合于使用随机收集的方式。
闪存和内存存在一定程度上的相似性,而现在闪存文件系统也需要某种有效的垃圾收集机制的支持,因此我将此收集算法改造出一个适用于闪存文件系统的变体。因为现在广泛使用的Nand_Flash需要按块读写,所以该收集算法还可用于基于Nand_Flash的闪存文件系统。Nand_Flash有擦写次数寿命的限制,需要写均衡技术的支持,对此我们只要把新块分配到尚未使用的新块中,即可达到写均衡的效果。由于Nand_Flash会随机的产生坏块,使用该收集算法可以不把物理坏块映射到任何逻辑块上,使得文件系统在逻辑层上是连续可用的。
为进一步提高收集效率还可以采取分代的策略,并把那些生存周期确定不变的数据(如C语言中的全局变量)放在一个块中并且不对该块进行扫描,让该块随着程序的结束而被释放,对于那些大型数据,进行标记远比复制的代价要小得多。
为了避免直接使用ANSI/ISO C的malloc()和free()函数而造成的内存碎片和操作时间不确定等问题,著名实时内核MicroC/OS-II把连续大块内存分区,每个分区又分成一些大小相同的块来进行管理。如果将该收集算法加以改进并应用在操作系统的内存管理和虚拟内存管理模块中,按区、块收集内存,简化了内存管理和虚拟内存管理的模型,同时也解决了内存碎片的问题,给整个系统和应用程序提供更有效的存储管理机制,从而带来更好的性能。在操作系统层面上提供垃圾收集机制不仅性能更好,又使得垃圾收集机制在整个系统上是统一的,而不必分别在不同的编译器和应用程序上实现垃圾收集机制,以避免一个系统中存在多个垃圾收集机制而造成的资源浪费和潜在的冲突。

2007年4月29日星期日

恩杰二面归来

本来今天是个开心的日子:以来昨天晚上弄到两点的POS机终于可以跑sqlite-2.8.17了,另一方面,今天收到了人生第一个offer(尽管只是实习offer)。但是刚才去ask.com进行二面让我忧心忡忡。一扫前一段日子baidu和ebay面试的轻松和愉悦,可能这个更能让我清醒的认识到自己依然是那么土。

好了,说说面经吧。

到了ask首先让我感到意外的是居然有三个男的面我,和我第一次去遭受的草草了事的面试截然不同,这次似乎ask是有备而来的。

ask: 熟悉正则表达式么?
我: 不熟(因为我知道他要问脚本的东西)。

ask: 熟悉脚本语言么?
我: 不熟悉。只是知道有bash, perl, python,昨天刚刚买了learning perl, 准备一个星期入门。

ask: 说一下你在实验室的工作?你的实验室就是那个和cgogo。。。。?
我: 是的。。。。。。

ask: 你们在实验室主要用win还是unix?
我: unix。

ask: 那你怎么不熟悉脚本呢?
我: 我一般只是编编C/C++程序,对系统管理和一块并不熟所以就不太写脚本,而且一些应用脚本网上都有的down的所以一般只是copy而已。
(那三人面面相觑,这个时候坐我右边的一个人出去接电话了,少了一个。)

ask: 那说说C吧,你用的比较多的。C里面如何优化分配内存的?如何提高内存分配和回首的效率?不停的malloc和不停free会产生什么问题?
我: 发呆状...突然提到一个“缓冲”,但是感觉不是用在内存上的,于是说“不清楚”。

ask: 在linux下面如何调试多线程和多进程?如何根据core.dump提供的信息调试?
我: 暗自想“土了土了”,——不知道

ask: 熟悉linux kernel么?你用linux用到什么层次?
我: 不太熟悉,理论懂一点,没有太多实践。分析过一些IPC,写过测试,分析它们的效率。我一般就用到系统API这种层面,在往下就不太接触了。(又被鄙视,脚本这种日常的维护不会,而底层高端的开发也不懂,我自我调侃到)

ask: 那说说unix下面有哪几种IPC?
我: &……%*(流畅,难得啊,终于有流畅的了,多亏高级OS的课程作业)

ask: 如果有一堆url,它们指向一堆content,如何设计一种方法让它可以根据url找到对应的content?
我: hash.

ask: hash的算法有哪些?
我: (回答成冲突解决了,原来他说的hash算法就是hash function,我居然说不知道,但被鄙视了!该回家翻书了)

ask: 给你一个中序和后序遍历的结点序列,把它们还原成一棵树,在黑板上画。
我: (花了一分钟解决,还是觉得做得太慢,应该10秒钟解决才对,如果是考研那会的话估计肯定是秒杀的)

ask: *&……%¥(说了一下实习的相关细节)
我: No problems。

ask: 你希望我们最迟什么时候给你答复?
我: 5月10号左右吧...

终。


感想:
这次面试给我留下深刻的印象,因为太出丑了。我在回来的路上仔细想了下,觉得C/C++还是很有区别的。比如百度和ebay,它们都问了STL,而且baidu还问道了stl内部的实现机制和存在的危险,多线程不安全等以及多线程的调试。多线程这一块看来是一定要去实践一下的,这个可能是众多c/c++程序员必备的素质,特别是搞搜索。数据结构三个公司都问到了hash, 百度和ebay还问到了b+树。显然ask对于c/c++和unix开发的基本功要求远远高于baidu和ebay,后两个都是大公司可能更在意综合能力,比如ebay会全程English面你,知道和我聊到ebay和google这种过于复杂的问题才让我插几句Chinese。而ask这样相对的小公司可能更能培养技术上的一些东西,这是我个人的想法,当然没有拿到baidu的offer挺遗憾的,其实我在这个领域也只能算是入门,以后的路还长,要坚定的走下去,别再东搞搞java西弄弄c/c++了。把c和linux API弄熟是我今后一段日子的主要目标,就是通过我们寝室的那个金融搜索作为我训练实战的舞台。
最后打算利用一个星期的时间把Perl入门了,然后如果有幸能进去就在ask那边一边做一边深入Perl和网页分析等技术。

ebay offer到手

虽然工资很低,但是过去做的事情还是挺吸引我的。先是自贺一把,生平第一个offer嘛,哈哈!

Confidential

Intern Offer Letter实习邀请函


Dear 亲爱的 **

I’m writing to confirm an invitation for you to join internship of eBay China Development Center. The following are the key elements of your offer.

本信函是邀请您来亿贝软件工程(上海 )有限公司进行实习。具体情况如下:

* DEPARTMENT 实习部门

You will be offered as an intern in the department: PD

* DAILY ALLOWANCE 实习补贴

Your daily salary will be RMB*** (Before tax), plus lunch allowance of RMB** per day. Monthly housing allowance will be RMB*** only for those interns not coming from Shanghai or studying in Shanghai local universities. You will be responsible for your personal income tax.

* INTERNSHIP PERIOD 实习期间

Your internship will be from July 17th, 2007

* OTHER 其他

We anticipate that your internship will last for 8-12 months until you graduate from school. Your internship agreement, however, might be renewed once every 3-month based on the business needs, a new agreement shall be formulated.

You must guarantee that all the information and materials you provided are true; any falsity will result in immediate dismissal by eBay CDC.

我们预期您将在毕业之前一直在eBay CDC实习。在您报到之日我们将与您签实习协议,此协议每三个月续签一次。您应保证您提供的所有应聘信息和资料全都属实。如有不真实者,亿贝软件工程(上海)有限公司有权立即解除协议。



Your sincerely,

您真诚的

eBay China Development Center

亿贝软件工程(上海)有限公司

Date/日期:April 28th, 2007

2007年4月27日星期五

是全新交友方式,还是一夜情的温床?

男人女人总是一场演不完的游戏,但是抛开性和肉的赤裸裸,还剩下些什么?说白了也就这么一回事了,女人是虚荣的,特别是物质经济刺激下的今天,所以如果遇到了一个真心爱你的人,千万不要轻易放弃。希望以后带着老婆去“八点半”刺激一下。我和她如果褪去学生装的话都是非常耀眼的帅哥美女,呵呵。



是全新交友方式,还是一夜情的温床?
www.zjol.com.cn  2004年07月30日  浙江在线新闻网站

  即使没有了绚烂的华灯,杭州的夜也依旧闪耀着诱惑的光华……就像是位于杭州天目山路的八点半交友酒吧里一盏盏暧昧的红灯。

  这家名叫“八点半”的交友酒吧开张才几个月,却已成为杭州一些个性开放的前卫人群当下最红的去处。

  坊间种种传闻,让这家八点半交友酒吧蒙上了一层暧昧而神秘的面纱。这究竟是一家怎样的酒吧?抱着这样的疑问,记者在7月26日至7月28日,连续三天前往这家交友酒吧以窥其真实面目。

  火红的生意 陌生的男女

  7月26日晚上22点,记者一行5人前往八点半交友酒吧。“八点半交友酒吧”几个硕大的字在夜色中分外的醒目,而门口的车已经停得满满的。两个穿黑T恤的男子正来回奔跑着指挥陆续前来的车辆,“拐弯停到伊家鲜门口去,满了满了”,看到我们的车,他很没好气地叫着,与一般酒吧泊车员的殷勤截然不同。

   推开酒吧的大门,里面的喧嚣连同热闹扑面而来,与门外暗了一半路灯的天目山路形成强烈的对比。黑暗中,一盏盏红色的灯亮在每张桌子的上方,空气里有一种蒙昧不清的潮热在涌动。而舞台上握着一根钢管舞动的女子,配上电子屏幕上滚动的红字,正在诉说着“L先生想对圆圆小姐说认识你真好”的心迹,耳边阿杜那首《撕夜》被演绎得分外凄迷。

  酒吧里面的MM或吊带小背心、或单肩蝙蝠衫、或紧身衫小热裤、或半个后背裸露着的清凉着装。黑暗中,一个戴着墨镜、穿着露出臀沟的超低腰裤的前卫女子款款而来。一旁穿着花衬衫的引座服务生很熟络地和那女子打了个招呼,回头对记者说,“常客稳定、新客又多,所以下次要赶早来才有空位”;边说着,边艰难地给记者一行在门口安排了位子。

  由于是坐在大门口的位置,所以可以很清晰地看清每一个进出的人。记者发现来这里的女子大多衣着前卫、大胆,且年纪从22岁到40岁不等;而来的男子多以中年为主,也有不少事业有成的年轻小白领,所以出手大多还算大方。据酒吧的服务生介绍,一旦陌生女子欣然应约前来,男子必出手大方地任前来的女子点她要吃的酒水、点心、水果。

  因为是定位在陌生男女的交友会,来这家酒吧玩的人多目标明确,所以同来的绝大多数是同性朋友——不是清一色的男人,就是一群女孩子,绝少带着异性同来的。用这家酒吧门口卖羊肉串的贩子的话来说“别的酒吧一起来的都是有男有女,但是这家来的时候都是男的归男的,女的归女的,但走的时候却已是配好了对的”。

  交友酒吧催生各种欲望

  火红的生意、陌生的男女宾客是每一个到八点半交友酒吧最初的印象。这个地方能在其后的几个小时里催生出人的各种欲望。

  这家类似夜总会的交友酒吧与一般酒吧迥异的是,在每一张桌子上都有一部红色的电话机,上方悬着一盏红色的灯,灯上贴着老远就能看清的桌号。这是酒吧为来宾提供的一种全新的交友方式。只要你看到喜欢的异性,就可拿起听筒来、按照灯上黑的数字直接拨打对方桌上的电话。此外,来宾还可以用自己的手机发送交友信息至1778301,或者写在千纸鹤上交由DJ涛仔为你一诉衷肠。

  于是,借着酒吧里昏暗的灯光,那些寻找刺激的人的道德和准则就这样瓦解,因为看不到对方、不会被当面拒绝,所以电话邀约更助长了陌生男女相互结识的“勇气”。更何况酒吧还专门打印了一份《女(男)人打电话十大注意事项》,以此教初来此处的男人、女人如何用电话邀约陌生人。

  少许的酒,迷离的音乐,再加上这个定位为“交友”的酒吧因此就有了这样堂皇的氛围,更让由陌生人变情人在这里跨越了时间,幻化成一种极其简单的步骤。记者在这三天中就几次目睹那些先前陌生的男女在一个“勇敢”的电话邀约后,在酒精和音乐的催生下,他们用两个小时完成了由陌生人到情人的转变,更将人们惯常的道德准则轻易地抛弃。于是乎,就有了虽然进来的多是同性朋友,可离去的时候他们大都已有了相熟的异性朋友同去的场景。他们或举止亲昵地一同离去,或在座位上搂抱在一起,更有几个开放的男女,当众情不自禁地互舔起来,全然不顾旁人的尴尬。

  夜交往的暧昧游戏

  与记者同来的男性朋友试着拨了远处两个女孩的桌号,稍顷就有一个打扮入时的长发女子欣然前来应邀。

  微笑、落座,这是一个容貌姣好的女子,端着自己的酒杯,她穿得尚算得体,并非如先前几位的暴露、清凉。记者顺手拿起桌上的酒瓶要为她斟酒,结果她一把用手捂上了酒杯,示意服务生拿来新酒——原来,不喝别人的残酒是这里的规则,据说主要是提防别有用心的人在开的酒中下药。

  她自称“晨晨”,对于“做什么职业、哪里人”的提问,她充耳不闻;不仅如此,她也对记者一行的职业、年纪等情况不问一句……不问背景、不交换名片是这里最基本的游戏规则。

  “你要吃什么自己点”,同去的朋友很大方地放出豪言。晨晨虽然衣着简朴,但为人热情,记者才刚去了一趟洗手间,回来便见她已经手拿薯条往记者同去的男性朋友嘴里喂,并摆出一副很可人的姿态,倒是那位男士露出几分腼腆来。

  据晨晨自己说,她是这里的常客,可来得并不频繁,一周最多来这里两次。她说来这里的客人多是收入还算可以的小白领,孤单寂寞是她们来这里的主要原因,但享受被男人追捧、体会自己超然吸引力是来这里的女子共同的爱好,所以她们必然是悉心装扮、艳丽登场的。晨晨说完,故意向记者显了一下手上那个欧米茄手表,以显示她的经济实力和非三陪的身份。

  “我是个孤独的人,我来这里是等我命中的白马王子……因为早上要上班的缘故,我通常12点多就撤了。”晨晨一边说着,一边娇羞地看了一眼刚才放豪言请她自己点东西的朋友,“你不送我一下吗?”晨晨临走的时候对记者同去的朋友发出这样暧昧的邀请……

  据酒吧门口的几个常在那里候车的出租车司机说:“晚上我们常有接到那种先是男的要送女的回家,后来不是直接去了宾馆,就是一起下了车、去其中的一个人家里的。”

  看着酒吧里种种所谓“全新”的交往,闻着在酒精催生下、人们忘却道德和行为准则的暧昧空气,记者不禁纳闷,八点半交友酒吧这样的服务究竟是在为人们搭起一座新的沟通桥梁,还是在为一夜情提供放纵的温床,并加速毁灭人们濒临灭绝的爱情信念?



来源: 今日早报 作者: 文/本报记者 莫际芳 摄/本报记者 程瑞鑫

2007年4月26日星期四

[ZZ]谈谈Web Mining研究者掌握动态脚本语言的必要性

发信人: xuqingyang (网事随风), 信区: SearchEngineTech
标 题: 谈谈Web Mining研究者掌握动态脚本语言的必要性
发信站: 水木社区 (Fri Mar 23 17:58:30 2007), 站内

自己平时很喜欢在闲暇时学习一些新的计算机语言,它使我对计算机的兴趣不至于在书写各种申报材料和论文中完全泯灭。特别是在我把工作平台转向Linux后,学习各种开源语言更成为自己掌握在Linux下工作的一种必然选择,除了C/C++、JAVA、C#这些主流开发语言外,我尤其对Perl、Python和Ruby这样的动态脚本语言情有独衷,这里特别谈谈自己在学习它们的过程中的一些体会。

第一次接触Perl语言是自己在作WebMining研究时从CMU大学的著名网页数据集Web-KB开始的,它的代码里有一些Perl语言脚本,往往几行代码就能完成JAVA这样的开发语言几十行代码才能完成的任务。显然它比Bash这样的Linux中的shell脚本功能要强大得多。由于第一次读到Perl语言书写的代码,感觉真的像天书一样难懂,特别是那些到处可见的$、@、%等奇怪的符号。为了完全弄懂Web-KB的这些代码,我下决心开始学习Perl,结果没想到从此一发不可收,很快就成为Perl铁定的忠实拥户。下面是我总结的几点它最突出的优点:

1。实用:
它像编码世界里的"瑞士军刀",很多常用的处理方式特别作了很大的简化,让你用最少的代码量完成最多的功能。在JAVA语言里你需要首先声明各种变量,在程序执行前需要进行编译,在ERL语言里这些都去掉了,这大大简化了程序开发人员的开发效率。在JAVA程序里即使你只是打印一行"HelloWorld"也要先定义一个类,再定义一个static voidmain函数作为程序调用的入口,在PERL程序中这些全都不必了;再比如从网上下载网页在JAVA语言里至少要写上几行的代码,在Perl里只用一句就可以: use LWP::Simple; $html =
get('http://www.sina.com.cn');由于经常需要引入package,所以选择use而不是JAVA里的import,因为它比后者少敲三个键。由于编程时90%的情况是处理文本,它就把正则表达式作为语言的内置功能而不像JAVA那样首先需要import re;在所有动态脚本语言中它也是运行速度最快的。

2。第三方类库有集中的CPAN网站及其镜像站点统一管理:
这是另一个让JAVA语言开发者嫉妒的优势,浏览一下网页http://search.cpan.org/吧,它把十年来所有开源的第三方软件包分门别类地集中存储,如果你愿意,也可以把自己开发的觉的有价值的软件包与全世界共享。JAVA虽然有Jarkata这样的第三方类库,但绝大多数还是需要我们自己去寻找。

3。它是最像自然语言的计算机语言,不要忘记它的作者以前曾经是一个语言学家,如果你牢记这一点,那学习Perl语言便容易得多。我认为Perl语言也是目前我学到的所有语言中最具创新性的语言(它直接影响了Ruby),深深影响了以后各种新的语言的设计。

4。它是完全开源的语言,它的作者是计算机历史上大名鼎鼎的Larry Wall,他发明了patch命令和一个新闻组阅读器,但更主要的是他发明了Perl和CGI动态网页技术,他对Web的迅猛发展有直接的影响力。如果你想学习一门很多年后依然保持生命力的语言,那最保险的办法就是学习一门开源的语言,因为如果你去学习Delphi这样的商业语言,等到Borland公司一旦在商业市场上份额下降,那这种语言可能就完全风光不再。现在最热门的语言JAVA是Sun公司的宠儿,虽然Sun公司总裁声称在今年年底会完全公开JAVA源码,但显然这门语言的发展完全掌握在商业的运营模式中。

Perl语言当然有它的缺点:不像Python和Ruby一样易学;面向对象功能不是一开始就设计而是后来添加的;代码可读性较差,过于灵活。Larry Wall在2000年每年一度的开源大会上宣称开发新一代perl语言(即Perl 6)的计划正式发始启动,它将摆脱现在向下兼容的包袱,从内核上对语言进行重新设计,采纳当下各门语言的精华同时保留它最根本的特性。这是让无数perl爱好者兴奋到整夜难眠的事情,现在开发Perl6还没有实现,这中间台湾的唐宗汉(现变性后改为唐凤)起了力挽狂澜的作用,是我们华人在开源领域最骄傲的人物。(见http://www.linuxeden.com/doc/21782.html)我认为学习像perl这样的脚本语言对计算机专业的研究者来说尤其适合,因为它可以大大加快开发原型或验证算法的过程,特别是它对文本处理的支持至今是无与伦比的。以我个人经验来说,作Web Mining研究有一个很漂亮的想法很容易,但想得到实验数据的支持必须经过大量的Coding,它占用整个过程99%以上的时间(只关心发论文不在意伪造数据的除外)。如果用C++或JAVA语言来实现,你要花大量的时间用在Debug上面,但用Perl这样的快速开发语言,只花费十分之一的精力就能将其搞定,它免去了声明和编译(包括make过程),同时由于有很多现成的第三方软件包可以使用(在cpan里很容易找到Naive Bayes,Support Vector Machine这些机器学习工具),它的功能却一点也不逊色。特别的,由于它的代码量只有前者的几分甚至十几分之一,debug也相对容易得多。另外,使用perl语言最好结合Unix/Linux系统(虽然它在跨平台特性上一点不比JAVA差),这更能发挥它应有的作用。事实上在Linux系统下Perl是必备的工具(很多系统管理命令甚至带窗口的程序都是用它编写的,比如synaptic),你甚至无须去安装它。

Python是另一个非常好的选择,它的好处是代码可读性好,对面向对象支持得非常好,非常易学(一个下午的时间可以学会80%场合用到的20%命令),另外与C/C++和Java的互相调用也作得非常出色(所以它是一种更好的glue language),还有一个Perl语言没有的交互式编译器,它类似于以前苹果机上的Basic,你可以无须编写程序而直接在终端一行行边输入程序边看到结果,所以非常有利于快速开发。它的作者现在为Google工作,据说Google很多的代码都是用Python书写的。Python的名字来自国外非常流行的一个黑色幽默电影,放假时中央电影频道还专门介绍过它(下次我再次学习它时会先那部电影看一遍,找找感觉),开源世界的一大特色就是这种无比轻松的幽默而不是像微软产品包含的那种浓浓的商业气息。

Ruby现在由于Ruby on Rails的流行而成为亲的上升之星,有人甚至预言它是未来的JAVA,它从Perl语言和Ada语言吸取了很多东西,自己打算有空闲时间的时候好好学习它。它的发明人是个日本人,有些中国程序员便排斥这门优秀的语言,我当然对此不会在意的。据说敏捷开发之父非常推崇这种语言,因为它是比JAVA还要纯的面向对象语言,像字符串和数学这些都作为对象处理,可以直接调用各种方法。可以想见,它也是速度最慢的动态脚本语言。但很多情况下这一点并不重要。

2007年4月24日星期二

[ZZ]C++内存分配


一、内存基本构成
可编程内存在基本上分为这样的几大部分:静态存储区、堆区和栈区。他们的功能不同,对他们使用方式也就不同。
静态存储区:内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。它主要存放静态数据、全局数据和常量。
栈区:在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。
堆区:亦称动态内存分配。程序在运行的时候用malloc或new申请任意大小的内存,程序员自己负责在适当的时候用free或delete释放内存。动态内存的生存期可以由我们决定,如果我们不释放内存,程序将在最后才释放掉动态内存。但是,良好的编程习惯是:如果某动态内存不再使用,需要将其释放掉,否则,我们认为发生了内存泄漏现象。

二、三者之间的区别
我们通过代码段来看看对这样的三部分内存需要怎样的操作和不同,以及应该注意怎样的地方。
例一:静态存储区与栈区

char* p = “Hello World1”;

char a[] = “Hello World2”;

p[2] = ‘A’;

a[2] = ‘A’;

char* p1 = “Hello World1;”


这个程序是有错误的,错误发生在p[2] = ‘A’这行代码处,为什么呢,是变量p和变量数组a都存在于栈区的(任何临时变量都是处于栈区的,包括在main()函数中定义的变量)。但是,数据 “Hello World1”和数据“Hello World2”是存储于不同的区域的。

因为数据“Hello World2”存在于数组中,所以,此数据存储于栈区,对它修改是没有任何问题的。因为指针变量p仅仅能够存储某个存储空间的地址,数据“Hello World1”为字符串常量,所以存储在静态存储区。虽然通过p[2]可以访问到静态存储区中的第三个数据单元,即字符‘l’所在的存储的单元。但是因为数据“Hello World1”为字符串常量,不可以改变,所以在程序运行时,会报告内存错误。并且,如果此时对p和p1输出的时候会发现p和p1里面保存的地址是完全相同的。换句话说,在数据区只保留一份相同的数据(见图1-1)。

例二:栈区与堆区

char* f1()

{

char* p = NULL;

char a;

p = &a;

return p;

}

char* f2()

{

char* p = NULL:

p =(char*) new char[4];

return p;

}

这两个函数都是将某个存储空间的地址返回,二者有何区别呢?f1()函数虽然返回的是一个存储空间,但是此空间为临时空间。也就是说,此空间只有短暂的生命周期,它的生命周期在函数f1()调用结束时,也就失去了它的生命价值,即:此空间被释放掉。所以,当调用f1()函数时,如果程序中有下面的语句:



char* p ;

p = f1();

*p = ‘a’;

此时,编译并不会报告错误,但是在程序运行时,会发生异常错误。因为,你对不应该操作的内存(即,已经释放掉的存储空间)进行了操作。但是,相比之下, f2()函数不会有任何问题。因为,new这个命令是在堆中申请存储空间,一旦申请成功,除非你将其delete或者程序终结,这块内存将一直存在。也可以这样理解,堆内存是共享单元,能够被多个函数共同访问。如果你需要有多个数据返回却苦无办法,堆内存将是一个很好的选择。但是一定要避免下面的事情发生:



void f()

{



char * p;

p = (char*)new char[100];



}


这个程序做了一件很无意义并且会带来很大危害的事情。因为,虽然申请了堆内存,p保存了堆内存的首地址。但是,此变量是临时变量,当函数调用结束时p变量消失。也就是说,再也没有变量存储这块堆内存的首地址,我们将永远无法再使用那块堆内存了。但是,这块堆内存却一直标识被你所使用(因为没有到程序结束,你也没有将其delete,所以这块堆内存一直被标识拥有者是当前您的程序),进而其他进程或程序无法使用。我们将这种不道德的“流氓行为”(我们不用,却也不让别人使用)称为内存泄漏。这是我们C++程序员的大忌!!请大家一定要避免这件事情的发生。

总之,对于堆区、栈区和静态存储区它们之间最大的不同在于,栈的生命周期很短暂。但是堆区和静态存储区的生命周期相当于与程序的生命同时存在(如果您不在程序运行中间将堆内存delete的话),我们将这种变量或数据成为全局变量或数据。但是,对于堆区的内存空间使用更加灵活,因为它允许你在不需要它的时候,随时将它释放掉,而静态存储区将一直存在于程序的整个生命周期中。
我们此专题仅仅是简要的分析了内存基本构成以及使用它们时需要注意的问题。对内存的分析和讨论将一直贯穿于我们以后所有的专题,这也就是为什么把它作为第一讲的原因。

Scott Meyers: 我之C++拍案惊奇

我之C++拍案惊奇

在本系列第五也是最后一篇文章里,我将选出让自己拍案惊奇、豁然开朗的五个时刻。

如果你从事某项工作的时间足够长,必然有几次在疑窦丛生的同时,忽感豁然开朗的经历(如果没有过,那你肯定是入错行了)。每当这些时刻来临的时候,我都惊得呆了,禁不住大口吸气;好像原来只见黑白二色,突然穿越了时空,来到一个五光十色的世界。最后我慢慢回过味来,面带微笑。如此时刻让人激动。疑窦烟消云散,洞明取代了它的位置。

这样的事情在1978年就出现过一次。经过长期的煎熬,有一天我突然明白了指针的工作原理——如果说软件学习之路上也有成年礼的话,那它就是吧。但我那时还在使用Pascal编程,因此不能将它列入这个和C++相关的名单。现在公开我的选择吧:


认识到C++中特殊成员函数可以声明为private[注释1],1988年。和很多朋友一样,那时我正在自学C++。某天,刚毕业的同事John Shewchuk跑到我的办公室问我,“如果得到一个不可拷贝对象?”在场的有好几个人,但都不知如何回答。我们知道,如果不定义拷贝构造函数和拷贝赋值操作符,那么编译器会自动加入,最后得到的对象是可拷贝的。若要阻止编译器自动生成,我们就必须手工定义,但这样一来,对象还是可拷贝的。就像Grinch[注释2]一样,我们个个迷惑不解,没有人能找到解决办法。

后来(可能是当天或第二天,我记不清了),John宣称自己有了解决办法:将拷贝构造函数声明为private就搞定了。现在看来,这个问题是多么简单啊!但当时对于我们来说,不蒂于发现了新大陆;这是我们对C++知识融会贯通的重要一步。三年后,我出《Effective C++》第一版时,将这个简单的发现供奉在一个独立的条款(不到一页,大概是这本书中最短的条款)里。再后来,我愈加意识到这个发现的重要性,因此在《Effective C++》的后面两版中都写了进去。1988年,我不觉得这种用private阻止编译器隐式生成函数的方法显而易见;现在是2006年,我还是这么认为。


理解Barton 和Nackman在单位分析(译者注:Dimensional Analysis。更学术化的叫法是量纲分析)法中提出的无类型模板参数(non-type template parameters)的用法,1995年。1988年5月,我在《IEEE Software》上读到Robert F. Cmelik和Narain H. Gehani合著的一篇文章——《Dimensional Analysis with C++》。他们提出了一种在物理单位(如长度、速度和时间等)的计算过程中检测单位错误的方法。比如,用长度除以时间,再将结果和一个速度量比较是正确的,但和加速度量(它由长度除以时间的平方得来)比较就错了。Cmelik和Gehani提出,可以将单位信息存储到对象中,然后在运行时进行错误检测。这种方法将使对象变大,而且耗费运行时间。我觉得应该有更好的办法,但折腾再三也没有结果,后来就不了了之。

John J. Barton和Lee R. Nackman在他们1994年出版的《Scientific and Engineering C++》(Addison-Wesley出版社)中提出了一个很好的单位问题解决方案。不过,虽然我当时也拿到了这本书,却没有注意到该项成果——老实说,这本书写得太糟糕了点,我开了个头就扔到一边。直到1995年,我通读了Barton和Nackman发表在《C++ Report》上的专栏文章,他们这次用通俗易懂的语言描述了自己的方案。结果给我留下了三方面深刻印象。第一,它涵盖了单位的所有可能组合,而不仅仅以命名为依据的组合,因为命名是不完全的。例如,我们将长度除以时间的结果命名为速度,还将压力除以长度的平方的结果命名为压强,但却没有给长度乘以时间的平方再除以角速度的立方的结果一个名份。至少我不知道。即便计算中产生了迄今为止还用不到的单位组合,B&N方案也会确保单位分析的正确性。

第二是B&N方案的运行时消耗:没有。对象没有变大,程序也没有变慢。因此可以说B&N方案是无本而万利[注释3] 。这才是我真正感兴趣的组合方式。

不过最让连连称奇的,还是他们对无类型模板参数(代表各种基本单位的指数式)及其上算术指令(计算结果单位类型)的使用 [注释4]。这样,他们不仅解决了多年前搞得我兴趣索然的实际问题,而且使用的还是一项C++特性(即无类型模板参数。它在那以前引发了我无穷的好奇心)。

直到今天,我还为Barton和Nackman的成果激动。原本打算将他们在《C++ Report》上的文章列入我的“C++历史上最重要文献”名单,但后来我发现它影响甚微——很少有人像我那样认为他们的成果具有重大意义。现在,我觉得自己有点可耻,因为我只顾自己满足,却没有将好东西与更多人分享。


理解Visitor模式的涵义,1996或1997年。命名恰当,是软件工程的一个基本原则。这儿就有一个例子,充分说明了糟糕的命名会多么折磨人。我没觉得Visitor模式的设计机理有什么特别问题,但就是一直弄不明白它的意义。我无法将支离破碎的认识融会贯通。直到后来有一天,我终于明白:Visitor模式和“访问”毫无关系。其实,它是一种体系设计方法——要求引入新的虚拟行为函数时不必改变原体系的结构。抓住这点后,我一下就理解了这个模式的含义。但其命名于我造成了巨大理解障碍,甚至看了《Design Patterns》(http://www.artima.com/cppsource/top_cpp_books.html#dp)如下的描述后:

Visitor使你不用改变行为操作的元素的类,就可以定义新行为。

这个解释清楚而直接,现在看来很好理解,但我当时就是盯住了模式的名字,总觉得“Visitor”应该和“访问”、“遍历”啥的发生点关系。

出现这种结果,我想有两个可能原因。一是我死心眼,见识短浅,鼠目寸光。再有就可能是这个名字选得过于随意。如果名字本身指的张三,而使用文档上说的却是李四,那么至少一些人——比较执拗的那种——肯定要搞糊涂了。我倾向于后一种解释。


理解“remove”为什么实际上并没有删除任何东西,1998年?我与STL的remove算法相遇得不是时候。当我期望Visitor设计模式访问个啥的时候,我也认定remove算法就应该删除某个东西。但结果让我非常震惊,我发现在容器上执行remove[注释5]时,容器内元素数目根本不会改变!我有一种被出卖的感觉——我是正儿八经要求删除啊!骗子!谎言!无聊的广告!

后来我读到一篇文章——可能是Andrew Koenig的《C++ Containers are Not Their Elements》(发表于《C++ Report》1998年11-12月刊)——它才让我明白STL内部的真相:算法不能改变容器内元素的数目,因为算法根本不知道容器的类型。容器还可能是一个数组呢,显然数组的大小是不可改变的[注释6]。自然,算法应该和容器彼此独立,互不影响。我认识到,“remove”不会改变容器内元素数目,因为它不能。直到那时,我才算真正理解了STL的内部结构,知道迭代器(iterator)虽然通常由容器成员函数提供,但就像容器和算法一样,其实它也是完全独立的实体。后来,我把这篇文章读了很多次。类似上面的解释,可能别人都说过很多回了,但别怪我鹦鹉学舌,这可是我第一次真正理解remove。

自此以后,我就能与remove和睦相处了。再后来,当发现remove不仅将份内事情做得很好,而且效率超过绝大多数程序员自己编写的循环(remove的运行时间是线性的,而普通循环是二次的)时,我甚至对它有点另眼相看了。虽然我仍然不太喜欢这个命名,但也说不清到底哪个名字既能准确描述其行为,又便于记忆。


理解Boost库里shared_ptr的deleter如何工作,2004年。Boost的引用计数智能指针shared_ptr很有趣——你可以向其构造器传递一个函数或者仿函数(function object,或functor),当引用计数归零的时候,它将在被引用对象上调用删除器(deleter)[译注7]。乍一看,似乎没啥了不起啊,但请看代码:



template

class shared_ptr {

public:

template

explicit shared_ptr(U* ptr, D deleter);

...

};


注意shared_ptr必然在析构时调用类型为D的删除器,然而它根本不知道D为何物。这个对象不能包含类型为D的数据成员,也不能指向类型为D的对象,因为声明其数据成员时,D对它而言还是未知的。那么,shared_ptr对象如何跟踪删除器(它在构造阶段传入;当T对象将被销毁时,还得使用它)呢?更通俗地说,构造器如何将未知类型的信息传递给它正在构造的对象,而这个对象本身对信息类型完全无知?

答案很简单:让此对象包含一个指向已知类型基类的指针(Boost叫它sp_counted_base),然后让构造器以D为参数实例化一个派生于上述基类的模板(Boost中叫sp_counted_impl_p和sp_counted_impl_pd),最后用声明于基类、实现于派生类的虚函数(Boost中使用dispose)去调用删除器。用图表示更为直观:


完全明白了——只要你看过这个图[译注8、9]。而且,看过此图后,我想你马上就会意识到它可以应用在很多领域;它为模板设计拓宽了思路,比如,模板化类使用很少的模板参数(例如shared_ptr只有一个),就可以跟踪无限个先前未知类型的信息)。当我想到这些的时候,我禁不住面露赞许的微笑,难抑钦佩之情[译注10]。


好了,文章结束,这就是我的5×5系列的末篇。简单总结一下本系列全部文章:C++历史上最重要的图书、文献、软件、人物,最后是对我来说最难以忘怀的五个神奇时刻。我还将再次和大家讨论这类有趣的话题,不过那至少应该是又一个18年后了。


注释:

1. 可能不一定非叫“特殊”成员函数不可——不过《Standard》如此称呼——具体包括缺省构造器、拷贝构造器、拷贝赋值操作符和析构函数。之所以“特殊”,是因为如果使用了它们而又未显式声明,编译器一般会隐式生成。

2. “Grinch两腿冰冷站在雪地里,想了又想:‘怎么会这样呢?’”参见Dr. Seuss的《Grinch如何偷走圣诞节》(Random House出版社,1957年)。不过在网上也能看到哦(http://www.kraftmstr.com/christmas/books/grinch.html,别告诉Random House)。

3. 我指的是不消耗更多运行时时间。他们大量使用了模板,当然会增加编译时间。

4. 你将两个数相乘时,就等于增加了指数次数,没忘记吧?

5. 我指的是STL的通用算法,而非list类的成员函数。

6. 重新分配内存不能算,不是所有的数组都是动态分配的。

7. 不要吃惊,TR1的shared_ptr就是以Boost的shared_ptr为基础并提供了相同功能。我正讨论的是Boost的shared_ptr,因为它有一个实现,我们这里说的也是实现问题。TR1仅仅是一个规范,如果你书生意气,问我TR1里讲的东西如何实现,那就没意思了。

8. 更准确地说,你以前应该看到过相关解释。譬如我,我得到的解释(我经常要别人给我讲解C++方面的问题)来自于Usenet新闻组的一个免费讨论(http://tinyurl.com/r66ql)。

9.我相信它也是外部多态(External Polymorphism,http://www.cs.wustl.edu/~schmidt/PDF/External-Polymorphism.pdf)设计模式的一个应用。自从我读过Chris Cleeland 和Douglas C. Schmidt于1998年9月发表在《C++ Report》上的有关此模式的文章后,我就喜欢上它了。不过直到现在,我仍然没看到这个模式的广泛应用。

10. 我觉得有两点需要说明。第一,它是Boost中大量成功创新的一个典范,Boost的创新性正是我将其列入“最重要C++软件”的原因之一。第二,很可惜的是,Boost中的这些创新未被整理并在C++社区广泛传播,很多有趣的东西,都掩藏在Boost库的盖子下不为人知。

9.我相信它也是外部多态(External Polymorphism,http://www.cs.wustl.edu/~schmidt/PDF/External-Polymorphism.pdf)设计模式的一个应用。自从我读过Chris Cleeland 和Douglas C. Schmidt于1998年9月发表在《C++ Report》上的有关此模式的文章后,我就喜欢上它了。不过直到现在,我仍然没看到这个模式的广泛应用。

10. 我觉得有两点需要说明。第一,它是Boost中大量成功创新的一个典范,Boost的创新性正是我将其列入“最重要C++软件”的原因之一。第二,很可惜的是,Boost中的这些创新未被整理并在C++社区广泛传播,很多有趣的东西,都掩藏在Boost库的盖子下不为人知。

Scott Meyers: C++历史上最重要人物

C++历史上最重要人物

C++是一门技术,而其背后支持它的是人——这些人发明、完善、繁荣和应用技术。本周,我将介绍我心目中最重要的五位C++大师。

本周名单上的这些人都拥有巨大的公众影响力。这些“公众人物”(抱歉,都是男性,不过我也没办法)能对很多人产生直接影响。受你影响的人越多,你自然就越重要。

不过,在系列的开篇文章里我也提到过,我是一个C++局外人。可能还有一些在台前幕后对C++产生了重要影响的人物不为我知。Stroustrup或许只是为众多技术工作者代言,标准委员也可能是替代一些不愿抛头露面团体做前台表演。如果是这样,那他们还真的达到目的了:我不知道他们。当然也就不会出现在我的名单上。

另外,我对入选者还有两点要求:(1)应该长期从事C++工作;(2)现在仍在为C++工作。重要的人很多,但最重要的人应该是长期而持续为C++做出贡献,并且直到现在仍在努力。

好了,现在公布我的名单,以较之新闻组文章更正式发表物(当然,这肯定比他们实际开始C++工作的时间晚,因为任何成果在产生影响力前都需要一个周期)的时间为序。在此,我仍然限制为五个名额,没有并列,也没有荣誉奖。因为我这是品活人而不是评静物,难度很大啊。规矩就是规矩,我必须要坚持。


Bjarne Stroustrup(http://www.research.att.com/~bs/homepage.html),1985年至今。嗯,怎么说呢,他是C++发明人,并编写了第一个编译器,还撰写了大量文章和图书(参见http://www.research.att.com/~bs/papers.html),积极参与C++的普及与标准化工作,并且一直持续到今天(如最近的SELLs(http://lcsd05.cs.tamu.edu/papers/stroustrup.pdf)和STAPL(http://parasol.tamu.edu/groups/rwergergroup/research/stapl/,合作者)研究项目)。本来,Stroustrup很多年前就可以退休了,躺在无数的赞誉声里懒洋洋地晒太阳。但他却选择了继续他30年前就开始的研究工作,这是对现代C++(最开始叫“C with Classes”)发展的巨大贡献。尽管我无意评比名单入选人孰轻孰重,但很难想象C++领域里还有谁比发明、最初实现,然后又引导它融入现代软件开发的人更为重要。



Andrew Koenig(http://www.acceleratedcpp.com/authors/koenig/),1988年至今。Andrew Koenig应该是绝无仅有、即便没有撰写任何C++文章和图书也能进入这个名单的人。他是一位真正的C++内部权威,这一点因其写作历史而少为人知。他撰写过两本C++图书(与Barbara Moo合作。译者注:Moo为Koenig夫人)、一本C图书(我没打算用这个为他在C++上加分,但即使这样,他仍然……)和大量专栏文章(可参看其主页上的介绍)。

不过,Koenig最让我感到吃惊的是别人对他的提及率,特别是在C++标准化方面。他一直是引导公众认识STL重要性的践行者,并带领Alexander Stepanov(译者注:原Compaq首席科学家,现Adobe首席科学家,STL之父)将STL引入C++标准。

我已经记不清多少次听到或读到标准化委员会成员类似这样的话了:“我们也在考虑这个问题,但Andrew指出……”,“那的确是个问题,不过Andrew建议……”。事实上据我所知,Koenig是唯一被以其名字命名C++语言特性的人——在标准化工作中,人们发现命名空间相关的名字搜索规范有时会导致编译失败,比如:

std::cout << someObject;

Koenig对此提出了修改意见,这就是后来的“Koenig lookup”[注释1]。在标准中,此规范的官方名称是ADL(argument-dependent lookup),但《Standard》对其的详细描述,则是“[basic.lookup.koenig]”(如果你一定要知道出处,那我告诉你,3.4.2小节)。



Scott Meyers(http://www.aristeia.com/),1991年至今。同样,我必须拿出真凭实据说明我的重要性,而不是靠主观臆断。我会尽量保持客观,用大量事实证明我在C++世界留下了自己的印记——或许是一个伤痕。我写过3本书(像我妻子那样不算再版,如果算上再版,就是6本),发行量都很大。我还撰写过近50篇有关C++及其应用的专栏文章,并且这些年来,我还在不少会议和开发者大会上做过报告。我公布的很多技术规范也成了C++最佳编程实践的一部分[注释2],不少厂商提供的“lint-like”C++检测工具都支持我的规范,它们常常将我的规范作为指导意见。

显然,我在C++标准工作中也扮演了一个小角色。嗯,别误会(译者注:他并没有正式从事C++标准工作,具体可见http://blog.csdn.net/lxpbuaa/archive/2006/11/09/1375988.aspx中他对自己的说明),我的意思是至少有两份标准建议稿,都受到我的影响:“我们还需要进一步讨论这个问题,因为即便Scott Meyers也还没弄明白”(如TR1中有关增加智能指针的建议部分(http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2003/n1450.html),可以在页面里搜索我的名字)。



Herb Sutter(http://www.gotw.ca/),1997年至今。Sutter走上C++舞台不过10年时间,但其活跃程度几乎让人瞠目结舌。最早是自撰三本图书,和人合著一本。他发表过200多篇文章(绝大部分是独立作者,少量合作),特别让人吃惊的是,他同时是三个专栏的长期作者或合作者[注释3]。我也记不清他为C++标准化委员会提交过多少建议和其他文件了——我好像提到过他是该委员会主席吧?他还是《C++ Report》前任首席编辑,在各种会议和技术大会上做报告,他一直是我知道的对C++最具热情的鼓手。同时,面对性能敏感应用支持的日益紧迫,他还在多线程领域首倡抽象再构造,并为此寻找新方法——可能就是C++语言和库的未来扩展方向——以让开发者更加容易编写多线程代码。[注释4]



Andrei Alexandrescu(http://erdani.org/),1998年至今。是他的《Modern C++ Design》(http://www.artima.com/cppsource/top_cpp_books.html#mcppd)促成了我们模版思想的巨变,从此,Alexandrescu的名字也就和模版紧紧相连。甚至在一些场合下,他的名字就是模版的代名词,但这对他是不公平的。他对C++的贡献,远远不仅仅是尖括号的新用法(尽管就我所知,他是第一个描述“template template parameters”——即将模版作为参数在模版编程中使用——用法之第一人)。

即便抛开与Herb Sutter合著的《C++ Coding Standards》(Addison-Wesley, 2005),他发表的40多篇文章(http://erdani.org/publications/main.html)也充分展示了他在对象拷贝(object copying)、对齐约束(alignment constraint)、多线程编程、异常安全和搜索等领域的巨大贡献;他常常将性能提升看得比符合标准更为重要。以我看来,近几年来工作值得我们一直关注的,仅此Alexandrescu。绝大多数其他写作者和报告人(包括我)时常会奔回收割过的田野一遍遍巡视,以期捡到遗留的麦穗,而Alexandrescu却以其无人望其项背的速度,不断开荒,还在里面种上了新的庄稼[注释5]。



截至目前,我已经开出了C++历史上最重要的图书、文献、软件和人物名单。这些名单相对来说是比较客观的,但在此系列的最后一篇文章里,我想抛开客观,完全表达下自己的主观想法。下次,我将列出C++最重要的五个历史时刻——五个突然来临后,我对C++语言、工作方式及其应用的认识达到新高度的时刻。


注释:

1.简单来说,Koenig lookup指的是:如果函数的参数类型定义在名字空间内,那么函数被调用时,首先应该在此名字空间内搜索被调用函数,然后再是其他地方。比如调用“std::cout someObject”,应该首先在定义someObject的名字空间内搜索操作符“”的实现。像这样的操作符函数,如果在被它操作的类型的名字空间内也有定义,Koenig lookup就发挥作用了。

2.我这些年公布的大多数规范都已经成为C++社区的“基本常识”。我的主要贡献不是去自己发明发现这些规范,而是普及之。

3.如果你像我一样,也作过专栏作家,那么你就能更容易理解其工作量之浩繁。我已经很难做到一年六次跟踪那些值得阅读的东西,而Sutter众所周知是每月三次。

4.他还是Microsoft的C++/CLI首席架构师。将这点放在注释里说明,是因为我认为C++/CLI是C++的一个分支语言,而不是C++本身的一部分。直到现在,我仍然搞不清他哪来那么多时间同时开展这么多工作。

5.当然这并不是说所有他写的和提到的东西都是他首创的。尤其是他2001年开始读博士后,也常常会将学术上的研究成果传播到C++社区,例如无锁编程(lock-free program
ming)(http://erdani.org/publications/cuj-2004-10.pdf)。

Scott Meyers: C++历史上最重要的软件

C++历史上最重要的软件

在本系列的第三篇文章里,我将把视线转移到评选过去最重要的C++软件上来。

用C++编写软件,你需要工具的帮助。在我看来,这些工具曾经是(将来也是)有关C++的最重要软件。可以想见,曾经出现了不少用C++开发的重磅软件,它们促使很多人为了以后项目的开发选择了这门语言,但我不关心这些。这门语言最重要的软件应该是软件开发人群使用的最基本的东西:编译器和库。可能,C++是更为库编写而不是应用开发所设计的一门语言。

我选择的C++历史上最重要的五个软件如下,以诞生年份为序:

Cfront,AT&T的Bell实验室开发,1985-1993。Cfront是最早的C++编译器。它可是真正的编译器,不过生成的是C格式的目标码。因此将它认作C++到C代码的预处理器是很自然的。很难让人在调试时不做它想,因为至少在我1998年开始使用它时,仍然没有C++调试器。头发花白的前辈们当时使用C调试器,必须要对付那些让人精神崩溃的名字(比如,识别调试器里指向C++源代码中某个加法函数的__pl__1Aff)。

事实上,Cfront生成C目标码有两个好处。第一,可以非常容易将Cfront移植到新的平台,因为C编译器到处都是。这就促成了C++在各种环境里的快速传播。第二,使用者可以观察编译生成的代码,知道编译所做的工作。当大多数人还是C++新手的时候,通过揭示其工作过程,有助于消除大家对C++的神秘感。它也扮演某种保护伞的角色。不用担心C++用黑纱蒙住你的眼睛,因为它所做的任何事情都清清楚楚摆在那里(至少和从前机器生成的C代码一样清楚)。

在1990年前,Cfront不仅是个编译器,而且也成了事实上的语言标准。C++诞生于AT&T,Cfront来自AT&T的C++小组,因此无论Cfront干什么,自然是不会错的。长期以来,其他厂商的编译器紧跟Cfront,以致Cfront的bug都被原样复制。直到《Annotated C++ Reference Manual》(ARM)发布,Cfront的标准色彩才逐渐消退,特别是大家认识到要为Cfront加入异常处理机制需要付出巨大努力的时候。

Cfront的最后版本发布于1993年,但阴魂不散。Edison Design Group,一家专业生产最贴近C++标准的front-end编译器的商业公司,在2006年7月的文件里还指出它们的编译器兼容Cfront模式。我猜测仍然Cfront仍然在一些不支持本地C++编译器的嵌入式项目中发挥作用,当然仅仅是猜测。

GCC,GNU工程的杰作,1987至今。GNU很早就进入了C++商业领域,并且发布了第一个生成本地代码的编译器(相对Cfront的C++-C转换而言)。多年以来,GNU编译器成了跨平台应用开发的不二选择。事实上,它是一个交叉编译器,这也使它在嵌入式系统开发领域广受欢迎。GCC本身是一个支持多种front-end(包括C、C++和FORTRAN等)和针对各种平台的back-end工具的编译器平台,其C++版本就是广为人之的g++。

g++的早期推动人是Michael Tiemann。我不由得回忆起自己曾经提交了一份g++的bug报告,很快就得到了Tiemann的回应。他提供了新的g++文法,并请我据此重建编译器,看看我报告的问题是否解决了。我记得问题依然存在,但一个编译器的作者特意送你一个修改补丁并请安装试用,已经难能可贵了。顺便提及一下,Tiemann于1989年与人合伙创办了Cygnus Support,我相信Cygnus是历史上第一个提供免费软件的公司。据说早期的时候,Tiemann有时会躺在浴盆里召开Cygnus的会议。

g++是开源的,这样C++社区就可免费获得与C++标准一致的 front-end工具。但我从未听说哪个以g++代码为基础编写的开源工具(如Lint、重构工具等等)的解析能力能与g++比肩。有不少可以解析所有C++声明的工具(比如gccxml),但据我所知,没有哪个工具能同时解析声明和定义部分(特别是函数体)。因此尽管我于它没有个人使用经历,但我怀疑g++的front-end是否在完全开源上有所保留。这对于C++开发者来说是不幸的,因为尽管有很多工具可供使用,但其真正威力应该是解析[注释1]C++源代码的能力,这是一个难以逾越的障碍。

Visual C++,Microsoft出品,1992至今。VC++是C++成功的最大推动力之一,也是延缓C++进步的最大障碍之一。尽管Bjarne Stroustrup断言“没有人知道大多数C++开发者到底在干什么”[注释2],但我基本上不会怀疑如果我将这颗星球上所有C++开发者召集到一起,并要求为他们使用的编译器投票,我想大多数人都会提到VC++。仅就这点而言,VC++已经并将继续对C++产生重要影响。而且,Microsoft的旗舰产品(如操作系统、Office等)也完全或主要用C++编写,并用VC++编译。这也加重了VC++在C++世界的分量。该公司对C++的倚重促使它开发了很多工具和API来支持其应用,从而导致很多Windows开发人员也使用这门语言。到了90年代晚期,很多人(这让我震惊甚至恐惧)将C++叫做“Microsoft语言”[注释3]。VC++在C++领域占据了统治地位。对于大多数程序员而言,Visual C++就是C++。

不幸的是在Microsoft的C++实现里,标准的缺失持续了很多年。1998年前,这算不上一个问题,因为根本就不存在可以遵从的标准,其他编译器厂商也是各行其道。然而,VC6于1998年发布,此时距C++标准完成已近一年。与同类产品相比,VC6与C++标准差得最远。当然这在当时仍然不是一个十分的严重问题,因为很少有程序员马上使用C++标准里的最新特性。糟糕的是VC6一直维持到2002年(VC7发布),甚至VC7编译器本身也没有大的改变。在这些年里,对标准的支持,基本上都是以更新库(如STL)的形式实现,而我们很多人都觉得多年前就应该发布升级版本。2003年的VC7.1终于解决了大部分标准兼容问题,但在VC6至VC7.1的六年间,使用VC6的跨平台开发者必须因其缺陷付出艰苦努力,最典型的就是通过条件编译(也就意味着代码零乱)解决模板偏特化等问题。不过,很多开发者直到今天仍在使用VC6,他们也得继续感谢VC6小组大范围忽视标准兼容的决定。比如,新的版本在内存耗尽时不抛出异常,而直接返回空指针。

2003年后,VC++不再存在我认为相当严重的标准兼容性问题,我相信它现在的领导者非常重视标准兼容。然而,我在1998到2003这六年来经常接触的都是工作(至少就编程序而言)中并无特别困难的开发者,因此我理直气壮地决定将我对Microsoft的“警惕”保持到2008年——从他们发布一个“真正的”C++编译器起的六年。

The Standard Template Library,源自HP,1993年至今。像C++这类语言的标准库应该提供一系列常用容器和算法,这个说法并不会让人格外惊讶,但即便这样的作品也可能上这个榜。而STL对C++的贡献远非如此。它引入了容器和算法的设计架构,这样,仅仅通过迭代器交互就能实现无缝协作。它示范了如何用容器和迭代器替代数组和指针。此外,它还告诉用户如何扩展架构,允许引入新的容器、算法和迭代类型——它们可以像STL自有组件那样和任何符合STL标准的组件一起工作。所有这些都以效率为实现基础,不考虑经典的面向对象、基于继承的解决方案,而几乎完全依赖于模板技术。它还为C++引入了泛型编程。这一切的努力给我们带来了库和库框架设计思路上的转变,也满足了那些寻找便利的、可移植容器者的需求。我在《Effective STL》(Addison-Wesley, 2001)里写到,“我已经编程三十年,但我从未发现STL的比肩者”。现在已经超过了30年,我仍然没有发现。

Boost类库,诞生于1999年。这项选择似乎带点儿欺骗,因为Boost实际上不是一个类库,而是一个收容人们因各种目的和想法而设计实现的一系列库的组织。因此,Boost库在提供什么以及如何提供功能上似乎漫无目的。然而,Boost及其库对C++产生了重大影响,这种影响在未来可能更为明显。TR1中规定的14类新功能(其中13个已经写入C++0x草案)里,有10个[注释4]来源于Boost库使用者的建议。

Boost的影响并非偶然。它就是以充当那些可能最终被加入C++标准的库的实验床和推动者为目的而创立的。TR1对Boost的重视已经证明了它的成功,而且TR2很可能包纳Boost中更多功能。

如果在Google中搜索“C++ libraries”(不带引号),你会发现Boost排在头条。这是否意味着人们想到C++库,就会想到Boost呢?我想它至少说明了Boost与C++库的关系是多么密切吧。

当前,软件工具已经成为C++成功的关键因素,但归根结底,任何美好的东西都要人来创造。C++是如此,人类任何别的劳动也是如此。在下篇系列文章里,我将告诉你我认为谁是最重要的人——C++世界的巨人[注释5]。

注释:

1.所谓“解析”,我指的不仅仅是构建抽象语法树,还包括执行隐式模板实例化、解析重载函数调用等。也就是说包括解析和语义分析,不过很多C++工具的威力起点是语义分析的结果。人们常常将这整个过程叫做“解析”。

2.C++社区是如此庞大和复杂,以至于无法分析“大多数程序员”的行为。

3.“Unix语言”是Java,我不知道Apple语言应该是什么。

4. 它们是:reference wrappers、smart pointers、enhanced member pointer adapters (mem_fn)、enhanced binders (bind)、generalized Functors (function)、type traits、random numbers、tuples、fixed-size arrays和regular expressions。

5.每个人都知道巨人的力量,但我做名人研究的老婆却提醒我说,尽管巨人们都力量超群,但在他们的漫漫长路上,更多依靠的是艰辛努力而不是他们天生的神力。这就是他们前仆后继投身与奥林匹斯山主宰者(宙斯和他的仆从)斗争的原因。我将把判断C++世界里是否存在同样现象的问题留给你们,如果有,那么是谁或者说是什么扮演着奥运会选手的角色呢?

Scott Meyers: C++历史上最重要的文献

C++历史上最重要的文献

在本系列的上一篇文章里,我列出了我认为最重要的五本C++图书,但大量有关C++的重要文献并非来自图书。比如期刊、杂志、网络上的文章;博士论文、会议纪要;新闻组帖子;博客;标准化文档等很多很多。它们对C++的进步与繁荣作出了巨大贡献。我没有读全,甚至谈不上读了大多数,但作为C++的长期关注者,我还是阅读了很多这类文献。在本期里,我将评选C++历史上最重要的五部非图书类文献。和上期评选图书一样,我仍然将数量限制为五,尽管我没有写出过重要到能上这个榜的东西,但仍然将自己列入了候选队伍。以下文献按时间为序。

一个让我无可回避的逻辑范畴两难问题是:如果文献A的思想对C++直接影响很小,但文献B的作者读到了A,将这个思想引入了B并产生了巨大影响,那么到底哪个文献更重要,A(“发明者”)还是B(“繁荣者”)?我最终选择了B,并不是因为这种做法天然就正确,而是因为我不想花力气拼命追查下列文献作者的思想是否从别的文献继承而来[注释1]。反过来,我随便翻到C++语言规范的某页。大家知道,const member functions里的const是不彻底的:指针数据成员自动变为const,但指针所指的数据本身不会。借鉴这个规定,我假设公布在下面的名单里的文献重要,而忽略它们引用的基础物(以及我所不知的其他文献)。当然,从C++本身来说,这可能不是正确的做法,但易于实现,所以我也这么干了[注释2]。

在详细解说名单之前,我想请各位发发善心,允许我为《C++ Report》——曾经为C++作出了最重大贡献的期刊——说几句悼词(如果你不那么仁慈,就请直接跳过去阅读后面内容吧)。在其存在的大部分时期(1989-2000)里,《C++ Report》一直是C++推动者和鼓吹者纸张写作的乐园(在此期间,首倡电子写作的是Usenet新闻组comp.lang.c++,后来还有comp.std.c++和comp.lang.c++.moderated)。在《C++ Report》发表文章的人里,有些名字你可能听说过(比如我上期列出的“最重要的C++图书”的作者),更多的可能你就不知道了(譬如下面要提到的一些文献的作者,以及——算了,名单太长,简直没办法开始。我知道,如果开了头列出一些名字,那就暗示着未列出的人没列出的那么重要,这样一来我的麻烦就大了。所以干脆一个不提,但请相信我,《C++ Report》有生之年一直相当兴盛,它吸引了这个领域最好的写作群体——都是最有兴趣,也是最
有实力去写作的人)。《C++ Report》关门的时候,很多专栏作家投向了《C/C++ Users Journal》,但这个杂志从来没有像《C++ Report》那样吸引过我;现在,CUJ也停刊了。《C++ Report》留给像我这样整天胡说八道的老怪物们的,就只有面对时光飞逝的无奈哀叹了。

唠叨这么多,我感觉好点了,还是继续说我的最重要非图书类文献名单吧:

《Programming in C++, Rules and Recommendations》,作者是Ellemtel电信系统实验室的Mats Henricson和Erik Nyquist,1992年。在我90年代早期前后的一些文章里,我提到过当时很多程序员渴求如何驾驭C++威力方面的指导意见,他们最感兴趣的是告诉他们该做什么、不该做什么的编程引导手册。几乎在我的《Effective C++》尝试提供这方面指导的同时,Mats Henricson和Erik Nyquist在互联网上发布了他们写的编程手册。其实在此之前,这本手册就出来一段时间了,但因为是瑞典文,所以大大限制了它的传播。

Ellemtel版手册以技术性语言写成,容易阅读,因此二位作者声名远播、影响很大。不久,大家得知他们准备成书出版,此时我就有不祥预感(竞争于市场可能是件好事,但那时,我是这个市场上仅有的参与者。我真的很喜欢这本书册的风格[注释3])。我们对这本书满怀期待,转眼间时间过去了几年。1996年底,它终于面世了(《Industrial Strength C++》,Mats Henricson和Erik Nyquist, Prentice Hall, 1997),但那个时候,这本书的很多指导意见与同时代的编译器相比已经过时,它包含的很多信息在C++社区里已经广为人知,与4年前第一次的英文版相比,人们感觉它的作用已经大打折扣。我阅读了这本书,先是兴趣满怀,然后就有点伤感,因为我感觉到花费4年时光从互联网文档到成书,不仅它包含的技术信息失去了当年的光芒,写作本身也丧失了原有的精神。我估计原稿已经被无数次修改,以期符合评审者在各方面的要求。这就解释了它为什么花费了如此长时间才得以出版,为什么最后的成书如此平淡。

我想,作者和出版商对这本书寄予厚望,但事与愿违,不过这并不能削弱最初互联网版本的影响力。它一出来,C++程序员就一口咬了上去。它是C++最佳实践规范总结道路上的重要里程碑。

《Exception Handling: A False Sense of Security》,作者Tom Cargill,1994年发表于《C++ Report》11、12月刊。1994年,C++社区矫矜之气弥漫。当时,C++是很热门的语言,工作岗位充足,很多人认为C++无所不能。在此前几年里,这门语言里增加很多重要的特性,比如多继承、模板,以及稍晚点的异常。因为异常是新事物,《C++ Report》上就出现了很多讨论文章,凡是C++程序员出现的场合,大家都是三句话不离异常。很多文章反映了属于那个时代的狂热:“异常美妙之极,它们让错误处理变得简单。你需要做的所有事情就是去理解try、throw和catch。看我编写一个堆栈类吧,告诉你们C++有了异常处理后,将比过去酷多少。” Tom Cargill的文章(是他的长期专栏“C++Gadfly”——这是多年来最名副其实的专栏之一——里的一篇)拂去了我们脸上集体自满的微笑。仅仅用一句话,Cargill就说明了try、throw和catch对这个问题毫无帮助:

运用异常的真正难点在于如何以如下方式编写所有介于二者(thow和catch)之间的代码:任何异常都能从throw处安全到达处理它的地方,且不破坏传递路线上的其他程序部分。

这个专栏继续剖析了《C++ Report》上我刚才提到过的“异常美妙之极”系列专栏上的文章[注释4],最后以一个擂台(Cargill称之为“邀请”)结束:发布一个异常安全的堆栈类。这个挑战引来了潮水般的回应,但我认为,直到1997年,Herb Sutter发表的一篇文章才算真正有分量(后面会说到这个事情)。

我认为Tom Cargill的专栏文章不仅证明了我们对于异常想法的幼稚,而且也还了C++一个清白。在我们明白如何编写异常安全的代码时,Java这门可爱的新语言出现了(就我所知,现在是Ruby on Rails),曾经信誓旦旦的“我们天下第一”狂想再也没有回来过。

《Curiously Recurring Template Patterns》,Jim Coplien于1995年发表在《C++ Report》2月。这篇文章的意义不在于它的内容本身,而在于它给所述内容的命名。真是双重巧合啊,这篇文章来自于Coplien的专栏“The Column Without a Name”,而且他给文章起的名字也已经直接成了一个模式名:The Curiously Recurring Template Pattern (CRTP)[注释5]。这个模式本身是指将派生类作为参数在它自己的模板化基类里使用:

template\

class Base { ... };

class Derived: public Base\

{ ... };

很多人对模板的使用都超出了T容器的范围,最后往往皈依到了CRTP的设计思想。这时候,他们通常都会怀疑这样的代码是否可以通过编译,当发现可以通过编译(可能他们大吃了一惊)后,就很担心自己弄出这样的设计,是否是头脑痴呆的早期症状。就在这时,Coplien投稿了。更多有经验的同事会保证说:“不是啊,你没有精神病。从基类派生一个类,基类又在派生类的基础上模板化,不仅是合法的设计技术,而且它还有一个正式的名字呢:the Curiously Recurring emplate Pattern。”

《Using C++ Template Metaprograms》,作者Todd Veldhuizen,《C++ Report》1995年5月。此文见证了template metaprogramming (TMP)的第一次大规模出现热潮。这是一个重要的时期,但我完全错过了这条船。我清楚记得阅读这篇文章时我的想法:“好,这样你就可以用递归的实例化模板去模拟编译时的循环。你能通过模板特化去实现编译时的switch表达式。太好了!但你为什么要这样做?”哦,我说谎了。我当时真正的想法是:“但你如果这样做,你就是个疯子。”[注释6]

尽管C++社区并不缺乏追捧,再后来有关TMP的文章也在各种论坛上陆续出现,不过这不让我惊诧。我那时深信TMP是一门概念过于离奇、语法过于超前以至于没有立足点的技术。现在,我知道大多数人似乎也同意它在语法上的确让人感觉不适,概念上不说过于怪异,至少也偏离了主流思想,这让我得到了些许安慰。但很清楚,它获得的支持日益增多,已经成为每个库程序员技巧包里的重要工具。为了弥补Veldhuizen首次描述它时我对其重要性严重低估的过失,我在Effective C++》第三版的一个条款里尽我全力总结了这门技术及其用途。

《Exception-Safety in Generic Components》,作者David Abrahams。我能找到的最早的是发表在德国《Dagstuhl Castle》1998年4月27日到5月1日的《Proceedings of the International Seminar on Generic Programming》。不过我想在1997年中,相关文献可能就出现了,因为Herb Sutter发表在《C++ Report》(也许是别的杂志)1997年9月刊的一篇文章引用了David Abrahams论文里的内容。我也发现有对David Abrahams于1997年4月发表在Usenet上的文章(http://tinyurl.com/nk5vn)里内容的引用,不幸的是,这个链接已经实效。

现在,保证函数提供基本、强健和无抛出的三个异常安全办法已经广为人知。Herb Sutter传播了这些术语,并且写了很多相关的文章,但最早提出它们的是David Abrahams,我这么说的主要理由是Sutter已经很细心地承认过这一点[注释7]。一些重要文献初生于相当狭小的空间,但读者中间很多人有条件发挥广泛影响力(比如正在定义C++标准,或者负责标准库的实现),因此文献的影响力将会被大大提高。我想,这就是一个例子。

有趣的是,尽管Abrahams的文献启蒙了我们对奠定C++标准库规范基础的异常安全的理解,但C++标准里却未提及“basic guarantee”、“strong guarantee”或是“nothrowguarantee”。

注释:

1.因为传递环路问题(比如B的想法起源于A,但A的灵感来自于Z,Z又是受Y的影响……),实际情况比这还要糟糕。

2. const不彻底的这个理由可能和简化实现并不相干。其实考察const和指针结合时的常见规则,它就是一个很自然的结果。我们定义指针p为const,但这并不能限定*p也是const。在const member function里,*this是const,如果p是*this的一个指针成员,我们也不能确定*p就是const。

3.稍早一点,还有另一本C++书册类图书,即Thomas Plum与Dan Saks合著的《C++ Programming Guidelines》,1991年由Plum Hall出版,但它从未引起过大量关注。我很早就读过这本书,现在我又快速阅读了一遍,给它的结论是:嗯……相当无趣。

4.我是《C++ Report》的专栏作家,我记得那时候就在想,Cargill肯定瞄上了我,要我去写异常方面的东西。系统且有专业眼光地挑出同僚作品中的缺点,是我曾经经历过的最费心劳神的事情。我确信从此以后,自己不是发表作品前要再三检查的唯一专栏作家了。

5.正在布朗大学计算机科学系攻读博士学位的Andrei Alexandrescu曾经公开指出这个模式的名字应该被还以本色,比如“F-bounded polymorphism”,但很不幸,他的意见没有引起人们的注意。这件事引起了我的兴趣,因为我就在那儿获得了博士学位,不过我从未听说过“F-bounded polymorphism”。CRTP尽管是一个古老的名字,但仍然能打动我,因为它比“F-bounded XXX”显得更自然。

6.它可能对文献里第一个例子实现编译时冒泡排序没有什么用处。我对冒泡排序一直有相当病态的反感。不仅是因为这种排序算法几乎从来就不适用于我的工作,而且也几乎从来不值得在工作中考虑这种算法。哦,我离题了。

7.初时,这点还不完全清楚。Sutter在《C++ Report》1997年9月刊上发表的文章里并没有将“basic guarantee”和“strong guarantee”归功于Abrahams,但他提供并允许放在我的1999版《Effective C++》光盘的文章提到了Abrahams。我有十足把握公开说《C++ Report》发表的文章和Sutter提供给我的文章是不一样的。

Scott Meyers: C++历史上最重要的图书

Scott Meyers,C++顶级权威之一,为世界各地客户提供培训和咨询服务。出版有畅销的Effective C++系列图书(《Effective C++》、《More Effective C++》和《Effective STL》),设计了创新型的Effective C++ CD,Addison Wesley的Effective Software Development Series顾问编辑,The C++ Source (http://www.artima.com/cppsource/)咨询板块专家。布朗大学计算机科学博士,他的网站是www.aristeia.com。

1991年,我写作了《Effective C++》。在此之前,我已学习C++多年。坐下来撰写本文的同时,我相信我的C++学习历程始于1986年,现在是2006,正好是我学习C++20周年和写作第一本C++图书15周年纪念。不过,记忆里——至少在我的记忆里——不敢保证,似乎有历史记录证明过我实际于1988年开始接触C++。如果是这样的话,那么现在应该是我与C++的19周年纪念年。虽然这个数字比不得20那么完整,但仍然说明C++于我已是多年老友。今年里,无论作何考虑,我都觉得我不应该关注C++的未来,而是回想它的过去。目前,C++的未来是热门话题,比如备受关注的下一个版本语言规范(C++0x),临近最后定稿阶段,首版技术报告已经出炉,二版也正蓄势待发。但我现在想驻足过去,做点与众不同的事:分门别类评选C++历史上最重要的贡献。

首先要公布的是最重要的五本C++图书。公布之前,我想先罗嗦几句。这些C++领域最重要贡献是在我个人阅历和准则基础上评选的,是我的主观看法,这点自不待言。而不得不说的恰恰是我个人的阅历和评选准则。

你们很多人不得不承认:过去20年里,我从未开发过商业软件,更遑论用C++写这类程序。不,不光过去,将来我也不会。因此,我不但不是真正的C++开发人员,更说不上是C++崇拜者。如果一定要扯点关系的话,那就是我在1985-1993年上大学时,用C++写过一个研究用软件,很小,就几千行代码,我一个人开发,它很快就成了历史的尘埃。自从我十余年前专心作咨询师以来,我的C++编程仅限于玩乐,比如“让我们看看它如何工作”(有时候也“让我们看看它能让多少编译器崩溃”),通常,都是一个文件就可以容纳的程序。make?谁需要讨厌的make?我以C++为生,但不是写C++程序。

和语言标准化亲密接触,于我的生活也没多大干系,因为我从未成为C++标准委员会成员,未被列入委员会的邮件名单,也从来没有参加过任何标准工作会议。我对委员会内部工作的了解——包括对语言有重大影响的工作——都靠阅读他人的文章,或者从他们那听说。因此我对像我们知道的那样推动了C++发展的那些重大努力知之甚少,可能只有委员会内部的同志对这些努力感受最深。

上面说了,我既不用C++写软件,也没为语言规范贡献力量,那我到底在干嘛呢?你可能满怀疑窦。基本上可以这么讲,我就研究C++本身以及它的应用。尽我所能,通过图书、杂志、新闻组、邮件以及和开发者、标准化委员会成员面对面交流,加上我自己写程序做实验玩儿等,我收集了大量有关C++的资料,然后对它们做整理、分析,最后集中打包(如写书、在杂志上发表文章、作技术报告等)提供给真正使用这门语言的朋友们,比如你。你们的工作是使用C++这个工具,开发有用的软件;我的工作是搜集、整理能帮助你们更好使用这个工具的资料。

我希望将自己定位为局外观察员,既不像程序员那样日复一日辛苦劳作,也不愿为标准化工作的纷繁琐事殚精竭思,但我要对二者都保持熟悉。这一系列的文章,就是我这个自封的局外观察员对C++历史上最重大贡献的总结。

所谓“最重大”,我指的是这些贡献对C++开发人员产生了最重大的影响。我的名单上所列项,很多都发挥了直接性的影响,我们也直接感觉到了。比如广泛使用的编译器、库和读者众多的图书与文章,它们的影响力非常直接。另外一些的影响则是间接的——我是说它们首先对中间渠道产生直接或间接的影响,然后由中间渠道直接影响我们(哈,绕口令,惹你讨厌了吧?)——比如你稍后会看到的、被我列为最重要图书之一的《ISO C++ Standard》,对于开发人员,它的影响是间接的,但对编译和库编写者的影响却是直接的。

有些贡献的重要性非常明显,比如和标准有关的。其他的,通常则需要时间老人来验证它们的影响力。因此,在C++圈子里出现相对较晚的,与已经存在较长时间的相比,更不容易列入我的名单。比如我的图书名单里,最晚的只到2001年。这倒不是说在过去五年里出版的图书就没有价值,而是说,以我来看,这些图书还没有表现出我名单上所列图书更大的重要性。如果过些年再来问我,或许就会有所变动了。

五本最重要的C++图书

为了让这个名单(也包括后续文章中提到的其他名单)更有价值,我给自己定了两条原则。第一,只挑五本。哪怕多一本,让它共享这份荣誉的位子也没有,也没有为那些差不多靠边者准备优秀奖。五个位子,五本书。第二,我没有将自己的书排除在竞争行列之外。如果我要评判别人的书,当然也不能放过自己的。

这两个原则已经相当公平而严格了,因此我在其他方面就可以偷点懒:我不打算对名单上的图书按重要性从高到低排座次(其他几个名单也一样)。我的评判粒度是“在名单上”和“不在名单上”。每个名单里,我都以年代为序,本文的单子里,自然就是出版日期。

言归正传,下面揭晓我评选的过去出版的最重要的五本C++图书:

《The C++ Programming Language》,作者Bjarne Stroustrup,Addison-Wesley出版社。1986年首版,1991年再版,1997年第三版,2000年特别版。对于C++老人们来说,此书的首版是打开知识大门的钥匙。目前的版本已经膨胀到千页以上,增加了很多那时候还不存在的特性,如模板、异常、多继承等,不过未提及标准模板库;显然首版——也是我逐页阅读过的唯一版本——更易驯服,只有328页。身处80年代后半页的C++程序员,对这本书的完全旅行是必不可少的,它对早期的C++从业者和追梦人的重要影响,怎么评价都不过分。即使介绍C++的图书满天飞的现在,我仍然推荐专业开发人员阅读这本书(当前版本),因为我认为无论就C++本身,还是如何用好C++来说,它都是最全面的参考书。

Stroustrup因为吝于词句的写作风格而遭诟病,大家都知道我说过,如果阅读TC++PL(译者注:即The C++ Programming Language的简称。此书行文简洁)时,你无意忽略了哪怕一个句子,就有可能丢掉了很重要的东西(再想想目前版本千页以上的篇幅,你就能明白我为什么说它是最全面的参考书了)。然而,精炼恰是这本书无与伦比的魅力之一。比如,首版中对自赋值处理方法的说明,就再好不过证明了这一点:

if (this == &a) return; // beware of s=s;

首版《Effective C++》里,我在同样问题上花费了了五页半的篇幅。你不得不为这样一本半行注释就解决问题的图书倾倒。

《Effective C++》,作者Scott Meyers,Addison-Wesley出版社。1992年首版,1998年再版,2005年第三版。它是超级天才的杰作!哦,不要吐,别在意。

我和此书都很幸运,它生逢其时。1991年前后,大部分程序员已经掌握了语言基础,能做到根据需求编写C++程序,但同时总觉得自己无法避免看似可以避免的诸多问题。这是业界第一本专注于如何深入使用C++,全力帮助开发者解决常见问题,最好地使用非C特性(如构造器、析构器、继承、虚函数和类内存分配器等)的图书。在这本书里,一反常态,不像当时大多数人将重点放在讨论C++的灵活性上,我采用了一种很特别的指导式思路来写作。以我的经验判断,我认为程序员渴望找到强能有力的办法来控制C++的灵活多变,因此我要告诉他们究竟该做什么。这本书做到了。“一定要这么做”,“千万不要那样干”。一代程序员将这些原则熟记于心,也催生了不少协助性工具。比如Gnu C++编译器,甚至设置了《Effective C++》指导原则的检测警告选项[注1]。此书的一个重要特点——直到写作我的第二本书(《More Effective C++》)难以为继时,我才深入领会到——是其条款内容短小精悍,这样,读者坐公交车、火车,等待会议开始,甚至
我曾经说过的坐在马桶上时都可以阅读这本书。

《Design Patterns》,作者Erich Gamma、Richard Helm、Ralph Johnson和John Vlissides。Addison-Wesley出版社,1995年。它本身不是一本针对C++的书,但它对使用C++以及其他任何面向对象编程语言进行良好设计都产生了深远影响。此书在专业软件开发人员中发起了模式启蒙运动,而且提出了一系列至今仍居常用模式词汇表核心地位的设计模式[注2]。如果你熟悉命令(Command)、访问者(Visitor)、装饰(Decorator)或单例(单态,Singleton)等模式——甚至已经深入掌握了设计模式,那么基本上可以肯定你读过这本书,我们应该感谢它。

此书中提到的模式,至今仍是C++技术界的热门话题。例如Herb Sutter于2003年9月,在其C/C++ Users Journal专栏提出了观察者(Observer)模式新的实现方法,Andrei Alexandrescu也分别于2005年4月和6月,在其C/C++ Users Journal专栏对这个模式做过讨论。

《International Standard for C++》,ISO/IEC。1998年首版,2003年第二版。标准类资料读起来枯燥乏味,但必须有这样的东西去定义什么是C++,什么不是C++,这本书就担此重任。在这以前,若想知道C++的正确实现,最好要读Margaret Ellis和Bjarne Stroustrup合著的《The Annotated C++ Reference Manual》(即著名的ARM,Addison-Wesley1990年版),并用不同的编译器检查代码,得想方设法理解Bjarne Stroustrup的意思(如通过读他的书)。1998年版的C++标准不能回答所有问题(任何资料都做不到),比如,vector的内存布局必须与C数组兼容吗?[注3] 但它仍不失为一本C++权威参考书,无论是为编程实现还是语言使用者,都在标准化上做出了巨大努力。2003年出的第二版,实际上是一个Bug修复更新版,更像是1.1而不是2.0版本,但它也是一个语言官方规范修订本。

《Modern C++ Design》,作者Andrei Alexandrescu。Addison-Wesley2001年版。设计模板的初衷是实现类型的容器(“containers of T”),但它能做的远不仅仅这点。实际上,在lexandrescu的书出现之前,1998版C++标准的STL部分要求的模板实现就远远超出“类型的容器”。但Modern C++ Design》的出现,才真正为C++技术界打破了模板的牢笼。此书影响深远,堪称新旧两代C++程序设计的分水岭;此后,“modern C++”一般都被用来专指采用了因此书而普及的模板技术的代码。通过Google搜索包含“modern C++”的新闻组帖子,1985到2000年共有662个结果,而从2001年到现在,则有3200个。

尽管无关于这本书在C++历史上的重要性,但我想有一点仍然值得提及,那就是在Alexandrescu写这本书的时候,他还找不到一个支持这本书的代码包含的所有C++标准的编译器!

当你再次浏览这个名单的时候,请记得我是Addison-Wesley的签约作家,这个身份使我有机会免费获得Addison-Wesley出版的任何优秀图书。因此,可能相对于其他出版社来说,我更熟悉的是Addison-Wesley的图书。不过,如果其他出版社有图书震撼了C++世界,我想我肯定也会听说过。

在本系列的下一篇文章里,我将选择C++历史上最重要的非图书出版物(如杂志的文章等)。

注1
即:-Weffc++。

注2
一直以来,我都有意在Google上搜索网页和新闻组最常提及的模式名,结果我发现多年来,20个最频繁提及的模式名,绝大部分出自这本书。这充分证明这本10年高龄的图书经久不衰。

注3
是的。

再说指针和数组

今天在书上看到大致这样的一段代码:
int main(int argc, char *argv[])
{
while (--argc)
printf("%s\n", *++argv);
return 0;
}

于是立刻感觉有地方不妥:++argv.
记得书上是说一个数组名代表的是一个常量指针,但是为何此处可以让一个数组名常量作自增运算呢?

为了证明我的理解的正确性,我还特地写了这么一段代码:
int main(int argc, char *argv[])
{
char *a[2] = {"1", "2"};
printf("%s %s\n", *a, *++a);
return 0;
}
编译:
greenlightzc@greenlightzc-desk:~/Desktop$ gcc -o test test.c
test.c: In function ‘main’:
test.c:6: error: invalid lvalue in increment

和我预期的一样。
但是为什么片段一可以通过编译呢?

原来是形参的问题:数组形参在编译的时候都是转换为指针类型的,也就是说char *argv[]作为形参的时候传进来的是参被转换为char **类型。

还要多亏豹仔和我一起讨论这个看似简单的问题呢: )

2007年4月22日星期日

eBay面经

早上是宣讲+笔试
笔试全英语的:单词填空+智力题+专业题(c++, 数据库, java)
下午寝室三人打牌,由于不确定名单,我一直没去。后来时至4点,那边的HR打电话给yinwei,于是我们赶了过去。

很幸运的是一面二面三面......开始的英语有点慌,然后越说约有信心。
最后那个老大私下和我说明天早上10点二楼见。

估计应该没有问题,可是我还在等baidu 的消息。
前几天去了趟我喜欢的ASK,结果觉得那个人很%^#*,失望,不说了。

2007年4月20日星期五

[ZZ]胡侃学习(理论)计算机

作者是南大毕业的,现在美国某校做教授
看完以后我实在不好意思说自己是计算机科学与技术专业的毕业生。

发信人: sir (阿涩), 信区: Mathematics
标 题: 胡侃学习(理论)计算机(0)
发信站: 南京大学小百合站 (Mon Oct 8 03:57:41 2001), 站内信件

我也来冒充一回高手,谈谈学习计算机的一点个人体会。
由于我是做理论的,所以先着重谈谈理论。

记得当年大一,刚上本科的时候,每周六课时数学分析,
六课时高等代数,天天作业不断(那时是六日工作制)。
颇有些同学惊呼走错了门:咱们这到底念的是什么系?
不错,你没走错门,这就是(当时的)南大计算机系。系
里的传统是培养做学术研究,尤其是理论研究的人。而
计算机的理论研究,说到底了就是数学,虽然也许是
正统数学家眼里非主流的数学。

数学分析这个东东,咱们学计算机的人对它有很复杂
的感情。爱它在于它是第一门,也是学分最多的一门
数学课,又长期为考研课程--94以前可以选考数学分析
与高等代数,以后则并轨到著名的所谓“工科数学一”。
其重要性可见一斑。恨它则在于它好象难得有用到的
机会,而且思维跟咱们平常做的这些离散/有限的工作
截然不同。当年出现的怪现象是:计算机系学生的高中
数学基础在全校数一数二(希望没有冒犯其它系的同学),
教学课时数也仅次于数学系,但学完之后的效果却几
乎是倒数第一。其中原因何在,发人深思。

我个人的浅见是:计算机类的学生,对数学的要求固然
跟数学系不同,跟物理类差别则更大。通常非数学专业
的所谓“高等数学”,无非是把数学分析中较困难的理论
部分删去,强调套用公式计算而已。而对计算机系来说,
数学分析里用处最大的恰恰是被删去的理论部分。说得
难听一点,对计算机系学生而言,追求算来算去的所谓
“工科数学一”已经彻底地走进了魔道。记上一堆曲面积分
的公式,难道就能算懂了数学分析?

中文的数学分析书,一般都认为以北大张筑生老师的
“数学分析新讲”为最好。我个人认为南大数学系的“数学
分析教程”也还不错,至少属于典型的南大风格,咱们
看着亲切。随便学通哪一本都行。万一你的数学实在
太好,这两本书都吃不饱,那就去看菲赫金哥尔茨的
“微积分学教程”好了--但我认为没什么必要,毕竟你
不想转到数学系去。

吉米多维奇的“数学分析习题集”也基本上是计算型的
东东。如果你打算去考那个什么“工科数学一”,可以
做一做。否则,不做也罢。

中国的所谓高等代数,就等于线性代数加上一点多项式
理论。我以为这有好的一面,因为可以让学生较早感觉
到代数是一种结构,而非一堆矩阵翻来覆去。当年我们
用林成森,盛松柏两位老师编的“高等代数”,感觉相当
舒服,我直到现在还保留着教材。此书相当全面地包含
了关于多项式和线性代数的基本初等结果,同时还提供
了一些有用的比较深的内容,如Sturm序列,Shermon
-Morrison公式,广义逆矩阵等等。可以说,作为本科
生如能吃透此书,就可以算高手。后来它得以在南大出
版社出版,可惜好象并轨以后就没有再用了。

国内较好的高等代数教材还有清华计算机系用的那本,
清华出版社出版,书店里多多,一看就知道。特点嘛,
跟南大那本差不太多。

但以上两本书也不能说完美无缺。从抽象代数的观点
来看,高等代数里的结果不过是代数系统性质的一些
例子而已。莫宗坚先生的“代数学”里,对此进行了深刻
的讨论。然而莫先生的书实在深得很,作为本科生恐
怕难以接受,不妨等到自己以后成熟了一些再读。

概率论与数理统计这门课很重要,可惜少了些东西。

少了的东西是随机过程。到毕业还没有听说过Markov
过程,此乃计算机系学生的耻辱。没有随机过程,
你怎么分析网络和分布式系统?怎么设计随机化算法
和协议?据说清华计算机系开有“随机数学”,早就是
必修课。人家可是工科学校,作为自以为“理科计算机
系”出身的人,我感到惭愧。

另外,离散概率对计算机系学生来说有特殊的重要性。
现在,美国已经有些学校开设了单纯的“离散概率论”
课程,干脆把连续概率删去,把离散概率讲深些。我们
不一定要这么做,但应该更加强调离散概率是没有疑
问的。

计算方法是最后一门由数学系给我们开的课。一般学生对
这门课的重视程度有限,以为没什么用。其实,做图形
图像可离不开它。而且,在很多科学工程中的应用计算,
都以数值的为主。

这门课有两个极端的讲法:一个是古典的“数值分析”,
完全讲数学原理和算法;另一个是现在日趋流行的“科学
与工程计算”,干脆教学生用软件包编程。南大数学系
的几位老师做了件大好事,把前者的一本极为经典的
教材翻译出版了:德国Stoer的“数值分析引论”。如果
你能学会此书中最浅显的三分之一,就算没有白上过
计算方法这门课!而后一种讲法似乎国内还没有跟上
潮流?不过,只要你有机会在自己的电脑上装个matlab
之类,完全可以无师自通。

本系里,通常开一门离散数学,包括集合论,图论,
和抽象代数,另外再单开一门数理逻辑。这样安排,
主要由于南大的逻辑传统:系里很多老师都算莫先生
的门人,就连孙先生都是逻辑专业出身(见孙先生自
述)。

不过,这么多内容挤在离散数学一门课里,是否
时间太紧了点?另外,计算机系学生不懂组合和
数论,也是巨大的缺陷。要做理论,不懂组合或
者数论吃亏可就太大了。

从理想的状态来看,最好分开六门课:集合,
逻辑,图论,组合,代数,数论。这个当然不现实,
因为没那么多课时。也许将来可以开三门课:集合
与逻辑,图论与组合,代数与数论。

不管课怎么开,学生总一样要学。下面分别谈
谈上面的三组内容。

古典集合论,北师大出过一本“基础集合论”不错。
南大出版朱梧(木贾)老师的“集合论导引”也许观点更
高些,但他的书形式化得太厉害,念起来吃力。

数理逻辑,莫先生的书自然是经典。然而我们也
不得不承认,此书年代久远,光读它恐怕不够。
尤其是命题/谓词演算本身有好多种不同的讲法,
多看几家能大大开阔自己的视野。例如陆钟万老师
的“面向计算机科学的数理逻辑”就不错。朱老师也
著有“数理逻辑教程”一书,但也同样读起来费力些。

总的来说,学集合/逻辑起手不难,但越往后越感觉
深不可测。建议有兴趣的同学读读朱老师的“数学
基础引论”--此书有点时间简史的风格,讲到精彩处,
所谓“天花乱坠,妙雨缤纷”,令人目不暇接。读完
以后,你对这些数学/哲学中最根本的问题有了个大概
了解,也知道了山有多高,海有多深。
学完以上各书之后,如果你还有精力兴趣进一步
深究,那么可以试一下GTM系列中的"Introduction
to Axiomatic Set Theory"和"A Course of Mathematical
Logic"。这两本都有世界图书的引进版。你如果
能搞定这两本,可以说在逻辑方面真正入了门,
也就不用再浪费时间听我瞎侃了。:)

据说全中国最多只有三十个人懂图论(当年上课时陈道蓄
老师转引张克民老师的话)。此言不虚。图论这东东,
技巧性太强,几乎每题都有一个独特的方法,让人头痛。
不过这也正是它魅力所在:只要你有创造性,它就能
给你成就感。所以学图论没什么好说的,做题吧。

国内的图论书中,王树禾老师的“图论及其算法”非常
成功。一方面,其内容在国内教材里算非常全面的。
另一方面,其对算法的强调非常适合计算机系(本来
就是科大计算机系教材)。有了这本书为主,再参考
几本翻译的,如Bondy&Murty的“图论及其应用”,
邮电出版社翻译的“图论和电路网络”等等,就马马
虎虎,对本科生足够了。

再进一步,世界图书引进有GTM系列的"Modern
Graph Theory"。此书确实经典!国内好象还有一
家出版了个翻译版。不过,学到这个层次,还是
读原版好。搞定这本书,也标志着图论入了门,呵呵。
组合感觉没有太适合的国产书。还是读Graham和Knuth
等人合著的经典“具体数学”吧,有翻译版,西电出的。

抽象代数,国内经典为莫宗坚先生的“代数学”。此书
是北大数学系教材,深得好评。然而对本科生来说,
此书未免太深。可以先学习一些其它的教材,然后
再回头来看“代数学”。国际上的经典可就多了,GTM
系列里就有一大堆。推荐一本谈不上经典,但却最简
单的,最容易学的:
http://www.math.miami.edu/...
这本“Introduction to Linear and Abstract Algebra"
非常通俗易懂,而且把抽象代数和线性代数结合起来,
对初学者来说非常理想。不过请注意版权问题,不要
违反法律噢。

数论方面,国内有经典而且以困难著称的”初等数论“
(潘氏兄弟著,北大版)。再追溯一点,还有更加经典
(可以算世界级)并且更加困难的”数论导引“(华罗庚先生
的名著,科学版,九章书店重印)。把基础的几章搞
定一个大概,对本科生来讲足够了。但这只是初等数
论。本科毕业后要学计算数论,你必须看英文的书,
如Bach的"Introduction to Algorithmic Number Theory"

理论计算机的根本,在于算法。现在系里给本科生
开设算法设计与分析,确实非常正确。环顾西方世界,
大约没有一个三流以上计算机系不把算法作为必修的。

算法教材目前公认以Corman等著的"Introduction to
Algorithms"为最优。对入门而言,这一本已经足够,
不需要再参考其它书。南大曾翻译出版此书,中文名
为”现代计算机常用数据结构与算法“。pie好象提供了
网上课程的link,我也就不用废话。

最后说说形式语言与自动机。我们用过北邮的教材,
应该说写的还清楚。但是,有一点要强调:形式语言
和自动机的作用主要在作为计算模型,而不是用来
做编译。事实上,编译前端已经是死领域,没有任何
open problem。如果为了这个,我们完全没必要去学
形式语言--用用yacc什么的就完了。北邮的那本,在深
度上,在跟可计算性的联系上都有较大的局限,现代
感也不足。所以建议有兴趣的同学去读英文书......不过
英文书中好的也不多,而且国内似乎没引进这方面的
教材。

入门以后,把形式语言与自动机中定义的模型,和
数理逻辑中用递归函数定义的模型比较一番,可以
说非常有趣。现在才知道,什么叫”宫室之美,百官
之富“!

发信人: sir (阿涩), 信区: Mathematics
标 题: 胡侃学习计算机--理论之外(0)
发信站: 南京大学小百合站 (Mon Oct 15 05:26:00 2001), 站内信件

如果计算机只有理论,那么它不过是数学的一个分支,而
不成为一门独立的科学。事实上,在理论之外,计算机
科学还有更广阔的天空。我一直认为,4年根本不够学习
计算机的基础知识,因为面太宽了......


一个一流计算机系的优秀学生决不该仅仅是一个编程高
手,但他一定首先是一个编程高手。

我上大学的时候,第一门专业课时程序设计,现在好象
改成了计算机科学导论?不管叫什么名字,总之,念计
算机的人就是靠程序吃饭。

去年在计算机系版有过一场争论,关于第一程序设计语言
该用哪一种。我个人认为,用哪种语言属于末节,关键在
养成良好的编程习惯。当年老师对我们说,打好基础后
学一门新语言只要一个星期。现在我觉得根本不用一个
星期--前提是先把基础打好。

数据结构有两种不同的上法:一种把它当成降低要求的
初级算法课,另一种把它当成高级的程序设计课。现在
国内的课程好象介乎两者之间,而稍偏向前者。我个人
认为,假如已经另有必修的算法课,恐怕后一个目的更
重要些。

国内流行的数据结构书也有两种:北大的红皮书(许卓
群等著,高教版)和清华的绿皮书(严蔚敏等著,清华版)。
两书差距不大。红皮书在理论上稍深一些,当然离严格
的算法书还差好远。绿皮书更易接受些,而且佩有一本
不错的习题集,但我觉得它让学生用伪代码写作业恐怕
不见得太好。最好还是把算法都code以后debug一番,
才能锻炼编程能力。

汇编预言和微机原理是两门特烦人的课。你的数学/理论
基础再好,也占不到什么便宜。这两门课之间的次序也
好比先有鸡还是先有蛋,无论你先学哪门,都会牵扯另
一门课里的东西。所以,只能静下来慢慢琢磨。这就是
典型的工程课,不需要太多的聪明和顿悟,却需要水滴
石穿的渐悟。

有关这两门课的书,电脑书店里不难找到。弄几本最新
的,对照着看吧。

模拟电路这东东,如今不仅计算机系学生搞不定,
电子系学生也多半害怕。如果你真想软硬件通吃,那么
建议你先看看邱关源的“电路原理”,也许此后再看模拟
电路底气会足些。

教材:康华光的“电子技术基础”还是不错的。有兴趣也
可以参考童诗白的书。

数字电路比模拟电路要好懂得多。阎石的书也算一本好
教材,遗憾的一点是集成电路讲少了些。真有兴趣,到
东南无线电系去旁听他们的课。

计算机系统结构该怎么教,国际上还在争论。国内能找
到的较好教材为Stallings的"Computer Organization
and Architecture:Designing for Performance"(清华影印
本)。国际上最流行的则是“Computer architecture: a
quantitative approach", by Patterson & Hennessy。

操作系统可以随便选用Tanenbaum的"Operating System
Design and Implementation"和"Modern Operating
System"两书之一。这两部都可以算经典,唯一缺点
就是理论上不够严格。不过这领域属于Hardcore System,
所以在理论上马虎一点也情有可原。

如果先把形式语言学好了,则编译原理中的前端我看只要
学四个算法:最容易实现的递归下降;最好的自顶向下
算法LL(k);最好的自底向上算法LR(k);LR(1)的简
化SLR(也许还有另一简化LALR?)。后端完全属于工程
性质,自然又是another story。

推荐教材:
Aho等人的著名的Dragon Book: "Compilers: Principles,
Techniques and Tools".
或者Appel的"Modern Compiler Implementation in C".

学数据库的第一意义是告诉你,会用VFP编程不等于懂
数据库。(这世界上自以为懂数据库的人太多了!)数据库
设计既是科学又是艺术,数据库实现则是典型的工程。
所以从某种意义上讲,数据库是最典型的一门计算机课
--理工结合,互相渗透。

推荐教材:Silberschatz, et al., "Database System
Concepts".

网络的标准教材还是来自Tanenbaum:”Computer
Networks"(清华影印本)。不过,网络也属于Hardcore
System,所以光看书是不够的。建议多读RFC,从
IP的读起。等到能掌握10种左右常用协议,就没有几个
人敢小看你了。

必须结束这篇“胡侃”了,再侃下去非我力所能及。其实
计算机还有很多基础课都值得一侃,如程序设计语言原
理,图形图像处理,人工智能等等。怎奈我造诣有限,
不敢再让内行耻笑。

最后声明:前后的两篇“胡侃”只针对本科阶段的学习。
即使把这些全弄通了,前面的路还长......

2007年4月19日星期四

你最想见到的浙籍IT精英

居然看到老板的名字了,太赞了:)

投票结果
正常排序 从高到低 从低到高

你最想见到的浙籍IT精英(排名不分先后)
原北京联众电脑技术有限责任公司CEO兼总裁鲍岳桥: 33票3.34%
浙大网新科技股份有限公司董事长陈纯: 22票2.22%
上海盛大网络发展有限公司创始人,董事长兼CEO陈天桥: 64票6.47%
中国搜索总裁陈沛: 15票1.51%
网易公司CEO丁磊: 67票6.78%
北京博客网信息技术有限公司董事长兼CEO方兴东: 52票5.26%
上海复星高科技(集团)有限公司董事长郭广昌: 23票2.32%
杭州天畅网络科技有限公司董事长郭羽: 13票1.31%
中国博客网CEO胡之光: 5票0.50%
分众传媒(中国)控股有限公司创始人、董事长江南春: 45票4.55%
浙江大学快威科技集团有限公司总裁蒋忆: 9票0.91%
游侠网CEO郦彦卿: 5票0.50%
阿里巴巴创办人、主席、首席执行官(CEO) 马云: 75票7.59%
金山软件股份有限公司董事长求伯君: 34票3.44%
四川托普集团科技发展有限责任公司主席兼CEO宋如华: 21票2.12%
浙江网盛科技股份有限公司总裁孙德良: 63票6.37%
eBay易趣董事长邵亦波: 82票8.29%
e龙公司董事长唐越: 81票8.19%
杭州富通集团有限公司董事长王建沂: 16票1.61%
中国•颐高集团有限公司董事长翁南道: 163票16.49%
浙大网新科技股份有限公司总裁赵建: 12票1.21%
赛伯乐(中国)创业投资管理有限公司董事长朱敏: 33票3.34%
Google执行总裁周韶宁: 5票0.50%
杭州华三通信技术有限公司总裁兼首席运营官郑树生: 50票5.06%

春回燕归.浙籍精英峰会

前几天丁磊,孙德良,吴晓波三人来我校作演讲,主题是“互联网的前十年,后十年”

三人的故事不必多说了。
最后丁磊的一席话确实让人受益,具体是什么我已经刻在脑子里了。
末了,没时间找丁磊签名了,因为约好了4个兄弟踢球,赶紧走人了...

昨天暴跌今天暴涨

“暴跌拉开阶段性调整序幕”——Yahoo!中国
昨天跌的我没什么感觉,因为我第一反应是大盘要把散户震荡出局;
今天果然涨得我爽,更加坚定了我的信念了,必定是操盘者的把戏。想骗大伙出市...

敌人拥护的我们就要反对,敌人反对的我们就要拥护!