IT牛人博客聚合网站 发现IT技术最优秀的内容, 寻找IT技术的价值 http://www.udpwork.com/ zh_CN http://www.udpwork.com/about hourly 1 Thu, 23 Feb 2012 08:40:59 +0800 <![CDATA[Xen和KVM的一些胡扯]]> http://www.udpwork.com/item/6889.html http://www.udpwork.com/item/6889.html#reviews Thu, 23 Feb 2012 05:34:26 +0800 liuw http://www.udpwork.com/item/6889.html 我始终是一个相信阳谋多过阴谋的人。在工程至上的开源软件世界,所有策略都是可以预知和分析出来的。

“开源”二字,对于不同的人来说可能有不同的理解。对于某些国内的公司、高校来说,国外“开源”就意味着国内的“自主创新”。对于个人开发者,“开源”也许意味着搞个github、google code、bitbucket之类的账号,把自己的一些成熟或者不成熟的代码放到网上与大家共享。对于依赖开源软件的公司或者社区,“开源”最主要的作用就是构建经营生态圈,为自己的生存和发展打下基础。

在开源软件的世界,有着同样的目的的几个产品互相竞争是很正常的事情,比如Xen和KVM就是如此。首要条件是产品能够达到基本的标准,产品本身是基础,不好后面就没有戏。构建经营生态圈是非常费时费力的一个事情。

Xen在过去几年在社区方面失去了很多阵地,究其原因就是公司方面对于社区不够重视(以前都忙着赚真金白银了)。比如说在Ubuntu 8.04的时候Xen是被直接打包到系统中的,但是9.x版本到11.x版本里面,由于公司方面没有专门聘请packager,没有人去维护这个package,它就被移除了。而最近的一个Ubuntu的survey也反映了这一点(虽然platform team内部对于统计方式有点疑问),Xen的占有率不是很高(其实我个人觉得数据已经比预想的要高了)。可以说,前几年的疏忽造成的影响现在开始慢慢浮出水面了。

而现在公司的策略已经转变了,准备以开源产品为基础吸引用户,然后为有需求的用户提供高阶的商业版本产品。我觉得这个套路倒是很正常,RedHat其实就是一直这样发展的,而这个非常传统的软件公司好像一开始不大转得过弯来。现在还不算晚,开始统一产品线,也开始在社区方面加大力量。比如说现在开始和一些开源云计算平台互通、聘请一些开发人员专门为主要发行版打包软件、专门成立团队来经营社区、放出商业产品的社区版本等等。

在开源软件界,Xen的最主要竞争者是由RedHat支持的KVM。但是Xen和KVM又不完全是一个层面上的产品。Xen本身只是一个hypervisor,实际的驱动放在Linux内核中,有自己完整的工具栈(libxc和libxl都是Xen的一部分),还有QEMU等用户空间程序,还有第三方支持的如Remus、XSM等扩展功能,所以Xen是一个很庞大的“东西”。而KVM则相对简单,只有内核部分以及作为模拟器的QEMU。KVM工具栈常见的是libvirt,它不属于KVM的一部分,其他的什么扩展功能也不是很多。打个不大恰当的比方,Xen就像FreeBSD,而KVM就像Linux。

KVM是一个很优秀的产品,它的设计非常简洁,概念相对Xen来说少很多,组件少开发维护起来方便,使用起来也方便(直接加载模块即可)。现在有RedHat的支持,发展起来顺风顺水。那么Xen能在什么方面与KVM竞争呢?所谓“成也萧何,败也萧何”,“十年河东十年河西”,Xen的复杂架构,让它在前硬件虚拟化时代领先,硬件虚拟化时代落后,也许在将来还是有机会反戈一击的,但处理不好,那也是无翻身之可能了。

先退一步来观察一下Xen虚拟化系统。整个系统由hypervisor、Dom0以及其上运行的DomU组成。hypervisor负责所有特权操作,Dom0负责管理和驱动硬件,DomU运行用户的任务。不特意提及的话,也许基本没有人会想到“微内核”这个词吧。实际上Xen系统可以看作一个比较实用化的微内核系统。今后Xen将有可能把一些关键的组件从Dom0中分离到各自的独立虚拟机中,这就更像一个微内核系统了。分解之后的好处就是隔离性更加好,关键组件可以独立重启。想想Dom0重启而DomU还可以正常运行,有趣,哈哈。网卡驱动有bug崩溃?反正在独立Domain里面,不会把整个系统搞垮。在公有云环境下,这样就意味着各个租客之间有更加好的独立性,服务可靠性会高点。也许——仅仅是也许——这样也意味着可以得到更好的安全性——Xen hypervisor的code base比起KVM用到的一整个Linux系统还是小很多的。(微内核的曲线逆袭?)

而Xen现在面临的问题,也是和架构有关。由于组件多,各个组件分属不同的社区,所以开发起来反而有点掣肘的感觉。而Xen最要警惕的就是复杂度问题。前面提到的分解,总有一个限度,不能无限制的随意分解。现在各个组件之间的兼容、上下游之间的兼容已经很让人头痛,若以后处理不好,只会更加恶化。而现在公司针对这个问题的策略就是组建platform team,在Linux Kernel和QEMU社区里加大影响,及时把Xen的一些变化反馈到上游。

而Xen的另外一个优势,就是产品的完整度比较高,从安全框架到高可用方案,从hypervisor到完整的云计算平台都有。而KVM多是通过libvirt接入到第三方云计算平台,一些增强功能从Linux系统中得到,更高级的需要RedHat来提供支持。这个点最后结果如何,还有待观察。

现在Xen正在社区方面发力,KVM也在高速发展,这一年将是很有趣的一年。

© 2012,liuw. All rights reserved.

]]>
我始终是一个相信阳谋多过阴谋的人。在工程至上的开源软件世界,所有策略都是可以预知和分析出来的。

“开源”二字,对于不同的人来说可能有不同的理解。对于某些国内的公司、高校来说,国外“开源”就意味着国内的“自主创新”。对于个人开发者,“开源”也许意味着搞个github、google code、bitbucket之类的账号,把自己的一些成熟或者不成熟的代码放到网上与大家共享。对于依赖开源软件的公司或者社区,“开源”最主要的作用就是构建经营生态圈,为自己的生存和发展打下基础。

在开源软件的世界,有着同样的目的的几个产品互相竞争是很正常的事情,比如Xen和KVM就是如此。首要条件是产品能够达到基本的标准,产品本身是基础,不好后面就没有戏。构建经营生态圈是非常费时费力的一个事情。

Xen在过去几年在社区方面失去了很多阵地,究其原因就是公司方面对于社区不够重视(以前都忙着赚真金白银了)。比如说在Ubuntu 8.04的时候Xen是被直接打包到系统中的,但是9.x版本到11.x版本里面,由于公司方面没有专门聘请packager,没有人去维护这个package,它就被移除了。而最近的一个Ubuntu的survey也反映了这一点(虽然platform team内部对于统计方式有点疑问),Xen的占有率不是很高(其实我个人觉得数据已经比预想的要高了)。可以说,前几年的疏忽造成的影响现在开始慢慢浮出水面了。

而现在公司的策略已经转变了,准备以开源产品为基础吸引用户,然后为有需求的用户提供高阶的商业版本产品。我觉得这个套路倒是很正常,RedHat其实就是一直这样发展的,而这个非常传统的软件公司好像一开始不大转得过弯来。现在还不算晚,开始统一产品线,也开始在社区方面加大力量。比如说现在开始和一些开源云计算平台互通、聘请一些开发人员专门为主要发行版打包软件、专门成立团队来经营社区、放出商业产品的社区版本等等。

在开源软件界,Xen的最主要竞争者是由RedHat支持的KVM。但是Xen和KVM又不完全是一个层面上的产品。Xen本身只是一个hypervisor,实际的驱动放在Linux内核中,有自己完整的工具栈(libxc和libxl都是Xen的一部分),还有QEMU等用户空间程序,还有第三方支持的如Remus、XSM等扩展功能,所以Xen是一个很庞大的“东西”。而KVM则相对简单,只有内核部分以及作为模拟器的QEMU。KVM工具栈常见的是libvirt,它不属于KVM的一部分,其他的什么扩展功能也不是很多。打个不大恰当的比方,Xen就像FreeBSD,而KVM就像Linux。

KVM是一个很优秀的产品,它的设计非常简洁,概念相对Xen来说少很多,组件少开发维护起来方便,使用起来也方便(直接加载模块即可)。现在有RedHat的支持,发展起来顺风顺水。那么Xen能在什么方面与KVM竞争呢?所谓“成也萧何,败也萧何”,“十年河东十年河西”,Xen的复杂架构,让它在前硬件虚拟化时代领先,硬件虚拟化时代落后,也许在将来还是有机会反戈一击的,但处理不好,那也是无翻身之可能了。

先退一步来观察一下Xen虚拟化系统。整个系统由hypervisor、Dom0以及其上运行的DomU组成。hypervisor负责所有特权操作,Dom0负责管理和驱动硬件,DomU运行用户的任务。不特意提及的话,也许基本没有人会想到“微内核”这个词吧。实际上Xen系统可以看作一个比较实用化的微内核系统。今后Xen将有可能把一些关键的组件从Dom0中分离到各自的独立虚拟机中,这就更像一个微内核系统了。分解之后的好处就是隔离性更加好,关键组件可以独立重启。想想Dom0重启而DomU还可以正常运行,有趣,哈哈。网卡驱动有bug崩溃?反正在独立Domain里面,不会把整个系统搞垮。在公有云环境下,这样就意味着各个租客之间有更加好的独立性,服务可靠性会高点。也许——仅仅是也许——这样也意味着可以得到更好的安全性——Xen hypervisor的code base比起KVM用到的一整个Linux系统还是小很多的。(微内核的曲线逆袭?)

而Xen现在面临的问题,也是和架构有关。由于组件多,各个组件分属不同的社区,所以开发起来反而有点掣肘的感觉。而Xen最要警惕的就是复杂度问题。前面提到的分解,总有一个限度,不能无限制的随意分解。现在各个组件之间的兼容、上下游之间的兼容已经很让人头痛,若以后处理不好,只会更加恶化。而现在公司针对这个问题的策略就是组建platform team,在Linux Kernel和QEMU社区里加大影响,及时把Xen的一些变化反馈到上游。

而Xen的另外一个优势,就是产品的完整度比较高,从安全框架到高可用方案,从hypervisor到完整的云计算平台都有。而KVM多是通过libvirt接入到第三方云计算平台,一些增强功能从Linux系统中得到,更高级的需要RedHat来提供支持。这个点最后结果如何,还有待观察。

现在Xen正在社区方面发力,KVM也在高速发展,这一年将是很有趣的一年。

© 2012,liuw. All rights reserved.

]]>
0
<![CDATA[技术工程师的能力与目标]]> http://www.udpwork.com/item/6888.html http://www.udpwork.com/item/6888.html#reviews Thu, 23 Feb 2012 02:20:05 +0800 Tim http://www.udpwork.com/item/6888.html 曾经有这样试验,随机选择一组对象进行工作的自评,几乎所有对象的自评分都在实际成绩的平均分以上。在工程师团队中也不例外,许多工程师有这样的困惑,自己觉得工作已经做得不错,但是上司好像察觉不到,甚至还对自己的工作吹毛求疵。如果有个合适参照标准,工程师或许就可以更好的对自己工作进行自评。

管理者也同样面临类似困惑,在一个组织中,需要定期对团队中的成员进行考核及晋升,但是考核的标准是什么?小团队中主要取决于管理者的意志;大型组织中l流程会更规范,但也存在考核者凭感觉来给被评估者打分的情况,或者是考核者心中的衡量标准千差万别。

从工程师自我提升追求及职业规划的角度,情况会更复杂。每一个工程师都有不同的追求目标,孟岩有一篇很有影响力的《技术路线的选择重要但不具有决定性》,文中工程师的追求类型被描述成事业目标型、团队精英型、技术高手型、得过且过或养家糊口型四种。文中将“独特的个性知识经验组合”看做是工程师的核心竞争力,不过对于这个经验的组合不同人会有不同的理解。

从团队或者企业的角度来看,主要从团队的贡献角度来评估技术工程师的成绩,就如同评估一个科学家的成看他看对人类的贡献类似。离开了环境,单纯评估技术没有意义。比如一个技术人员对Linux内核看得滚瓜烂熟,单就这一点并不能看到太大直接价值。

但是在业界,知识点的重要性通常被放大了,业界也形成了一些非理性的氛围。工程师会努力学习某个技术(如C++语言)的方方面面,即使大部分场合只用到了其中小部分功能。技术管理者在招聘、考核、晋升等过程中也通常把知识点放在一个很重要的地位,面试题中会出现工作中并不常用的领域的各种知识点。

这跟前几天某条关于大学教育的微博有异曲同工之妙,大学教育经历了全部是知识点的教育,慢慢意识到需要培养的能力不仅是知识点,而主要应是独立思考能力。

技术人员追求的也不仅是知识点,而是在专业领域正确做事的方法及达成目标的能力。两个同时入职的员工,一段时间后技术好的那个就发展得好吗?还是有更好做事方法及能达成目标的人更容易得到认可?

我认为一个好的工程师必要的能力

设计能力

设计能力参见前文技术评审中关于设计的描述,简要的说就是具备设计简洁、易于扩展及维护的功能及特性能力。

需要补充一个设计方面的anti pattern,选择合适的技术及架构,意味着不引入及增加不必要的抽象层或框架,并提供高质量、稳定、高效、安全的代码。不少能力还不错的人员有这个缺点,一个简单的项目,出于追求流行或者对于某项技术的崇拜心理,引入了复杂的技术或框架,对于个人来说确实提高了见识,增加了业内交流的资本,但是对于组织来说这种锻炼却是团队成效的噩梦,对于技术从业人员来说,不盲目引入不必要的高深技术来保证项目进展是一种基本的职业素养。

此外设计中还有一个隐含的条件,就是选择的方案能相对减少开发周期,加快交付时间。也就是下一点介绍的。

交付能力

通俗的说就是不管发生了什么,都能按时交付。
充分考虑自身技术能力、项目依赖、队员排期冲突、负面情绪、技术方案风险、未预知的技术障碍、需求变化等。
具备为功能的设计做取舍的能力,但功能取舍并不以牺牲产品的核心愿景为前提。

规范与协作

在编码前能够完成模块或特性的清晰架构或设计文档,并保持在开发过程以及代码重构过程中文档的一致性。
推动及促进团队的代码及设计规范,并确保执行过程中与规范的一致,并能根据实际情况对流程及规范提供优化建议。
编写的代码通常当做团队的模板或者是最佳实践的设计模式。

团队效率贡献

有改善团队效率方面的贡献吗?比如做一个相似项目为何周期很长?为什么开发完成之后又花了比开发周期更长的时间调试或修改bug?
推进代码复用,你的代码和工具其他小组或部门愿意用吗,准备让他们用吗?有推动让他们用吗?
自动化体系来帮助提高测试、开发、debug、跟踪用户问题的效率
能够用服务化的方法来解决异构、多版本问题
有优化流程贡献?

已经不是那个独行侠或个人技术英雄的时代了,融入团队,多考虑对团队的贡献,更容易得到成长。

后记: 职业发展方面话题比较大,不容易写好,本文也写得比较辛苦,改了两个晚上,暂不写类似话题。这也再次说明,当你有一个非常大的愿景(想当青年导师?),但系统能力还跟不上时,更应从小处着手。

Similar Posts: ]]>
曾经有这样试验,随机选择一组对象进行工作的自评,几乎所有对象的自评分都在实际成绩的平均分以上。在工程师团队中也不例外,许多工程师有这样的困惑,自己觉得工作已经做得不错,但是上司好像察觉不到,甚至还对自己的工作吹毛求疵。如果有个合适参照标准,工程师或许就可以更好的对自己工作进行自评。

管理者也同样面临类似困惑,在一个组织中,需要定期对团队中的成员进行考核及晋升,但是考核的标准是什么?小团队中主要取决于管理者的意志;大型组织中l流程会更规范,但也存在考核者凭感觉来给被评估者打分的情况,或者是考核者心中的衡量标准千差万别。

从工程师自我提升追求及职业规划的角度,情况会更复杂。每一个工程师都有不同的追求目标,孟岩有一篇很有影响力的《技术路线的选择重要但不具有决定性》,文中工程师的追求类型被描述成事业目标型、团队精英型、技术高手型、得过且过或养家糊口型四种。文中将“独特的个性知识经验组合”看做是工程师的核心竞争力,不过对于这个经验的组合不同人会有不同的理解。

从团队或者企业的角度来看,主要从团队的贡献角度来评估技术工程师的成绩,就如同评估一个科学家的成看他看对人类的贡献类似。离开了环境,单纯评估技术没有意义。比如一个技术人员对Linux内核看得滚瓜烂熟,单就这一点并不能看到太大直接价值。

但是在业界,知识点的重要性通常被放大了,业界也形成了一些非理性的氛围。工程师会努力学习某个技术(如C++语言)的方方面面,即使大部分场合只用到了其中小部分功能。技术管理者在招聘、考核、晋升等过程中也通常把知识点放在一个很重要的地位,面试题中会出现工作中并不常用的领域的各种知识点。

这跟前几天某条关于大学教育的微博有异曲同工之妙,大学教育经历了全部是知识点的教育,慢慢意识到需要培养的能力不仅是知识点,而主要应是独立思考能力。

技术人员追求的也不仅是知识点,而是在专业领域正确做事的方法及达成目标的能力。两个同时入职的员工,一段时间后技术好的那个就发展得好吗?还是有更好做事方法及能达成目标的人更容易得到认可?

我认为一个好的工程师必要的能力

设计能力

设计能力参见前文技术评审中关于设计的描述,简要的说就是具备设计简洁、易于扩展及维护的功能及特性能力。

需要补充一个设计方面的anti pattern,选择合适的技术及架构,意味着不引入及增加不必要的抽象层或框架,并提供高质量、稳定、高效、安全的代码。不少能力还不错的人员有这个缺点,一个简单的项目,出于追求流行或者对于某项技术的崇拜心理,引入了复杂的技术或框架,对于个人来说确实提高了见识,增加了业内交流的资本,但是对于组织来说这种锻炼却是团队成效的噩梦,对于技术从业人员来说,不盲目引入不必要的高深技术来保证项目进展是一种基本的职业素养。

此外设计中还有一个隐含的条件,就是选择的方案能相对减少开发周期,加快交付时间。也就是下一点介绍的。

交付能力

通俗的说就是不管发生了什么,都能按时交付。
充分考虑自身技术能力、项目依赖、队员排期冲突、负面情绪、技术方案风险、未预知的技术障碍、需求变化等。
具备为功能的设计做取舍的能力,但功能取舍并不以牺牲产品的核心愿景为前提。

规范与协作

在编码前能够完成模块或特性的清晰架构或设计文档,并保持在开发过程以及代码重构过程中文档的一致性。
推动及促进团队的代码及设计规范,并确保执行过程中与规范的一致,并能根据实际情况对流程及规范提供优化建议。
编写的代码通常当做团队的模板或者是最佳实践的设计模式。

团队效率贡献

有改善团队效率方面的贡献吗?比如做一个相似项目为何周期很长?为什么开发完成之后又花了比开发周期更长的时间调试或修改bug?
推进代码复用,你的代码和工具其他小组或部门愿意用吗,准备让他们用吗?有推动让他们用吗?
自动化体系来帮助提高测试、开发、debug、跟踪用户问题的效率
能够用服务化的方法来解决异构、多版本问题
有优化流程贡献?

已经不是那个独行侠或个人技术英雄的时代了,融入团队,多考虑对团队的贡献,更容易得到成长。

后记: 职业发展方面话题比较大,不容易写好,本文也写得比较辛苦,改了两个晚上,暂不写类似话题。这也再次说明,当你有一个非常大的愿景(想当青年导师?),但系统能力还跟不上时,更应从小处着手。

Similar Posts: ]]>
0
<![CDATA[法律和实践]]> http://www.udpwork.com/item/6887.html http://www.udpwork.com/item/6887.html#reviews Wed, 22 Feb 2012 22:47:59 +0800 Jian Shuo Wang http://www.udpwork.com/item/6887.html 在西安到兰田晃悠的大巴上(旁边是秦岭的余脉在远处蜿蜒),我向一位法律界的朋友请教如何建立社区秩序的问题。他教会我一个常识:法律(Law)重要,实践(Practice)更重要。

组织经常会有制度,但没有实践,制度就死了。
也会有实践而没有制度,实践会因为时间的推移或新人加入而被弱化。

百姓网就有很多的实践,口口相传多年,却没有制度。比如书的报销制度等无数的潜规则,这些规则在组织变大的时候开始变得让人迷惑。用制度把实践纪录下来变得很重要。(每一条制度都不应该超过140个字)

]]>
在西安到兰田晃悠的大巴上(旁边是秦岭的余脉在远处蜿蜒),我向一位法律界的朋友请教如何建立社区秩序的问题。他教会我一个常识:法律(Law)重要,实践(Practice)更重要。

组织经常会有制度,但没有实践,制度就死了。
也会有实践而没有制度,实践会因为时间的推移或新人加入而被弱化。

百姓网就有很多的实践,口口相传多年,却没有制度。比如书的报销制度等无数的潜规则,这些规则在组织变大的时候开始变得让人迷惑。用制度把实践纪录下来变得很重要。(每一条制度都不应该超过140个字)

]]>
0
<![CDATA[RELAY]]> http://www.udpwork.com/item/6877.html http://www.udpwork.com/item/6877.html#reviews Wed, 22 Feb 2012 12:06:05 +0800 Felix021 http://www.udpwork.com/item/6877.html 挑战:某Linux机器A有外网访问权限,但其上运行的ssh服务(22端口)仅对内网开放,希望通过外网的某Linux机器B进行RELAY,实现对机器A的ssh登录。特别地,只要能够进行ssh连接,就可以建立socks代理,实现内网其余机器的访问。

原理:(ssh服务器)A:22 <---- 连接 ~ 连接 ---->  监听B:10001 ~ 监听B:10002 <---- 连接(ssh客户端)
    其中的 ~ 表示将两个连接/监听的socket的输入和输出分别连接起来。

简单实现(nc + shell):
1. 在机器B上运行

引用
mkfifo pipe
nc -l -p 10002 < pipe | nc -l -p 10001 > pipe

2. 在机器A上运行
引用
mkfifo pipe
nc localhost 22 < pipe | nc [B.ip] 10002 > pipe

3. 使用ssh客户端连接B:10001即可。

简单实现的主要问题是,一旦ssh客户端断开连接,部分/所有的nc会结束,无法再建立连接。所以需要改进:
1. 写一个死循环脚本来保证nc的运行,例如  for ((;;)); do nc localhost 22 <pipe | nc [B.ip] 10002 >;pipe; done
2. 将该脚本放入 /etc/rc.local ,保证每次开机后自动运行。

还有一个蛋疼的问题是,(在我的测试中)如果ssh客户端被强制断开连接(不是 $exit ),B上面监听10002端口的那个nc不一定会结束。虽然我特意安排了B机器的脚本管道前监听10002,管道后监听10001,希望能利用SIGPIPE来搞定,但是系统似乎抽风。所以还是需要一个机制来保证一旦某个nc结束了,另一个nc也会结束。可能还有一些其他更蛋疼的情况,无法一一列出来。

为了解决nc不结束的蛋疼情况,可以用脚本来实现:记录2个nc的PID,然后定时grep之。如果只剩下1个,就把另一个也kill掉。不过我没有采用这个方案,而是写了一个c程序来处理,pipe出两对fd,fork出两个child,把两对fd dup成两个child的stdin/stdout,child分别exec执行nc,然后wait之,当wait返回以后,就用kill向两个pid送个SIGTERM,结束。然后进入下一轮循环

代码如下(此代码用于B机器,A机器只要稍微修改下exec的参数就行了):
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <assert.h>
#include <stdarg.h>
#include <sys/types.h>
#include <sys/wait.h>

void error(const char *fmt, ...)
{
    perror("Infomation");
    fprintf(stderr, "  => ");
    va_list ap;
    va_start(ap, fmt);
    vfprintf(stderr, fmt, ap);
    va_end(ap);
}

int main(int argc, char *argv[])
{
    int fd_left[2], fd_right[2];
    if (pipe(fd_left) < 0 ) {
        perror("pipe left failed");
        return 1;
    }
    if (pipe(fd_right) < 0 ) {
        perror("pipe right failed");
        return 1;
    }

    pid_t pid1 = fork();
    if (pid1 < 0) {
        perror("fork1");
        return 1;
    }

    if (pid1 == 0) {
        //child
        if (dup2(fd_left[0], STDIN_FILENO) < 0) {
            error("dup2@1@stdin");
        }
        if (dup2(fd_right[1], STDOUT_FILENO) < 0) {
            error("dup2@1@stdout");
        }
        execlp("nc", "nc", "-l", "-p", "10001", NULL);
        perror("execlp");
        return 1;
    }
    fprintf(stderr, "pid1 = %d\n", pid1);

    //parent
    pid_t pid2 = fork();
    if (pid2 < 0) {
        perror("fork2");
        return 1;
    }

    if (pid2 == 0) {
        //child
        if (dup2(fd_right[0], STDIN_FILENO) < 0) {
            error("dup2@1@stdin");
        }
        if (dup2(fd_left[1], STDOUT_FILENO) < 0) {
            error("dup2@1@stdout");
        }
        execlp("nc", "nc", "-l", "-p", "10002", NULL);
        perror("execlp");
        return 1;
    }
    fprintf(stderr, "pid2 = %d\n", pid2);

    int status;
    pid_t pid = wait(&status);
    error("Process[%d] exits\n", pid);

    kill(pid1, SIGTERM);
    kill(pid2, SIGTERM);

    return 0;
}
]]>
挑战:某Linux机器A有外网访问权限,但其上运行的ssh服务(22端口)仅对内网开放,希望通过外网的某Linux机器B进行RELAY,实现对机器A的ssh登录。特别地,只要能够进行ssh连接,就可以建立socks代理,实现内网其余机器的访问。

原理:(ssh服务器)A:22 <---- 连接 ~ 连接 ---->  监听B:10001 ~ 监听B:10002 <---- 连接(ssh客户端)
    其中的 ~ 表示将两个连接/监听的socket的输入和输出分别连接起来。

简单实现(nc + shell):
1. 在机器B上运行

引用
mkfifo pipe
nc -l -p 10002 < pipe | nc -l -p 10001 > pipe

2. 在机器A上运行
引用
mkfifo pipe
nc localhost 22 < pipe | nc [B.ip] 10002 > pipe

3. 使用ssh客户端连接B:10001即可。

简单实现的主要问题是,一旦ssh客户端断开连接,部分/所有的nc会结束,无法再建立连接。所以需要改进:
1. 写一个死循环脚本来保证nc的运行,例如  for ((;;)); do nc localhost 22 <pipe | nc [B.ip] 10002 >;pipe; done
2. 将该脚本放入 /etc/rc.local ,保证每次开机后自动运行。

还有一个蛋疼的问题是,(在我的测试中)如果ssh客户端被强制断开连接(不是 $exit ),B上面监听10002端口的那个nc不一定会结束。虽然我特意安排了B机器的脚本管道前监听10002,管道后监听10001,希望能利用SIGPIPE来搞定,但是系统似乎抽风。所以还是需要一个机制来保证一旦某个nc结束了,另一个nc也会结束。可能还有一些其他更蛋疼的情况,无法一一列出来。

为了解决nc不结束的蛋疼情况,可以用脚本来实现:记录2个nc的PID,然后定时grep之。如果只剩下1个,就把另一个也kill掉。不过我没有采用这个方案,而是写了一个c程序来处理,pipe出两对fd,fork出两个child,把两对fd dup成两个child的stdin/stdout,child分别exec执行nc,然后wait之,当wait返回以后,就用kill向两个pid送个SIGTERM,结束。然后进入下一轮循环

代码如下(此代码用于B机器,A机器只要稍微修改下exec的参数就行了):
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <assert.h>
#include <stdarg.h>
#include <sys/types.h>
#include <sys/wait.h>

void error(const char *fmt, ...)
{
    perror("Infomation");
    fprintf(stderr, "  => ");
    va_list ap;
    va_start(ap, fmt);
    vfprintf(stderr, fmt, ap);
    va_end(ap);
}

int main(int argc, char *argv[])
{
    int fd_left[2], fd_right[2];
    if (pipe(fd_left) < 0 ) {
        perror("pipe left failed");
        return 1;
    }
    if (pipe(fd_right) < 0 ) {
        perror("pipe right failed");
        return 1;
    }

    pid_t pid1 = fork();
    if (pid1 < 0) {
        perror("fork1");
        return 1;
    }

    if (pid1 == 0) {
        //child
        if (dup2(fd_left[0], STDIN_FILENO) < 0) {
            error("dup2@1@stdin");
        }
        if (dup2(fd_right[1], STDOUT_FILENO) < 0) {
            error("dup2@1@stdout");
        }
        execlp("nc", "nc", "-l", "-p", "10001", NULL);
        perror("execlp");
        return 1;
    }
    fprintf(stderr, "pid1 = %d\n", pid1);

    //parent
    pid_t pid2 = fork();
    if (pid2 < 0) {
        perror("fork2");
        return 1;
    }

    if (pid2 == 0) {
        //child
        if (dup2(fd_right[0], STDIN_FILENO) < 0) {
            error("dup2@1@stdin");
        }
        if (dup2(fd_left[1], STDOUT_FILENO) < 0) {
            error("dup2@1@stdout");
        }
        execlp("nc", "nc", "-l", "-p", "10002", NULL);
        perror("execlp");
        return 1;
    }
    fprintf(stderr, "pid2 = %d\n", pid2);

    int status;
    pid_t pid = wait(&status);
    error("Process[%d] exits\n", pid);

    kill(pid1, SIGTERM);
    kill(pid2, SIGTERM);

    return 0;
}
]]>
0
<![CDATA[P和NP那些事]]> http://www.udpwork.com/item/6876.html http://www.udpwork.com/item/6876.html#reviews Wed, 22 Feb 2012 00:54:02 +0800 isnowfy http://www.udpwork.com/item/6876.html
在计算机科学里,有时间复杂度的概念,然后就有P,NP,NPC,NP-hard的概念,平常大家说的这个题是np的,只能搜了,其实是理解错了np的概念,首先要明确np并不是“不是p”的意思,p大家都清楚是指有多项式时间的算法,而np看全称是“nondeterministic polynomial time”,是说非确定多项式时间,进一步说,正如图片上说的p是属于np的,所有p问题都是np问题。

ok,下面仔细说一下,按照官方的解释呢

p是指在多项式时间能由确定型图灵机解决的问题

np是指在多项式时间能由非确定型图灵机解决的问题

确定型图灵机可以理解为就是按照某种固定的算法,一步步求出解的程序,而非确定型图灵机其实是概念上的,他的理论价值更大一些,他是指某个程序rp非常好,能猜出答案。怎么叫猜出答案呢,举个例子比如说哈密顿问题(是说经过图上的所有点一次且仅一次的路径),程序猜出了个路径ACDEFB啥的,然后程序要自己看看这个路径是不是哈密顿路,验证了一下,我勒个去,还真是,于是答案就出现了。所以这里的猜是指给出一个正确的答案,就是不仅猜出了个答案,还要自己验证一下是不是正确的,所以在有些地方解释np就是说能在多项式时间验证的问题就是np的,也不能说不对吧。

我们看到p是属于np的,那么是不是有是np但不是p的问题呢,也就是p是否等于np呢,这是个亘古难题,但是大家普遍倾向于不等于。

这里有点跳跃啊,我们先要介绍一个规约的概念,规约就是说一个问题A可以在多项式时间转化为问题B,然后问题A有解当前仅当转化后的问题B有解,也就是说只要解决了B那么就可以解决A,注意这个转化是单向的,比如你可以用解二次方程的方法解一次方程,但是反过来却不行。

然后有个叫Cook的人发现所有的np问题都可以规约到一种叫做SAT的问题,也就是说只要SAT能有效的解决,所有问题都能利这种方法经过相应转化而有效解决,后来人们发现所有的问题能规约到的问题不止一种,而是一大类,有很多个,这类问题就被称作NP-complete问题,俗称np完全问题,就是说这类问题是np里最难的,所有的np问题都可以规约到他们。到这里我们注意到npc问题是有两个条件的:

(1)他是np问题

(2)所有的np问题都可以规约到他。

如果只满足条件(2)那么被称作NP-hard,俗称np难问题,也就是说NP-hard和np问题的交集就是npc,那么以后要证明某个问题是npc问题只要先证明他是np问题,再证明某个npc问题可以规约到他(注意这个顺序是某npc规约到你要求的问题,很多人都犯顺序的错误)就可以了,如果你证明了某个问题是npc的,那么你就不用费劲心思的去想优雅的多项式算法了。

好了,让我们看看传奇的第一个npc问题SAT到底是什么,SAT全称是satisfiability,他是问对于一个合取范式,是否有一种输入使得他的输出是1,具体点就是类似这样的布尔表达式(x1 or x2 or x3)and(x3 or x4)and(x1 or x5)对于所有的x是否有一种01取值,使得最后的结果是1,据说证明这个是npc非常复杂,大体思想就是说给定输入的图灵机操作都可以表示成这种形式的布尔表达式。而对于acmer比较熟悉的2-SAT问题就是SAT的特例,并且他是有多项式算法的,就是2-SAT是p的,而3-SAT就是npc问题了。

npc问题有很多的,比较有名的有团问题,顶点覆盖集问题,支配集问题,独立集问题,哈密顿路问题,旅行商问题等,同样有很多是NP-hard而不是npc的问题,比如围棋,停机问题等。

前一段时间比较火的就是一位HP的研究员Vinay Deolalikar声称证明了P≠NP,但是后来被证明是错误的╮( ̄▽ ̄")╭ ,不过我感觉这个可能和哥德巴赫猜想一样,按照哥德尔不完备定理来说,在现有理论的体系下,可能无法证明,需要利用到理论体系之外的东西,当然我也只是猜测,至于到底怎么样我们还是静观其变吧。
我猜您可能还会喜欢:

]]>

在计算机科学里,有时间复杂度的概念,然后就有P,NP,NPC,NP-hard的概念,平常大家说的这个题是np的,只能搜了,其实是理解错了np的概念,首先要明确np并不是“不是p”的意思,p大家都清楚是指有多项式时间的算法,而np看全称是“nondeterministic polynomial time”,是说非确定多项式时间,进一步说,正如图片上说的p是属于np的,所有p问题都是np问题。

ok,下面仔细说一下,按照官方的解释呢

p是指在多项式时间能由确定型图灵机解决的问题

np是指在多项式时间能由非确定型图灵机解决的问题

确定型图灵机可以理解为就是按照某种固定的算法,一步步求出解的程序,而非确定型图灵机其实是概念上的,他的理论价值更大一些,他是指某个程序rp非常好,能猜出答案。怎么叫猜出答案呢,举个例子比如说哈密顿问题(是说经过图上的所有点一次且仅一次的路径),程序猜出了个路径ACDEFB啥的,然后程序要自己看看这个路径是不是哈密顿路,验证了一下,我勒个去,还真是,于是答案就出现了。所以这里的猜是指给出一个正确的答案,就是不仅猜出了个答案,还要自己验证一下是不是正确的,所以在有些地方解释np就是说能在多项式时间验证的问题就是np的,也不能说不对吧。

我们看到p是属于np的,那么是不是有是np但不是p的问题呢,也就是p是否等于np呢,这是个亘古难题,但是大家普遍倾向于不等于。

这里有点跳跃啊,我们先要介绍一个规约的概念,规约就是说一个问题A可以在多项式时间转化为问题B,然后问题A有解当前仅当转化后的问题B有解,也就是说只要解决了B那么就可以解决A,注意这个转化是单向的,比如你可以用解二次方程的方法解一次方程,但是反过来却不行。

然后有个叫Cook的人发现所有的np问题都可以规约到一种叫做SAT的问题,也就是说只要SAT能有效的解决,所有问题都能利这种方法经过相应转化而有效解决,后来人们发现所有的问题能规约到的问题不止一种,而是一大类,有很多个,这类问题就被称作NP-complete问题,俗称np完全问题,就是说这类问题是np里最难的,所有的np问题都可以规约到他们。到这里我们注意到npc问题是有两个条件的:

(1)他是np问题

(2)所有的np问题都可以规约到他。

如果只满足条件(2)那么被称作NP-hard,俗称np难问题,也就是说NP-hard和np问题的交集就是npc,那么以后要证明某个问题是npc问题只要先证明他是np问题,再证明某个npc问题可以规约到他(注意这个顺序是某npc规约到你要求的问题,很多人都犯顺序的错误)就可以了,如果你证明了某个问题是npc的,那么你就不用费劲心思的去想优雅的多项式算法了。

好了,让我们看看传奇的第一个npc问题SAT到底是什么,SAT全称是satisfiability,他是问对于一个合取范式,是否有一种输入使得他的输出是1,具体点就是类似这样的布尔表达式(x1 or x2 or x3)and(x3 or x4)and(x1 or x5)对于所有的x是否有一种01取值,使得最后的结果是1,据说证明这个是npc非常复杂,大体思想就是说给定输入的图灵机操作都可以表示成这种形式的布尔表达式。而对于acmer比较熟悉的2-SAT问题就是SAT的特例,并且他是有多项式算法的,就是2-SAT是p的,而3-SAT就是npc问题了。

npc问题有很多的,比较有名的有团问题,顶点覆盖集问题,支配集问题,独立集问题,哈密顿路问题,旅行商问题等,同样有很多是NP-hard而不是npc的问题,比如围棋,停机问题等。

前一段时间比较火的就是一位HP的研究员Vinay Deolalikar声称证明了P≠NP,但是后来被证明是错误的╮( ̄▽ ̄")╭ ,不过我感觉这个可能和哥德巴赫猜想一样,按照哥德尔不完备定理来说,在现有理论的体系下,可能无法证明,需要利用到理论体系之外的东西,当然我也只是猜测,至于到底怎么样我们还是静观其变吧。
我猜您可能还会喜欢:

]]>
0
<![CDATA[自动添加NavigationController按钮动画]]> http://www.udpwork.com/item/6875.html http://www.udpwork.com/item/6875.html#reviews Tue, 21 Feb 2012 23:19:09 +0800 bang http://www.udpwork.com/item/6875.html 问题

UINavigationController顶部的返回按钮在切换视图时有左右滑动的动画(效果见iMail),但在自定义了这个按钮后(通过设self.navigationItem.leftBarButtonItem自定义),切换视图时按钮没有了动画。

最初解决方法

在每个viewController的viewWillAppear和viewDisappear方法上手动让按钮左右动。由于每个视图出现/消失的方向都有左/右两种,在这里难以判断,需要各种变量辅助,实现十分恶心,中间需要新加view时逻辑还要重新修改,被折腾。

最终解决方法

继承UINavigationController,重写pushViewController和popViewController方法,在这两个方法里面获取NavigationController里的view栈,因为push和pop总是操作最前面的View,所以可以从栈里知道哪两个view是要做动画的。调用这些view相应的方法使他们让自己的按钮动。这些方法可以写在UIViewController的扩展里,不用修改每一个viewController。

换言之,把下面的代码加入项目里,把项目里的UINavigationController换成下面的类,那些自定义的返回按钮就会自动做动画了。

代码

https://github.com/bang590/iOSPlayground/tree/master/NavigationButtonAnimate

//.h
#import <Foundation/Foundation.h>
@interface MainNavigationController : UINavigationController {
}
@end

//.m
#import "MainNavigationController.h"
@implementation UIViewController (UINavigationButtonAnimate)
- (void) viewAppearFromLeft
{
	[self.navigationItem.leftBarButtonItem.customView setTransform:CGAffineTransformMakeTranslation(-60, 0)];
	[UIView beginAnimations:@"viewAppearFromLeft" context:nil];
	[UIView setAnimationDuration:0.3];
	[self.navigationItem.leftBarButtonItem.customView setAlpha:1.0];
	[self.navigationItem.leftBarButtonItem.customView setTransform:CGAffineTransformMakeTranslation(0, 0)];
	[UIView commitAnimations];
}
- (void) viewAppearFromRight
{
	[self.navigationItem.leftBarButtonItem.customView setTransform:CGAffineTransformMakeTranslation(60, 0)];
	[UIView beginAnimations:@"viewAppearFromRight" context:nil];
	[UIView setAnimationDuration:0.3];
	[self.navigationItem.leftBarButtonItem.customView setAlpha:1.0];
	[self.navigationItem.leftBarButtonItem.customView setTransform:CGAffineTransformMakeTranslation(0, 0)];
	[UIView commitAnimations];
}
- (void) viewDisappearFromLeft
{
	[UIView beginAnimations:@"viewDisappearFromLeft" context:nil];
	[self.navigationItem.leftBarButtonItem.customView setTransform:CGAffineTransformMakeTranslation(60, 0)];
	[UIView setAnimationDuration:0.3];
	[self.navigationItem.leftBarButtonItem.customView setAlpha:0];
	[UIView commitAnimations];
}
- (void) viewDisappearFromRight
{
	[UIView beginAnimations:@"viewDisappearFromRight" context:nil];
	[self.navigationItem.leftBarButtonItem.customView setTransform:CGAffineTransformMakeTranslation(-60, 0)];
	[UIView setAnimationDuration:0.3];
	[self.navigationItem.leftBarButtonItem.customView setAlpha:0];
	[UIView commitAnimations];
}
@end

@implementation MainNavigationController
- (UIViewController *)popViewControllerAnimated:(BOOL)animated
{
	if (animated) {
		UIViewController *popController = [self.viewControllers lastObject];
		UIViewController *pushController = [self.viewControllers objectAtIndex:self.viewControllers.count - 2];
		[popController viewDisappearFromLeft];
		[pushController viewAppearFromLeft];
	}
	return [super popViewControllerAnimated:animated];
}
- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated
{
	if (animated) {
		UIViewController *popController = [self.viewControllers lastObject];
		UIViewController *pushController = viewController;
		[popController viewDisappearFromRight];
		[pushController viewAppearFromRight];
	}
	[super pushViewController:viewController animated:animated];
}
@end
]]>
问题

UINavigationController顶部的返回按钮在切换视图时有左右滑动的动画(效果见iMail),但在自定义了这个按钮后(通过设self.navigationItem.leftBarButtonItem自定义),切换视图时按钮没有了动画。

最初解决方法

在每个viewController的viewWillAppear和viewDisappear方法上手动让按钮左右动。由于每个视图出现/消失的方向都有左/右两种,在这里难以判断,需要各种变量辅助,实现十分恶心,中间需要新加view时逻辑还要重新修改,被折腾。

最终解决方法

继承UINavigationController,重写pushViewController和popViewController方法,在这两个方法里面获取NavigationController里的view栈,因为push和pop总是操作最前面的View,所以可以从栈里知道哪两个view是要做动画的。调用这些view相应的方法使他们让自己的按钮动。这些方法可以写在UIViewController的扩展里,不用修改每一个viewController。

换言之,把下面的代码加入项目里,把项目里的UINavigationController换成下面的类,那些自定义的返回按钮就会自动做动画了。

代码

https://github.com/bang590/iOSPlayground/tree/master/NavigationButtonAnimate

//.h
#import <Foundation/Foundation.h>
@interface MainNavigationController : UINavigationController {
}
@end

//.m
#import "MainNavigationController.h"
@implementation UIViewController (UINavigationButtonAnimate)
- (void) viewAppearFromLeft
{
	[self.navigationItem.leftBarButtonItem.customView setTransform:CGAffineTransformMakeTranslation(-60, 0)];
	[UIView beginAnimations:@"viewAppearFromLeft" context:nil];
	[UIView setAnimationDuration:0.3];
	[self.navigationItem.leftBarButtonItem.customView setAlpha:1.0];
	[self.navigationItem.leftBarButtonItem.customView setTransform:CGAffineTransformMakeTranslation(0, 0)];
	[UIView commitAnimations];
}
- (void) viewAppearFromRight
{
	[self.navigationItem.leftBarButtonItem.customView setTransform:CGAffineTransformMakeTranslation(60, 0)];
	[UIView beginAnimations:@"viewAppearFromRight" context:nil];
	[UIView setAnimationDuration:0.3];
	[self.navigationItem.leftBarButtonItem.customView setAlpha:1.0];
	[self.navigationItem.leftBarButtonItem.customView setTransform:CGAffineTransformMakeTranslation(0, 0)];
	[UIView commitAnimations];
}
- (void) viewDisappearFromLeft
{
	[UIView beginAnimations:@"viewDisappearFromLeft" context:nil];
	[self.navigationItem.leftBarButtonItem.customView setTransform:CGAffineTransformMakeTranslation(60, 0)];
	[UIView setAnimationDuration:0.3];
	[self.navigationItem.leftBarButtonItem.customView setAlpha:0];
	[UIView commitAnimations];
}
- (void) viewDisappearFromRight
{
	[UIView beginAnimations:@"viewDisappearFromRight" context:nil];
	[self.navigationItem.leftBarButtonItem.customView setTransform:CGAffineTransformMakeTranslation(-60, 0)];
	[UIView setAnimationDuration:0.3];
	[self.navigationItem.leftBarButtonItem.customView setAlpha:0];
	[UIView commitAnimations];
}
@end

@implementation MainNavigationController
- (UIViewController *)popViewControllerAnimated:(BOOL)animated
{
	if (animated) {
		UIViewController *popController = [self.viewControllers lastObject];
		UIViewController *pushController = [self.viewControllers objectAtIndex:self.viewControllers.count - 2];
		[popController viewDisappearFromLeft];
		[pushController viewAppearFromLeft];
	}
	return [super popViewControllerAnimated:animated];
}
- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated
{
	if (animated) {
		UIViewController *popController = [self.viewControllers lastObject];
		UIViewController *pushController = viewController;
		[popController viewDisappearFromRight];
		[pushController viewAppearFromRight];
	}
	[super pushViewController:viewController animated:animated];
}
@end
]]>
0
<![CDATA[开发笔记 (11) : 组播服务]]> http://www.udpwork.com/item/6874.html http://www.udpwork.com/item/6874.html#reviews Tue, 21 Feb 2012 21:17:26 +0800 云风 http://www.udpwork.com/item/6874.html 最近一口气招了 5 个程序员, 在他们没有到岗之前,我不想把自己过多陷入游戏实现的细节上面。所以,除了维护一些前面的代码,陆续发现和修复一些 bug 外,我计划再完善一些基础设施的设计。这些基础模块暂时可以没有,如果实现好了又可以直接加入现有系统。这样比较利于后面的工作划分。

其中之一是服务器组播的模块。

先回顾一下前面提到的服务器架构(skynet),它可以解决各个服务节点的命名问题,以及把消息从一个节点发送到另一个节点的能力。在不改变接口协议的前提下,最简单实现组播的方法就是把一个组命名为一个节点,由这个节点负责群法消息。

其工作原理类似于 UDP 的局域网组播。我还在网易时,实现过另一个类似的服务,见这篇 blog。这次重新在另一个系统结构上做,有稍许不同。

主要的问题在于,组的管理(加入,退出组)和组本身如何区分。

按道理,组的管理和组播本来依赖同一份数据,即组员列表。系统构架最好是实现为表里如一。也就是说,接口上看起来是怎样切分功能的,那么实际功能就怎样物理上隔离比较好。那么,组播消息和组管理,最好看起来就在一个组的节点上。

但这样,会把“组”变成一个于单点不同的对象。必须对网络框架的 API 做修改。我首先放弃了这种方案。

另一个自然的方案是,把所有组的管理放在一起,由一个具名服务来提供。任何一个节点可以向这个组管理节点发送组管理的请求。比如创建一个新的组,并且把若干节点加入这个组里。并给这个组取一个名字。以后向这个特定名字的节点发送的消息,均组播给所有组员。

这样做比较干净,对组发消息可以和对单节点发消息一致。但组管理服务器和组本身看起来是两个名字。如果遵循表里如一的原则,它们的实现也最好在两个节点上。可是用现有的 skynet 接口,它们两者之间很难通讯。

蜗牛同学说服我,组播将是一个重要的基础设施,可以做在底层,成为系统的一部分。那么脏一点也无所谓了。就是说,表面上看起来它是一个独立服务,实际上是系统的一部分。我接受这个建议,这样就不用纠结于怎么把这个东西设计的更好的问题上了。反正不改动之前的设计,额外加这么一块最不影响开发进度了。

有了这样一个基础服务,下面做场景服务、队伍 API 、聊天服务,都会简单很多。

在没有把这个基础服务在通讯框架上实现出来之前,暂时先在 lua 层的库里面做个钩子,截获这些组播相关的调用,由每个节点自行群发消息也很容易模拟出来。

]]>
最近一口气招了 5 个程序员, 在他们没有到岗之前,我不想把自己过多陷入游戏实现的细节上面。所以,除了维护一些前面的代码,陆续发现和修复一些 bug 外,我计划再完善一些基础设施的设计。这些基础模块暂时可以没有,如果实现好了又可以直接加入现有系统。这样比较利于后面的工作划分。

其中之一是服务器组播的模块。

先回顾一下前面提到的服务器架构(skynet),它可以解决各个服务节点的命名问题,以及把消息从一个节点发送到另一个节点的能力。在不改变接口协议的前提下,最简单实现组播的方法就是把一个组命名为一个节点,由这个节点负责群法消息。

其工作原理类似于 UDP 的局域网组播。我还在网易时,实现过另一个类似的服务,见这篇 blog。这次重新在另一个系统结构上做,有稍许不同。

主要的问题在于,组的管理(加入,退出组)和组本身如何区分。

按道理,组的管理和组播本来依赖同一份数据,即组员列表。系统构架最好是实现为表里如一。也就是说,接口上看起来是怎样切分功能的,那么实际功能就怎样物理上隔离比较好。那么,组播消息和组管理,最好看起来就在一个组的节点上。

但这样,会把“组”变成一个于单点不同的对象。必须对网络框架的 API 做修改。我首先放弃了这种方案。

另一个自然的方案是,把所有组的管理放在一起,由一个具名服务来提供。任何一个节点可以向这个组管理节点发送组管理的请求。比如创建一个新的组,并且把若干节点加入这个组里。并给这个组取一个名字。以后向这个特定名字的节点发送的消息,均组播给所有组员。

这样做比较干净,对组发消息可以和对单节点发消息一致。但组管理服务器和组本身看起来是两个名字。如果遵循表里如一的原则,它们的实现也最好在两个节点上。可是用现有的 skynet 接口,它们两者之间很难通讯。

蜗牛同学说服我,组播将是一个重要的基础设施,可以做在底层,成为系统的一部分。那么脏一点也无所谓了。就是说,表面上看起来它是一个独立服务,实际上是系统的一部分。我接受这个建议,这样就不用纠结于怎么把这个东西设计的更好的问题上了。反正不改动之前的设计,额外加这么一块最不影响开发进度了。

有了这样一个基础服务,下面做场景服务、队伍 API 、聊天服务,都会简单很多。

在没有把这个基础服务在通讯框架上实现出来之前,暂时先在 lua 层的库里面做个钩子,截获这些组播相关的调用,由每个节点自行群发消息也很容易模拟出来。

]]>
0
<![CDATA[关于RSS系统]]> http://www.udpwork.com/item/6873.html http://www.udpwork.com/item/6873.html#reviews Tue, 21 Feb 2012 15:36:18 +0800 Luke Fan http://www.udpwork.com/item/6873.html RSS阅读,对于我们这些人来说肯定是一天都离不开的,但其实这也是一个小众应用。
我个人对于RSS阅读的需求是这样的:

  • 多平台的书签同步,标记那些读过,哪些未读。
  • 不论源提供的是什么样的,所有的内容都分为标题、简介和全文三种状态,也就是说对于那些不提供简介的,要自动生成简介,对于那些不提供全文的要自动提供全文。
  • 移动端的图片,进行预先的处理,下载的内容预先压缩,比如生成类似于epub的格式。
  • 自动去重,如果我订阅的各个源之间,都报道了同一事件,那么就要去除重复的内容。
  • 智能内容推荐,推荐那些热门的内容,推荐那些我好友关注的内容,推荐那些有趣的,新的源。
  • 智能过滤,当我的时间比较紧张的时候,按照优先级和重要度,筛选比较重要的内容发给我。
  • 提醒,当某些特殊事件发生时,主动的提醒我。
  • 归纳总结,定期,比如一天、一周、一个月,出一份总结性的杂志。可以离线阅读。
  • 分享和关注,分享我的阅读体验,关注我好友的阅读过程
  • 将所有源的所有内容,按照类别进行分类、去重,然后按照类别进行阅读

我目前主要在使用Google Reader,IOS端使用的是Reeder。主要的功能是可以满足的。但是Google Reader是按照时间和源为基础来管理内容的。我希望能够安装状态和优先级、类型来进行组织和排列。

希望能够有跨平台的RSS离线阅读工具。
较为重度的RSS阅读者,对RSS阅读提出的想法。

相关内容

  • 2012年01月2日 --帮助人们规划人生、实现梦想的系统结构设想
    帮助人们实现梦想的系统 前文已经写了很多内容,那里面其实已经将系统的结构和工作方式都讲过一些了,但是这里还是想再将这两个部分分拆开来,分别讲述一下。 本文主要是想要讲解帮助人们实现梦想的系统,可能会拥有哪些模块,哪些参与者,以及这些模块和参与者之间是如何在系统中进行互动的。 本文包含的内容,应该会比use case稍微丰富一些吧。 以后应该还会找时间再写一写这个系统的开发、...
  • 2011年11月12日 --帮助人们规划人生、实现梦想的系统
    这个标题看起来好像太大了,也太痴人说梦了。其实这里要讲述得是一种基于任务管理系统的扩展方式。前面已经有很多文章讨论过任务管理系统了,这应该算是一个延续吧。 这里要讨论得并不完全是传统意义上的任务管理系统,可以说是一种帮助人们实现其人生梦想得系统。人们并不一定希望受到计算机或软件系统太多得约束,但他们确实有很多想要实现的人生梦想。 记得小时候总是被教育说:君子立长志,小人常立志。也就是说那些...
  • 2011年09月18日 --关于任务管理系统
    书接上文。任务管理系统中的很多特性都是从复杂的工程管理软件中借鉴来的。那么在探寻任务管理系统的设计时,最好能够先来看看工程管理软件都有哪些常见的特性;然后再看看对于个人使用的任务系统来说,对于这些特性的需求会有什么样的变化;最后,阐述一下我个人对任务管理系统的一些看法。 工程管理软件分析 工程管理软件中的任务,是有相互的依赖关系的。有些是由于后续任务需要使用前置任务的成果,有些则是由...
]]>
RSS阅读,对于我们这些人来说肯定是一天都离不开的,但其实这也是一个小众应用。
我个人对于RSS阅读的需求是这样的:

  • 多平台的书签同步,标记那些读过,哪些未读。
  • 不论源提供的是什么样的,所有的内容都分为标题、简介和全文三种状态,也就是说对于那些不提供简介的,要自动生成简介,对于那些不提供全文的要自动提供全文。
  • 移动端的图片,进行预先的处理,下载的内容预先压缩,比如生成类似于epub的格式。
  • 自动去重,如果我订阅的各个源之间,都报道了同一事件,那么就要去除重复的内容。
  • 智能内容推荐,推荐那些热门的内容,推荐那些我好友关注的内容,推荐那些有趣的,新的源。
  • 智能过滤,当我的时间比较紧张的时候,按照优先级和重要度,筛选比较重要的内容发给我。
  • 提醒,当某些特殊事件发生时,主动的提醒我。
  • 归纳总结,定期,比如一天、一周、一个月,出一份总结性的杂志。可以离线阅读。
  • 分享和关注,分享我的阅读体验,关注我好友的阅读过程
  • 将所有源的所有内容,按照类别进行分类、去重,然后按照类别进行阅读

我目前主要在使用Google Reader,IOS端使用的是Reeder。主要的功能是可以满足的。但是Google Reader是按照时间和源为基础来管理内容的。我希望能够安装状态和优先级、类型来进行组织和排列。

希望能够有跨平台的RSS离线阅读工具。
较为重度的RSS阅读者,对RSS阅读提出的想法。

相关内容

  • 2012年01月2日 --帮助人们规划人生、实现梦想的系统结构设想
    帮助人们实现梦想的系统 前文已经写了很多内容,那里面其实已经将系统的结构和工作方式都讲过一些了,但是这里还是想再将这两个部分分拆开来,分别讲述一下。 本文主要是想要讲解帮助人们实现梦想的系统,可能会拥有哪些模块,哪些参与者,以及这些模块和参与者之间是如何在系统中进行互动的。 本文包含的内容,应该会比use case稍微丰富一些吧。 以后应该还会找时间再写一写这个系统的开发、...
  • 2011年11月12日 --帮助人们规划人生、实现梦想的系统
    这个标题看起来好像太大了,也太痴人说梦了。其实这里要讲述得是一种基于任务管理系统的扩展方式。前面已经有很多文章讨论过任务管理系统了,这应该算是一个延续吧。 这里要讨论得并不完全是传统意义上的任务管理系统,可以说是一种帮助人们实现其人生梦想得系统。人们并不一定希望受到计算机或软件系统太多得约束,但他们确实有很多想要实现的人生梦想。 记得小时候总是被教育说:君子立长志,小人常立志。也就是说那些...
  • 2011年09月18日 --关于任务管理系统
    书接上文。任务管理系统中的很多特性都是从复杂的工程管理软件中借鉴来的。那么在探寻任务管理系统的设计时,最好能够先来看看工程管理软件都有哪些常见的特性;然后再看看对于个人使用的任务系统来说,对于这些特性的需求会有什么样的变化;最后,阐述一下我个人对任务管理系统的一些看法。 工程管理软件分析 工程管理软件中的任务,是有相互的依赖关系的。有些是由于后续任务需要使用前置任务的成果,有些则是由...
]]>
0
<![CDATA[钱的魔咒:读《十亿美金的教训》]]> http://www.udpwork.com/item/6872.html http://www.udpwork.com/item/6872.html#reviews Tue, 21 Feb 2012 14:04:31 +0800 魏武挥 http://www.udpwork.com/item/6872.html 十亿美金的教训虽然托尔斯泰给我们留下了这样的一句名言:“幸福的家庭都是相似的,不幸的家庭各有各的不幸。”但至少对于商业组织来说,这句话是有问题的。事实上,大部分失败的公司都是相似的,而成功的公司各有各的原因。道理无它,每个成功的公司的历史上,都会出现几个非常关键的偶然点,而这些偶然点,主事人的选择和判断,老实讲一句,不无运气成分。而运气这两个字,怎么复制呢?

所以看看失败案例,避免前车之鉴,还是有意义的。林军的这本《十亿美金的教训》,颇有点象吴晓波的《大败局》,不过聚焦于中国互联网圈里的十个失败案例:51.com、3721、ITAT、PPG、分众、港湾、联想FM365、联众、盛大盒子、8848。按照林氏的说法,这十个案例,本来都有可能成为市值十亿美金的公司,但林军虽然给这十个案例都提供了一个“一句话小结”的失败教训,但在我看来,其实都差不多:钱惹出来的祸。

大概没有一个行业对风险投资那么饥渴了,究其根本原因,数字经济——无论你做什么——都是追求规模的。因为公司本质上是建立在注意力之上的。虽然有所谓“长尾理论”、“丰裕理论”,但到底有一样东西是亘古不变的稀缺:信息消费者的时间。这一秒TA在关注A网页,就很难再同时关注B网页。

风险投资能够帮助创业项目迅速扩大规模,而这一点几乎成了大多数创业项目的生死之线。互联网的创业项目,很少是今天开张明天就来钱的(团购这个业态例外,它第一天就可能会有现金流,这也是2011年那么多资金蜂拥到团购业里的原因)。既然没有产出,又要快速做大规模,那么,除了项目创始人自己有钱以外,就只有一条路了:风险投资。

有钱不仅可以扩大规模,还可以做到狙击竞争者的作用。在团购业里,有所谓1亿元的门槛,但其实团购网站几乎没有什么技术含量。而这一亿门槛,就是用巨大而集中的资金,在短期内投放各种广告,迅速占领用户的意识占有率——在用户打算团购的那一刹那,TA想到的是A网站而不是B网站。视频行业里也用巨资来争夺作品的播放权,从而让不够有钱的公司迅速出局。中国的微博业,先发者饭否、叽歪、嘀咕等,均不够有钱,被后来者新浪腾讯之类迅速超越。有钱就意味着可以雇佣更多的运营人员,也能吸引到足够多的明星名人捧场。虽然不能说有钱是唯一的原因,但要说有钱是原因之一,应该没什么问题。

不过,创业者和风险投资人的利益并不完全一致。对于创业者而言,把公司做大做强日进斗金是他们的终极目标,但风险投资人并不见得是这样的考虑。风险投资的玩法大致是这样的:投十个项目,其实完全明白会有7-8个项目失败,所倚仗的,无非是那2-3个项目的成功罢了。而那2-3个项目的成功能够覆盖住7-8个项目的失败且有巨大盈余,唯一的答案就是:这2-3个项目不是一年回报30%、50%那么可怜兮兮的收益率,必须有几倍的回报才够本。本世纪初南非MIH集团在中国投了5-6个项目,几乎全部颗粒无收的失败,但那个唯一的成功的项目就全部冲抵了损失还赚得盆丰钵满,那个项目就是:腾讯的QQ——这是一个相当经典的案例。

风险投资人其实并不关心你的项目到底在做什么,因为几乎没有一个项目靠自己赚钱就能赚来个一年几倍乃至十数倍的回报。他们更关心的是,我100万投入了这个项目,会不会有愿意出价500万的买家接盘?风险投资这个行当,概念太重要了,如果缺少概念,哪里会有接盘者。于是乎,今天你还能看到门户、RSS烧制、博客之类过时概念的创业项目获得投资么?很多行业里并不存在“过时概念”,但数字经济里,的确存在。

风险投资的这种心态,注定了会导致一些本来挺有希望的项目被拔苗助长,大干快上突击规模之后迅速失败。数字经济行内风行一种名为对赌协议的做法,电商行业里就很常见。这里所谓的对赌,就是设定一个规模上的目标(通常不是利润目标,而是销售额),达到目标会得到某种奖励,没有达到要受到某种惩罚乃至创始人出局。

本书提到了PPG这个案子。在具体手法上,PPG创始人利用广告洗钱的传闻其实并无太靠谱的证据,但为了迎合风险投资人的规模要求,拼命扩张,是显而易见的。而PPG的后来者,凡客,最近也碰到了不少的麻烦。创始人陈年公开承认,步子太快,商品品类搞得太多(甚至有凡客牌拖把)。商品品类搞那么多干吗呢?还是为了销售额的规模。

林军在本书中所提及的十个案例,细看之下,你会发现,几乎全部是输给了自己(唯一的例外是港湾其实是被华为进攻而死,但导致华为出手的原因,还是在于港湾为了寻求利益增长点突击到了华为的既有地盘)。钱到底给他们带来了什么样的灾难呢?

一个非常值得注意的地方就是盲目扩大战线。所谓扩张,人员增加倒是其次,而是战线的拉长。这方面,盛大盒子、联想365都是很好的例子。战线一旦拉长,想要获胜,就必须保证每个环节不出纰漏,而反过来,千里之堤毁于蚁穴却很常见。为了扩大规模去拉长战线,管理运营又无法迅速跟进,团队管理失去章法,最终全盘皆输,没什么夸张的。

这十个案例或多或少都有拉长战线的影子。我总结下来,就是这么根链条:

项目不错—>风险投资(或者其它什么热钱)进入—>要求扩大规模—>战线拉长—>管理运营失控—>包括投资人、创始人在内的董事会吵架—>项目失败

从这么一个链条里,我们可以发现,失败的要害其实在于管理运营能力。钱这个东西,多多益善是有条件的:你有这份管理运营能力么?林军写道:“创业英雄与商业领袖的最关键区别之一就是领导力的区别。而当创始人缺乏对自我的约束,缺乏对团队的包容,以及自我决断出现重大失误的时候,创业英雄们往往就变成了莽夫,这个时候自然会出现众叛亲离的局面。”这十个项目的主事者个个都是英雄式的优秀人物,但英雄走不到领袖那一步,所缺的,不正是那份管理能力么?

最后推荐几本相关的书籍:

《IT大败局》

《沸腾十五年》

《中国网络媒体的第一个十年》

《网络江湖三十六计》

《浪潮之癫》

——刊发于《网络传播》2012年2期 ——

关于作者
魏武挥, 上海交通大学媒体与设计学院, 教师
哥不做总好多年
Copyleft © 2010 知识共享署名-非商业性使用-禁止演绎 注意:转载勿改标题!
ItTalks -- 魏武挥的Blog (digitalfingerprint:fc4f8fc31f70097eea4b780b13146415)

欢迎 浏览我收集的信息图关注我的微博访问我的分享
无觅猜您也喜欢:

媒体的威胁

波士顿法律

中美网络营销的差异

冒个泡
无觅

与本日志可能相关的文章有:

]]>
十亿美金的教训虽然托尔斯泰给我们留下了这样的一句名言:“幸福的家庭都是相似的,不幸的家庭各有各的不幸。”但至少对于商业组织来说,这句话是有问题的。事实上,大部分失败的公司都是相似的,而成功的公司各有各的原因。道理无它,每个成功的公司的历史上,都会出现几个非常关键的偶然点,而这些偶然点,主事人的选择和判断,老实讲一句,不无运气成分。而运气这两个字,怎么复制呢?

所以看看失败案例,避免前车之鉴,还是有意义的。林军的这本《十亿美金的教训》,颇有点象吴晓波的《大败局》,不过聚焦于中国互联网圈里的十个失败案例:51.com、3721、ITAT、PPG、分众、港湾、联想FM365、联众、盛大盒子、8848。按照林氏的说法,这十个案例,本来都有可能成为市值十亿美金的公司,但林军虽然给这十个案例都提供了一个“一句话小结”的失败教训,但在我看来,其实都差不多:钱惹出来的祸。

大概没有一个行业对风险投资那么饥渴了,究其根本原因,数字经济——无论你做什么——都是追求规模的。因为公司本质上是建立在注意力之上的。虽然有所谓“长尾理论”、“丰裕理论”,但到底有一样东西是亘古不变的稀缺:信息消费者的时间。这一秒TA在关注A网页,就很难再同时关注B网页。

风险投资能够帮助创业项目迅速扩大规模,而这一点几乎成了大多数创业项目的生死之线。互联网的创业项目,很少是今天开张明天就来钱的(团购这个业态例外,它第一天就可能会有现金流,这也是2011年那么多资金蜂拥到团购业里的原因)。既然没有产出,又要快速做大规模,那么,除了项目创始人自己有钱以外,就只有一条路了:风险投资。

有钱不仅可以扩大规模,还可以做到狙击竞争者的作用。在团购业里,有所谓1亿元的门槛,但其实团购网站几乎没有什么技术含量。而这一亿门槛,就是用巨大而集中的资金,在短期内投放各种广告,迅速占领用户的意识占有率——在用户打算团购的那一刹那,TA想到的是A网站而不是B网站。视频行业里也用巨资来争夺作品的播放权,从而让不够有钱的公司迅速出局。中国的微博业,先发者饭否、叽歪、嘀咕等,均不够有钱,被后来者新浪腾讯之类迅速超越。有钱就意味着可以雇佣更多的运营人员,也能吸引到足够多的明星名人捧场。虽然不能说有钱是唯一的原因,但要说有钱是原因之一,应该没什么问题。

不过,创业者和风险投资人的利益并不完全一致。对于创业者而言,把公司做大做强日进斗金是他们的终极目标,但风险投资人并不见得是这样的考虑。风险投资的玩法大致是这样的:投十个项目,其实完全明白会有7-8个项目失败,所倚仗的,无非是那2-3个项目的成功罢了。而那2-3个项目的成功能够覆盖住7-8个项目的失败且有巨大盈余,唯一的答案就是:这2-3个项目不是一年回报30%、50%那么可怜兮兮的收益率,必须有几倍的回报才够本。本世纪初南非MIH集团在中国投了5-6个项目,几乎全部颗粒无收的失败,但那个唯一的成功的项目就全部冲抵了损失还赚得盆丰钵满,那个项目就是:腾讯的QQ——这是一个相当经典的案例。

风险投资人其实并不关心你的项目到底在做什么,因为几乎没有一个项目靠自己赚钱就能赚来个一年几倍乃至十数倍的回报。他们更关心的是,我100万投入了这个项目,会不会有愿意出价500万的买家接盘?风险投资这个行当,概念太重要了,如果缺少概念,哪里会有接盘者。于是乎,今天你还能看到门户、RSS烧制、博客之类过时概念的创业项目获得投资么?很多行业里并不存在“过时概念”,但数字经济里,的确存在。

风险投资的这种心态,注定了会导致一些本来挺有希望的项目被拔苗助长,大干快上突击规模之后迅速失败。数字经济行内风行一种名为对赌协议的做法,电商行业里就很常见。这里所谓的对赌,就是设定一个规模上的目标(通常不是利润目标,而是销售额),达到目标会得到某种奖励,没有达到要受到某种惩罚乃至创始人出局。

本书提到了PPG这个案子。在具体手法上,PPG创始人利用广告洗钱的传闻其实并无太靠谱的证据,但为了迎合风险投资人的规模要求,拼命扩张,是显而易见的。而PPG的后来者,凡客,最近也碰到了不少的麻烦。创始人陈年公开承认,步子太快,商品品类搞得太多(甚至有凡客牌拖把)。商品品类搞那么多干吗呢?还是为了销售额的规模。

林军在本书中所提及的十个案例,细看之下,你会发现,几乎全部是输给了自己(唯一的例外是港湾其实是被华为进攻而死,但导致华为出手的原因,还是在于港湾为了寻求利益增长点突击到了华为的既有地盘)。钱到底给他们带来了什么样的灾难呢?

一个非常值得注意的地方就是盲目扩大战线。所谓扩张,人员增加倒是其次,而是战线的拉长。这方面,盛大盒子、联想365都是很好的例子。战线一旦拉长,想要获胜,就必须保证每个环节不出纰漏,而反过来,千里之堤毁于蚁穴却很常见。为了扩大规模去拉长战线,管理运营又无法迅速跟进,团队管理失去章法,最终全盘皆输,没什么夸张的。

这十个案例或多或少都有拉长战线的影子。我总结下来,就是这么根链条:

项目不错—>风险投资(或者其它什么热钱)进入—>要求扩大规模—>战线拉长—>管理运营失控—>包括投资人、创始人在内的董事会吵架—>项目失败

从这么一个链条里,我们可以发现,失败的要害其实在于管理运营能力。钱这个东西,多多益善是有条件的:你有这份管理运营能力么?林军写道:“创业英雄与商业领袖的最关键区别之一就是领导力的区别。而当创始人缺乏对自我的约束,缺乏对团队的包容,以及自我决断出现重大失误的时候,创业英雄们往往就变成了莽夫,这个时候自然会出现众叛亲离的局面。”这十个项目的主事者个个都是英雄式的优秀人物,但英雄走不到领袖那一步,所缺的,不正是那份管理能力么?

最后推荐几本相关的书籍:

《IT大败局》

《沸腾十五年》

《中国网络媒体的第一个十年》

《网络江湖三十六计》

《浪潮之癫》

——刊发于《网络传播》2012年2期 ——

关于作者
魏武挥, 上海交通大学媒体与设计学院, 教师
哥不做总好多年
Copyleft © 2010 知识共享署名-非商业性使用-禁止演绎 注意:转载勿改标题!
ItTalks -- 魏武挥的Blog (digitalfingerprint:fc4f8fc31f70097eea4b780b13146415)

欢迎 浏览我收集的信息图关注我的微博访问我的分享
无觅猜您也喜欢:

媒体的威胁

波士顿法律

中美网络营销的差异

冒个泡
无觅

与本日志可能相关的文章有:

]]>
0
<![CDATA[调用opencv的grabcut函数采用valgrind检测时出现疑似内存泄漏的问题讨论]]> http://www.udpwork.com/item/6871.html http://www.udpwork.com/item/6871.html#reviews Tue, 21 Feb 2012 13:57:11 +0800 yongpan http://www.udpwork.com/item/6871.html 调用opencv的grabcut函数后,采用valgrind进行检测时,会提示存在疑似内存泄漏的问题。我想大家都遇到了 。

首先,这是一种正常情况,不会真的出现内存泄漏问题。发现这个问题的同学无需顾虑。

下面简单分析该问题和出现该问题的原因:

问题描绘:

valgrind 显示检测结果

16 bytes in 2 blocks are still reachable in loss record 1 of 2

at 0x4A06019: operator new(unsigned long) (vg_replace_malloc.c:167)

by 0x54E83CC: cv::theRNG() (in /home/yongpan/.bm_plat/lib/libcxcore.so.2.1)

找到对应的opencv中源代码:

RNG& theRNG()
{
      pthread_once(&tlsRNGKeyOnce, makeRNGKey);  
      RNG* rng = (RNG*)pthread_getspecific(tlsRNGKey);//tls是指线程局部静态变量 
      if( !rng )
      {
          rng = new RNG;
          pthread_setspecific(tlsRNGKey, rng);
      }   
      return *rng;
}

深入查看源代码,在线程结束时,主动调用了cv::deleteThreadRNGData ,释放该部分内存。

void deleteThreadRNGData()
{
     if( tlsRNGKey != TLS_OUT_OF_INDEXES )
     delete (RNG*)TlsGetValue( tlsRNGKey );
}

Valgrind 检测提示存在疑似错误的原因是: New在一个分支中调用,valgrind检测时,就会提示错误。

下面简单写一个测试程序来再现该问题:

void test_valgrind(char* p, const bool need )
{
     if(need)
     {
         p = new char[10];
     }
}
<pre>

测试1

void main(int argc, char* argv[])
{
    char* p = NULL;
    test_grind(p ,  true);
    if(p) delete [] p;
    return 0;
}

<pre>

对应的valgrind显示:10 bytes in 1 blocks are definitely lost in loss record 1 of 2。definitely lost: 10 bytes in 1 blocks.

测试2

void main(int argc, char* argv[])
{
    char* p = NULL;
    test_grind(p ,  false);
    if(p) delete [] p;
    return 0;
}

<pre>

对应的valgrind显示: definitely lost: 0 bytes in 0 blocks.

测试3



void main(int argc, char* argv[])
{
    char* p = NULL;
    p = new char[10];
    if(p) delete [] p;
    return 0;
}

<pre>
对应的valgrind显示: definitely lost: 0 bytes in 0 blocks.

随机文章

感谢关注我的一亩三分地:www.imagerabit.com ]]>
调用opencv的grabcut函数后,采用valgrind进行检测时,会提示存在疑似内存泄漏的问题。我想大家都遇到了 。

首先,这是一种正常情况,不会真的出现内存泄漏问题。发现这个问题的同学无需顾虑。

下面简单分析该问题和出现该问题的原因:

问题描绘:

valgrind 显示检测结果

16 bytes in 2 blocks are still reachable in loss record 1 of 2

at 0x4A06019: operator new(unsigned long) (vg_replace_malloc.c:167)

by 0x54E83CC: cv::theRNG() (in /home/yongpan/.bm_plat/lib/libcxcore.so.2.1)

找到对应的opencv中源代码:

RNG& theRNG()
{
      pthread_once(&tlsRNGKeyOnce, makeRNGKey);  
      RNG* rng = (RNG*)pthread_getspecific(tlsRNGKey);//tls是指线程局部静态变量 
      if( !rng )
      {
          rng = new RNG;
          pthread_setspecific(tlsRNGKey, rng);
      }   
      return *rng;
}

深入查看源代码,在线程结束时,主动调用了cv::deleteThreadRNGData ,释放该部分内存。

void deleteThreadRNGData()
{
     if( tlsRNGKey != TLS_OUT_OF_INDEXES )
     delete (RNG*)TlsGetValue( tlsRNGKey );
}

Valgrind 检测提示存在疑似错误的原因是: New在一个分支中调用,valgrind检测时,就会提示错误。

下面简单写一个测试程序来再现该问题:

void test_valgrind(char* p, const bool need )
{
     if(need)
     {
         p = new char[10];
     }
}
<pre>

测试1

void main(int argc, char* argv[])
{
    char* p = NULL;
    test_grind(p ,  true);
    if(p) delete [] p;
    return 0;
}

<pre>

对应的valgrind显示:10 bytes in 1 blocks are definitely lost in loss record 1 of 2。definitely lost: 10 bytes in 1 blocks.

测试2

void main(int argc, char* argv[])
{
    char* p = NULL;
    test_grind(p ,  false);
    if(p) delete [] p;
    return 0;
}

<pre>

对应的valgrind显示: definitely lost: 0 bytes in 0 blocks.

测试3



void main(int argc, char* argv[])
{
    char* p = NULL;
    p = new char[10];
    if(p) delete [] p;
    return 0;
}

<pre>
对应的valgrind显示: definitely lost: 0 bytes in 0 blocks.

随机文章

感谢关注我的一亩三分地:www.imagerabit.com ]]>
0
<![CDATA[udpip: 用UDP封装IP数据包建立VPN]]> http://www.udpwork.com/item/6870.html http://www.udpwork.com/item/6870.html#reviews Tue, 21 Feb 2012 11:57:48 +0800 Xiaoxia http://www.udpwork.com/item/6870.html

原理

使用Linux内核提供的tun设备建立可以在脚本读写的虚拟网卡,然后通过UDP将两个网卡的数据连接。


此方法能够使用以下特殊环境下:

1、客户端所在网络的路由不支持ppp,或者网络受到限制
2、TCP数据包被劫持或者受到限制
3、服务器是OpenVZ等不支持建立pptp,像我的burst的VPS就是这样子。

使用

服务器:

# python udptun.py -s 86 -l 10.0.0.1/24
Configuring interface t0 with ip 10.0.0.1/24

客户端:

# python udptun.py -c xiaoxia.org,86 -l 10.0.0.2/24
Configuring interface t0 with ip 10.0.0.2/24
Setting up new gateway ...
Do login ...
Logged in server succefully!

脚本代码

udptun.py:

#!/usr/bin/python

'''
    UDP Tunnel VPN
    Xiaoxia (xiaoxia@xiaoxia.org)
    Updated: 2012-2-21
'''

import os, sys
import hashlib
import getopt
import fcntl
import time
import struct
import socket, select
import traceback
import signal
import ctypes
import binascii

SHARED_PASSWORD = hashlib.sha1("xiaoxia").digest()
TUNSETIFF = 0x400454ca
IFF_TUN   = 0x0001

BUFFER_SIZE = 8192
MODE = 0
DEBUG = 0
PORT = 0
IFACE_IP = "10.0.0.1/24"
MTU = 1500
TIMEOUT = 60*10 # seconds

class Tunnel():
    def create(self):
        try:
            self.tfd = os.open("/dev/net/tun", os.O_RDWR)
        except:
            self.tfd = os.open("/dev/tun", os.O_RDWR)
        ifs = fcntl.ioctl(self.tfd, TUNSETIFF, struct.pack("16sH", "t%d", IFF_TUN))
        self.tname = ifs[:16].strip("\x00")

    def close(self):
        os.close(self.tfd)

    def config(self, ip):
        print "Configuring interface %s with ip %s" % (self.tname, ip)
        os.system("ip link set %s up" % (self.tname))
        os.system("ip link set %s mtu 1000" % (self.tname))
        os.system("ip addr add %s dev %s" % (ip, self.tname))

    def config_routes(self):
        if MODE == 1: # Server
            pass
        else: # Client
            print "Setting up new gateway ..."
            # Look for default route
            routes = os.popen("ip route show").readlines()
            defaults = [x.rstrip() for x in routes if x.startswith("default")]
            if not defaults:
                raise Exception("Default route not found, maybe not connected!")
            self.prev_gateway = defaults[0]
            self.prev_gateway_metric = self.prev_gateway + " metric 2"
            self.new_gateway = "default dev %s metric 1" % (self.tname)
            self.tun_gateway = self.prev_gateway.replace("default", IP)
            self.old_dns = file("/etc/resolv.conf", "rb").read()
            # Remove default gateway
            os.system("ip route del " + self.prev_gateway)
            # Add default gateway with metric
            os.system("ip route add " + self.prev_gateway_metric)
            # Add exception for server
            os.system("ip route add " + self.tun_gateway)
            # Add new default gateway
            os.system("ip route add " + self.new_gateway)
            # Set new DNS to 8.8.8.8
            file("/etc/resolv.conf", "wb").write("nameserver 8.8.8.8")

    def restore_routes(self):
        if MODE == 1: # Server
            pass
        else: # Client
            print "Restoring previous gateway ..."
            os.system("ip route del " + self.new_gateway)
            os.system("ip route del " + self.prev_gateway_metric)
            os.system("ip route del " + self.tun_gateway)
            os.system("ip route add " + self.prev_gateway)
            file("/etc/resolv.conf", "wb").write(self.old_dns)

    def run(self):
        global PORT
        self.udpfd = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        if MODE == 1:
            self.udpfd.bind(("", PORT))
        else:
            self.udpfd.bind(("", 0))

        self.clients = {}
        self.logged = False
        self.try_logins = 5
        self.log_time = 0

        while True:
            if MODE == 2 and not self.logged and time.time() - self.log_time > 2.:
                print "Do login ..."
                self.udpfd.sendto("LOGIN:" + SHARED_PASSWORD + ":" +
                    IFACE_IP.split("/")[0], (IP, PORT))
                self.try_logins -= 1
                if self.try_logins == 0:
                    raise Exception("Failed to log in server.")
                self.log_time = time.time()

            rset = select.select([self.udpfd, self.tfd], [], [], 1)[0]
            for r in rset:
                if r == self.tfd:
                    if DEBUG: os.write(1, ">")
                    data = os.read(self.tfd, MTU)
                    if MODE == 1: # Server
                        src, dst = data[16:20], data[20:24]
                        for key in self.clients:
                            if dst == self.clients[key]["localIPn"]:
                                self.udpfd.sendto(data, key)
                        # Remove timeout clients
                        curTime = time.time()
                        for key in self.clients.keys():
                            if curTime - self.clients[key]["aliveTime"] > TIMEOUT:
                                print "Remove timeout client", key
                                del self.clients[key]
                    else: # Client
                        self.udpfd.sendto(data, (IP, PORT))
                elif r == self.udpfd:
                    if DEBUG: os.write(1, "<")
                    data, src = self.udpfd.recvfrom(BUFFER_SIZE)
                    if MODE == 1: # Server
                        key = src
                        if key not in self.clients:
                            # New client comes
                            try:
                                if data.startswith("LOGIN:") and data.split(":")[1]==SHARED_PASSWORD:
                                    localIP = data.split(":")[2]
                                    self.clients[key] = {"aliveTime": time.time(),
                                                        "localIPn": socket.inet_aton(localIP)}
                                    print "New Client from", src, "request IP", localIP
                                    self.udpfd.sendto("LOGIN:SUCCESS", src)
                            except:
                                print "Need valid password from", src
                                self.udpfd.sendto("LOGIN:PASSWORD", src)
                        else:
                            # Simply write the packet to local or forward them to other clients ???
                            os.write(self.tfd, data)
                            self.clients[key]["aliveTime"] = time.time()
                    else: # Client
                        if data.startswith("LOGIN"):
                            if data.endswith("PASSWORD"):
                                self.logged = False
                                print "Need password to login!"
                            elif data.endswith("SUCCESS"):
                                self.logged = True
                                self.try_logins = 5
                                print "Logged in server succefully!"
                        else:
                            os.write(self.tfd, data)

def usage(status = 0):
    print "Usage: %s [-s port|-c serverip] [-hd] [-l localip]" % (sys.argv[0])
    sys.exit(status)

def on_exit(no, info):
    raise Exception("TERM signal caught!")

if __name__=="__main__":
    opts = getopt.getopt(sys.argv[1:],"s:c:l:hd")
    for opt,optarg in opts[0]:
        if opt == "-h":
            usage()
        elif opt == "-d":
            DEBUG += 1
        elif opt == "-s":
            MODE = 1
            PORT = int(optarg)
        elif opt == "-c":
            MODE = 2
            IP, PORT = optarg.split(",")
            IP = socket.gethostbyname(IP)
            PORT = int(PORT)
        elif opt == "-l":
            IFACE_IP = optarg

    if MODE == 0 or PORT == 0:
        usage(1)

    tun = Tunnel()
    tun.create()
    tun.config(IFACE_IP)
    signal.signal(signal.SIGTERM, on_exit)
    tun.config_routes()
    try:
        tun.run()
    except KeyboardInterrupt:
        pass
    except:
        print traceback.format_exc()
    finally:
        tun.restore_routes()
        tun.close()
]]>

原理

使用Linux内核提供的tun设备建立可以在脚本读写的虚拟网卡,然后通过UDP将两个网卡的数据连接。


此方法能够使用以下特殊环境下:

1、客户端所在网络的路由不支持ppp,或者网络受到限制
2、TCP数据包被劫持或者受到限制
3、服务器是OpenVZ等不支持建立pptp,像我的burst的VPS就是这样子。

使用

服务器:

# python udptun.py -s 86 -l 10.0.0.1/24
Configuring interface t0 with ip 10.0.0.1/24

客户端:

# python udptun.py -c xiaoxia.org,86 -l 10.0.0.2/24
Configuring interface t0 with ip 10.0.0.2/24
Setting up new gateway ...
Do login ...
Logged in server succefully!

脚本代码

udptun.py:

#!/usr/bin/python

'''
    UDP Tunnel VPN
    Xiaoxia (xiaoxia@xiaoxia.org)
    Updated: 2012-2-21
'''

import os, sys
import hashlib
import getopt
import fcntl
import time
import struct
import socket, select
import traceback
import signal
import ctypes
import binascii

SHARED_PASSWORD = hashlib.sha1("xiaoxia").digest()
TUNSETIFF = 0x400454ca
IFF_TUN   = 0x0001

BUFFER_SIZE = 8192
MODE = 0
DEBUG = 0
PORT = 0
IFACE_IP = "10.0.0.1/24"
MTU = 1500
TIMEOUT = 60*10 # seconds

class Tunnel():
    def create(self):
        try:
            self.tfd = os.open("/dev/net/tun", os.O_RDWR)
        except:
            self.tfd = os.open("/dev/tun", os.O_RDWR)
        ifs = fcntl.ioctl(self.tfd, TUNSETIFF, struct.pack("16sH", "t%d", IFF_TUN))
        self.tname = ifs[:16].strip("\x00")

    def close(self):
        os.close(self.tfd)

    def config(self, ip):
        print "Configuring interface %s with ip %s" % (self.tname, ip)
        os.system("ip link set %s up" % (self.tname))
        os.system("ip link set %s mtu 1000" % (self.tname))
        os.system("ip addr add %s dev %s" % (ip, self.tname))

    def config_routes(self):
        if MODE == 1: # Server
            pass
        else: # Client
            print "Setting up new gateway ..."
            # Look for default route
            routes = os.popen("ip route show").readlines()
            defaults = [x.rstrip() for x in routes if x.startswith("default")]
            if not defaults:
                raise Exception("Default route not found, maybe not connected!")
            self.prev_gateway = defaults[0]
            self.prev_gateway_metric = self.prev_gateway + " metric 2"
            self.new_gateway = "default dev %s metric 1" % (self.tname)
            self.tun_gateway = self.prev_gateway.replace("default", IP)
            self.old_dns = file("/etc/resolv.conf", "rb").read()
            # Remove default gateway
            os.system("ip route del " + self.prev_gateway)
            # Add default gateway with metric
            os.system("ip route add " + self.prev_gateway_metric)
            # Add exception for server
            os.system("ip route add " + self.tun_gateway)
            # Add new default gateway
            os.system("ip route add " + self.new_gateway)
            # Set new DNS to 8.8.8.8
            file("/etc/resolv.conf", "wb").write("nameserver 8.8.8.8")

    def restore_routes(self):
        if MODE == 1: # Server
            pass
        else: # Client
            print "Restoring previous gateway ..."
            os.system("ip route del " + self.new_gateway)
            os.system("ip route del " + self.prev_gateway_metric)
            os.system("ip route del " + self.tun_gateway)
            os.system("ip route add " + self.prev_gateway)
            file("/etc/resolv.conf", "wb").write(self.old_dns)

    def run(self):
        global PORT
        self.udpfd = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        if MODE == 1:
            self.udpfd.bind(("", PORT))
        else:
            self.udpfd.bind(("", 0))

        self.clients = {}
        self.logged = False
        self.try_logins = 5
        self.log_time = 0

        while True:
            if MODE == 2 and not self.logged and time.time() - self.log_time > 2.:
                print "Do login ..."
                self.udpfd.sendto("LOGIN:" + SHARED_PASSWORD + ":" +
                    IFACE_IP.split("/")[0], (IP, PORT))
                self.try_logins -= 1
                if self.try_logins == 0:
                    raise Exception("Failed to log in server.")
                self.log_time = time.time()

            rset = select.select([self.udpfd, self.tfd], [], [], 1)[0]
            for r in rset:
                if r == self.tfd:
                    if DEBUG: os.write(1, ">")
                    data = os.read(self.tfd, MTU)
                    if MODE == 1: # Server
                        src, dst = data[16:20], data[20:24]
                        for key in self.clients:
                            if dst == self.clients[key]["localIPn"]:
                                self.udpfd.sendto(data, key)
                        # Remove timeout clients
                        curTime = time.time()
                        for key in self.clients.keys():
                            if curTime - self.clients[key]["aliveTime"] > TIMEOUT:
                                print "Remove timeout client", key
                                del self.clients[key]
                    else: # Client
                        self.udpfd.sendto(data, (IP, PORT))
                elif r == self.udpfd:
                    if DEBUG: os.write(1, "<")
                    data, src = self.udpfd.recvfrom(BUFFER_SIZE)
                    if MODE == 1: # Server
                        key = src
                        if key not in self.clients:
                            # New client comes
                            try:
                                if data.startswith("LOGIN:") and data.split(":")[1]==SHARED_PASSWORD:
                                    localIP = data.split(":")[2]
                                    self.clients[key] = {"aliveTime": time.time(),
                                                        "localIPn": socket.inet_aton(localIP)}
                                    print "New Client from", src, "request IP", localIP
                                    self.udpfd.sendto("LOGIN:SUCCESS", src)
                            except:
                                print "Need valid password from", src
                                self.udpfd.sendto("LOGIN:PASSWORD", src)
                        else:
                            # Simply write the packet to local or forward them to other clients ???
                            os.write(self.tfd, data)
                            self.clients[key]["aliveTime"] = time.time()
                    else: # Client
                        if data.startswith("LOGIN"):
                            if data.endswith("PASSWORD"):
                                self.logged = False
                                print "Need password to login!"
                            elif data.endswith("SUCCESS"):
                                self.logged = True
                                self.try_logins = 5
                                print "Logged in server succefully!"
                        else:
                            os.write(self.tfd, data)

def usage(status = 0):
    print "Usage: %s [-s port|-c serverip] [-hd] [-l localip]" % (sys.argv[0])
    sys.exit(status)

def on_exit(no, info):
    raise Exception("TERM signal caught!")

if __name__=="__main__":
    opts = getopt.getopt(sys.argv[1:],"s:c:l:hd")
    for opt,optarg in opts[0]:
        if opt == "-h":
            usage()
        elif opt == "-d":
            DEBUG += 1
        elif opt == "-s":
            MODE = 1
            PORT = int(optarg)
        elif opt == "-c":
            MODE = 2
            IP, PORT = optarg.split(",")
            IP = socket.gethostbyname(IP)
            PORT = int(PORT)
        elif opt == "-l":
            IFACE_IP = optarg

    if MODE == 0 or PORT == 0:
        usage(1)

    tun = Tunnel()
    tun.create()
    tun.config(IFACE_IP)
    signal.signal(signal.SIGTERM, on_exit)
    tun.config_routes()
    try:
        tun.run()
    except KeyboardInterrupt:
        pass
    except:
        print traceback.format_exc()
    finally:
        tun.restore_routes()
        tun.close()
]]>
0
<![CDATA[千万别惹程序员]]> http://www.udpwork.com/item/6869.html http://www.udpwork.com/item/6869.html#reviews Tue, 21 Feb 2012 07:54:57 +0800 陈皓 http://www.udpwork.com/item/6869.html 酷壳好久没有发娱乐性质的技术文章了,搞得气氛有点严肃了,考虑到程序员们都是比较严肃和容易较真的类书呆子的群体,所以,需要更新一个有娱乐性质的文章了。正好最近看到了两个比较有趣的图,在新浪微博上都得到了比较不错的反响,因此,更新到酷壳上来。

如果编程语言是一种刀

下面这个图是把编程语言看做是一种刀,那么会是什么样的。这个图我个人感觉很有意思。

对于这个图,最好不要解释,意会就好。不过,我却有点想不解风情,忍不住想解释一下。

  • C++,C,Pascal 都是瑞士军刀,说明是用来做细活的工具。C语言的刀上有个USB,说明是可以做硬件操作的。C++的刀是什么都有,说明C++是一种功能繁多的语言。(图中C++的那把瑞士军刀很强大,不要以为其是虚构的,这把刀是真实存在的,叫Wenger巨人刀,http://www.wenger.ch/giant-knife-wenger-swiss-army-knife(这个网页上有个Youtube视频,可以爬墙去看),淘宝上有卖的,价格在1万4左右。)
  • Java/C#是一把塑料餐刀,这说明,Java和C#语言是带虚拟机的,而且其语法和使用并不像C++那么复杂,其泛型编程可以有很多种玩法,而Java和C#的泛型编程是比较单一的。
  • Python是把电锯,人挡杀人,佛招杀佛,威力很大,面对大型的物体的修整,比C++/C/Java什么的得心应手得多得多,但是对于一些精细的调优工作,明显不行。这和Ruby很像。
  • PHP没有MySQL,明显是被幽默了一把。不过最近对PHP的批评越来越多,不过,facebook的PHP的引擎HiPo已经很牛B了。
  • Perl是一本日本武士刀,是忍者玩的语言。
  • VB,就是一个玩具。你见过用塑料玩具勺当刀的吗?Haskell感觉是外星来的。呵呵

千万别惹程序员

下图一张昨天我公司内部被传递的图片。经典的SQL注入式攻击。千万别惹程序员

这是一个有技术含量的号牌遮挡。我们先不说其是不是能奏效,不过,这个创意相当的NB啊。当你驾车通过某些路口时,被摄像头捕捉到你的车牌,通过OCR变成文本,然后插入数据库,于是,上图的这个车牌就成了SQL注入。(不要以为车牌的OCR技术还不行,这项目技术已经非常成熟了,无论是国内还是国外)。这张图片就如同“Web开发中应该知道的事”中说的一样——永远不要相信用户的输入。

插曲 :我昨天把这张图片放到微博,结果,被转了几万次,上了热门转发的top list和一些社会热点和明星八卦排在了一起 。主要是被“@微博搞笑排行榜:  @全球潮流趣闻:  @实用小百科: @经典英文语录:  @当时我就泪奔了: @老榕: @全球经典音乐: @环球汽车搜罗: @怪诞心理行为学: @精彩电影: @互联网的那点事: @潮混搭:  @热门微博: @SinaAppEngine:” 还有些什么体育记者,法律记者都转了, 这些转发了。这多少让我觉得有些诧异,这是很技术的一件事啊,怎么连什么电影,英文对白,汽车,音乐什么的都转了?我是相当的费解啊,我只能有两个认为——

  1. 简单的认为关心技术的人还是很多的。
  2. 复杂地认为国人是喜欢起哄的,不问为什么。

(全文完)

您可能也喜欢:

程序员眼中的编程语言

弱爆程序员的特征值

漫画:程序员的一生

一个女程序员的故事

程序员需要具备的基本技能
无觅

相关文章

]]>
酷壳好久没有发娱乐性质的技术文章了,搞得气氛有点严肃了,考虑到程序员们都是比较严肃和容易较真的类书呆子的群体,所以,需要更新一个有娱乐性质的文章了。正好最近看到了两个比较有趣的图,在新浪微博上都得到了比较不错的反响,因此,更新到酷壳上来。

如果编程语言是一种刀

下面这个图是把编程语言看做是一种刀,那么会是什么样的。这个图我个人感觉很有意思。

对于这个图,最好不要解释,意会就好。不过,我却有点想不解风情,忍不住想解释一下。

  • C++,C,Pascal 都是瑞士军刀,说明是用来做细活的工具。C语言的刀上有个USB,说明是可以做硬件操作的。C++的刀是什么都有,说明C++是一种功能繁多的语言。(图中C++的那把瑞士军刀很强大,不要以为其是虚构的,这把刀是真实存在的,叫Wenger巨人刀,http://www.wenger.ch/giant-knife-wenger-swiss-army-knife(这个网页上有个Youtube视频,可以爬墙去看),淘宝上有卖的,价格在1万4左右。)
  • Java/C#是一把塑料餐刀,这说明,Java和C#语言是带虚拟机的,而且其语法和使用并不像C++那么复杂,其泛型编程可以有很多种玩法,而Java和C#的泛型编程是比较单一的。
  • Python是把电锯,人挡杀人,佛招杀佛,威力很大,面对大型的物体的修整,比C++/C/Java什么的得心应手得多得多,但是对于一些精细的调优工作,明显不行。这和Ruby很像。
  • PHP没有MySQL,明显是被幽默了一把。不过最近对PHP的批评越来越多,不过,facebook的PHP的引擎HiPo已经很牛B了。
  • Perl是一本日本武士刀,是忍者玩的语言。
  • VB,就是一个玩具。你见过用塑料玩具勺当刀的吗?Haskell感觉是外星来的。呵呵

千万别惹程序员

下图一张昨天我公司内部被传递的图片。经典的SQL注入式攻击。千万别惹程序员

这是一个有技术含量的号牌遮挡。我们先不说其是不是能奏效,不过,这个创意相当的NB啊。当你驾车通过某些路口时,被摄像头捕捉到你的车牌,通过OCR变成文本,然后插入数据库,于是,上图的这个车牌就成了SQL注入。(不要以为车牌的OCR技术还不行,这项目技术已经非常成熟了,无论是国内还是国外)。这张图片就如同“Web开发中应该知道的事”中说的一样——永远不要相信用户的输入。

插曲 :我昨天把这张图片放到微博,结果,被转了几万次,上了热门转发的top list和一些社会热点和明星八卦排在了一起 。主要是被“@微博搞笑排行榜:  @全球潮流趣闻:  @实用小百科: @经典英文语录:  @当时我就泪奔了: @老榕: @全球经典音乐: @环球汽车搜罗: @怪诞心理行为学: @精彩电影: @互联网的那点事: @潮混搭:  @热门微博: @SinaAppEngine:” 还有些什么体育记者,法律记者都转了, 这些转发了。这多少让我觉得有些诧异,这是很技术的一件事啊,怎么连什么电影,英文对白,汽车,音乐什么的都转了?我是相当的费解啊,我只能有两个认为——

  1. 简单的认为关心技术的人还是很多的。
  2. 复杂地认为国人是喜欢起哄的,不问为什么。

(全文完)

您可能也喜欢:

程序员眼中的编程语言

弱爆程序员的特征值

漫画:程序员的一生

一个女程序员的故事

程序员需要具备的基本技能
无觅

相关文章

]]>
0
<![CDATA[使用Jscex改进Node Club(2):引入Jscex类库]]> http://www.udpwork.com/item/6868.html http://www.udpwork.com/item/6868.html#reviews Mon, 20 Feb 2012 21:57:45 +0800 zhaojie http://www.udpwork.com/item/6868.html 之前我们已经将Node Club在本地运行起来了,接着我们便来引入Jscex类库,为常用异步方法扩展出Jscex版本,并试着编写一些最简单的Jscex代码。

安装Jscex包

为项目中安装Jscex十分容易,因为Jscex已经发布在官方NPM源中,我们只需修改package.json文件即可:

{
    "name": "NodeClub"
  , "version": "0.0.1"
  , "main": "./app.js"
  , "private": true
  , "dependencies": {
      "express": "2.5.1",
      "ejs": "0.5.0",
      "eventproxy": "0.1.0",
      "mongoose": "2.4.1",
      "node-markdown": "0.1.0",
      "validator": "0.3.7",
      "nodemailer": "0.3.1",
      "jscex": "0.6.x",
      "jscex-jit": "0.6.x",
      "jscex-async": "0.6.x",
      "jscex-async-powerpack": "0.6.x"
  }
}

在此我们引入四个和Jscex相关的包,并指定为0.6.x版本,以便与NPM上的Jscex同步更新。修改之后,便可以使用npm install安装新增的Jscex包:

$ npm install
jscex-async@0.6.0 ./node_modules/jscex-async 
jscex-async-powerpack@0.6.0 ./node_modules/jscex-async-powerpack 
jscex@0.6.0 ./node_modules/jscex 
jscex-jit@0.6.0 ./node_modules/jscex-jit

有了NPM之后,每次为项目添加依赖库也只需编辑package.json再npm install就行了。如果需要将本地版本与NPM源保持同步更新,也只需一句npm update命令。

Node Club中的异步方法

在JavaScript有各种各样的异步方法,要配合Jscex使用的话,则必须使用能与Jscex适配的异步方法,简称Jscex异步方法。在Node Club项目中出现最多的异步方法便是使用mongoose访问MongoDB。mongoose不仅仅提供了MongoDB的访问能力,它还提供相当的ORM功能,可以让我们快速的定义模型,并与MongoDB数据库里的集合映射起来,例如:

var mongoose = require("mongoose"),
    Schema = mongoose.Schema,
    ObjectId = Schema.ObjectId;

var UserSchema = new Schema({
    name: String,
    password: String,
    createAt: Date
});

var User = mongoose.model('User', UserSchema);

此时User类型便已经和数据库建立了映射关系,例如我们可以操作数据:

function updatePassword(id, password, cb) {
    User.findOne({ _id: id }, function (error, user) {
        if (error) return cb(error);

        user.password = password;
        user.save(function (error) {
            if (error) return cb(error);

            cb(null);
        });
    });
}

以上代码的作用是更新一个用户的密码,我们先使用User.findOne获得用户对象,修改后再使用save方法存回数据库。这里用到的都是一种“标准”的异步方法模式,即使用回调函数获得(或返回)结果,其第一个参数为错误对象,如果不为空,则表示出错了。因此每次调用一个异步方法的时候,我们都需要在回调方法里判断是否出错,这也是异步编程的麻烦之一。

加载Jscex类库

如果要在项目里加载Jscex,我会建议使用一个简单的模块来存放与Jscex初始化相关的代码,例如libs/jscex.js

var Jscex = require("jscex");
require("jscex-jit").init(Jscex);
require("jscex-async").init(Jscex);
require("jscex-async-powerpack").init(Jscex);

var Jscexify = Jscex.Async.Jscexify;

var mongoose = require("mongoose");

var mp = mongoose.Model.prototype;
mp.saveAsync = Jscexify.fromStandard(mp.save);
mp.removeAsync = Jscexify.fromStandard(mp.remove);

var m = mongoose.Model;
m.findByIdAsync = Jscexify.fromStandard(m.findById);
m.findOneAsync = Jscexify.fromStandard(m.findOne);
m.findAsync = Jscexify.fromStandard(m.find);
m.countAsync = Jscexify.fromStandard(m.count);

exports.Jscex = Jscex;

前几行代码是标准的Jscex引入方式。而从mongoose相关的代码开始,便是在为其扩展Jscex异步方法。mongoose对扩展十分友好,它把内部的元数据暴露出来,让我们进行统一扩展。例如为Model扩展一些静态或是实例方法,便相当于为所有的模型及其它们的实例添加了方法。这种做法充分利用了JavaScript的灵活性,假如它把所有的元数据都隐藏起来,那我们没法如此简单直接地引入Jscex扩展了——当然总是有办法的,只是麻烦一些。

这里我强烈建议每个JavaScript类库或框架的开发者都能实现这种方式,给使用者充分的扩展途径。以Jscex自身为例,它的每个异步任务对象都是Jscex.Async.Task类型的实例,这样jscex-async-powerpack模块便可以轻易补充各种强大的辅助方法。

此外,由于mongoose遵守异步方法“标准模式”(即使用回调函数传回结果,其第一个参数为错误对象),因此只要一个fromStandard辅助函数便可全部应对。在以后的代码中,我们会大量使用这些扩展后的Jscex异步方法。

编写简单的Jscex异步函数

现在我们就直接写几行Jscex代码吧。例如在controllers/user.js文件里定义了以下几个方法:

function get_user_by_id(id, cb) {
    User.findOne({ _id: id }, function (err, user) {
        if (err) return cb(err, null);
        return cb(err, user);
    });
}

function get_user_by_name(name, cb) {
    User.findOne({ name: name }, function (err, user) {
        if (err) return cb(err, null);
        return cb(err, user);
    });
}

function get_user_by_loginname(name, cb) {
    User.findOne({ loginname: name }, function (err, user) {
        if (err) return cb(err, null);
        return cb(err, user);
    });
}

function get_users_by_ids(ids, cb) {
    User.find({ '_id': { '$in': ids } }, function (err, users) {
        if (err) return cb(err, null);
        return cb(err, users);
    });
}

function get_users_by_query(query, opt, cb) {
    User.find(query, [], opt, function (err, users) {
        if (err) return cb(err, null);
        return cb(err, users);
    });
}

以上几个方法都有相同的特征:它们都是简单的调用一个mongoose的异步方法,判断是否出错,并返回结果。正如之前提过的那样,编写异步代码的麻烦之一,便是在每次回调时都要判断是否出错,因此在实际项目中的异步代码都会比各种“演示”用的玩具代码更麻烦一些。不过我们可以使用Jscex来改写这些代码

var Jscex = require("../libs/jscex").Jscex;

var get_user_by_id_async = eval(Jscex.compile("async", function (id) {
    return $await(User.findOneAsync({ _id: id }));
}));

var get_user_by_name_async = eval(Jscex.compile("async", function (name) {
    return $await(User.findOneAsync({ name: name }));
}));

var get_user_by_loginname_async = eval(Jscex.compile("async", function (name) {
    return $await(User.findOneAsync({ loginname: name }));
}));

var get_users_by_ids_async = eval(Jscex.compile("async", function (ids) {
    return $await(User.findAsync({ '_id': { '$in': ids } }));
}));

var get_users_by_query_async = eval(Jscex.compile("async", function (query, opt) {
    return $await(User.findAsync(query, [], opt));
}));

在编写Jscex方法中,我们无需操作回调函数,只要在异步点上使用$await进行“等待”即可。我们也无需显式地处理错误,因为一旦出现错误便会抛出异常,异常如果没有被某个try…catch捕获到,则会顺着调用路径一路向上传递,直到被我们的代码或是系统捕获为止。Jscex将简单易用的传统编程模式与实践重新带回异步编程中,做到“同步编写,异步执行”的效果。这就是Jscex诞生的意义。

从下一篇文章开始,我们将逐步改造Node Club网站中现有的代码。

相关文章

]]>
之前我们已经将Node Club在本地运行起来了,接着我们便来引入Jscex类库,为常用异步方法扩展出Jscex版本,并试着编写一些最简单的Jscex代码。

安装Jscex包

为项目中安装Jscex十分容易,因为Jscex已经发布在官方NPM源中,我们只需修改package.json文件即可:

{
    "name": "NodeClub"
  , "version": "0.0.1"
  , "main": "./app.js"
  , "private": true
  , "dependencies": {
      "express": "2.5.1",
      "ejs": "0.5.0",
      "eventproxy": "0.1.0",
      "mongoose": "2.4.1",
      "node-markdown": "0.1.0",
      "validator": "0.3.7",
      "nodemailer": "0.3.1",
      "jscex": "0.6.x",
      "jscex-jit": "0.6.x",
      "jscex-async": "0.6.x",
      "jscex-async-powerpack": "0.6.x"
  }
}

在此我们引入四个和Jscex相关的包,并指定为0.6.x版本,以便与NPM上的Jscex同步更新。修改之后,便可以使用npm install安装新增的Jscex包:

$ npm install
jscex-async@0.6.0 ./node_modules/jscex-async 
jscex-async-powerpack@0.6.0 ./node_modules/jscex-async-powerpack 
jscex@0.6.0 ./node_modules/jscex 
jscex-jit@0.6.0 ./node_modules/jscex-jit

有了NPM之后,每次为项目添加依赖库也只需编辑package.json再npm install就行了。如果需要将本地版本与NPM源保持同步更新,也只需一句npm update命令。

Node Club中的异步方法

在JavaScript有各种各样的异步方法,要配合Jscex使用的话,则必须使用能与Jscex适配的异步方法,简称Jscex异步方法。在Node Club项目中出现最多的异步方法便是使用mongoose访问MongoDB。mongoose不仅仅提供了MongoDB的访问能力,它还提供相当的ORM功能,可以让我们快速的定义模型,并与MongoDB数据库里的集合映射起来,例如:

var mongoose = require("mongoose"),
    Schema = mongoose.Schema,
    ObjectId = Schema.ObjectId;

var UserSchema = new Schema({
    name: String,
    password: String,
    createAt: Date
});

var User = mongoose.model('User', UserSchema);

此时User类型便已经和数据库建立了映射关系,例如我们可以操作数据:

function updatePassword(id, password, cb) {
    User.findOne({ _id: id }, function (error, user) {
        if (error) return cb(error);

        user.password = password;
        user.save(function (error) {
            if (error) return cb(error);

            cb(null);
        });
    });
}

以上代码的作用是更新一个用户的密码,我们先使用User.findOne获得用户对象,修改后再使用save方法存回数据库。这里用到的都是一种“标准”的异步方法模式,即使用回调函数获得(或返回)结果,其第一个参数为错误对象,如果不为空,则表示出错了。因此每次调用一个异步方法的时候,我们都需要在回调方法里判断是否出错,这也是异步编程的麻烦之一。

加载Jscex类库

如果要在项目里加载Jscex,我会建议使用一个简单的模块来存放与Jscex初始化相关的代码,例如libs/jscex.js

var Jscex = require("jscex");
require("jscex-jit").init(Jscex);
require("jscex-async").init(Jscex);
require("jscex-async-powerpack").init(Jscex);

var Jscexify = Jscex.Async.Jscexify;

var mongoose = require("mongoose");

var mp = mongoose.Model.prototype;
mp.saveAsync = Jscexify.fromStandard(mp.save);
mp.removeAsync = Jscexify.fromStandard(mp.remove);

var m = mongoose.Model;
m.findByIdAsync = Jscexify.fromStandard(m.findById);
m.findOneAsync = Jscexify.fromStandard(m.findOne);
m.findAsync = Jscexify.fromStandard(m.find);
m.countAsync = Jscexify.fromStandard(m.count);

exports.Jscex = Jscex;

前几行代码是标准的Jscex引入方式。而从mongoose相关的代码开始,便是在为其扩展Jscex异步方法。mongoose对扩展十分友好,它把内部的元数据暴露出来,让我们进行统一扩展。例如为Model扩展一些静态或是实例方法,便相当于为所有的模型及其它们的实例添加了方法。这种做法充分利用了JavaScript的灵活性,假如它把所有的元数据都隐藏起来,那我们没法如此简单直接地引入Jscex扩展了——当然总是有办法的,只是麻烦一些。

这里我强烈建议每个JavaScript类库或框架的开发者都能实现这种方式,给使用者充分的扩展途径。以Jscex自身为例,它的每个异步任务对象都是Jscex.Async.Task类型的实例,这样jscex-async-powerpack模块便可以轻易补充各种强大的辅助方法。

此外,由于mongoose遵守异步方法“标准模式”(即使用回调函数传回结果,其第一个参数为错误对象),因此只要一个fromStandard辅助函数便可全部应对。在以后的代码中,我们会大量使用这些扩展后的Jscex异步方法。

编写简单的Jscex异步函数

现在我们就直接写几行Jscex代码吧。例如在controllers/user.js文件里定义了以下几个方法:

function get_user_by_id(id, cb) {
    User.findOne({ _id: id }, function (err, user) {
        if (err) return cb(err, null);
        return cb(err, user);
    });
}

function get_user_by_name(name, cb) {
    User.findOne({ name: name }, function (err, user) {
        if (err) return cb(err, null);
        return cb(err, user);
    });
}

function get_user_by_loginname(name, cb) {
    User.findOne({ loginname: name }, function (err, user) {
        if (err) return cb(err, null);
        return cb(err, user);
    });
}

function get_users_by_ids(ids, cb) {
    User.find({ '_id': { '$in': ids } }, function (err, users) {
        if (err) return cb(err, null);
        return cb(err, users);
    });
}

function get_users_by_query(query, opt, cb) {
    User.find(query, [], opt, function (err, users) {
        if (err) return cb(err, null);
        return cb(err, users);
    });
}

以上几个方法都有相同的特征:它们都是简单的调用一个mongoose的异步方法,判断是否出错,并返回结果。正如之前提过的那样,编写异步代码的麻烦之一,便是在每次回调时都要判断是否出错,因此在实际项目中的异步代码都会比各种“演示”用的玩具代码更麻烦一些。不过我们可以使用Jscex来改写这些代码

var Jscex = require("../libs/jscex").Jscex;

var get_user_by_id_async = eval(Jscex.compile("async", function (id) {
    return $await(User.findOneAsync({ _id: id }));
}));

var get_user_by_name_async = eval(Jscex.compile("async", function (name) {
    return $await(User.findOneAsync({ name: name }));
}));

var get_user_by_loginname_async = eval(Jscex.compile("async", function (name) {
    return $await(User.findOneAsync({ loginname: name }));
}));

var get_users_by_ids_async = eval(Jscex.compile("async", function (ids) {
    return $await(User.findAsync({ '_id': { '$in': ids } }));
}));

var get_users_by_query_async = eval(Jscex.compile("async", function (query, opt) {
    return $await(User.findAsync(query, [], opt));
}));

在编写Jscex方法中,我们无需操作回调函数,只要在异步点上使用$await进行“等待”即可。我们也无需显式地处理错误,因为一旦出现错误便会抛出异常,异常如果没有被某个try…catch捕获到,则会顺着调用路径一路向上传递,直到被我们的代码或是系统捕获为止。Jscex将简单易用的传统编程模式与实践重新带回异步编程中,做到“同步编写,异步执行”的效果。这就是Jscex诞生的意义。

从下一篇文章开始,我们将逐步改造Node Club网站中现有的代码。

相关文章

]]>
0
<![CDATA[使用Jscex改进Node Club(1):运行Node Club网站]]> http://www.udpwork.com/item/6867.html http://www.udpwork.com/item/6867.html#reviews Mon, 20 Feb 2012 14:24:33 +0800 zhaojie http://www.udpwork.com/item/6867.html 一直想做个相对完整的项目来演示下Jscex的使用,可惜缺少创意和精力,一直没能实现。前几天看到Node Club将其网站开源了,不禁让我十分欢喜。Node Club网站是个真实案例,复杂度适中,既是Jscex的典型使用场景,又能避开我不擅长的网页样式设计和制作,简直是一个再合适不过的基础样板了。周末我大致看了下代码,也试着将几个部分使用Jscex改进了一下,效果也十分显著,于是打算写作一个系列指引,希望可以对Jscex类库的推广有所帮助。在此第一篇,自然是最基本的环境建设开始说起。

配置系统环境

首先,我强烈建议您可以在GitHub上Fork一下Node Club网站的项目,有个版本管理环境做后盾,做什么都会放心许多。我也将所有修改存放在GitHub上,其master分支是我个人不断改进的版本,您可以时刻关注其最新发展。除此之外,我还创建了一个tutorial分支,专门为这个系列文章存放代码,保持两者进度一致,同时尽量将修改过程和版本提交对应起来。

要运行Node Code,首先您得安装必要的环境:

无论您使用的是Windows,Linux还是OS X,以上都有对应的安装方式,在此就不详细描述了。文章中会出现一些控制台脚本,是我在OS X上运行的命令和结果。如果没有特殊说明,则路径是nodeclub项目的根目录。如果你使用的是Windows,则需要进行一些修改,其实最常见的修改就是分割路径的斜线还有可执行文件的使用。

这里假设您对以上三个工具,最好还包括Git有了一定了解。其实目前也无需十分熟悉,看看官方文档,搜几篇文章瞅瞅,就应该差不多了。

配置Node Club环境

假设您已经下载了Node Club代码(当然最好您是git clone一份自己fork出来项目),并将其解压缩至nodeclub目录中。现在您应该可以使用npm install命令安装所有依赖的包:

$ npm install
eventproxy@0.1.0 ./node_modules/eventproxy 
ejs@0.5.0 ./node_modules/ejs 
validator@0.3.7 ./node_modules/validator 
node-markdown@0.1.0 ./node_modules/node-markdown 
mongoose@2.4.1 ./node_modules/mongoose 
├── colors@0.5.1
├── hooks@0.1.9
└── mongodb@0.9.7-1.4
express@2.5.1 ./node_modules/express 
├── mime@1.2.5
├── qs@0.4.2
├── mkdirp@0.0.7
└── connect@1.8.5
nodemailer@0.3.1 ./node_modules/nodemailer 
├── mailcomposer@0.1.4 (mimelib-noiconv@0.1.6)
└── simplesmtp@0.1.12

要运行网站,则还需要准备一份配置文件。您可以将config.default.js复制一份至config.js文件,并建议修改一下一些配置:

exports.config = {
    // 网站端口号,默认为80,可能会有冲突,建议改成其他值
    port: 8080,

    // 发系统邮件时使用的用户名
    mail_user: 'xxxxx@gmail.com',
    // 发系统邮件时使用的密码
    mail_pass: 'xxxxx',
    // SMTP服务器地址
    mail_host: 'smtp.gmail.com',
    // 系统邮件发信人
    mail_sender: 'xxxxx@gmail.com',
    // 根据需求配置是否验证
    mail_use_authentication: true,
};

其中大部分的设置是在配置系统邮件的SMTP服务器,您可以像我一样使用Gmail,或是跟Node Club原项目一样使用126的邮箱。

运行Node Club网站

要运行Node Club网站,则需要启动MongoDB数据库,例如:

mongodb-osx-x86_64-2.0.2/bin$ mkdir data
mongodb-osx-x86_64-2.0.2/bin$ ./mongodb --dbpath data

此时您就在本地启动了一个MongoDB进程,使用默认端口27017,这与网站的默认配置相符。此时您就可以执行app.js来启动网站:

$ node app.js
NodeClub listening on port 8080 in development mode
God bless love....

在浏览器里访问http://127.0.0.1:8080,您应该就能看到一个空白的Node Club站点:

Node Club空白首页

此时您可以点击右上角的“注册”链接,注册一个名为admin的用户,部分操作(例如标签管理)需要使用该账号才能进行。注册时会要求填一个邮箱,提交后会收到一封激活邮件,但其中的链接可能不能直接访问(遗漏了端口号,应该是个bug),您可以将其地址复制到浏览器里修改并访问。激活成功后便可登录,登陆后会进入空白的后台页面:

Node Club空白后台

至此万事俱备,任何时候您想重启Node Club网站,只需ctrl+c中断node app.js命令再重新运行即可。从下一篇文章开始,我们将正式开始Jscex之旅。

相关文章

  • 使用Jscex改进Node Club(1):运行Node Club网站
]]>
一直想做个相对完整的项目来演示下Jscex的使用,可惜缺少创意和精力,一直没能实现。前几天看到Node Club将其网站开源了,不禁让我十分欢喜。Node Club网站是个真实案例,复杂度适中,既是Jscex的典型使用场景,又能避开我不擅长的网页样式设计和制作,简直是一个再合适不过的基础样板了。周末我大致看了下代码,也试着将几个部分使用Jscex改进了一下,效果也十分显著,于是打算写作一个系列指引,希望可以对Jscex类库的推广有所帮助。在此第一篇,自然是最基本的环境建设开始说起。

配置系统环境

首先,我强烈建议您可以在GitHub上Fork一下Node Club网站的项目,有个版本管理环境做后盾,做什么都会放心许多。我也将所有修改存放在GitHub上,其master分支是我个人不断改进的版本,您可以时刻关注其最新发展。除此之外,我还创建了一个tutorial分支,专门为这个系列文章存放代码,保持两者进度一致,同时尽量将修改过程和版本提交对应起来。

要运行Node Code,首先您得安装必要的环境:

无论您使用的是Windows,Linux还是OS X,以上都有对应的安装方式,在此就不详细描述了。文章中会出现一些控制台脚本,是我在OS X上运行的命令和结果。如果没有特殊说明,则路径是nodeclub项目的根目录。如果你使用的是Windows,则需要进行一些修改,其实最常见的修改就是分割路径的斜线还有可执行文件的使用。

这里假设您对以上三个工具,最好还包括Git有了一定了解。其实目前也无需十分熟悉,看看官方文档,搜几篇文章瞅瞅,就应该差不多了。

配置Node Club环境

假设您已经下载了Node Club代码(当然最好您是git clone一份自己fork出来项目),并将其解压缩至nodeclub目录中。现在您应该可以使用npm install命令安装所有依赖的包:

$ npm install
eventproxy@0.1.0 ./node_modules/eventproxy 
ejs@0.5.0 ./node_modules/ejs 
validator@0.3.7 ./node_modules/validator 
node-markdown@0.1.0 ./node_modules/node-markdown 
mongoose@2.4.1 ./node_modules/mongoose 
├── colors@0.5.1
├── hooks@0.1.9
└── mongodb@0.9.7-1.4
express@2.5.1 ./node_modules/express 
├── mime@1.2.5
├── qs@0.4.2
├── mkdirp@0.0.7
└── connect@1.8.5
nodemailer@0.3.1 ./node_modules/nodemailer 
├── mailcomposer@0.1.4 (mimelib-noiconv@0.1.6)
└── simplesmtp@0.1.12

要运行网站,则还需要准备一份配置文件。您可以将config.default.js复制一份至config.js文件,并建议修改一下一些配置:

exports.config = {
    // 网站端口号,默认为80,可能会有冲突,建议改成其他值
    port: 8080,

    // 发系统邮件时使用的用户名
    mail_user: 'xxxxx@gmail.com',
    // 发系统邮件时使用的密码
    mail_pass: 'xxxxx',
    // SMTP服务器地址
    mail_host: 'smtp.gmail.com',
    // 系统邮件发信人
    mail_sender: 'xxxxx@gmail.com',
    // 根据需求配置是否验证
    mail_use_authentication: true,
};

其中大部分的设置是在配置系统邮件的SMTP服务器,您可以像我一样使用Gmail,或是跟Node Club原项目一样使用126的邮箱。

运行Node Club网站

要运行Node Club网站,则需要启动MongoDB数据库,例如:

mongodb-osx-x86_64-2.0.2/bin$ mkdir data
mongodb-osx-x86_64-2.0.2/bin$ ./mongodb --dbpath data

此时您就在本地启动了一个MongoDB进程,使用默认端口27017,这与网站的默认配置相符。此时您就可以执行app.js来启动网站:

$ node app.js
NodeClub listening on port 8080 in development mode
God bless love....

在浏览器里访问http://127.0.0.1:8080,您应该就能看到一个空白的Node Club站点:

Node Club空白首页

此时您可以点击右上角的“注册”链接,注册一个名为admin的用户,部分操作(例如标签管理)需要使用该账号才能进行。注册时会要求填一个邮箱,提交后会收到一封激活邮件,但其中的链接可能不能直接访问(遗漏了端口号,应该是个bug),您可以将其地址复制到浏览器里修改并访问。激活成功后便可登录,登陆后会进入空白的后台页面:

Node Club空白后台

至此万事俱备,任何时候您想重启Node Club网站,只需ctrl+c中断node app.js命令再重新运行即可。从下一篇文章开始,我们将正式开始Jscex之旅。

相关文章

  • 使用Jscex改进Node Club(1):运行Node Club网站
]]>
0
<![CDATA[事关规则 无关伦理]]> http://www.udpwork.com/item/6866.html http://www.udpwork.com/item/6866.html#reviews Mon, 20 Feb 2012 13:33:53 +0800 魏武挥 http://www.udpwork.com/item/6866.html 唯冠vs苹果就iPad商标,唯冠深圳公司认为苹果公司在获取时有欺诈行为。曾有报道说,唯冠欲在美国索赔20亿美元。坊间舆论一片哗然,很多人都认为唯冠狮子大开口,逮着个有钱的主,就要求若干若干。不过,后续报道称,唯冠方面“没有提出具体赔偿额”,唯冠“寻求的只是一个商业性质的合作,即把唯冠所有的中国大陆iPad商标权卖给苹果。”而有论者注意到,唯冠这个曾经的全球四大显示屏制造商之一,今天已经非常困顿,大部分资产已遭法院冻结,前主席杨荣山更是在2010年8月就被法院颁令破产。

一个是如日中天市值超过微软加英特尔的苹果,一个是日薄西山苦苦挣扎的唯冠。一个是风靡全球人人皆想拥有的苹果iPad电脑,一个是2000年出过概念产品且用的是老式显示器(CRT)最终不了了之的唯冠iPad电脑。唯冠自己做得一塌糊涂,却眼红苹果的巨大财富,想从中分一杯羹。国人自己实业上不争气,不老老实实秉着创新精神开发自己的东西,在这上面挖空心思搞钱,殊为可叹——这大抵就是有些论者的论调,极端的,甚至上升到国民素质劣根性问题上。

不过,可惜的是,如果非要说“劣根性”,大抵老外也颇有些劣根性。中国有一本很有名的杂志名为《读者文摘》,发行量号称数百万之巨。在93年的时候,碰到与美国《读者文摘》的法律纠纷,最终被迫更名为《读者》。我们能简单地说老美眼红中国版《读者文摘》的巨大市场份额么?

另外一个相当著名的和数字世界有关的品牌纠纷,就是腾讯的QQ。在早期,QQ并不叫QQ,而叫OICQ这个带有典型的C2C(Copy to China)风格的名字。腾讯也从来不否认,这款产品是在“山寨”美国的即时通讯软件ICQ。发展了几年后,远在大洋彼岸没有多少中国用户的ICQ认为OICQ侵犯了它的利益,在巨大的压力下,OICQ被迫改名为QQ。不过,时至今日,恐怕知道QQ的人远远比知道OICQ的人多得多。这个故事有一个比较有趣的结局:ICQ后来经营不利,试图出售,腾讯一度是竞购者之一。

不仅中国人和老外之间很有些品牌方面的纠纷,老外与老外之间,也一向是剑拔弩张绝不是什么好好先生。谷歌旗下知名的邮件产品Gmail在德国就规规矩矩地叫做“Google Mail”,概因德国早就有一个电邮服务商Gmail。谷歌为什么不买下这个德国电邮品牌呢?其实就是个“价格”。价格合适就买,价格不合适就拉倒。商业社会里,谈的都是利益,至于动机不动机,一来别人肚子里的东西,外人不好乱加揣摩,二来说穿了就是利益,商人不图利,就不是好的合格的商人。

至于唯冠不好好经营自己的实业,在品牌二字上大做文章,是不是有悖商业伦理呢?也不见得。在美国,大有一些所谓专利公司,做的就是专利囤积,然后伺机倒卖——这也算是一种“实业”吧。如果囤积专利还需要一些专业的技术知识,一般人还不会摆弄,那么,囤积域名倒买倒卖的“米虫”,这又算什么呢?这一行后来颇出了几个大佬,比如蔡文胜,还是不少人的偶像,我倒是不觉得,蔡先生有什么商业伦理问题。

市场经济是所谓的“规则经济”、“法律经济”,这一点已成为了大多数人的共识。但反过来想想,有规则的地方就会有市场——这话的意思是,总有人会靠“玩弄规则”为生,甚至玩出一个大市场来。中国古人对这一点是很有矛盾心态的。著名的“子产铸刑鼎”事件围绕的就是这样一个核心:该不该把规则(这个故事里就是法律)公之于众。反对者认为一旦公布规则,那就意味着有人要去玩弄规则,钻规则的空子,人心就此败坏。这种观点在后来的历史发展中,每个人都看到了,渐渐消没。而专门折腾法律规则的,更是发展出一个巨大的行业:律师业。你能说律师就是没有伦理的工种么?

平心而论,iPad这四个字母,本无意义(或商业价值),的确是苹果将之变成一个高价值的单词。但怎么也绕不过去的是,唯冠出手的早,且就各方报道来看,唯冠深圳并没有把大陆的iPad商标权给出售出去。如果这一点是事实的话,苹果要拿回在大陆的iPad,就必须要付出巨大的代价——这也就成了很多人的疑问:我辛辛苦苦让这四个字母成为财富,凭什么我还要为拥有它买单?不凭什么,市场规则,本来如此。

在我看来,这种事件,其实是和“全球化”有关的。一本名为《世界是平的》的畅销书,对全球化不乏溢美之词。我向来对一边倒的论著兴趣平平,草草翻过便算数。但这本书所注意到的现象:全球化在加剧却是一个不争的事实。也正是因为全球化,才产生了异国商标品牌在另一国的通行问题。正常的国家都有自己的司法主权,在全球99%的地区卖得再好再知名,在剩下的1%地区,就得老老实实地先去拿下这个1%地区的通行证。你可以说是这是一种弊端,但恐怕这种弊端,在可见的未来里,还很难消除——它是要全球消灭国家这个概念为前提的。

有鉴于此,大名鼎鼎的Facebook已经开始着手做类似的准备工作,据称它在华注册了六十一个商标,坊间戏言“就差非死不可没有注册了”。这样的事看似有些无聊,但却是必要的前提。

按规则办事,没按(在司法机关的认定下),就得事后付出代价。这是市场经济的铁律,和商业伦理、道德,没啥关系。

—— 应邀,刊发于当日《东方早报》——

题外话几句:

1、配图是网络所得,其实反映出唯冠和苹果之间争斗,背后的银行图谋。这事有点银行幕后操纵,唯冠台前扮演的意思。不过也不好苛求银行什么,眼睁睁看着自己的放贷要烂账,忽然有这么一个机会,傻子才会放弃。

2、本文不探讨具体法律细节,官司究竟谁赢,那是法院的事。只说点法律之外的事。

3、果粉,特别是极端果粉,很看不惯这事,认为唯冠是从山上跑下来抢桃子。不过,苹果也不是善主。有一天有位朋友跟我说,乔布斯崇尚自由。我琢磨着就是早期苹果那个广告给ta的印象——那时候苹果还是微软的挑战者。现在上了台,嘴脸自然就要变了。乔布斯是个典型的独裁者+暴君,和“自由”两个字,离得极远。

4、苹果拿iPad商标,唯冠认为有欺诈嫌疑,这点网上已有报道,有兴趣可以去找找。搞个类似的段子给大家看看:坊间传闻,当时dudu.com在蔡文胜手中,陈一舟想买下,却被蔡开价100万。陈觉得太贵,让副总编故事来智取。副总发邮件给蔡,说她是一个新生儿的母亲,女儿的名字叫dudu,希望搭建一个记录女儿成长的网站。故事很美丽,蔡总很感动,最终以 5000元价格卖给了“母亲”。—— 这就是商战。

关于作者
魏武挥, 上海交通大学媒体与设计学院, 教师
哥不做总好多年
Copyleft © 2010 知识共享署名-非商业性使用-禁止演绎 注意:转载勿改标题!
ItTalks -- 魏武挥的Blog (digitalfingerprint:fc4f8fc31f70097eea4b780b13146415)

欢迎 浏览我收集的信息图关注我的微博访问我的分享
无觅猜您也喜欢:

规则

从方韩斗所引发的媒体伦理问题思考

当伦理遇见利益

新媒体的道德伦理
无觅

与本日志可能相关的文章有:

]]>
唯冠vs苹果就iPad商标,唯冠深圳公司认为苹果公司在获取时有欺诈行为。曾有报道说,唯冠欲在美国索赔20亿美元。坊间舆论一片哗然,很多人都认为唯冠狮子大开口,逮着个有钱的主,就要求若干若干。不过,后续报道称,唯冠方面“没有提出具体赔偿额”,唯冠“寻求的只是一个商业性质的合作,即把唯冠所有的中国大陆iPad商标权卖给苹果。”而有论者注意到,唯冠这个曾经的全球四大显示屏制造商之一,今天已经非常困顿,大部分资产已遭法院冻结,前主席杨荣山更是在2010年8月就被法院颁令破产。

一个是如日中天市值超过微软加英特尔的苹果,一个是日薄西山苦苦挣扎的唯冠。一个是风靡全球人人皆想拥有的苹果iPad电脑,一个是2000年出过概念产品且用的是老式显示器(CRT)最终不了了之的唯冠iPad电脑。唯冠自己做得一塌糊涂,却眼红苹果的巨大财富,想从中分一杯羹。国人自己实业上不争气,不老老实实秉着创新精神开发自己的东西,在这上面挖空心思搞钱,殊为可叹——这大抵就是有些论者的论调,极端的,甚至上升到国民素质劣根性问题上。

不过,可惜的是,如果非要说“劣根性”,大抵老外也颇有些劣根性。中国有一本很有名的杂志名为《读者文摘》,发行量号称数百万之巨。在93年的时候,碰到与美国《读者文摘》的法律纠纷,最终被迫更名为《读者》。我们能简单地说老美眼红中国版《读者文摘》的巨大市场份额么?

另外一个相当著名的和数字世界有关的品牌纠纷,就是腾讯的QQ。在早期,QQ并不叫QQ,而叫OICQ这个带有典型的C2C(Copy to China)风格的名字。腾讯也从来不否认,这款产品是在“山寨”美国的即时通讯软件ICQ。发展了几年后,远在大洋彼岸没有多少中国用户的ICQ认为OICQ侵犯了它的利益,在巨大的压力下,OICQ被迫改名为QQ。不过,时至今日,恐怕知道QQ的人远远比知道OICQ的人多得多。这个故事有一个比较有趣的结局:ICQ后来经营不利,试图出售,腾讯一度是竞购者之一。

不仅中国人和老外之间很有些品牌方面的纠纷,老外与老外之间,也一向是剑拔弩张绝不是什么好好先生。谷歌旗下知名的邮件产品Gmail在德国就规规矩矩地叫做“Google Mail”,概因德国早就有一个电邮服务商Gmail。谷歌为什么不买下这个德国电邮品牌呢?其实就是个“价格”。价格合适就买,价格不合适就拉倒。商业社会里,谈的都是利益,至于动机不动机,一来别人肚子里的东西,外人不好乱加揣摩,二来说穿了就是利益,商人不图利,就不是好的合格的商人。

至于唯冠不好好经营自己的实业,在品牌二字上大做文章,是不是有悖商业伦理呢?也不见得。在美国,大有一些所谓专利公司,做的就是专利囤积,然后伺机倒卖——这也算是一种“实业”吧。如果囤积专利还需要一些专业的技术知识,一般人还不会摆弄,那么,囤积域名倒买倒卖的“米虫”,这又算什么呢?这一行后来颇出了几个大佬,比如蔡文胜,还是不少人的偶像,我倒是不觉得,蔡先生有什么商业伦理问题。

市场经济是所谓的“规则经济”、“法律经济”,这一点已成为了大多数人的共识。但反过来想想,有规则的地方就会有市场——这话的意思是,总有人会靠“玩弄规则”为生,甚至玩出一个大市场来。中国古人对这一点是很有矛盾心态的。著名的“子产铸刑鼎”事件围绕的就是这样一个核心:该不该把规则(这个故事里就是法律)公之于众。反对者认为一旦公布规则,那就意味着有人要去玩弄规则,钻规则的空子,人心就此败坏。这种观点在后来的历史发展中,每个人都看到了,渐渐消没。而专门折腾法律规则的,更是发展出一个巨大的行业:律师业。你能说律师就是没有伦理的工种么?

平心而论,iPad这四个字母,本无意义(或商业价值),的确是苹果将之变成一个高价值的单词。但怎么也绕不过去的是,唯冠出手的早,且就各方报道来看,唯冠深圳并没有把大陆的iPad商标权给出售出去。如果这一点是事实的话,苹果要拿回在大陆的iPad,就必须要付出巨大的代价——这也就成了很多人的疑问:我辛辛苦苦让这四个字母成为财富,凭什么我还要为拥有它买单?不凭什么,市场规则,本来如此。

在我看来,这种事件,其实是和“全球化”有关的。一本名为《世界是平的》的畅销书,对全球化不乏溢美之词。我向来对一边倒的论著兴趣平平,草草翻过便算数。但这本书所注意到的现象:全球化在加剧却是一个不争的事实。也正是因为全球化,才产生了异国商标品牌在另一国的通行问题。正常的国家都有自己的司法主权,在全球99%的地区卖得再好再知名,在剩下的1%地区,就得老老实实地先去拿下这个1%地区的通行证。你可以说是这是一种弊端,但恐怕这种弊端,在可见的未来里,还很难消除——它是要全球消灭国家这个概念为前提的。

有鉴于此,大名鼎鼎的Facebook已经开始着手做类似的准备工作,据称它在华注册了六十一个商标,坊间戏言“就差非死不可没有注册了”。这样的事看似有些无聊,但却是必要的前提。

按规则办事,没按(在司法机关的认定下),就得事后付出代价。这是市场经济的铁律,和商业伦理、道德,没啥关系。

—— 应邀,刊发于当日《东方早报》——

题外话几句:

1、配图是网络所得,其实反映出唯冠和苹果之间争斗,背后的银行图谋。这事有点银行幕后操纵,唯冠台前扮演的意思。不过也不好苛求银行什么,眼睁睁看着自己的放贷要烂账,忽然有这么一个机会,傻子才会放弃。

2、本文不探讨具体法律细节,官司究竟谁赢,那是法院的事。只说点法律之外的事。

3、果粉,特别是极端果粉,很看不惯这事,认为唯冠是从山上跑下来抢桃子。不过,苹果也不是善主。有一天有位朋友跟我说,乔布斯崇尚自由。我琢磨着就是早期苹果那个广告给ta的印象——那时候苹果还是微软的挑战者。现在上了台,嘴脸自然就要变了。乔布斯是个典型的独裁者+暴君,和“自由”两个字,离得极远。

4、苹果拿iPad商标,唯冠认为有欺诈嫌疑,这点网上已有报道,有兴趣可以去找找。搞个类似的段子给大家看看:坊间传闻,当时dudu.com在蔡文胜手中,陈一舟想买下,却被蔡开价100万。陈觉得太贵,让副总编故事来智取。副总发邮件给蔡,说她是一个新生儿的母亲,女儿的名字叫dudu,希望搭建一个记录女儿成长的网站。故事很美丽,蔡总很感动,最终以 5000元价格卖给了“母亲”。—— 这就是商战。

关于作者
魏武挥, 上海交通大学媒体与设计学院, 教师
哥不做总好多年
Copyleft © 2010 知识共享署名-非商业性使用-禁止演绎 注意:转载勿改标题!
ItTalks -- 魏武挥的Blog (digitalfingerprint:fc4f8fc31f70097eea4b780b13146415)

欢迎 浏览我收集的信息图关注我的微博访问我的分享
无觅猜您也喜欢:

规则

从方韩斗所引发的媒体伦理问题思考

当伦理遇见利益

新媒体的道德伦理
无觅

与本日志可能相关的文章有:

]]>
0
<![CDATA[The cost of knowledge: JMLR 的 8g]]> http://www.udpwork.com/item/6865.html http://www.udpwork.com/item/6865.html#reviews Mon, 20 Feb 2012 09:21:28 +0800 pluskid http://www.udpwork.com/item/6865.html 这一阵子数学家们发起了一场抵制 Elsevier 的运动,建立了一个网站叫做the cost of knowledge。简而言之,Elsevier 是一个出版商,它旗下有很多期刊,但是在众多恶劣的出版商之中,Elsevier 似乎尤其恶劣,价格高昂,并且搞捆绑销售捆绑一些非常低质量的期刊,而且还被爆参与一些非正当的学术行为,另外还和前一阵子引起很大风波的让 Wikipedia 下线一天的SOPA有关。期刊本来的主要作用是传播学术成果,学术论文发表是没有稿费的,并且论文评审和编辑工作完全是有这些科研工作者“志愿”完成的,出版商也不会支付任何费用,他们的收入则主要来源于将期刊集出售给各大学的图书馆之类的机构。而现在呢,随着 Internet 的发展,期刊的主要作用“传播”似乎越发显得不重要了,然而许多出版商却变本加厉,订阅价格越来越贵,许多学校都越来越难以负担,于是几乎是历史必然地,出现了抵制运动,这次首先针对的是据说行为非常恶劣的 Elsevier ,具体可以参见这次运动的Statement of Purpose,来龙去脉讲得非常清楚。此运动发起之后,各个领域的人也都参与进来,表示支持,抵制的方式就是可以选择 (1) 不投稿;(2) 不引用;(3) 不参与编辑工作。在他们的网站上可以看到已经有各行各业的的公开支持了。

总而言之这是一场非常振奋人心的运动,相信此后科学出版或者说学术界又会有一些好的变化吧。不过这次要 8g 的主角实际上是Journal of Machine Learning Research (JMLR),那已经是十几年前的老故事了。

JMLR 可以算是目前机器学习领域里最重要的期刊吧。可是关于它的起源我也是最近才了解到的。它出生于 2000 年。那个时候机器学习里的主要期刊是一个叫做Machine Learning的期刊(当然现在也是很重要的一个期刊)。那个时候互联网也逐渐开始发展起来,人们可以通过在自己的主页上放置自己的论文的方式来使得论文传播更加容易。出版商们——虽然他们存在的目的实际上是为了传播——害怕这样会威胁到他们的利益,于是那个时候许多出版商还有大学之类的都有规定不允许将带版权的论文放在网上传播。不过大部分人都直接无视这个规定的。

在禁止互联网传播电子版论文是同时,期刊的价格又在不断地提高,甚至到许多图书馆都无法负担的地步。于是,在和当时 Machine Learning 的出版商 Kluwer 协商无数次试图降低价格以及允许论文在互联网上自由传播失败之后,2000 年的时候,Leslie Pack Kaelbling(当时也是 Machine Learning 的编委会成员)站出来了,她成立了一个新的 Journal ,就是 JMLR 。JMLR 的论文的版权归作者,并且论文全部可以免费在互联网上下载到,当然,他们也出版纸质期刊,主要作为图书馆存档用,价格也公道。

Machine Learning 的编委会中有三分之二的成员辞职转而支持 JMLR 的发展,2001 年的那封辞职信现在还可以访问得到,如果你是机器学习领域里的人,不妨看看那个 40 个署名里你认识多少个牛人。:D另外,从那个信的内容中可以看到,即使在 2001 年的时候,这样的事情也已经不是第一次发生了!

当然,虽然当时几乎是一呼百应的一边倒情形,但是,期刊不是你想办,想办就能办的,特别是要办好的期刊。所以一开始大家也做了很多努力,比如说 Leslie Kaelbling 说她邀请了四位领域大牛来写第一期的内容。但总的来说过程也并没有太多波折,到 2004 年的时候 JMLR 已经是计算机里(包括所有其他研究领域)影响因子第二高的期刊了。后来 Kluwer 也做了相应的妥协,降低了费用,并放宽了论文电子版公开传播的限制。

总的来说,JMLR 的发展倒并没有什么跌宕起伏惊心动魄的交战,按照 Leslie Kaelbling 自己的说法是“The story of JMLR is surprisingly uncomplicated”,但是也是相当有趣的一段趣闻,而且是促进学术界更加开放自由的进程中的一个相当鼓舞人心的正例。特别是在了解到 JMLR 的创始人竟然是一位女性的时候(并没有贬低女性的意思),顿时就对 Leslie Kaelbling 非常仰慕了!工业界现在已经有开源社区的模式似乎正如日中天,希望有朝一日无论学术界还是工业界都能更加自由、开放、透明吧!^_^敬仰所有为此历程做出贡献的人!其实还有许多地方在做一些改变或者相应的尝试,例如数学里的polymath项目,还有 ICML 近几年也在自己的论文在线 archive 后面添加留言讨论功能,以及像CVPapers这样把几个著名的 CV 会议的论文作者自己放在自己主页上的论文链接搜集整理起来供大家访问的网站(因为上 IEEE 等网站下载的话,是要收费的,如果 IEEE 之类的 policy 是允许作者把论文放在自己主页上的话,CVPapers 由于只是搜集整理了链接,所以应该也不会有诸如版权之类的问题,但是却是一件很重要的事)等。

末了,令我有点小抓狂的是,虽然收到了 Leslie Kaelbling 在近期的 Campus Visit 上要举办一个邀请 Machine Learning 方向的新老同学以及一些相关的教授见面的一个 dinner 的邀请,然而我却由于签证时间紧急和昂贵的机票(这大概大部分是国际学生的问题吧)而无法参加 Campus Visit 。 >_< 哎,不过来日方长,这么多的牛人,以后再慢慢仰慕吧!

JMLR 的小 8g 相关的参考资料:

]]>
这一阵子数学家们发起了一场抵制 Elsevier 的运动,建立了一个网站叫做the cost of knowledge。简而言之,Elsevier 是一个出版商,它旗下有很多期刊,但是在众多恶劣的出版商之中,Elsevier 似乎尤其恶劣,价格高昂,并且搞捆绑销售捆绑一些非常低质量的期刊,而且还被爆参与一些非正当的学术行为,另外还和前一阵子引起很大风波的让 Wikipedia 下线一天的SOPA有关。期刊本来的主要作用是传播学术成果,学术论文发表是没有稿费的,并且论文评审和编辑工作完全是有这些科研工作者“志愿”完成的,出版商也不会支付任何费用,他们的收入则主要来源于将期刊集出售给各大学的图书馆之类的机构。而现在呢,随着 Internet 的发展,期刊的主要作用“传播”似乎越发显得不重要了,然而许多出版商却变本加厉,订阅价格越来越贵,许多学校都越来越难以负担,于是几乎是历史必然地,出现了抵制运动,这次首先针对的是据说行为非常恶劣的 Elsevier ,具体可以参见这次运动的Statement of Purpose,来龙去脉讲得非常清楚。此运动发起之后,各个领域的人也都参与进来,表示支持,抵制的方式就是可以选择 (1) 不投稿;(2) 不引用;(3) 不参与编辑工作。在他们的网站上可以看到已经有各行各业的的公开支持了。

总而言之这是一场非常振奋人心的运动,相信此后科学出版或者说学术界又会有一些好的变化吧。不过这次要 8g 的主角实际上是Journal of Machine Learning Research (JMLR),那已经是十几年前的老故事了。

JMLR 可以算是目前机器学习领域里最重要的期刊吧。可是关于它的起源我也是最近才了解到的。它出生于 2000 年。那个时候机器学习里的主要期刊是一个叫做Machine Learning的期刊(当然现在也是很重要的一个期刊)。那个时候互联网也逐渐开始发展起来,人们可以通过在自己的主页上放置自己的论文的方式来使得论文传播更加容易。出版商们——虽然他们存在的目的实际上是为了传播——害怕这样会威胁到他们的利益,于是那个时候许多出版商还有大学之类的都有规定不允许将带版权的论文放在网上传播。不过大部分人都直接无视这个规定的。

在禁止互联网传播电子版论文是同时,期刊的价格又在不断地提高,甚至到许多图书馆都无法负担的地步。于是,在和当时 Machine Learning 的出版商 Kluwer 协商无数次试图降低价格以及允许论文在互联网上自由传播失败之后,2000 年的时候,Leslie Pack Kaelbling(当时也是 Machine Learning 的编委会成员)站出来了,她成立了一个新的 Journal ,就是 JMLR 。JMLR 的论文的版权归作者,并且论文全部可以免费在互联网上下载到,当然,他们也出版纸质期刊,主要作为图书馆存档用,价格也公道。

Machine Learning 的编委会中有三分之二的成员辞职转而支持 JMLR 的发展,2001 年的那封辞职信现在还可以访问得到,如果你是机器学习领域里的人,不妨看看那个 40 个署名里你认识多少个牛人。:D另外,从那个信的内容中可以看到,即使在 2001 年的时候,这样的事情也已经不是第一次发生了!

当然,虽然当时几乎是一呼百应的一边倒情形,但是,期刊不是你想办,想办就能办的,特别是要办好的期刊。所以一开始大家也做了很多努力,比如说 Leslie Kaelbling 说她邀请了四位领域大牛来写第一期的内容。但总的来说过程也并没有太多波折,到 2004 年的时候 JMLR 已经是计算机里(包括所有其他研究领域)影响因子第二高的期刊了。后来 Kluwer 也做了相应的妥协,降低了费用,并放宽了论文电子版公开传播的限制。

总的来说,JMLR 的发展倒并没有什么跌宕起伏惊心动魄的交战,按照 Leslie Kaelbling 自己的说法是“The story of JMLR is surprisingly uncomplicated”,但是也是相当有趣的一段趣闻,而且是促进学术界更加开放自由的进程中的一个相当鼓舞人心的正例。特别是在了解到 JMLR 的创始人竟然是一位女性的时候(并没有贬低女性的意思),顿时就对 Leslie Kaelbling 非常仰慕了!工业界现在已经有开源社区的模式似乎正如日中天,希望有朝一日无论学术界还是工业界都能更加自由、开放、透明吧!^_^敬仰所有为此历程做出贡献的人!其实还有许多地方在做一些改变或者相应的尝试,例如数学里的polymath项目,还有 ICML 近几年也在自己的论文在线 archive 后面添加留言讨论功能,以及像CVPapers这样把几个著名的 CV 会议的论文作者自己放在自己主页上的论文链接搜集整理起来供大家访问的网站(因为上 IEEE 等网站下载的话,是要收费的,如果 IEEE 之类的 policy 是允许作者把论文放在自己主页上的话,CVPapers 由于只是搜集整理了链接,所以应该也不会有诸如版权之类的问题,但是却是一件很重要的事)等。

末了,令我有点小抓狂的是,虽然收到了 Leslie Kaelbling 在近期的 Campus Visit 上要举办一个邀请 Machine Learning 方向的新老同学以及一些相关的教授见面的一个 dinner 的邀请,然而我却由于签证时间紧急和昂贵的机票(这大概大部分是国际学生的问题吧)而无法参加 Campus Visit 。 >_< 哎,不过来日方长,这么多的牛人,以后再慢慢仰慕吧!

JMLR 的小 8g 相关的参考资料:

]]>
0
<![CDATA[让Sublime Text2支持浏览器中预览]]> http://www.udpwork.com/item/6864.html http://www.udpwork.com/item/6864.html#reviews Mon, 20 Feb 2012 01:36:58 +0800 JerryQu http://www.udpwork.com/item/6864.html 从Editplus转到Sublime Text2的同学,不知道有没觉得它少了个很有用的功能:view in browser(ctrl+b)。平时写点小demo时,那种一秒钟切浏览器看下效果,一秒后再切回来改下代码的爽快感,是我坚守Editplus多年的一大重要原因。

Editplus提供的ctrl+b功能,好用的原因有二:一是内置了webbrowser,切换时不用离开编辑器;二是对于本地调试的web站点,配置规则后自动将编辑的文件映射为URL来预览,这对预览php等服务端文件非常有用。Mac下的Coda,也有类似的预览功能,但我一直用不习惯它,暂不考虑。

Sublime Text2支持用Python编写插件,我很想自己动手解决这个问题,但估计短期内我没可能研究出如何创建webbrowser嵌进ST2里,于是打算偷点懒,直接调系统默认浏览器预览。下面是我研究出来的详细步骤:

一、 点击菜单Tools -> New Plugin...,在创建好的py文件输入下列内容:

import sublime, sublime_plugin
import webbrowser

url_map = {
	'/Users/jerry/Sites/test/' : 'http://test/',
}

class OpenBrowserCommand(sublime_plugin.TextCommand):
	def run(self,edit):
		url = self.view.file_name()
		for path, domain in url_map.items():
			if url.startswith(path):
				url = url.replace(path, domain).replace('\\', '\/')
				break

		webbrowser.open_new(url)

代码只有几行,大部分还是参考的这个帖子,相信大家一眼就能看明白,不解释了。

将文件保存到Packages/User目录(Packages可通过菜单里的Browser Packages...打开),文件名随意,如open_browser.py。插件部分完工了。

二、 接下来,为刚才的插件分配快捷键。点菜单Tools -> Command Palette...,或者shift+cmd+p,打开命令集,选择“key Bindings - User”打开个人快捷键配置,输入下列内容:

[{ "keys": ["ctrl+shift+b"], "command": "open_browser" }]

这就是要做的全部工作,可以测试下了。打开一个html文件,ctrl+shift+b试试,没意外的话文件会在默认浏览器打开了。url_map里配置的站点目录到URL的映射应该也是可用的。

PS:如果要指定用什么浏览器预览,也可以将最后一行代码改成这样:

webbrowser.get('safari').open_new(url)

webbrowser具体支持get哪些浏览器,可以通过webbrowser._browsers查看。只是,ST2默认使用的是python2.6,在我的mac os下,python2.6找不到任何可用的浏览器。2.7的webbrowser.py里多了一段patch,可以找到firefox和safari。好在,对我来说,能在默认浏览器预览已经够用了。

先写到这里,如果我哪天找到了更好的方法,再来补充。

]]>
从Editplus转到Sublime Text2的同学,不知道有没觉得它少了个很有用的功能:view in browser(ctrl+b)。平时写点小demo时,那种一秒钟切浏览器看下效果,一秒后再切回来改下代码的爽快感,是我坚守Editplus多年的一大重要原因。

Editplus提供的ctrl+b功能,好用的原因有二:一是内置了webbrowser,切换时不用离开编辑器;二是对于本地调试的web站点,配置规则后自动将编辑的文件映射为URL来预览,这对预览php等服务端文件非常有用。Mac下的Coda,也有类似的预览功能,但我一直用不习惯它,暂不考虑。

Sublime Text2支持用Python编写插件,我很想自己动手解决这个问题,但估计短期内我没可能研究出如何创建webbrowser嵌进ST2里,于是打算偷点懒,直接调系统默认浏览器预览。下面是我研究出来的详细步骤:

一、 点击菜单Tools -> New Plugin...,在创建好的py文件输入下列内容:

import sublime, sublime_plugin
import webbrowser

url_map = {
	'/Users/jerry/Sites/test/' : 'http://test/',
}

class OpenBrowserCommand(sublime_plugin.TextCommand):
	def run(self,edit):
		url = self.view.file_name()
		for path, domain in url_map.items():
			if url.startswith(path):
				url = url.replace(path, domain).replace('\\', '\/')
				break

		webbrowser.open_new(url)

代码只有几行,大部分还是参考的这个帖子,相信大家一眼就能看明白,不解释了。

将文件保存到Packages/User目录(Packages可通过菜单里的Browser Packages...打开),文件名随意,如open_browser.py。插件部分完工了。

二、 接下来,为刚才的插件分配快捷键。点菜单Tools -> Command Palette...,或者shift+cmd+p,打开命令集,选择“key Bindings - User”打开个人快捷键配置,输入下列内容:

[{ "keys": ["ctrl+shift+b"], "command": "open_browser" }]

这就是要做的全部工作,可以测试下了。打开一个html文件,ctrl+shift+b试试,没意外的话文件会在默认浏览器打开了。url_map里配置的站点目录到URL的映射应该也是可用的。

PS:如果要指定用什么浏览器预览,也可以将最后一行代码改成这样:

webbrowser.get('safari').open_new(url)

webbrowser具体支持get哪些浏览器,可以通过webbrowser._browsers查看。只是,ST2默认使用的是python2.6,在我的mac os下,python2.6找不到任何可用的浏览器。2.7的webbrowser.py里多了一段patch,可以找到firefox和safari。好在,对我来说,能在默认浏览器预览已经够用了。

先写到这里,如果我哪天找到了更好的方法,再来补充。

]]>
0
<![CDATA[Clear——乱花渐欲迷人眼的待办事项工具]]> http://www.udpwork.com/item/6863.html http://www.udpwork.com/item/6863.html#reviews Sun, 19 Feb 2012 16:07:48 +0800 elya妞 http://www.udpwork.com/item/6863.html 一、深泽直人的“无意识设计”
Clear这款产品,无处不萌发着一股无意识设计的味道,用户进来之后,看到一个没有任何按钮的list界面,之后任何下意识的手势操作,都对应着合理的引导,比如点击列表看详情、点击空白新建条目;比如下拉列表之后,会出现pull to creat list的提醒,松手就可以新建list;比如猛拉列表之后,会出现switch to menu的提醒,松手就可以回到menu列表;比如在列表上从左到右滑动完成待办事项,从右到左滑动删除待办事项;比如长按列表项就可以挪动位置……这些,都是用户直觉反应下会去探索和执行的操作。
“无意识设计”(Without Thought),又叫“直觉设计”,是深泽直人首次提出的一种设计理念,即:“将无意识的行动转化为可见之物”。设计是为了满足人的生活需求,而非颠覆,设计是方便人的生活方式,而非复杂。因此,好的设计必须以人为本,注重人的生活细节,方便人的生活习惯,使设计让生活更美好。特别是在手机产品设计高度发达的今天,很多设计师力图否定约定俗成的设计,用自己的思想创造一种新的生活方式,这样就无形中加重了人们的“适应负担”,“无意识设计”并不是一种全新的设计,而是关注一些别人没有意识到的细节,把这些细节放大,注入到原有的产品中,这种改变有时比创造一种新的产品更伟大。

 

二、特殊的层级导航设计

Clear的导航模式很有意思,没有标签页,没有选项卡,没有按钮,完全依赖手势操作。
首先让我们来看看Clear的层级结构——
menu list item Clear——乱花渐欲迷人眼的待办事项工具
如图所示,Clear大概分三个层级,Menu——>Lists——>Items,这三个层级分别是三个独立界面,利用手势向下滑动,回到上一层级,利用手势向上滑动,回到上一层级。
clear ges Clear——乱花渐欲迷人眼的待办事项工具
如图所示,Clear没有采用iPhone标准的Navigation Bar导航,而是利用手势下拉回到上一层级,这种交互方式比较新颖,也符合心智模型,但是仅仅适合层级关系较少(3个之内),结构清晰简单的应用。

 

三、对移动端特性的充分利用

1.乱花渐欲迷人眼的手势操作

Clear的最大特色,就是“无按钮”,依赖手势完成全部的操作,这在PC端是不可想象的,在按键手机上也是无法实现的,但是在触屏机上,这种交互形式成为可能,成为亮点,甚至成为卖点。让我们来拆解一下Clear对手势的利用——
clear ges list Clear——乱花渐欲迷人眼的待办事项工具
clear ges eg Clear——乱花渐欲迷人眼的待办事项工具
最基本的点击(Tap)手势,对应查看、编辑和新建;拖拽-滑动(Drag-Flick)手势,对应新建及层级切换;长按拖拽(Touch and Hold-Drag)既可以调整待办事项的顺序;两指捏合(Pinch)手势,对应回到上一级界面;两指扩张(Spread)手势,对应在两个条目中新建事项。
Clear还未用到的手势包括双击(Double Tap)、两指旋转(Rotate)、两指拖拽(2 Finger Drag)等,这些未用到的手势对用户来说学习成本就比较高了,不过相信随着Clear不断完善丰富自己的功能,也会考虑为高端用户提供更复杂更便捷的手势操作。
手势设计,本就具有隐蔽性高、需要学习、需要记忆、预防误操作等等问题,尽管Clear努力将所有的手势与用户的直觉和无意识动作结合起来,某些手势仍然具有一些问题,比如——
  • 轻微下拉和猛力下拉,拉动的距离不可见,比较容易误操作,经常想回到上一级的时候会执行了新建操作
  • 下拉手势与IOS的下拉唤醒System Notification Bar冲突
  • 当列表满了的时候,无法在最下边新建一个条目
  • 在路上或车上基本没法用
这两个问题不能很好的解决的话,还是比较影响用户简洁高效的完成操作的。

2.曼妙可人的声音阶梯

Clear对声音的利用,跟Tweetbot有的一拼了。Tweetbot因为采用金属质感的界面合计,所以对应的声音都是很机械化的金属音,让用户的操作反馈变得更加立体。而Clear因为采用了渐变色、长列表层级结构的设计,所以声音反馈也巧妙的采用了声音阶梯,具体是手风琴,还是口风琴,我的耳朵没听出来,不过真的很曼妙,让人忍不住经常在几个界面之间拉来拉去。
新建的时候是趣味的冒泡音,删除的时候是刷子音;依次选择完成的时候,是六个一组有规律的音阶;依次取消完成的时候,先是四个一组有规律的音阶,之后就是同样的声音反馈了。
我只能说,clear的设计师,很用心,把声音处理的美妙绝伦。

3.用渐变色来体现重要程度

Clear的配色足以让人眼前一亮,渐变色让人感受到色彩的跳跃,比iPhone单一的列表要活泼的多,同时色彩的深浅也对应了事项的重要程度,可谓用心良苦。
clear color Clear——乱花渐欲迷人眼的待办事项工具
默认Lists列表是蓝色渐变,Items列表是红色渐变,用户还可以在设置中改变配色方案,改成粉色、绿色、灰色,装了Tweetbot的用户还可以选择Tweetbot的配色方案。

 

四、空白界面的文艺范

如果用户没有添加任务的时候,Clear会帮你内置一些教程,帮助你快速学习Clear的使用方法,这已经是目前比较常见的一种用户引导方式了。
但当你把吧教程全部删除掉,或者当你新建了一个list,里面还没有任何items的时候,Clear就会精心处理这些界面了。
Clear nodata Clear——乱花渐欲迷人眼的待办事项工具
Clear内置了非常多的名言警句,利用一种随机算法出现在用户会遇到的空白界面上,缓解用户遇到空白界面的挫败感,增强产品性格,提升文艺范。

 

五、Clear的改进建议

1.手势的改良

既然Clear是以手势取胜的,就先说说手势,前文也提到了Clear在手势设计时遇到的一些问题,这里我的设计建议就是:
(1)关于下拉手势的冲突
IOS5的System Notification Bar是可以下拉显示的,当应用默认全屏的时候,IOS5为了预防误操作,是下拉会先显示System bar的小把手,再拉小把手才显示System Notification Bar。那么当用户下拉界面的时候,如果触发了IOS5的System Bar,只要同时触发Clear的新建操作就可以了,这样就算是误操作,也不会影响正确的流程。
(2)关于新建列表最低端的条目
只需要增加一个上拉列表新建最底下的条目就可以了
(3)关于轻微下拉和猛力下拉
建议Clear增加Flick手势的精准判断,Flick Down应该可以直接回到上一级了,而目前Flick Down和Drag Down还会经常误判。只要能清晰的判断Flick和Drag手势,用户只需快速的拨动,就可以做层级切换,而不需要滑动很长一段距离。
(4)晃动环境下的手势
晃动环境下,左右横滑列表跟上下拉动界面有严重的手势冲突,那么这个时候,建议增加点击一下标注完成,再点一下取消完成,长按可以移动位置,并可以拖拽删除,就比较合理了。
(5)增加鼠标手势
其实一些PC浏览器中的鼠标手势,还比较适合手机端,海豚浏览器算是这方面的先行者。Clear可以考虑增加鼠标手势,用户在界面上写个“L”,就可以回到上一级,写个“O”,就可以打开任务详情,写个“E”,就可以编辑任务详情……
甚至可以增加摄像头捕捉手势,这样用户的手都不需要触控屏幕,只需要食指在摄像头前方做一些动作,就可以操控应用跳转,具体例子见“口袋体检”测心率的例子。

2.功能的建议

(1)可以增加时间线
目前的待办事项还没有时间维度,今日待办、明日待办、收集箱、按日历查看,这些都还没有。因为Clear有很好的手势操作,又是一个长列表,增加时间线不是难事。
(2)社会化待办
可以增加关系维度、分享维度,把自己的待办分享到社交网络上,邀请朋友一起完成。可以是任务分配至,一个Lists里有多个Items分别指派给不同的人,就变成了协同待办。

真心喜欢Clear这样敢于吃螃蟹的应用,推荐一下——
appstoredownload Clear——乱花渐欲迷人眼的待办事项工具

该日志未加标签。 ]]>
一、深泽直人的“无意识设计”
Clear这款产品,无处不萌发着一股无意识设计的味道,用户进来之后,看到一个没有任何按钮的list界面,之后任何下意识的手势操作,都对应着合理的引导,比如点击列表看详情、点击空白新建条目;比如下拉列表之后,会出现pull to creat list的提醒,松手就可以新建list;比如猛拉列表之后,会出现switch to menu的提醒,松手就可以回到menu列表;比如在列表上从左到右滑动完成待办事项,从右到左滑动删除待办事项;比如长按列表项就可以挪动位置……这些,都是用户直觉反应下会去探索和执行的操作。
“无意识设计”(Without Thought),又叫“直觉设计”,是深泽直人首次提出的一种设计理念,即:“将无意识的行动转化为可见之物”。设计是为了满足人的生活需求,而非颠覆,设计是方便人的生活方式,而非复杂。因此,好的设计必须以人为本,注重人的生活细节,方便人的生活习惯,使设计让生活更美好。特别是在手机产品设计高度发达的今天,很多设计师力图否定约定俗成的设计,用自己的思想创造一种新的生活方式,这样就无形中加重了人们的“适应负担”,“无意识设计”并不是一种全新的设计,而是关注一些别人没有意识到的细节,把这些细节放大,注入到原有的产品中,这种改变有时比创造一种新的产品更伟大。

 

二、特殊的层级导航设计

Clear的导航模式很有意思,没有标签页,没有选项卡,没有按钮,完全依赖手势操作。
首先让我们来看看Clear的层级结构——
menu list item Clear——乱花渐欲迷人眼的待办事项工具
如图所示,Clear大概分三个层级,Menu——>Lists——>Items,这三个层级分别是三个独立界面,利用手势向下滑动,回到上一层级,利用手势向上滑动,回到上一层级。
clear ges Clear——乱花渐欲迷人眼的待办事项工具
如图所示,Clear没有采用iPhone标准的Navigation Bar导航,而是利用手势下拉回到上一层级,这种交互方式比较新颖,也符合心智模型,但是仅仅适合层级关系较少(3个之内),结构清晰简单的应用。

 

三、对移动端特性的充分利用

1.乱花渐欲迷人眼的手势操作

Clear的最大特色,就是“无按钮”,依赖手势完成全部的操作,这在PC端是不可想象的,在按键手机上也是无法实现的,但是在触屏机上,这种交互形式成为可能,成为亮点,甚至成为卖点。让我们来拆解一下Clear对手势的利用——
clear ges list Clear——乱花渐欲迷人眼的待办事项工具
clear ges eg Clear——乱花渐欲迷人眼的待办事项工具
最基本的点击(Tap)手势,对应查看、编辑和新建;拖拽-滑动(Drag-Flick)手势,对应新建及层级切换;长按拖拽(Touch and Hold-Drag)既可以调整待办事项的顺序;两指捏合(Pinch)手势,对应回到上一级界面;两指扩张(Spread)手势,对应在两个条目中新建事项。
Clear还未用到的手势包括双击(Double Tap)、两指旋转(Rotate)、两指拖拽(2 Finger Drag)等,这些未用到的手势对用户来说学习成本就比较高了,不过相信随着Clear不断完善丰富自己的功能,也会考虑为高端用户提供更复杂更便捷的手势操作。
手势设计,本就具有隐蔽性高、需要学习、需要记忆、预防误操作等等问题,尽管Clear努力将所有的手势与用户的直觉和无意识动作结合起来,某些手势仍然具有一些问题,比如——
  • 轻微下拉和猛力下拉,拉动的距离不可见,比较容易误操作,经常想回到上一级的时候会执行了新建操作
  • 下拉手势与IOS的下拉唤醒System Notification Bar冲突
  • 当列表满了的时候,无法在最下边新建一个条目
  • 在路上或车上基本没法用
这两个问题不能很好的解决的话,还是比较影响用户简洁高效的完成操作的。

2.曼妙可人的声音阶梯

Clear对声音的利用,跟Tweetbot有的一拼了。Tweetbot因为采用金属质感的界面合计,所以对应的声音都是很机械化的金属音,让用户的操作反馈变得更加立体。而Clear因为采用了渐变色、长列表层级结构的设计,所以声音反馈也巧妙的采用了声音阶梯,具体是手风琴,还是口风琴,我的耳朵没听出来,不过真的很曼妙,让人忍不住经常在几个界面之间拉来拉去。
新建的时候是趣味的冒泡音,删除的时候是刷子音;依次选择完成的时候,是六个一组有规律的音阶;依次取消完成的时候,先是四个一组有规律的音阶,之后就是同样的声音反馈了。
我只能说,clear的设计师,很用心,把声音处理的美妙绝伦。

3.用渐变色来体现重要程度

Clear的配色足以让人眼前一亮,渐变色让人感受到色彩的跳跃,比iPhone单一的列表要活泼的多,同时色彩的深浅也对应了事项的重要程度,可谓用心良苦。
clear color Clear——乱花渐欲迷人眼的待办事项工具
默认Lists列表是蓝色渐变,Items列表是红色渐变,用户还可以在设置中改变配色方案,改成粉色、绿色、灰色,装了Tweetbot的用户还可以选择Tweetbot的配色方案。

 

四、空白界面的文艺范

如果用户没有添加任务的时候,Clear会帮你内置一些教程,帮助你快速学习Clear的使用方法,这已经是目前比较常见的一种用户引导方式了。
但当你把吧教程全部删除掉,或者当你新建了一个list,里面还没有任何items的时候,Clear就会精心处理这些界面了。
Clear nodata Clear——乱花渐欲迷人眼的待办事项工具
Clear内置了非常多的名言警句,利用一种随机算法出现在用户会遇到的空白界面上,缓解用户遇到空白界面的挫败感,增强产品性格,提升文艺范。

 

五、Clear的改进建议

1.手势的改良

既然Clear是以手势取胜的,就先说说手势,前文也提到了Clear在手势设计时遇到的一些问题,这里我的设计建议就是:
(1)关于下拉手势的冲突
IOS5的System Notification Bar是可以下拉显示的,当应用默认全屏的时候,IOS5为了预防误操作,是下拉会先显示System bar的小把手,再拉小把手才显示System Notification Bar。那么当用户下拉界面的时候,如果触发了IOS5的System Bar,只要同时触发Clear的新建操作就可以了,这样就算是误操作,也不会影响正确的流程。
(2)关于新建列表最低端的条目
只需要增加一个上拉列表新建最底下的条目就可以了
(3)关于轻微下拉和猛力下拉
建议Clear增加Flick手势的精准判断,Flick Down应该可以直接回到上一级了,而目前Flick Down和Drag Down还会经常误判。只要能清晰的判断Flick和Drag手势,用户只需快速的拨动,就可以做层级切换,而不需要滑动很长一段距离。
(4)晃动环境下的手势
晃动环境下,左右横滑列表跟上下拉动界面有严重的手势冲突,那么这个时候,建议增加点击一下标注完成,再点一下取消完成,长按可以移动位置,并可以拖拽删除,就比较合理了。
(5)增加鼠标手势
其实一些PC浏览器中的鼠标手势,还比较适合手机端,海豚浏览器算是这方面的先行者。Clear可以考虑增加鼠标手势,用户在界面上写个“L”,就可以回到上一级,写个“O”,就可以打开任务详情,写个“E”,就可以编辑任务详情……
甚至可以增加摄像头捕捉手势,这样用户的手都不需要触控屏幕,只需要食指在摄像头前方做一些动作,就可以操控应用跳转,具体例子见“口袋体检”测心率的例子。

2.功能的建议

(1)可以增加时间线
目前的待办事项还没有时间维度,今日待办、明日待办、收集箱、按日历查看,这些都还没有。因为Clear有很好的手势操作,又是一个长列表,增加时间线不是难事。
(2)社会化待办
可以增加关系维度、分享维度,把自己的待办分享到社交网络上,邀请朋友一起完成。可以是任务分配至,一个Lists里有多个Items分别指派给不同的人,就变成了协同待办。

真心喜欢Clear这样敢于吃螃蟹的应用,推荐一下——
appstoredownload Clear——乱花渐欲迷人眼的待办事项工具

该日志未加标签。 ]]>
0
<![CDATA[Linux系统内存相关信息获取]]> http://www.udpwork.com/item/6862.html http://www.udpwork.com/item/6862.html#reviews Sun, 19 Feb 2012 14:08:47 +0800 Yu Feng http://www.udpwork.com/item/6862.html

原创文章,转载请注明: 转载自Erlang非业余研究

本文链接地址: Linux系统内存相关信息获取

大型的服务器,特别是数据库服务器的主要瓶颈主要在内存,CPU,以及IO上。CPU是可再生资源,不够用等等就有了;内存和土地一样是不可再生资源,被占用了,后续的使用必须等到该资源释放.而IO也非常依赖于内存的使用情况,故内存的倒腾效率会大大影响服务器的效率,那么了解服务器内存的使用情况就非常重要。

Linux内核的内存相关的信息主要有下面几个获取管道,这里我们主要讨论的是系统级别的,没具体到各个进程级别:

1. 内核启动时候,VM内存相关模块初始化信息,透过dmesg查看。
详细描述可参考这里
比如:

NUMA: Using 30 for the hash shift.
Bootmem setup node 0 0000000000000000-0000000340000000
Bootmem setup node 1 0000000340000000-0000000640000000
On node 0 totalpages: 3095549
DMA zone: 2613 pages, LIFO batch:0
DMA32 zone: 765896 pages, LIFO batch:31
Normal zone: 2327040 pages, LIFO batch:31
On node 1 totalpages: 3102720
Normal zone: 3102720 pages, LIFO batch:31

Memory: 24543920k/26214400k available (2547k kernel code, 612792k reserved, 1289k data, 208k init)

Total HugeTLB memory allocated, 0

2. /proc/meminfo。
每个字段的意思,可参考这里
比如:

$cat /proc/meminfo
MemTotal: 24545764 kB
MemFree: 957064 kB
Buffers: 1739164 kB
Cached: 10699300 kB
SwapCached: 3816 kB
Active: 17884180 kB
Inactive: 4479128 kB
HighTotal: 0 kB
HighFree: 0 kB
LowTotal: 24545764 kB
LowFree: 957064 kB
SwapTotal: 6289320 kB
SwapFree: 5005124 kB
Dirty: 28 kB
Writeback: 0 kB
AnonPages: 9923908 kB
Mapped: 27068 kB
Slab: 1132452 kB
PageTables: 41560 kB
NFS_Unstable: 0 kB
Bounce: 0 kB
CommitLimit: 18562200 kB
Committed_AS: 33474640 kB
VmallocTotal: 34359738367 kB
VmallocUsed: 268160 kB
VmallocChunk: 34359470063 kB
HugePages_Total: 0
HugePages_Free: 0
HugePages_Rsvd: 0
Hugepagesize: 2048 kB

3. sysrq-trigger的m键。
首先启用这个特性:
sudo sysctl kernel.sysrq=1
获取帮助:
echo h | sudo tee /proc/sysrq-trigger
SysRq : HELP : loglevel0-8 reBoot Crashdump tErm Full kIll thaw-filesystems(J) saK showMem Nice powerOff showPc unRaw Sync showTasks Unmount shoWcpus
获取内存信息:
echo m | sudo tee /proc/sysrq-trigger

SysRq : Show Memory
Mem-info:
Node 0 DMA per-cpu:
cpu 0 hot: high 0, batch 1 used:0
cpu 0 cold: high 0, batch 1 used:0
cpu 1 hot: high 0, batch 1 used:0
cpu 1 cold: high 0, batch 1 used:0
cpu 2 hot: high 0, batch 1 used:0
cpu 2 cold: high 0, batch 1 used:0
cpu 3 hot: high 0, batch 1 used:0
cpu 3 cold: high 0, batch 1 used:0
cpu 4 hot: high 0, batch 1 used:0
cpu 4 cold: high 0, batch 1 used:0
cpu 5 hot: high 0, batch 1 used:0
cpu 5 cold: high 0, batch 1 used:0
cpu 6 hot: high 0, batch 1 used:0
cpu 6 cold: high 0, batch 1 used:0
cpu 7 hot: high 0, batch 1 used:0
cpu 7 cold: high 0, batch 1 used:0
cpu 8 hot: high 0, batch 1 used:0
cpu 8 cold: high 0, batch 1 used:0
cpu 9 hot: high 0, batch 1 used:0
cpu 9 cold: high 0, batch 1 used:0
cpu 10 hot: high 0, batch 1 used:0
cpu 10 cold: high 0, batch 1 used:0
cpu 11 hot: high 0, batch 1 used:0
cpu 11 cold: high 0, batch 1 used:0
cpu 12 hot: high 0, batch 1 used:0
cpu 12 cold: high 0, batch 1 used:0
cpu 13 hot: high 0, batch 1 used:0
cpu 13 cold: high 0, batch 1 used:0
cpu 14 hot: high 0, batch 1 used:0
cpu 14 cold: high 0, batch 1 used:0
cpu 15 hot: high 0, batch 1 used:0
cpu 15 cold: high 0, batch 1 used:0
Node 0 DMA32 per-cpu:
cpu 0 hot: high 186, batch 31 used:70
cpu 0 cold: high 62, batch 15 used:59
cpu 1 hot: high 186, batch 31 used:24
cpu 1 cold: high 62, batch 15 used:13
cpu 2 hot: high 186, batch 31 used:7
cpu 2 cold: high 62, batch 15 used:11
cpu 3 hot: high 186, batch 31 used:19
cpu 3 cold: high 62, batch 15 used:54
cpu 4 hot: high 186, batch 31 used:0
cpu 4 cold: high 62, batch 15 used:0
cpu 5 hot: high 186, batch 31 used:0
cpu 5 cold: high 62, batch 15 used:0
cpu 6 hot: high 186, batch 31 used:0
cpu 6 cold: high 62, batch 15 used:0
cpu 7 hot: high 186, batch 31 used:0
cpu 7 cold: high 62, batch 15 used:0
cpu 8 hot: high 186, batch 31 used:16
cpu 8 cold: high 62, batch 15 used:50
cpu 9 hot: high 186, batch 31 used:23
cpu 9 cold: high 62, batch 15 used:2
cpu 10 hot: high 186, batch 31 used:40
cpu 10 cold: high 62, batch 15 used:41
cpu 11 hot: high 186, batch 31 used:157
cpu 11 cold: high 62, batch 15 used:5
cpu 12 hot: high 186, batch 31 used:0
cpu 12 cold: high 62, batch 15 used:0
cpu 13 hot: high 186, batch 31 used:0
cpu 13 cold: high 62, batch 15 used:0
cpu 14 hot: high 186, batch 31 used:0
cpu 14 cold: high 62, batch 15 used:0
cpu 15 hot: high 186, batch 31 used:0
cpu 15 cold: high 62, batch 15 used:0
Node 0 Normal per-cpu:
cpu 0 hot: high 186, batch 31 used:50
cpu 0 cold: high 62, batch 15 used:13
cpu 1 hot: high 186, batch 31 used:159
cpu 1 cold: high 62, batch 15 used:14
cpu 2 hot: high 186, batch 31 used:155
cpu 2 cold: high 62, batch 15 used:9
cpu 3 hot: high 186, batch 31 used:13
cpu 3 cold: high 62, batch 15 used:14
cpu 4 hot: high 186, batch 31 used:0
cpu 4 cold: high 62, batch 15 used:0
cpu 5 hot: high 186, batch 31 used:0
cpu 5 cold: high 62, batch 15 used:0
cpu 6 hot: high 186, batch 31 used:0
cpu 6 cold: high 62, batch 15 used:0
cpu 7 hot: high 186, batch 31 used:0
cpu 7 cold: high 62, batch 15 used:0
cpu 8 hot: high 186, batch 31 used:81
cpu 8 cold: high 62, batch 15 used:8
cpu 9 hot: high 186, batch 31 used:1
cpu 9 cold: high 62, batch 15 used:13
cpu 10 hot: high 186, batch 31 used:69
cpu 10 cold: high 62, batch 15 used:14
cpu 11 hot: high 186, batch 31 used:77
cpu 11 cold: high 62, batch 15 used:10
cpu 12 hot: high 186, batch 31 used:0
cpu 12 cold: high 62, batch 15 used:0
cpu 13 hot: high 186, batch 31 used:135
cpu 13 cold: high 62, batch 15 used:0
cpu 14 hot: high 186, batch 31 used:0
cpu 14 cold: high 62, batch 15 used:0
cpu 15 hot: high 186, batch 31 used:0
cpu 15 cold: high 62, batch 15 used:0
Node 0 HighMem per-cpu: empty
Node 1 DMA per-cpu: empty
Node 1 DMA32 per-cpu: empty
Node 1 Normal per-cpu:
cpu 0 hot: high 186, batch 31 used:1
cpu 0 cold: high 62, batch 15 used:0
cpu 1 hot: high 186, batch 31 used:0
cpu 1 cold: high 62, batch 15 used:0
cpu 2 hot: high 186, batch 31 used:0
cpu 2 cold: high 62, batch 15 used:0
cpu 3 hot: high 186, batch 31 used:0
cpu 3 cold: high 62, batch 15 used:0
cpu 4 hot: high 186, batch 31 used:87
cpu 4 cold: high 62, batch 15 used:10
cpu 5 hot: high 186, batch 31 used:30
cpu 5 cold: high 62, batch 15 used:12
cpu 6 hot: high 186, batch 31 used:77
cpu 6 cold: high 62, batch 15 used:13
cpu 7 hot: high 186, batch 31 used:28
cpu 7 cold: high 62, batch 15 used:2
cpu 8 hot: high 186, batch 31 used:34
cpu 8 cold: high 62, batch 15 used:0
cpu 9 hot: high 186, batch 31 used:94
cpu 9 cold: high 62, batch 15 used:0
cpu 10 hot: high 186, batch 31 used:0
cpu 10 cold: high 62, batch 15 used:0
cpu 11 hot: high 186, batch 31 used:0
cpu 11 cold: high 62, batch 15 used:0
cpu 12 hot: high 186, batch 31 used:0
cpu 12 cold: high 62, batch 15 used:8
cpu 13 hot: high 186, batch 31 used:33
cpu 13 cold: high 62, batch 15 used:8
cpu 14 hot: high 186, batch 31 used:133
cpu 14 cold: high 62, batch 15 used:2
cpu 15 hot: high 186, batch 31 used:155
cpu 15 cold: high 62, batch 15 used:5
Node 1 HighMem per-cpu: empty
Free pages: 962792kB (0kB HighMem)
Active:4471040 inactive:1118163 dirty:52 writeback:0 unstable:0 free:240698 slab:283135 mapped-file:6766 mapped-anon:2481065 pagetables:10524
Node 0 DMA free:10836kB min:8kB low:8kB high:12kB active:0kB inactive:0kB present:10452kB pages_scanned:0 all_unreclaimable? yes
lowmem_reserve[]: 0 2991 12081 12081
Node 0 DMA32 free:66704kB min:2460kB low:3072kB high:3688kB active:2650608kB inactive:122492kB present:3063584kB pages_scanned:0 all_unreclaimable? no
lowmem_reserve[]: 0 0 9090 9090
Node 0 Normal free:39604kB min:7476kB low:9344kB high:11212kB active:8293932kB inactive:618776kB present:9308160kB pages_scanned:0 all_unreclaimable? no
lowmem_reserve[]: 0 0 0 0
Node 0 HighMem free:0kB min:128kB low:128kB high:128kB active:0kB inactive:0kB present:0kB pages_scanned:0 all_unreclaimable? no
lowmem_reserve[]: 0 0 0 0
Node 1 DMA free:0kB min:0kB low:0kB high:0kB active:0kB inactive:0kB present:0kB pages_scanned:0 all_unreclaimable? no
lowmem_reserve[]: 0 0 12120 12120
Node 1 DMA32 free:0kB min:0kB low:0kB high:0kB active:0kB inactive:0kB present:0kB pages_scanned:0 all_unreclaimable? no
lowmem_reserve[]: 0 0 12120 12120
Node 1 Normal free:845648kB min:9968kB low:12460kB high:14952kB active:6939620kB inactive:3731384kB present:12410880kB pages_scanned:0 all_unreclaimable? no
lowmem_reserve[]: 0 0 0 0
Node 1 HighMem free:0kB min:128kB low:128kB high:128kB active:0kB inactive:0kB present:0kB pages_scanned:0 all_unreclaimable? no
lowmem_reserve[]: 0 0 0 0
Node 0 DMA: 3*4kB 3*8kB 3*16kB 2*32kB 3*64kB 2*128kB 2*256kB 1*512kB 1*1024kB 0*2048kB 2*4096kB = 10836kB
Node 0 DMA32: 4260*4kB 1224*8kB 26*16kB 1*32kB 2*64kB 1*128kB 1*256kB 0*512kB 0*1024kB 1*2048kB 9*4096kB = 66704kB
Node 0 Normal: 201*4kB 884*8kB 1117*16kB 141*32kB 2*64kB 2*128kB 1*256kB 1*512kB 0*1024kB 0*2048kB 2*4096kB = 39604kB
Node 0 HighMem: empty
Node 1 DMA: empty
Node 1 DMA32: empty
Node 1 Normal: 31200*4kB 39388*8kB 14353*16kB 3307*32kB 906*64kB 2*128kB 1*256kB 1*512kB 1*1024kB 1*2048kB 2*4096kB = 845648kB
Node 1 HighMem: empty
3108846 pagecache pages
Swap cache: add 337062, delete 336108, find 261230/261555, race 0+0
Free swap = 5005124kB
Total swap = 6289320kB
Free swap: 5005124kB
6553600 pages of RAM
417159 reserved pages
2320912 pages shared
954 pages swap cached

4. 通过vm的活动情况, /proc/vmstat

$cat /proc/vmstat
nr_anon_pages 2480490
nr_mapped 6548
nr_file_pages 3110632
nr_slab 283141
nr_page_table_pages 10334
nr_dirty 1
nr_writeback 0
nr_unstable 0
nr_bounce 0
numa_hit 59784369537
numa_miss 44505232088
numa_foreign 44505232088
numa_interleave 288929
numa_local 59783481501
numa_other 44506120124
pgpgin 6085462783
pgpgout 16203578552
pswpin 2556
pswpout 334506
pgalloc_dma 1
pgalloc_dma32 12614733
pgalloc_normal 104277734751
pgalloc_high 0
pgfree 104290591256
pgactivate 50646465
pgdeactivate 4822484
pgfault 104165467862
pgmajfault 16210
pgrefill_dma 0
pgrefill_dma32 7653915
pgrefill_normal 26774426
pgrefill_high 0
pgsteal_dma 0
pgsteal_dma32 256875
pgsteal_normal 4129645
pgsteal_high 0
pgscan_kswapd_dma 0
pgscan_kswapd_dma32 343521
pgscan_kswapd_normal 4447936
pgscan_kswapd_high 0
pgscan_direct_dma 0
pgscan_direct_dma32 0
pgscan_direct_normal 0
pgscan_direct_high 0
pginodesteal 0
slabs_scanned 4000512
kswapd_steal 4386520
kswapd_inodesteal 2810883
pageoutrun 37456
allocstall 0
pgrotated 337233

5. 每个区的内存使用情况:/proc/zoneinfo

$ cat /proc/zoneinfo
Node 0, zone DMA32
pages free 668878
min 615
low 768
high 922
active 71404
inactive 7034
scanned 0 (a: 0 i: 0)
spanned 1044480
present 765896
nr_anon_pages 75672
nr_mapped 759
nr_file_pages 2766
nr_slab 551
nr_page_table_pages 266
nr_dirty 2
nr_writeback 0
nr_unstable 0
nr_bounce 0
numa_hit 444062598
numa_miss 11649541
numa_foreign 0
numa_interleave 0
numa_local 443992176
numa_other 11719963
protection: (0, 0, 9090, 9090)
pagesets
cpu: 0 pcp: 0
count: 180
high: 186
batch: 31
cpu: 0 pcp: 1
count: 58
high: 62
batch: 15
vm stats threshold: 60
cpu: 1 pcp: 0
count: 178
high: 186
batch: 31
cpu: 1 pcp: 1
count: 12
high: 62
batch: 15
vm stats threshold: 60
cpu: 2 pcp: 0
count: 184
high: 186
batch: 31
cpu: 2 pcp: 1
count: 51
high: 62
batch: 15
vm stats threshold: 60
cpu: 3 pcp: 0
count: 155
high: 186
batch: 31
cpu: 3 pcp: 1
count: 9
high: 62
batch: 15
vm stats threshold: 60
cpu: 8 pcp: 0
count: 181
high: 186
batch: 31
cpu: 8 pcp: 1
count: 53
high: 62
batch: 15
vm stats threshold: 60
cpu: 9 pcp: 0
count: 157
high: 186
batch: 31
cpu: 9 pcp: 1
count: 14
high: 62
batch: 15
vm stats threshold: 60
cpu: 10 pcp: 0
count: 162
high: 186
batch: 31
cpu: 10 pcp: 1
count: 1
high: 62
batch: 15
vm stats threshold: 60
cpu: 11 pcp: 0
count: 158
high: 186
batch: 31
cpu: 11 pcp: 1
count: 23
high: 62
batch: 15
vm stats threshold: 60
all_unreclaimable: 0
prev_priority: 12
start_pfn: 4096

6. 每个区的伙伴页面信息:/proc/buddyinfo

$ cat /proc/buddyinfo
Node 0, zone DMA 3 3 3 2 3 2 2 1 1 0 2
Node 0, zone DMA32 12238 4658 2889 2209 1697 1331 1008 750 467 238 143
Node 0, zone Normal 266 201 169 354 64 51 172 118 87 47 306
Node 1, zone Normal 102 288 500 394 255 1877 5161 3788 2053 826 372

有了这些信息,我们可以详细知道内存资源是如何使用的以及目前剩余情况,为下一步的优化做些决策上的依据。信息的解读可以google相关的关键词,但是充分理解这些信息点,要对内核的VM相关部分很熟悉。

PS. 以上我粗粗的列出几点,其他的同学们可以进一步补充,多谢!

祝玩得开心!

Post Footer automatically generated bywp-posturl pluginfor wordpress.

]]>

原创文章,转载请注明: 转载自Erlang非业余研究

本文链接地址: Linux系统内存相关信息获取

大型的服务器,特别是数据库服务器的主要瓶颈主要在内存,CPU,以及IO上。CPU是可再生资源,不够用等等就有了;内存和土地一样是不可再生资源,被占用了,后续的使用必须等到该资源释放.而IO也非常依赖于内存的使用情况,故内存的倒腾效率会大大影响服务器的效率,那么了解服务器内存的使用情况就非常重要。

Linux内核的内存相关的信息主要有下面几个获取管道,这里我们主要讨论的是系统级别的,没具体到各个进程级别:

1. 内核启动时候,VM内存相关模块初始化信息,透过dmesg查看。
详细描述可参考这里
比如:

NUMA: Using 30 for the hash shift.
Bootmem setup node 0 0000000000000000-0000000340000000
Bootmem setup node 1 0000000340000000-0000000640000000
On node 0 totalpages: 3095549
DMA zone: 2613 pages, LIFO batch:0
DMA32 zone: 765896 pages, LIFO batch:31
Normal zone: 2327040 pages, LIFO batch:31
On node 1 totalpages: 3102720
Normal zone: 3102720 pages, LIFO batch:31

Memory: 24543920k/26214400k available (2547k kernel code, 612792k reserved, 1289k data, 208k init)

Total HugeTLB memory allocated, 0

2. /proc/meminfo。
每个字段的意思,可参考这里
比如:

$cat /proc/meminfo
MemTotal: 24545764 kB
MemFree: 957064 kB
Buffers: 1739164 kB
Cached: 10699300 kB
SwapCached: 3816 kB
Active: 17884180 kB
Inactive: 4479128 kB
HighTotal: 0 kB
HighFree: 0 kB
LowTotal: 24545764 kB
LowFree: 957064 kB
SwapTotal: 6289320 kB
SwapFree: 5005124 kB
Dirty: 28 kB
Writeback: 0 kB
AnonPages: 9923908 kB
Mapped: 27068 kB
Slab: 1132452 kB
PageTables: 41560 kB
NFS_Unstable: 0 kB
Bounce: 0 kB
CommitLimit: 18562200 kB
Committed_AS: 33474640 kB
VmallocTotal: 34359738367 kB
VmallocUsed: 268160 kB
VmallocChunk: 34359470063 kB
HugePages_Total: 0
HugePages_Free: 0
HugePages_Rsvd: 0
Hugepagesize: 2048 kB

3. sysrq-trigger的m键。
首先启用这个特性:
sudo sysctl kernel.sysrq=1
获取帮助:
echo h | sudo tee /proc/sysrq-trigger
SysRq : HELP : loglevel0-8 reBoot Crashdump tErm Full kIll thaw-filesystems(J) saK showMem Nice powerOff showPc unRaw Sync showTasks Unmount shoWcpus
获取内存信息:
echo m | sudo tee /proc/sysrq-trigger

SysRq : Show Memory
Mem-info:
Node 0 DMA per-cpu:
cpu 0 hot: high 0, batch 1 used:0
cpu 0 cold: high 0, batch 1 used:0
cpu 1 hot: high 0, batch 1 used:0
cpu 1 cold: high 0, batch 1 used:0
cpu 2 hot: high 0, batch 1 used:0
cpu 2 cold: high 0, batch 1 used:0
cpu 3 hot: high 0, batch 1 used:0
cpu 3 cold: high 0, batch 1 used:0
cpu 4 hot: high 0, batch 1 used:0
cpu 4 cold: high 0, batch 1 used:0
cpu 5 hot: high 0, batch 1 used:0
cpu 5 cold: high 0, batch 1 used:0
cpu 6 hot: high 0, batch 1 used:0
cpu 6 cold: high 0, batch 1 used:0
cpu 7 hot: high 0, batch 1 used:0
cpu 7 cold: high 0, batch 1 used:0
cpu 8 hot: high 0, batch 1 used:0
cpu 8 cold: high 0, batch 1 used:0
cpu 9 hot: high 0, batch 1 used:0
cpu 9 cold: high 0, batch 1 used:0
cpu 10 hot: high 0, batch 1 used:0
cpu 10 cold: high 0, batch 1 used:0
cpu 11 hot: high 0, batch 1 used:0
cpu 11 cold: high 0, batch 1 used:0
cpu 12 hot: high 0, batch 1 used:0
cpu 12 cold: high 0, batch 1 used:0
cpu 13 hot: high 0, batch 1 used:0
cpu 13 cold: high 0, batch 1 used:0
cpu 14 hot: high 0, batch 1 used:0
cpu 14 cold: high 0, batch 1 used:0
cpu 15 hot: high 0, batch 1 used:0
cpu 15 cold: high 0, batch 1 used:0
Node 0 DMA32 per-cpu:
cpu 0 hot: high 186, batch 31 used:70
cpu 0 cold: high 62, batch 15 used:59
cpu 1 hot: high 186, batch 31 used:24
cpu 1 cold: high 62, batch 15 used:13
cpu 2 hot: high 186, batch 31 used:7
cpu 2 cold: high 62, batch 15 used:11
cpu 3 hot: high 186, batch 31 used:19
cpu 3 cold: high 62, batch 15 used:54
cpu 4 hot: high 186, batch 31 used:0
cpu 4 cold: high 62, batch 15 used:0
cpu 5 hot: high 186, batch 31 used:0
cpu 5 cold: high 62, batch 15 used:0
cpu 6 hot: high 186, batch 31 used:0
cpu 6 cold: high 62, batch 15 used:0
cpu 7 hot: high 186, batch 31 used:0
cpu 7 cold: high 62, batch 15 used:0
cpu 8 hot: high 186, batch 31 used:16
cpu 8 cold: high 62, batch 15 used:50
cpu 9 hot: high 186, batch 31 used:23
cpu 9 cold: high 62, batch 15 used:2
cpu 10 hot: high 186, batch 31 used:40
cpu 10 cold: high 62, batch 15 used:41
cpu 11 hot: high 186, batch 31 used:157
cpu 11 cold: high 62, batch 15 used:5
cpu 12 hot: high 186, batch 31 used:0
cpu 12 cold: high 62, batch 15 used:0
cpu 13 hot: high 186, batch 31 used:0
cpu 13 cold: high 62, batch 15 used:0
cpu 14 hot: high 186, batch 31 used:0
cpu 14 cold: high 62, batch 15 used:0
cpu 15 hot: high 186, batch 31 used:0
cpu 15 cold: high 62, batch 15 used:0
Node 0 Normal per-cpu:
cpu 0 hot: high 186, batch 31 used:50
cpu 0 cold: high 62, batch 15 used:13
cpu 1 hot: high 186, batch 31 used:159
cpu 1 cold: high 62, batch 15 used:14
cpu 2 hot: high 186, batch 31 used:155
cpu 2 cold: high 62, batch 15 used:9
cpu 3 hot: high 186, batch 31 used:13
cpu 3 cold: high 62, batch 15 used:14
cpu 4 hot: high 186, batch 31 used:0
cpu 4 cold: high 62, batch 15 used:0
cpu 5 hot: high 186, batch 31 used:0
cpu 5 cold: high 62, batch 15 used:0
cpu 6 hot: high 186, batch 31 used:0
cpu 6 cold: high 62, batch 15 used:0
cpu 7 hot: high 186, batch 31 used:0
cpu 7 cold: high 62, batch 15 used:0
cpu 8 hot: high 186, batch 31 used:81
cpu 8 cold: high 62, batch 15 used:8
cpu 9 hot: high 186, batch 31 used:1
cpu 9 cold: high 62, batch 15 used:13
cpu 10 hot: high 186, batch 31 used:69
cpu 10 cold: high 62, batch 15 used:14
cpu 11 hot: high 186, batch 31 used:77
cpu 11 cold: high 62, batch 15 used:10
cpu 12 hot: high 186, batch 31 used:0
cpu 12 cold: high 62, batch 15 used:0
cpu 13 hot: high 186, batch 31 used:135
cpu 13 cold: high 62, batch 15 used:0
cpu 14 hot: high 186, batch 31 used:0
cpu 14 cold: high 62, batch 15 used:0
cpu 15 hot: high 186, batch 31 used:0
cpu 15 cold: high 62, batch 15 used:0
Node 0 HighMem per-cpu: empty
Node 1 DMA per-cpu: empty
Node 1 DMA32 per-cpu: empty
Node 1 Normal per-cpu:
cpu 0 hot: high 186, batch 31 used:1
cpu 0 cold: high 62, batch 15 used:0
cpu 1 hot: high 186, batch 31 used:0
cpu 1 cold: high 62, batch 15 used:0
cpu 2 hot: high 186, batch 31 used:0
cpu 2 cold: high 62, batch 15 used:0
cpu 3 hot: high 186, batch 31 used:0
cpu 3 cold: high 62, batch 15 used:0
cpu 4 hot: high 186, batch 31 used:87
cpu 4 cold: high 62, batch 15 used:10
cpu 5 hot: high 186, batch 31 used:30
cpu 5 cold: high 62, batch 15 used:12
cpu 6 hot: high 186, batch 31 used:77
cpu 6 cold: high 62, batch 15 used:13
cpu 7 hot: high 186, batch 31 used:28
cpu 7 cold: high 62, batch 15 used:2
cpu 8 hot: high 186, batch 31 used:34
cpu 8 cold: high 62, batch 15 used:0
cpu 9 hot: high 186, batch 31 used:94
cpu 9 cold: high 62, batch 15 used:0
cpu 10 hot: high 186, batch 31 used:0
cpu 10 cold: high 62, batch 15 used:0
cpu 11 hot: high 186, batch 31 used:0
cpu 11 cold: high 62, batch 15 used:0
cpu 12 hot: high 186, batch 31 used:0
cpu 12 cold: high 62, batch 15 used:8
cpu 13 hot: high 186, batch 31 used:33
cpu 13 cold: high 62, batch 15 used:8
cpu 14 hot: high 186, batch 31 used:133
cpu 14 cold: high 62, batch 15 used:2
cpu 15 hot: high 186, batch 31 used:155
cpu 15 cold: high 62, batch 15 used:5
Node 1 HighMem per-cpu: empty
Free pages: 962792kB (0kB HighMem)
Active:4471040 inactive:1118163 dirty:52 writeback:0 unstable:0 free:240698 slab:283135 mapped-file:6766 mapped-anon:2481065 pagetables:10524
Node 0 DMA free:10836kB min:8kB low:8kB high:12kB active:0kB inactive:0kB present:10452kB pages_scanned:0 all_unreclaimable? yes
lowmem_reserve[]: 0 2991 12081 12081
Node 0 DMA32 free:66704kB min:2460kB low:3072kB high:3688kB active:2650608kB inactive:122492kB present:3063584kB pages_scanned:0 all_unreclaimable? no
lowmem_reserve[]: 0 0 9090 9090
Node 0 Normal free:39604kB min:7476kB low:9344kB high:11212kB active:8293932kB inactive:618776kB present:9308160kB pages_scanned:0 all_unreclaimable? no
lowmem_reserve[]: 0 0 0 0
Node 0 HighMem free:0kB min:128kB low:128kB high:128kB active:0kB inactive:0kB present:0kB pages_scanned:0 all_unreclaimable? no
lowmem_reserve[]: 0 0 0 0
Node 1 DMA free:0kB min:0kB low:0kB high:0kB active:0kB inactive:0kB present:0kB pages_scanned:0 all_unreclaimable? no
lowmem_reserve[]: 0 0 12120 12120
Node 1 DMA32 free:0kB min:0kB low:0kB high:0kB active:0kB inactive:0kB present:0kB pages_scanned:0 all_unreclaimable? no
lowmem_reserve[]: 0 0 12120 12120
Node 1 Normal free:845648kB min:9968kB low:12460kB high:14952kB active:6939620kB inactive:3731384kB present:12410880kB pages_scanned:0 all_unreclaimable? no
lowmem_reserve[]: 0 0 0 0
Node 1 HighMem free:0kB min:128kB low:128kB high:128kB active:0kB inactive:0kB present:0kB pages_scanned:0 all_unreclaimable? no
lowmem_reserve[]: 0 0 0 0
Node 0 DMA: 3*4kB 3*8kB 3*16kB 2*32kB 3*64kB 2*128kB 2*256kB 1*512kB 1*1024kB 0*2048kB 2*4096kB = 10836kB
Node 0 DMA32: 4260*4kB 1224*8kB 26*16kB 1*32kB 2*64kB 1*128kB 1*256kB 0*512kB 0*1024kB 1*2048kB 9*4096kB = 66704kB
Node 0 Normal: 201*4kB 884*8kB 1117*16kB 141*32kB 2*64kB 2*128kB 1*256kB 1*512kB 0*1024kB 0*2048kB 2*4096kB = 39604kB
Node 0 HighMem: empty
Node 1 DMA: empty
Node 1 DMA32: empty
Node 1 Normal: 31200*4kB 39388*8kB 14353*16kB 3307*32kB 906*64kB 2*128kB 1*256kB 1*512kB 1*1024kB 1*2048kB 2*4096kB = 845648kB
Node 1 HighMem: empty
3108846 pagecache pages
Swap cache: add 337062, delete 336108, find 261230/261555, race 0+0
Free swap = 5005124kB
Total swap = 6289320kB
Free swap: 5005124kB
6553600 pages of RAM
417159 reserved pages
2320912 pages shared
954 pages swap cached

4. 通过vm的活动情况, /proc/vmstat

$cat /proc/vmstat
nr_anon_pages 2480490
nr_mapped 6548
nr_file_pages 3110632
nr_slab 283141
nr_page_table_pages 10334
nr_dirty 1
nr_writeback 0
nr_unstable 0
nr_bounce 0
numa_hit 59784369537
numa_miss 44505232088
numa_foreign 44505232088
numa_interleave 288929
numa_local 59783481501
numa_other 44506120124
pgpgin 6085462783
pgpgout 16203578552
pswpin 2556
pswpout 334506
pgalloc_dma 1
pgalloc_dma32 12614733
pgalloc_normal 104277734751
pgalloc_high 0
pgfree 104290591256
pgactivate 50646465
pgdeactivate 4822484
pgfault 104165467862
pgmajfault 16210
pgrefill_dma 0
pgrefill_dma32 7653915
pgrefill_normal 26774426
pgrefill_high 0
pgsteal_dma 0
pgsteal_dma32 256875
pgsteal_normal 4129645
pgsteal_high 0
pgscan_kswapd_dma 0
pgscan_kswapd_dma32 343521
pgscan_kswapd_normal 4447936
pgscan_kswapd_high 0
pgscan_direct_dma 0
pgscan_direct_dma32 0
pgscan_direct_normal 0
pgscan_direct_high 0
pginodesteal 0
slabs_scanned 4000512
kswapd_steal 4386520
kswapd_inodesteal 2810883
pageoutrun 37456
allocstall 0
pgrotated 337233

5. 每个区的内存使用情况:/proc/zoneinfo

$ cat /proc/zoneinfo
Node 0, zone DMA32
pages free 668878
min 615
low 768
high 922
active 71404
inactive 7034
scanned 0 (a: 0 i: 0)
spanned 1044480
present 765896
nr_anon_pages 75672
nr_mapped 759
nr_file_pages 2766
nr_slab 551
nr_page_table_pages 266
nr_dirty 2
nr_writeback 0
nr_unstable 0
nr_bounce 0
numa_hit 444062598
numa_miss 11649541
numa_foreign 0
numa_interleave 0
numa_local 443992176
numa_other 11719963
protection: (0, 0, 9090, 9090)
pagesets
cpu: 0 pcp: 0
count: 180
high: 186
batch: 31
cpu: 0 pcp: 1
count: 58
high: 62
batch: 15
vm stats threshold: 60
cpu: 1 pcp: 0
count: 178
high: 186
batch: 31
cpu: 1 pcp: 1
count: 12
high: 62
batch: 15
vm stats threshold: 60
cpu: 2 pcp: 0
count: 184
high: 186
batch: 31
cpu: 2 pcp: 1
count: 51
high: 62
batch: 15
vm stats threshold: 60
cpu: 3 pcp: 0
count: 155
high: 186
batch: 31
cpu: 3 pcp: 1
count: 9
high: 62
batch: 15
vm stats threshold: 60
cpu: 8 pcp: 0
count: 181
high: 186
batch: 31
cpu: 8 pcp: 1
count: 53
high: 62
batch: 15
vm stats threshold: 60
cpu: 9 pcp: 0
count: 157
high: 186
batch: 31
cpu: 9 pcp: 1
count: 14
high: 62
batch: 15
vm stats threshold: 60
cpu: 10 pcp: 0
count: 162
high: 186
batch: 31
cpu: 10 pcp: 1
count: 1
high: 62
batch: 15
vm stats threshold: 60
cpu: 11 pcp: 0
count: 158
high: 186
batch: 31
cpu: 11 pcp: 1
count: 23
high: 62
batch: 15
vm stats threshold: 60
all_unreclaimable: 0
prev_priority: 12
start_pfn: 4096

6. 每个区的伙伴页面信息:/proc/buddyinfo

$ cat /proc/buddyinfo
Node 0, zone DMA 3 3 3 2 3 2 2 1 1 0 2
Node 0, zone DMA32 12238 4658 2889 2209 1697 1331 1008 750 467 238 143
Node 0, zone Normal 266 201 169 354 64 51 172 118 87 47 306
Node 1, zone Normal 102 288 500 394 255 1877 5161 3788 2053 826 372

有了这些信息,我们可以详细知道内存资源是如何使用的以及目前剩余情况,为下一步的优化做些决策上的依据。信息的解读可以google相关的关键词,但是充分理解这些信息点,要对内核的VM相关部分很熟悉。

PS. 以上我粗粗的列出几点,其他的同学们可以进一步补充,多谢!

祝玩得开心!

Post Footer automatically generated bywp-posturl pluginfor wordpress.

]]>
0
<![CDATA[湾区买房记(3)]]> http://www.udpwork.com/item/6861.html http://www.udpwork.com/item/6861.html#reviews Sun, 19 Feb 2012 07:18:15 +0800 Xin LI http://www.udpwork.com/item/6861.html 前面几乎是流水帐,这里说说一些对其他人更有参考意义的话题。

第一个话题是,如何确定应该出价多少。

这个问题其实非常非常的复杂,出价之前一定要先做足功课。我的建议是在 Zillow 上研究附近房子最近的成交价,并仔细做实地考察。Zillow 会给出一个它的估价,但这个估价只能作为参考,并不一定准确。

另外一个估价指标是房子的在市时间(Days on market)。资本主义市场经济的效率是很高的,价格合适的房子,除非是经济危机的时候,否则不太可能出现长时间卖不出去的情况。假如一个房子在市场上呆了30天还没有任何人提出购买意向,通常都是因为要价太高。遇到这种情况,假如房子本身没有特别不可接受的问题,就可以考虑狠狠砍价。

还有一个非常重要的指标是房屋是否有未经许可的改建。在加州,屋主如果对房屋进行了加盖、内部增加或拆除墙壁,改装车库等,而没有事先取得许可,则在交易时这些部分是不被认可的。买家可以要求卖家还原、取得许可,或砍价。不过,对于不太正常的交易,例如购买短售(short sale)的房子,卖家可能选择什么都不做。

由于之前银行不负责任地随意放贷,许多屋主的房子由于周围房子遭到短售或法拍(Foreclose),导致其市场价值跌到了贷款金额以下。一些屋主此时会选择不再继续缴放贷,导致自己的房子被短售或法拍。作为一个拥有相当健全法律体系的国家,美国对于这种行为是有约束的,尽管银行可能同意以短售所得金额将欠款一笔勾销,但欠款总额与短售所得的差额,有可能会作为债务人当年的个人收入而课税,假如差价达到30万的话,相当于一下子在当年多出了30万的收入,尽管债务人可能一分钱都没有拿到,但却还是要缴纳联邦税和州税。

一旦短售或法拍交易成功,这些价格就会反映到(拉低)周边的房子的价格上,因为估价时是按周围房子做的参考。在出价时,需要考虑这些交易可能产生的影响,最好是在 Redfin 或其他网站上检查一下附近的短售情况,或请自己的房产经纪提供数据做一些判断。有时,房产经纪能够提供更为全面的数据来帮助进行决策。

一般来说,出价不应低于要价的95%----尽管我个人并不非常认可这种说法,但有些卖家经纪可能根本不会去回应这样的购买意向合同。如果你认为一个房子要价太高,但又觉得房子本身不错,个人认为比较好的做法是口头询问对方的经纪,或者去Open House看一看是否已经有人出价,等等。假如没什么人出价,则应坚持自己的价位,或者等一段时间再说。持有房产的人需要缴纳房产税,所以长时间持有不住的房子对他们来说是现金上的损失,并不是所有的投资者都能够扛过这一关。

在有些地区,出价必须比要价高。这种现象往往会出现在学区非常好的地区,例如 Cupertino。在卖方经纪看来,高出价和直接现金支付相比,他们很可能更倾向于选择后者。需要注意的是,今天需求旺盛并不必然带来未来需求的旺盛,在高价位买房可能能赚到一大笔,但也可能会缺少成长空间。

在决定出价之前,还需要对房屋状况做大体的检查。一般来说自己可以做的检查包括:

  1. 厨房、卫生间的洗手池下面的木头是否有水蚀痕迹。这类水蚀痕迹很可能表示之前发生过漏水,这类漏水如果处理不当,有可能会给白蚁留下机会,渗水甚至可能导致地基出现问题,因此不能掉以轻心。
  2. 屋顶是否有明显的老化现象,如果有梯子,可以爬到高处看一看(不过,如果不是专业人士请不要轻易走上房顶,以免发生危险)。
  3. 墙壁是否有裂缝。
  4. 从屋内看天花板是否有水蚀痕迹。
  5. 是否有明显的改装痕迹,如果有,应确认这些改装是有许可 (permit) 的。

多看Open House,多做笔记,有助于了解市场行情。经过一段时间的了解,往往就可以在进入一个房子之后给出大致准确的估值了。

第二个话题是关于short sale、"银行屋"或REO。

short sale是一种特殊的交易。这个过程中真正卖房子的是银行:房主可能会和你讨论价格,但实际上是否接受是银行的事情。有些不肖房产经纪会配合房主以极低的价格将房子放到市场上,例如把一个本应卖70万的房子故意标成50万,来吸引那些只能支付50万的贷款人来购买。这种情况银行多半会拒绝这个价格,并按实际银行希望的价格出价,而这会导致交易失败。通过一些不正当的手法,房主可以继续占用房子,有时可达数年之久。

在下定决心买短售屋之前,一定要做好房屋的估价,并调查清楚屋主欠银行多少钱。一般来说,房产经纪可以从公开的资料中得到答案。

short sale的另一个问题是交易时间会比较长,因为涉及银行审批等许多步骤。银行通常希望看到现金非常充裕的买家,当然全部现金支付是最好的,这可以大大缩短交易审批所需的时间。假如遇到试图欺诈的房主或房产经纪,应考虑以法律手段来维护自己的合法权益,但这需要大量的时间精力,而且从对方角度去思考,假如对方真的是那种还不起自己房子贷款的屋主而不是因为经营不善导致资不抵债的炒房团,将他们最终轰出房子,也就造就了新的无家可归者,个人认为这种做法还是要慎重考虑再做决定的。

最后的问题是,short sale时,屋主很可能对房子进行一些故意的破坏。在购买时,应有相关的心理和财务方面的准备。

在房子做short sale失败之后,便会进入法拍流程。银行收走的房子俗称银行屋,被投资人买走的房子通常叫REO,这类房子通常内部会有一些破坏(因为失去房子的屋主可能会做些破坏),通常银行或投资人并不会做任何修缮,就以"按现状"(as-is)的方式卖出。

这类房子最大的优点在于产权清晰,不会有诉讼、欠债等问题威胁房子的产权归属。此外,它们的价格通常会比较便宜,缺点是这类房子通常也会是按现状方式出售的,也就是说维修费用可能会略高。

个人认为如果现金充裕,只要房子本身主体结构没有太大的问题,银行屋是比较不错的选择。由于屋内的状况往往比较糟糕,因此许多人不愿意购买导致价格相对比较便宜;另一方面,假如原先的内饰已经被毁的差不多了,重新做新的装修也会比较容易,不会有需要与原有装修风格相匹配的问题。

第三个话题是关于房产经纪。

作为买房的房产经纪,对于首次买房来说可能会比较重要,特别是他们可以去实地考察,以及起草合同文本。不过,由于现时的科技已十分发达,Redfin等网站能够完成房产经纪能告诉你的绝大多数事情(除了被对方经纪藏起来的记录之外),这包括房屋的历史价格、周边房子的价格、房屋的现状,包括目前的房主每年的缴税情况、学区,包括具体会分到哪个学校,等等。

如果熟悉业务,可以直接找一个有房产经纪执照的朋友来做自己的买方经纪,只帮助完成合同签署,并自己完成其他部分的事项。由于房产经纪执照并不太贵,事实上如果精力足够,也可以自己拿一个执照,从而省掉3%的佣金。

个人认为一般情况下最好是避免让卖方经纪同时做自己的买方经纪,不过在现实的交易中,这种情况也并不少见。

第四个话题是关于贷款。

通常华人特别是第一代移民的信用记录都会很好。贷款利率直接取决于信用分数,一般来说730分以上可以很容易地取得贷款,并获得较低的利率。

名义上的利率是按年给出的,然而,由于每月会计算一次利息,因此实际上这个利率会低于实际支付的利率。例如,如果贷款利率是 4.125%,则实际每年支付的利率是略多于 4.20%。

一般来说贷款的银行会允许以预付利息(术语叫做points)的方式来降低后期支付的利息。例如,如果一个人以正常的 no-point, no-fee 贷款可以拿到 3.75% 的利率,那么多付一定的 points 可以将利率再降低 0.125% 甚至 0.25%。与此相反,如果愿意接受较高的利率,则银行也可能会以返点的形式向贷款人支付一定的现金。

如何选择是否支付 points 或是要返点呢?这个要看个人的情况。我个人认为向银行要返点是很不合算的,一来这种返点有上限限制,二来它导致利率上升,可能会在最开始几年就打平得到的返点金额了。是不是要支付 points 呢?这个要看打算持有房子多久,假如只打算住几年就卖掉,支付 points 可能就不合算(因为提前还清贷款时 points 是完全不退的)。

如果首付金额低于 20% 的房款,则银行会要求贷款人支付贷款保险 (MIP)。通常,这个保险的金额是分段的,例如首付在 15% 到 20% 时是一个价格,而在 10% - 15% 时是另一个价格,而不是根据首付的比例计算。

贷款保险是完全"扔掉"的一笔钱。因此,它的支付又有几种不同的方式,有提前一次性付清,也可以先付一部分,之后逐月支付,或是最开始完全不付,而到还款时每月支付一部分。一次性付清比较适合完全按照还款计划来还款的人(也就是不多付贷款),因为它的成本最便宜;后两种方案,应根据自己的具体情况来计算,法律规定,如果贷款人支付的本金超过了最初房款的 22%,银行便必须停止征收贷款保险了。

贷款方案也有很多种不同的种类,例如 30年或15年固定利率(在30年或15年期限内还清,每月还固定金额),以及5年、7年或10年固定利率的可变利率 ARM 贷款。ARM的初始利率较低,但风险是未来利率可以按市场利率浮动(有条件限制,每年最多不能超过一个百分点数),适合于计划在一开始大量多付贷款,或预期几年之后利率会下降/收入会大幅增加,或准备在几年后提前付清贷款(例如将房子转手卖掉)的借款人。固定利率贷款通常利率会高一些,但不会有需要突然增加月付金额的情况,适合准备在房子中住较长时间(例如超过7年),或比较保守的借款人。

美国的企业通常是每两周或半个月发一次工资。如果能保持良好的财务规划,可以通过每两周存一次房款,并在年初或年底多付一个月房款的方法来减少房贷的长度(相当于一年付13个月的月付金额)。另一种方法是每个月多还 1/12 的月付金额,这部分金额会立即偿付本金,使得贷款的长度缩短。涉及 MIP 时,还款方案的涉及会更为复杂,需要用电子表格或写个简单的程序来计算一下。

第四个话题是到底应该租房还是买房。

纯粹从经济角度考虑,如果20年的房租超过房价,并且未来不会搬到很远的地方,就应该买房。

另一方面,加州政府对于低收入家庭提供的租房补贴,实际上对硅谷绝大多数工程师来说都是不适用的。在税收方面,地产税是交给州政府和郡政府的直接用于本地造桥修路和改善教育、公共服务的地方税,并且可以抵消一部分收入,从而减少其他方面的税负。具体来说,地产税和利息通常是可以抵扣调整后收入的,根据自己的税筐,可以比较容易地算出具体的金额(注意如果准备在税表中申请此类抵扣,则 standard deduction 就不再适用了)。

从负面影响来看,房子没办法跟着人搬走,而且需要做修缮,因此房子本身是贬值资产,并且如果发生地震、洪水等自然灾害,房屋的维修可能会是一大笔花费,并且房贷本身在较长时间之内会是比较显著的经济压力。当然,如果选择的地段合适且维护得当,房子的价值还是会逐渐增长,另外,作为多样化投资的一部分,它也可以作为对冲通胀的一个可选的途径。

]]>
前面几乎是流水帐,这里说说一些对其他人更有参考意义的话题。

第一个话题是,如何确定应该出价多少。

这个问题其实非常非常的复杂,出价之前一定要先做足功课。我的建议是在 Zillow 上研究附近房子最近的成交价,并仔细做实地考察。Zillow 会给出一个它的估价,但这个估价只能作为参考,并不一定准确。

另外一个估价指标是房子的在市时间(Days on market)。资本主义市场经济的效率是很高的,价格合适的房子,除非是经济危机的时候,否则不太可能出现长时间卖不出去的情况。假如一个房子在市场上呆了30天还没有任何人提出购买意向,通常都是因为要价太高。遇到这种情况,假如房子本身没有特别不可接受的问题,就可以考虑狠狠砍价。

还有一个非常重要的指标是房屋是否有未经许可的改建。在加州,屋主如果对房屋进行了加盖、内部增加或拆除墙壁,改装车库等,而没有事先取得许可,则在交易时这些部分是不被认可的。买家可以要求卖家还原、取得许可,或砍价。不过,对于不太正常的交易,例如购买短售(short sale)的房子,卖家可能选择什么都不做。

由于之前银行不负责任地随意放贷,许多屋主的房子由于周围房子遭到短售或法拍(Foreclose),导致其市场价值跌到了贷款金额以下。一些屋主此时会选择不再继续缴放贷,导致自己的房子被短售或法拍。作为一个拥有相当健全法律体系的国家,美国对于这种行为是有约束的,尽管银行可能同意以短售所得金额将欠款一笔勾销,但欠款总额与短售所得的差额,有可能会作为债务人当年的个人收入而课税,假如差价达到30万的话,相当于一下子在当年多出了30万的收入,尽管债务人可能一分钱都没有拿到,但却还是要缴纳联邦税和州税。

一旦短售或法拍交易成功,这些价格就会反映到(拉低)周边的房子的价格上,因为估价时是按周围房子做的参考。在出价时,需要考虑这些交易可能产生的影响,最好是在 Redfin 或其他网站上检查一下附近的短售情况,或请自己的房产经纪提供数据做一些判断。有时,房产经纪能够提供更为全面的数据来帮助进行决策。

一般来说,出价不应低于要价的95%----尽管我个人并不非常认可这种说法,但有些卖家经纪可能根本不会去回应这样的购买意向合同。如果你认为一个房子要价太高,但又觉得房子本身不错,个人认为比较好的做法是口头询问对方的经纪,或者去Open House看一看是否已经有人出价,等等。假如没什么人出价,则应坚持自己的价位,或者等一段时间再说。持有房产的人需要缴纳房产税,所以长时间持有不住的房子对他们来说是现金上的损失,并不是所有的投资者都能够扛过这一关。

在有些地区,出价必须比要价高。这种现象往往会出现在学区非常好的地区,例如 Cupertino。在卖方经纪看来,高出价和直接现金支付相比,他们很可能更倾向于选择后者。需要注意的是,今天需求旺盛并不必然带来未来需求的旺盛,在高价位买房可能能赚到一大笔,但也可能会缺少成长空间。

在决定出价之前,还需要对房屋状况做大体的检查。一般来说自己可以做的检查包括:

  1. 厨房、卫生间的洗手池下面的木头是否有水蚀痕迹。这类水蚀痕迹很可能表示之前发生过漏水,这类漏水如果处理不当,有可能会给白蚁留下机会,渗水甚至可能导致地基出现问题,因此不能掉以轻心。
  2. 屋顶是否有明显的老化现象,如果有梯子,可以爬到高处看一看(不过,如果不是专业人士请不要轻易走上房顶,以免发生危险)。
  3. 墙壁是否有裂缝。
  4. 从屋内看天花板是否有水蚀痕迹。
  5. 是否有明显的改装痕迹,如果有,应确认这些改装是有许可 (permit) 的。

多看Open House,多做笔记,有助于了解市场行情。经过一段时间的了解,往往就可以在进入一个房子之后给出大致准确的估值了。

第二个话题是关于short sale、"银行屋"或REO。

short sale是一种特殊的交易。这个过程中真正卖房子的是银行:房主可能会和你讨论价格,但实际上是否接受是银行的事情。有些不肖房产经纪会配合房主以极低的价格将房子放到市场上,例如把一个本应卖70万的房子故意标成50万,来吸引那些只能支付50万的贷款人来购买。这种情况银行多半会拒绝这个价格,并按实际银行希望的价格出价,而这会导致交易失败。通过一些不正当的手法,房主可以继续占用房子,有时可达数年之久。

在下定决心买短售屋之前,一定要做好房屋的估价,并调查清楚屋主欠银行多少钱。一般来说,房产经纪可以从公开的资料中得到答案。

short sale的另一个问题是交易时间会比较长,因为涉及银行审批等许多步骤。银行通常希望看到现金非常充裕的买家,当然全部现金支付是最好的,这可以大大缩短交易审批所需的时间。假如遇到试图欺诈的房主或房产经纪,应考虑以法律手段来维护自己的合法权益,但这需要大量的时间精力,而且从对方角度去思考,假如对方真的是那种还不起自己房子贷款的屋主而不是因为经营不善导致资不抵债的炒房团,将他们最终轰出房子,也就造就了新的无家可归者,个人认为这种做法还是要慎重考虑再做决定的。

最后的问题是,short sale时,屋主很可能对房子进行一些故意的破坏。在购买时,应有相关的心理和财务方面的准备。

在房子做short sale失败之后,便会进入法拍流程。银行收走的房子俗称银行屋,被投资人买走的房子通常叫REO,这类房子通常内部会有一些破坏(因为失去房子的屋主可能会做些破坏),通常银行或投资人并不会做任何修缮,就以"按现状"(as-is)的方式卖出。

这类房子最大的优点在于产权清晰,不会有诉讼、欠债等问题威胁房子的产权归属。此外,它们的价格通常会比较便宜,缺点是这类房子通常也会是按现状方式出售的,也就是说维修费用可能会略高。

个人认为如果现金充裕,只要房子本身主体结构没有太大的问题,银行屋是比较不错的选择。由于屋内的状况往往比较糟糕,因此许多人不愿意购买导致价格相对比较便宜;另一方面,假如原先的内饰已经被毁的差不多了,重新做新的装修也会比较容易,不会有需要与原有装修风格相匹配的问题。

第三个话题是关于房产经纪。

作为买房的房产经纪,对于首次买房来说可能会比较重要,特别是他们可以去实地考察,以及起草合同文本。不过,由于现时的科技已十分发达,Redfin等网站能够完成房产经纪能告诉你的绝大多数事情(除了被对方经纪藏起来的记录之外),这包括房屋的历史价格、周边房子的价格、房屋的现状,包括目前的房主每年的缴税情况、学区,包括具体会分到哪个学校,等等。

如果熟悉业务,可以直接找一个有房产经纪执照的朋友来做自己的买方经纪,只帮助完成合同签署,并自己完成其他部分的事项。由于房产经纪执照并不太贵,事实上如果精力足够,也可以自己拿一个执照,从而省掉3%的佣金。

个人认为一般情况下最好是避免让卖方经纪同时做自己的买方经纪,不过在现实的交易中,这种情况也并不少见。

第四个话题是关于贷款。

通常华人特别是第一代移民的信用记录都会很好。贷款利率直接取决于信用分数,一般来说730分以上可以很容易地取得贷款,并获得较低的利率。

名义上的利率是按年给出的,然而,由于每月会计算一次利息,因此实际上这个利率会低于实际支付的利率。例如,如果贷款利率是 4.125%,则实际每年支付的利率是略多于 4.20%。

一般来说贷款的银行会允许以预付利息(术语叫做points)的方式来降低后期支付的利息。例如,如果一个人以正常的 no-point, no-fee 贷款可以拿到 3.75% 的利率,那么多付一定的 points 可以将利率再降低 0.125% 甚至 0.25%。与此相反,如果愿意接受较高的利率,则银行也可能会以返点的形式向贷款人支付一定的现金。

如何选择是否支付 points 或是要返点呢?这个要看个人的情况。我个人认为向银行要返点是很不合算的,一来这种返点有上限限制,二来它导致利率上升,可能会在最开始几年就打平得到的返点金额了。是不是要支付 points 呢?这个要看打算持有房子多久,假如只打算住几年就卖掉,支付 points 可能就不合算(因为提前还清贷款时 points 是完全不退的)。

如果首付金额低于 20% 的房款,则银行会要求贷款人支付贷款保险 (MIP)。通常,这个保险的金额是分段的,例如首付在 15% 到 20% 时是一个价格,而在 10% - 15% 时是另一个价格,而不是根据首付的比例计算。

贷款保险是完全"扔掉"的一笔钱。因此,它的支付又有几种不同的方式,有提前一次性付清,也可以先付一部分,之后逐月支付,或是最开始完全不付,而到还款时每月支付一部分。一次性付清比较适合完全按照还款计划来还款的人(也就是不多付贷款),因为它的成本最便宜;后两种方案,应根据自己的具体情况来计算,法律规定,如果贷款人支付的本金超过了最初房款的 22%,银行便必须停止征收贷款保险了。

贷款方案也有很多种不同的种类,例如 30年或15年固定利率(在30年或15年期限内还清,每月还固定金额),以及5年、7年或10年固定利率的可变利率 ARM 贷款。ARM的初始利率较低,但风险是未来利率可以按市场利率浮动(有条件限制,每年最多不能超过一个百分点数),适合于计划在一开始大量多付贷款,或预期几年之后利率会下降/收入会大幅增加,或准备在几年后提前付清贷款(例如将房子转手卖掉)的借款人。固定利率贷款通常利率会高一些,但不会有需要突然增加月付金额的情况,适合准备在房子中住较长时间(例如超过7年),或比较保守的借款人。

美国的企业通常是每两周或半个月发一次工资。如果能保持良好的财务规划,可以通过每两周存一次房款,并在年初或年底多付一个月房款的方法来减少房贷的长度(相当于一年付13个月的月付金额)。另一种方法是每个月多还 1/12 的月付金额,这部分金额会立即偿付本金,使得贷款的长度缩短。涉及 MIP 时,还款方案的涉及会更为复杂,需要用电子表格或写个简单的程序来计算一下。

第四个话题是到底应该租房还是买房。

纯粹从经济角度考虑,如果20年的房租超过房价,并且未来不会搬到很远的地方,就应该买房。

另一方面,加州政府对于低收入家庭提供的租房补贴,实际上对硅谷绝大多数工程师来说都是不适用的。在税收方面,地产税是交给州政府和郡政府的直接用于本地造桥修路和改善教育、公共服务的地方税,并且可以抵消一部分收入,从而减少其他方面的税负。具体来说,地产税和利息通常是可以抵扣调整后收入的,根据自己的税筐,可以比较容易地算出具体的金额(注意如果准备在税表中申请此类抵扣,则 standard deduction 就不再适用了)。

从负面影响来看,房子没办法跟着人搬走,而且需要做修缮,因此房子本身是贬值资产,并且如果发生地震、洪水等自然灾害,房屋的维修可能会是一大笔花费,并且房贷本身在较长时间之内会是比较显著的经济压力。当然,如果选择的地段合适且维护得当,房子的价值还是会逐渐增长,另外,作为多样化投资的一部分,它也可以作为对冲通胀的一个可选的途径。

]]>
0