Apr 24

知识的本质 不指定

felix021 @ 2012-4-24 01:10 [随想] 评论(2) , 引用(0) , 阅读(8160) | Via 本站原创
转载自 武城路下段 By 宋大妈 http://dharmasong.net/2012/02/642.html

知识的本质

抓住知识的本质是提升学习效率的重要方法。

从数量上说,现代社会的“知识”有两个特点,第一是“总量大”,第二是“增长快”,这两个特点合在一起就是过去常说的“知识爆炸”。但知识还有另外一个特点——相比表层知识的庞大数量和几何式增长,知识的核心部分的发展要平缓得多。

以计算机领域为例,虽然计算机是二战以后发展最快的领域,但著名的黑客Paul Graham却说今天最先进的计算机技术在思想上和20世纪50年代并没有什么不同;在经济学领域,无论涉足到那一个分支,都无法离开亚当·斯密这个根本;在管理学领域,尽管各种工具、方法层出不穷,但像价值链分析这样的方法仍然根源性的;而从更大的范围上讲,思考问题的方式、解决问题的方法同样是相对稳定不会过时的;甚至知识的发展也是有规律可循,并且这些规律同样是相对稳定的。

这些知识中相对“不变”的部分恰恰是知识中最关键的部分,一个人知道很多表层的知识,我们只会说他懂点“皮毛”,只有他掌握了“不变”的知识,我们才会认为他有“学识”。而另一方面,由于知识的总量太大了,如果没有这些“不变”的知识,学习和创造就会成为不可能的事,我们常说“触类旁通”,其基础就是“不变”的知识。

上面说的道理并不复杂,但执行起来却并不容易。在现实生活中,我们见到的懂点皮毛的人要远远多过功底深厚的人,究其原因,我觉得有以下几个:

首先,相比表层知识的具体,知识的本质部分总体上是相对抽象的。理解知识的本质,其难度比认识表层要高得多,陡峭的学习曲线经常会让人望而怯步;

第二,相比表层知识的“有用”,知识的本质部分往往很难立刻发现其实际用途。这并非功利不功利的问题,而是眼光的问题,追逐长远利益的人和只看得见眼前利益的人对“有用”的认识往往是大相径庭的。但眼光长远的人之所以受到普遍的尊敬,一个重要的原因是他们是社会的少数。

第三,知识的本质经常会落实在一些常用、普通也因此容易被忽视的概念上,因为这些概念太常见了,我们经常以为自己懂这些概念,但实际上却是似是而非的。比如经济学上最基本的成本、价格这些概念,现在随便看个报纸、听听新闻都能遇到很多次,但又有多少人去深究成本与价格的概念所指?当我弄清价格其实是成本的一种特例——市场揭示出来的成本时,我是很吃惊的,既惊讶于这些概念内涵的深刻,也惊讶于我自己学习的疏忽。

写到这里,似乎应该总结几个方法去提升学习知识本质的能力,但想来想去并没有任何可以讨巧的方法,事实上,讨巧本身就是惰性的一种,而学习、创造的过程也就是克服惰性的过程,学习之苦也正是学习之甜。

(原文完)

== Felix简评 ==

觉得好像很久没有看到这样的文章了,句句切中要点。

在计算机学科,现在新技术太多了,硬件、软件、网络、架构设计……满满当当,任何一部分在任何一个层面上都学不完。甚至有很多新的技术你还没能完全掌握,就已经被淘汰了。怎么办!

好办!因为你根本不需要学习那么多的东西,只要在掌握了底层知识的基础上,适当开阔眼界,那么就根本不必烦恼知识爆炸带来的问题。

然而底层知识的学习往往是枯燥、晦涩,并且“很难立刻发现其实际用途”。例如,计算机专业开设的操作系统原理这门课,课本通常都非常理论化,内存分配策略,进程调度,生产者消费者,竞争,同步,互斥……学生们往往在学的时候不知所云,学完以后不知何用。然而在实际的项目开发中,如果开发者对于这些知识缺乏了解,那么灾难就在所难免了。

反映到实际就业来说,大部分刚毕业的计算机专业学生代码都没多少项目经验,而社会上的培训机构如北大青鸟,能够让一个人在数月内学会某个开发技术。然而那些业内顶级的公司(例如NTMGB等公司),往往并不愿意招那些培训过的,而是选择校园招聘,其中很重要的原因就是,科班出身的毕业生学习过基础知识,尽管不能马上干活,但是经过短时间的培训就能比那些培训人员做的更好。

(简评烂尾)

注:NTMGB是指网易、腾讯、微软、谷歌、百度。
Apr 20
很多应用层协议都有HeartBeat机制,通常是客户端每隔一小段时间向服务器发送一个数据包,通知服务器自己仍然在线,并传输一些可能必要的数据。使用心跳包的典型协议是IM,比如QQ/MSN/飞信等协议。

学过TCP/IP的同学应该都知道,传输层的两个主要协议是UDP和TCP,其中UDP是无连接的、面向packet的,而TCP协议是有连接、面向流的协议。

所以非常容易理解,使用UDP协议的客户端(例如早期的“OICQ”,听说OICQ.com这两天被抢注了来着,好古老的回忆)需要定时向服务器发送心跳包,告诉服务器自己在线。

然而,MSN和现在的QQ往往使用的是TCP连接了,尽管TCP/IP底层提供了可选的KeepAlive(ACK-ACK包)机制,但是它们也还是实现了更高层的心跳包。似乎既浪费流量又浪费CPU,有点莫名其妙。

具体查了下,TCP的KeepAlive机制是这样的,首先它貌似默认是不打开的,要用setsockopt将SOL_SOCKET.SO_KEEPALIVE设置为1才是打开,并且可以设置三个参数tcp_keepalive_time/tcp_keepalive_probes/tcp_keepalive_intvl,分别表示连接闲置多久开始发keepalive的ack包、发几个ack包不回复才当对方死了、两个ack包之间间隔多长,在我测试的Ubuntu Server 10.04下面默认值是7200秒(2个小时,要不要这么蛋疼啊!)、9次、75秒。于是连接就了有一个超时时间窗口,如果连接之间没有通信,这个时间窗口会逐渐减小,当它减小到零的时候,TCP协议会向对方发一个带有ACK标志的空数据包(KeepAlive探针),对方在收到ACK包以后,如果连接一切正常,应该回复一个ACK;如果连接出现错误了(例如对方重启了,连接状态丢失),则应当回复一个RST;如果对方没有回复,服务器每隔intvl的时间再发ACK,如果连续probes个包都被无视了,说明连接被断开了。

这里有一篇非常详细的介绍文章: http://tldp.org/HOWTO/html_single/TCP-Keepalive-HOWTO ,包括了KeepAlive的介绍、相关内核参数、C编程接口、如何为现有应用(可以或者不可以修改源码的)启用KeepAlive机制,很值得详读。

这篇文章的2.4节说的是“Preventing disconnection due to network inactivity”,阻止因网络连接不活跃(长时间没有数据包)而导致的连接中断,说的是,很多网络设备,尤其是NAT路由器,由于其硬件的限制(例如内存、CPU处理能力),无法保持其上的所有连接,因此在必要的时候,会在连接池中选择一些不活跃的连接踢掉。典型做法是LRU,把最久没有数据的连接给T掉。通过使用TCP的KeepAlive机制(修改那个time参数),可以让连接每隔一小段时间就产生一些ack包,以降低被T掉的风险,当然,这样的代价是额外的网络和CPU负担。

前面说到,许多IM协议实现了自己的心跳机制,而不是直接依赖于底层的机制,不知道真正的原因是什么。

就我看来,一些简单的协议,直接使用底层机制就可以了,对上层完全透明,降低了开发难度,不用管理连接对应的状态。而那些自己实现心跳机制的协议,应该是期望通过发送心跳包的同时来传输一些数据,这样服务端可以获知更多的状态。例如某些客户端很喜欢收集用户的信息……反正是要发个包,不如再塞点数据,否则包头又浪费了……

大概就是这样吧,如果有大牛知道真正的原因,还望不吝赐教。


@2012-04-21

p.s. 通过咨询某个做过IM的同事,参考答案应该是,自己实现的心跳机制通用,可以无视底层的UDP或TCP协议。如果只是用TCP协议的话,那么直接使用KeepAlive机制就足够了。

@2015-09-14
补充一下 @Jack的回复:
“心跳除了说明应用程序还活着(进程还在,网络通畅),更重要的是表明应用程序还能正常工作。而 TCP keepalive 有操作系统负责探查,即便进程死锁,或阻塞,操作系统也会如常收发 TCP keepalive 消息。对方无法得知这一异常。摘自《Linux 多线程服务端编程》”
Apr 18

纯吐槽 - 奇葩邮箱163 不指定

felix021 @ 2012-4-18 19:18 [IT » 其他] 评论(4) , 引用(0) , 阅读(8946) | Via 本站原创
163邮箱之所以没落,不是因为腾讯太能抄,实在是因为产品经理太不行啊。

系统中只有“收件箱”里有“举报垃圾邮件”按钮,通过点击这个按钮,可以选择将发件人加入黑名单(拒收);

而收到的广告和垃圾邮件会被自动分类到对应的文件夹,没有举报按钮。

也就是说,我想要把发件人加入黑名单,只有两种方式:

1. 拷贝发件人地址,进入设置->黑名单,添加

2. 选择“这不是垃圾邮件”,回到收件箱,选择该邮件,点击“举报垃圾邮件”。

建议选择第二种方式,更快,更蛋疼。

p.s. 对于有强迫症的我来说,还需要再进入垃圾邮箱,全选、彻底删除。
Apr 11

吐槽简历 不指定

felix021 @ 2012-4-11 15:46 [随想] 评论(0) , 引用(0) , 阅读(5358) | Via 本站原创
  总赖在学校不走的悲剧就是,别人都毕业工作好久了,你还在蛋疼地写毕设。更蛋疼的是,若是早先毕业的学长们亲切地问起“还在武汉吗?最近做什么呢?”,下一句一般都是,我们来招人啦,帮忙收些简历。

  其实代收简历这事儿,早两年就在做了,也算是见过各种形形色色的简历,大牛小牛普通人,当然也不乏二逼青年们。总的来说,大部分人,对简历的概念仅仅局限于下载一份模板并填满这样的程度,实在是让我忍不住不吐槽。当然,吐槽仅限于IT行业相关的简历,隔行如隔山。

  首先,网上那些简历模板弱爆了啊亲!又不是让你办入职手续,那些生日民族籍贯户口身高体重地址政治面貌教育背景有个鸟用啊,全TM是废话,而且占用了最重要的位置!更奇葩的是,美女帅哥贴照片增加印象分也就算了,长得很抽象的兄弟姐妹们啊,你们也贴?尤其是那种在宿舍阳台拍了后面还吊一条裤子的,你是来给HR们无聊的时光增添乐趣的吗?那你怎么不穿个比基尼上镜呢!

  ——建议:如果不是去面试特别正规的国企什么的,别用那些乱七八糟的模板,自己设计一个;不会设计的,则力求简单明了。个人信息包含最基本的姓名、学历(毕业时间)、学校、专业、联系方式外的那些,能省就省了,或者扔到末尾去。除了帅哥美女之外,照片还是别留了。简历一般以1~2页为宜,如果只有一页多一点,就排排版缩到一页;不要搞封面,纯粹浪费生命。

  其次,二逼青年们对信息的重要性完全没概念啊!到底申请什么职位啊?发个简历过来就完事儿了?知道申请的这职位要求是啥不?啥都不知道就撂个简历过来,搞毛线啊。申请技术职位,居然把学生工作、课程(成绩)之类毫无意义的内容放在开篇,还写得TM那么详细!申请C/C++开发职位,丫写那么多Java有毛用!项目经历写了几坨却跟没写一样的,根本看不出来丫在项目里做了什么事情、做了多少事情啊!

  ——建议:写上申请的职位/方向;充分了解职位要求,职位相关的技能、项目经历、获奖放在开篇醒目位置,其余信息放到末尾。项目经历里写清楚自己做了什么事情、多少事情,技术类的项目写清用了什么技术,编码类可以写明自己完成的代码量。申请技术类的职位,学生工作、文娱竞赛、兴趣爱好等信息适当提及就好了。

  再次,各种奇葩。找我收简历的都是在B公司、T公司、M公司的同学/学长,希望内推的也要掂量一下自己啊,什么项目经验都没有、什么都不会的简历也发过来,还说“希望贵公司给一个展现的平台、相信我的努力会把工作做好”……这个实在是无力吐槽了。

  ——建议:在校期间多努力,机会只给有准备的人。

OVER.
Apr 8
翻译自:How To Read C Declarations 英文原文
p.s. 以前还真没注意到这篇文章最后提到的vtable是啥意思……

就算是非常有经验的C程序员,也对那些比简单数组/指针更复杂一些的声明感到头疼。比如说,下面这个是一个指针的数组,还是一个数组的指针?
int *a[10];

下面这货到底是什么?
int (*(*vtable)[])();

当然了,这货是一个指针,指向一个数组,这个数组的每个元素是一个指针,指向一个函数,函数的返回值类型是int  :)

这篇短文希望能够教会你一个非常简单地读懂复杂声明的方法。我99%肯定我在80年代读过这篇,但是不记得具体是在什么地方读到的了。我怀疑是我自己发现这个的(尽管我总会被计算机语言结构和神秘的事物搞得很兴奋)。然而我的确记得,能够写出一个程序,将任何声明转换成英语。

== 黄金法则 ==

这个法则是这样说的:
引用
从标识符开始(或者最内层的结构,如果不存在标识符的话,通常出现于函数指针),首先向右看,直到遇到 ) 括号或者结束,看到什么就说出来;然后向左看,直到遇到 ( 括号或者回到行首,看到什么就说出来。跳出一层括号,重复上述过程:右看看,说出来;左看看,说出来。直到你说出变量的类型或者返回值(针对函数指针),也就表示你把声明都读完了。


最简单的情况是这样的:
int i;

从 i 开始,你向右看,啥都没看到;然后就向左看,看到了int,说出来:i是一个int。

然后看个复杂一点的:
int *a[3];

从 a 开始:向右看,说“是一个包含3个元素的数组”;向左看,说“数组的每个元素是指针”;向右看,啥都没;向左看,说“指针指向int”。综合起来就是: a 是一个包含3个元素的数组,每个元素是一个指针,指向int。

加上一对括号让它看起来更怪异点儿:
int (*a)[3];

像在普通表达式中一样,括号改变了阅读/计算的顺序。从 a 开始:向右看,遇到括号了,往回;向左看,说“是一个指针”,遇到(括号,跳出来;向右看,[3],说“指向一个包含3个元素的数组”;向左看,int,说“数组的每个元素是int”。综合起来:a是一个指针,指向一个包含3个元素的数组,数组的每个元素是一个int。

好,再来看看这个:
extern int *foo();

赞,你说:foo是一个函数,返回一个指针,指向int。

接下来跳一步:就像我们可以定义一个指向int的指针,我们也可以定义一个指向函数的指针。在这种情况下,不需要extern了(因为不是函数的前向引用声明),而是一个变量的定义。这是一个基本的函数指针:
int (*foo)();

从foo开始:向右看,遇到括号,往回;向左看,*,说“是一个指针”,遇到左括号,跳出来;向右看,(),说“指向一个函数”;向左看,int,说“函数返回int”。综合起来:foo是一个指针,指向一个函数,函数返回int。

下面是一个数组,每个元素是一个指针,指向函数,函数返回int:
int (*Object_vtable[])();


你还需要最后一个,诡异的难以置信的声明:
int (*(*vtable)[])();

这是一个指针,指向一个数组,数组的每个元素是个指针,指向一个函数,函数的返回值是int。发现了吗?这货就是上面那个object_vtable的指针,也就是你定义的每一个对象需要的虚函数表(vtable)的指针。

这个指向vtable的指针是一个vtable的地址,例如,&Truck_vtable (就是某个Truck类的实例虚函数表的指针)。

== 总结 ==

接下来的例子总结了所有C++为了实现多态性所建造的虚函数表需要的所有情形(就像最初的C Front - C++转C翻译器)。
int *ptr_to_int;
int *func_returning_ptr_to_int();
int (*ptr_to_func_returning_int)();
int (*array_of_ptr_to_func_returning_int[])();
int (*(*ptr_to_an_array_of_ptr_to_func_returning_int)[])();
分页: 1/1 第一页 1 最后页 [ 显示模式: 摘要 | 列表 ]