DongHao: 01 2010存档
于是按太太的意见,买了点健胃消食片和大山楂丸。
今天中午吃饭。
我: 昨天吃了消食片和山楂丸,胃好多了。
太太: 说明你胃没什么事儿,就是吃太快了,牛肉不易消化。
我: 但是何以肯定是牛肉呢?如果是bug,必须能重现才行,所以,我要再喝一碗牛肉汤。
太太: ....
使用http_load的过程中遇到了一个报错:Cannot assign requested address
网上找到了原因和解决方案 http://gcoder.blogbus.com/logs/41839731.html
但是要注意,这是客户端的问题,是客户端用光了端口号,所以要改客户端机器的配置和代码,不是改服务端的!
我在http_load.c里给socket加了一个SO_REUSEADDR,才能工作正常。为什么http_load不加上这个option呢?因为http_load测的是标准的http服务器,比如apache,这些服务端会关闭socket,而我测的是自己写的httpd,所以....看来细节很多啊。
====== 2010.08.18 ======
今天又遇到这个问题,在代码里加了SO_REUSEADDR也没用,只能采用在sysctl.conf里加:
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_tw_recycle = 1
的办法
pthread_t pthr;
pthread_create(&pthr, NULL, thread_handler, NULL);
...
void* thread_handler(void* arg)
{
/* do something */
pthread_join(pthr, NULL);
}
上面的代码不好使,pthread_join不能放在pthread调用的handler内,虽然不报错,但是thread无法正常回收,如果多次创建thread,内存会越来越大(另一种形式的内存泄露)。
正确的做法是在handler外面pthread_join:
pthread_t pthr;
pthread_create(&pthr, NULL, thread_handler, NULL);
pthread_join(pthr, NULL);
...
void* thread_handler(void* arg)
{
/* do something */
}
如果不用pthread_join,改用pthread_detach呢?那最方便,但要注意:pthread_detach最好是放在handler里面第一句。
void* thread_handler(void* arg)
{
pthread_detach(pthr);
/* do something */
}
如果pthread_create后紧跟pthread_detach,有可能会出错。
小心使用grep里的星号(*)
grep "/home/.*test"
上面这句是找出所有/home目录下,以test结尾的文件
grep "/home/*test"
这句可就不是了,这句grep只匹配以下的文件名:
/hometest
/home/test
/home//test
/home///test
看明白了,星号(*)代表重复其前面的字符任意次(包括零),而“/*“当然就表示重复”/”任意次。
另外注意grep后面的正则表达式务必加上双引号,如果不加,在一些特殊场合会有完全不同的含义。
比如我当前目录下有六个文件:
testa
testb
testc
testd
tt
t
运行:
ls -l|grep t*t
它会把这五个文件都列出来吗?不,结果显示只找到了文件tt
因为上面的shell命令,grep实际上是在找以t开头且以t结尾的文件(这一规则和ls的规则一样)
要想grep把后面的”t*t”当成正则表达式,需要加上双引号:
ls -l|grep "t*t"
这下结果ok了:所有六个文件都可以被找到。
{
....
int errno
};
上面的example结构使用errno做成员变量名,按理说不会和<errno.h>里的errno宏冲突,因为它在struct里,何况我根本没有包含errno.h文件
在大部分机器上,这可以编译成功。但是,在有的环境确实无法编译通过,gcc不认errno前面的“->”指针符号。
看来<errno.h>里的errno宏漏进了文件,但是怎么漏进来的?不详。
这位老兄和我遇到的问题一样: http://gcc.gnu.org/ml/gcc/2001-12/msg00766.html
但那毕竟是老gcc下的问题了,现在应该没有了。
在查到详细原因之前,还是把errno改了为妙,毕竟变量名和系统全局变量(或宏)弄成一样,是有风险的。
“俺们要一款特能装人的飞机,最好是分上下两层,使劲装,装得越多,俺们钱赚得越多哩”
波音的老大说:
“好办!我叫人做个超大的,像货轮那么大的!”
两个老大拍了板,下面的人就忙起来。
乔.萨特从小在西雅图长大,只比1916年建立的波音公司小5岁,他的童年是看着波音的一架架试飞飞机渡过的,每款他都很熟悉,于是干脆二战结束后就进了波音,当工程师。
听到航空公司的客户需求代表说要造大飞机,乔.萨特问:
“你要装200人?300人?还是400人?”
“450人!“
“扶住我......”,乔.萨特喘了口气,重新站稳:“没问题!俺们给你造!”
乔.萨特出任波音747项目的技术总监,为了在几年内造出这架人类史上最大的喷气式客机,整个747项目组都在加班,从9点到23点,从周一到周六,经常半夜上线....不好意思,说蹿了,是半夜交付设计图纸。
波音当时还有“超音速运输机”计划、“第二代737改型“计划,还派出工程师去帮NASA造火箭——总之,747项目的资源很少,顶尖的工程师和管理人员都在搞很前卫的“超音速运输机”项目。
乔. 萨特不仅要去抢资源,还要对付“外来的婆婆”——就是从其它项目调来的、一来就想代替萨特当主管的新管理人员,这些人很讨厌,由于中途做747项目,他们 啥也不清楚,但却想表现自己的“决断力”,于是,一通瞎指挥。此时,乔.萨特表现出了他工程师的斩钉截铁的个性,他直接对总部说:
“要么调他们离开,要么调我离开”
说完开车回家。
要不说波音牛呢,他们在关键时刻选择了“专家”,而不是“专管”。于是,那家伙走人了,乔.萨特复原职。
波音747用了4台普惠公司的JT9D发动机,其中一台的推力就是4万磅(波音737的发动机是一台1.5万磅,大家可以畅想一下),如此巨大而新型的发动机,当然是问题不断。747原型机在测试的时候,经常会有发动机损坏、起火、融化、爆炸等,俨然四个定时炸弹。
乔.萨特每遇问题,必狠踢普惠公司的屁股,直到惠普交付合格的发动机。
但是,普惠的JT9D发动机当时还有“喘振”的问题。所谓“喘振”,就是发动机会突然像患了哮喘一样,一会儿吸不进气,一会儿有大量吸进,有时甚至倒转往前喷气。没有哪个乘客想遭遇这样的发动机吧?
“喘振”问题是航空发动机最难克服的问题,很难重现,重现条件不明,原因也就难以定位,就像软件的内存泄露bug一样,难以对付。
更夸张的是,惠普根本不打算去fix这个bug,他们等着波音来fix。波音也算人才济济,居然有空气动力学的专家找出了原因,告诉了普惠,普惠这才有了今天优良的JT9D发动机。
所以,用别人的组件遇到问题时,别光等,找找原因,如果自己克服了,以后就能鄙视提供组件的人。
波音747于1969年首飞成功,缔造了航空业的传奇。成为美国航空技术的标志。即使到今天也只有美、俄、欧洲能造大型客机。其它的国家还有机会吗?先确立正确的科学观念、培养实用的技术人才再说吧。
今天大家只记得采用成熟技术的波音747飞机,而那个采用太空技术的高级货“超音速运输机”项目,早已没人记得了。
技术这种东西,重要的是好用,好赚钱,而不是为了好听,或听起来酷。
就像当年何等火热的COM技术、ATL、网格计算、SOA等等,现在应该是冷门了吧?那今天的云计算、框计算,是否也会一样呢?让我们拭目以待。
可怜的乖学生,估计平时刻苦读书,不怎么上网,结果大学面试却问网络词汇,他一定觉得很冤:我这么刻苦,却反而不如爱上网玩的“懒学生”吗?!
我多年前的名言早有预见,足见在下已然半仙附体。
记得读研的时候,我乖乖的学操作系统、学编译器、学数据结构,成天对着教材看,东西是学会了,也理解得比较深。
但当我去面试的时候,考官问了:“你懂网络攻防吗?”,我木然,我是乖学生啊,从不敢攻击别人的机器,又怎么知道该如何防别人攻击呢?
我再去面试的时候,考官问了:“你懂广告防恶意点击吗?”,我再次木然,我是乖学生啊,从不敢恶意乱点别人的广告,又怎么知道该如何防别人恶意点击呢?
你熟悉操作系统?毛病,这儿又没人做操作系统。
你喜欢编译器?毛病,这儿又没人做编译器。
你老老实实的读书顶个屁用?
熊猫烧香的作者出狱了,一出来就多家公司聘请,他可是大学都没读过。对比当下连研究生找工作都困难,我只能说:
如今已是坏孩子的天空。
所以,各位还在苦读的同学——知道该怎么做了吧。
做项目中遇到一个问题。两台机器上用socket建立一个TCP连接,双向通信,流量很大,这时,通过在路由器上设置100%的丢包率将网络断开,这时 socket当然是发不了包,也收不了,出现大量的重传,然后,取消路由器上的设置,恢复网络,结果,TCP连接client去往server的流量正常 了,但server去往client却不通,任凭你如何使劲的send,返回值就是0,而且errno为EAGAIN。
我用tcpdump看了一下此时的包数据(tc2是server,tc1是client):
12:08:21.020291 IP tc1.corp.com.42171 > tc2.corp.com.3003: S 4009389430:4009389430(0) win 5840
12:08:21.020571 IP tc2.corp.com.3003 > tc1.corp.com.42171: R 0:0(0) ack 4009389431 win 0
12:08:38.934329 IP tc2.corp.com.3903 > tc1.corp.com.3904: P 2398055392:2398056153(761) ack 2538876742 win 724
12:08:38.934519 IP tc1.corp.com.3904 > tc2.corp.com.3903: . ack 2165 win 13756
12:08:39.958457 IP tc1.corp.com.3904 > tc2.corp.com.3903: P 1:763(762) ack 2165 win 13756
12:08:39.958485 IP tc2.corp.com.3903 > tc1.corp.com.3904: . ack 763 win 1448
12:08:39.958653 IP tc1.corp.com.3904 > tc2.corp.com.3903: P 763:881(118) ack 2165 win 13756
12:08:39.958660 IP tc1.corp.com.3904 > tc2.corp.com.3903: P 881:997(116) ack 2165 win 13756
12:08:39.958719 IP tc2.corp.com.3903 > tc1.corp.com.3904: . ack 997 win 1448
12:08:39.958890 IP tc1.corp.com.3904 > tc2.corp.com.3903: P 997:1114(117) ack 2165 win 13756
12:08:39.958898 IP tc1.corp.com.3904 > tc2.corp.com.3903: P 1114:1232(118) ack 2165 win 13756
12:08:39.958903 IP tc1.corp.com.3904 > tc2.corp.com.3903: P 1232:1349(117) ack 2165 win 13756
12:08:39.958971 IP tc2.corp.com.3903 > tc1.corp.com.3904: . ack 1349 win 1448
12:08:39.959141 IP tc1.corp.com.3904 > tc2.corp.com.3903: P 1349:1466(117) ack 2165 win 13756
12:08:39.959149 IP tc1.corp.com.3904 > tc2.corp.com.3903: P 1466:1583(117) ack 2165 win 13756
12:08:39.959154 IP tc1.corp.com.3904 > tc2.corp.com.3903: P 1583:1700(117) ack 2165 win 13756
12:08:39.959222 IP tc2.corp.com.3903 > tc1.corp.com.3904: . ack 1700 win 1448
tc2不发自己的数据,却只是一味的ACK从tc1传来的数据,等上半个小时,依然如此。它为什么不发呢?
最后发现是因为我们在socket上设了TCP_NODELAY。去掉这个设置,重启程序,断网恢复以后,TCP双向正常工作。同样用tcpdump看:
16:05:38.782427 IP tc2.corp.alimama.com.3903 > tc1.corp.alimama.com.3904: P 0:887(887) ack 1 win 26064
16:05:38.782619 IP tc1.corp.alimama.com.3904 > tc2.corp.alimama.com.3903: . ack 3783 win 25352
16:05:38.782634 IP tc2.corp.alimama.com.3903 > tc1.corp.alimama.com.3904: . 3783:5231(1448) ack 1 win 26064
16:05:38.782637 IP tc2.corp.alimama.com.3903 > tc1.corp.alimama.com.3904: . 5231:6679(1448) ack 1 win 26064
16:05:38.782890 IP tc1.corp.alimama.com.3904 > tc2.corp.alimama.com.3903: . ack 5231 win 25352
16:05:38.782896 IP tc2.corp.alimama.com.3903 > tc1.corp.alimama.com.3904: . 6679:8127(1448) ack 1 win 26064
16:05:38.782898 IP tc2.corp.alimama.com.3903 > tc1.corp.alimama.com.3904: . 8127:9575(1448) ack 1 win 26064
16:05:38.782901 IP tc1.corp.alimama.com.3904 > tc2.corp.alimama.com.3903: . ack 6679 win 25352
16:05:38.782904 IP tc2.corp.alimama.com.3903 > tc1.corp.alimama.com.3904: . 9575:11023(1448) ack 1 win 26064
16:05:38.783183 IP tc1.corp.alimama.com.3904 > tc2.corp.alimama.com.3903: . ack 8127 win 25352
16:05:38.783188 IP tc2.corp.alimama.com.3903 > tc1.corp.alimama.com.3904: . 11023:12471(1448) ack 1 win 26064
16:05:38.783191 IP tc1.corp.alimama.com.3904 > tc2.corp.alimama.com.3903: . ack 9575 win 25352
16:05:38.783193 IP tc2.corp.alimama.com.3903 > tc1.corp.alimama.com.3904: . 12471:13919(1448) ack 1 win 26064
16:05:38.783196 IP tc1.corp.alimama.com.3904 > tc2.corp.alimama.com.3903: . ack 11023 win 25352
16:05:38.783199 IP tc2.corp.alimama.com.3903 > tc1.corp.alimama.com.3904: . 13919:15367(1448) ack 1 win 26064
16:05:38.783201 IP tc2.corp.alimama.com.3903 > tc1.corp.alimama.com.3904: . 15367:16815(1448) ack 1 win 26064
16:05:38.783502 IP tc1.corp.alimama.com.3904 > tc2.corp.alimama.com.3903: . ack 12471 win 25352
16:05:38.783506 IP tc2.corp.alimama.com.3903 > tc1.corp.alimama.com.3904: . 16815:18263(1448) ack 1 win 26064
16:05:38.783509 IP tc1.corp.alimama.com.3904 > tc2.corp.alimama.com.3903: . ack 13919 win 25352
16:05:38.783512 IP tc2.corp.alimama.com.3903 > tc1.corp.alimama.com.3904: . 18263:19711(1448) ack 1 win 26064
16:05:38.783514 IP tc1.corp.alimama.com.3904 > tc2.corp.alimama.com.3903: . ack 15367 win 25352
16:05:38.783517 IP tc2.corp.alimama.com.3903 > tc1.corp.alimama.com.3904: . 19711:21159(1448) ack 1 win 26064
16:05:38.783519 IP tc1.corp.alimama.com.3904 > tc2.corp.alimama.com.3903: . ack 16815 win 25352
tc2这次发自己的数据流了,tc1对其ACK,过了一段时间,tc1也开始发数据,最后双向正常。
为什么带了TCP_NODEALY的socket,在网络好了以后恢复不了正常?
看看recv系统调用的实现(2.6.9内核),一直追溯到tcp_recvmsg函数:
[net/ipv4/tcp.c --> tcp_recvmsg]
813 while (--iovlen >= 0) {
814 int seglen = iov->iov_len;
815 unsigned char __user *from = iov->iov_base;
816
817 iov++;
818
819 while (seglen > 0) {
820 int copy;
821
822 skb = sk->sk_write_queue.prev;
823
824 if (!sk->sk_send_head ||
825 (copy = mss_now - skb->len) <= 0) {
826
827 new_segment:
828 /* Allocate new segment. If the interface is SG,
829 * allocate skb fitting to single page.
830 */
831 if (!sk_stream_memory_free(sk))
832 goto wait_for_sndbuf;
833
834 skb = sk_stream_alloc_pskb(sk, select_size(sk, tp),
835 0, sk->sk_allocation);
836 if (!skb)
837 goto wait_for_memory;
831行判断sndbuf里还有没有空间,如果没有,跳到wait_for_sndbuf
[net/ipv4/tcp.c --> tcp_recvmsg]
958 wait_for_sndbuf:
959 set_bit(SOCK_NOSPACE, &sk->sk_socket->flags);
960 wait_for_memory:
961 if (copied)
962 tcp_push(sk, tp, flags & ~MSG_MORE, mss_now, TCP_NAGLE_PUSH);
963
964 if ((err = sk_stream_wait_memory(sk, &timeo)) != 0)
965 goto do_error;
966
967 mss_now = tcp_current_mss(sk, !(flags&MSG_OOB));
968 }
969 }
970
971 out:
972 if (copied)
973 tcp_push(sk, tp, flags, mss_now, tp->nonagle);
974 TCP_CHECK_TIMER(sk);
975 release_sock(sk);
976 return copied;
977
978 do_fault:
979 if (!skb->len) {
980 if (sk->sk_send_head == skb)
981 sk->sk_send_head = NULL;
982 __skb_unlink(skb, skb->list);
983 sk_stream_free_skb(sk, skb);
984 }
985
986 do_error:
987 if (copied)
988 goto out;
989 out_err:
990 err = sk_stream_error(sk, flags, err);
991 TCP_CHECK_TIMER(sk);
992 release_sock(sk);
993 return err;
sndbuf 不够,于是设个bit位,961行的判断不成立,因为这会儿还啥也没发送,copied为0。继续,执行sk_stream_wait_memory,顾 名思义,它是等snbbuf有可用空间,但是我们的socket是设了NONBLOCK的,所以sk_stream_wait_memory很快返回,并 设返回值为-EAGAIN。所以,又要跳到do_error,987行的判断依然不成立,于是到了out_err,最后带着-EAGAIN离开 tcp_recvmsg函数。
这就是我们不停send,却返回结果为0且errno为EAGAIN的原因。
如果一切正常,socket不停的往外发数据,早晚sndbuf会出现可用空间的。但如果异常呢?比如设了TCP_NODELAY而网络又断了,那就瞬间会发送大量的包,对端却没有ACK。
我们再看看如果正常,tcp_sendmsg会如何:832行的跳转是不会发生了,于是,程序继续往下(略去一部分skb的操作代码)
[net/ipv4/tcp.c --> tcp_sendmsg]
936 if (!copied)
937 TCP_SKB_CB(skb)->flags &= ~TCPCB_FLAG_PSH;
938
939 tp->write_seq += copy;
940 TCP_SKB_CB(skb)->end_seq += copy;
941 skb_shinfo(skb)->tso_segs = 0;
942
943 from += copy;
944 copied += copy;
945 if ((seglen -= copy) == 0 && iovlen == 0)
946 goto out;
如果这一把就把消息全放进了skb,且iovec也轮完了,此时945行的判断就生效了,直接跳转out,执行tcp_push。tcp_push调用__tcp_push_pending_frame:
[net/ipv4/tcp.h --> __tcp_push_pending_frame]
1508 static __inline__ void __tcp_push_pending_frames(struct sock *sk,
1509 struct tcp_opt *tp,
1510 unsigned cur_mss,
1511 int nonagle)
1512 {
1513 struct sk_buff *skb = sk->sk_send_head;
1514
1515 if (skb) {
1516 if (!tcp_skb_is_last(sk, skb))
1517 nonagle = TCP_NAGLE_PUSH;
1518 if (!tcp_snd_test(tp, skb, cur_mss, nonagle) ||
1519 tcp_write_xmit(sk, nonagle))
1520 tcp_check_probe_timer(sk, tp);
1521 }
1522 tcp_cwnd_validate(sk, tp);
1523 }
1518行的这个"||"符号很讲究,只有tcp_snd_test返回1了,tcp_write_xmit才会被执行。所以我们先看tcp_snd_test
[net/ipv4/tcp.h --> tcp_snd_test]
1452 static __inline__ int tcp_snd_test(struct tcp_opt *tp, struct sk_buff *skb,
1453 unsigned cur_mss, int nonagle)
1454 {
1455 int pkts = tcp_skb_pcount(skb);
1456
1457 if (!pkts) {
1458 tcp_set_skb_tso_segs(skb, tp->mss_cache_std);
1459 pkts = tcp_skb_pcount(skb);
1460 }
1461
1462 /* RFC 1122 - section 4.2.3.4
1463 *
1464 * We must queue if
1465 *
1466 * a) The right edge of this frame exceeds the window
1467 * b) There are packets in flight and we have a small segment
1468 * [SWS avoidance and Nagle algorithm]
1469 * (part of SWS is done on packetization)
1470 * Minshall version sounds: there are no _small_
1471 * segments in flight. (tcp_nagle_check)
1472 * c) We have too many packets 'in flight'
1473 *
1474 * Don't use the nagle rule for urgent data (or
1475 * for the final FIN -DaveM).
1476 *
1477 * Also, Nagle rule does not apply to frames, which
1478 * sit in the middle of queue (they have no chances
1479 * to get new data) and if room at tail of skb is
1480 * not enough to save something seriously (<32 for now).
1481 */
1482
1483 /* Don't be strict about the congestion window for the
1484 * final FIN frame. -DaveM
1485 */
1486 return (((nonagle&TCP_NAGLE_PUSH) || tp->urg_mode
1487 || !tcp_nagle_check(tp, skb, cur_mss, nonagle)) &&
1488 (((tcp_packets_in_flight(tp) + (pkts-1)) < tp->snd_cwnd) ||
1489 (TCP_SKB_CB(skb)->flags & TCPCB_FLAG_FIN)) &&
1490 !after(TCP_SKB_CB(skb)->end_seq, tp->snd_una + tp->snd_wnd));
1491 }
这个函数的注释比实现代码还多,return后面复杂的条件判断可以被拆开:其实是三个条件的“and”操作,我们看第二个条件,就是:
(((tcp_packets_in_flight(tp) + (pkts-1)) < tp->snd_cwnd) || (TCP_SKB_CB(skb)->flags & TCPCB_FLAG_FIN))
其中,tcp_packets_in_flight是指正在“飞行中”的packet数,也就是正在网络上的包数,它的计算方法是:
发送过一次的包数 + 重发过的包数 - 队列中存留的包数
而TCPCB_FLAG_FIN是指一端是否发完了数据,这个在我们的项目中不存在,我们的数据没个完。
这下清楚了:
如 果设了NODELAY,则关闭了nagle算法,大量的小数据包被发出去(看看上面第一个tcpdump的数据),在突然断网时,in_flight的包 很多,多得超过了snd_cwnd即发送窗口的大小,于是tcp_snd_test返回0,真正的发送没有发生。不发送存着的数据,snd_buf中的空 间就腾不出来,tcp_sendmsg就一直返回0。恶性循环。
有人要问了,既然snd_buf没空间了,那ACK又是怎么发出去的呢?答案是:发ACK不需要snd_buf空间,它直接就扔出去了。在socket收消息时,会调用tcp_recvmsg,收完后会清空读缓冲cleanup_rbuf,cleanup_rbug里会发送ACK消息,如图:
tcp_write_xmit里的操作其实就是从发送队列里循环拿skb,然后调用tcp_transmit_skb发到网络上去,而ACK是直接就调用tcp_transmit_skb,故而不经过发送队列,也就不受snd_buf空间的影响。
还有人可能问,这岂不是linux tcp协议栈的bug?我觉得有可能,因为在linux的2.6.32的内核里,tcp_snd_test函数已经没有了(实际上从2.6.13开始tcp_snd_test就没了,用rhel5的人可以松口气了),__tcp_push_pending_frame里那个别扭的“||”操作也拿掉了,改为直接调用tcp_write_xmit,再在tcp_write_xmit里对窗口和nagle算法就行判断,决定是否该发送包。逻辑更清晰,bug也避开了。
“我能见一下为我做这份晚餐的厨师吗?”
因为他要当面感谢这个为烹饪美食的厨师。
食物是上帝赐予的,所以要感谢上帝;美餐是厨师制造的,所以也要感谢厨师。这是多好的习惯!法式料理全球有名,皆是此伟大习惯所赐。
昨晚梅坚请客,看了电影《avatar》。没啥可说的,也不需我再加评论——满分。这才称得上大片。大家抓紧去电影院看,12年了,我就推荐大家去电影院看这么一部,不算过分了。
真得好好感谢导演,James Cameron同学,他从97年拍完《泰坦尼克号》就开始构思《avatar》的剧情,2006年8月开始拍摄,三年多的辛劳,终成大作。James,可爱的老头,12年了,终于落叶归根,从商业片里沉淀而出,回到了童年,拍出了这部如所有人童年一般纯洁透明的电影。
做为一个对音乐和声音敏感的人,我还要感谢影片的作曲,老熟人 James Horner,之所以称熟人,是因为我所喜欢的《勇敢的心》、梅坚所喜欢的《异形2》、大家所喜欢的《泰坦尼克号》(97年)、奥斯卡评委所喜欢的《美丽 心灵》,都是由他作曲配乐。而《avatar》里的配乐,更秉承了James Horner一贯的轻盈、灵快的风格。感谢James Horner为我们带来的天籁之音。
当然,还要感谢梅坚。有人请客看电影,感觉更好。
当我用gcc的时候,我感谢Richard Stallman,如果不是他带头做出了如此出色的编译器,带头启动了GNU,我可能还在用VC开发,VC太贵了,我买不起的。
当我用linux的时候,我感谢Linus,感谢Alon Cox,当然,还有所有内核开发者,不是他们做出了这么出色的OS,我现在可能还在windows下开发,windows太贵了,我买不起的。
感谢这些计算机领域的英雄,他们就是潘多拉星球上的“魅影骑士”,有他们在,我们的editor,我们的compiler,我们的OS,我们的家园,才不会被某个超级跨国软件公司所霸占、所蚕食、所掠夺。我们才能在大树下自由的开发。
请追随这些英雄们。
《avatar》开篇曲
http://www.liamdelahunty.com/tips/linux_send_html_email_from_command_line.php
很简单,在html文件(比如my.html)前面补上三行:
To: hao.bigrat@example.com
Subject: OhMyGod
Content-Type: text/html; charset="gbk"
<html>
<table>
......
然后
sendmail hao.bigrat@example.com < my.html
没有基督教信仰带来的力量,鲁滨逊无法生存,一个人吃饱喝足就是生存的全部了吗?但国人是没有宗教信仰的,所以当初看的时候,肯定觉得挺别扭,故而删之。可 惜啊,一部优秀小说最后被我们的教科书评价为:“代表了资本主义的扩张”,多么恶心的历史教科书(亏我当年还背得那么卖力,乖孩子总是比较倒霉)。
鲁滨逊在那个小岛上生活了数年后,大家就都承认他是岛屿的“总督”,没有哪个人说“这岛是属于我们大英帝国的,所以就是女王陛下的,所以不是你的”。又想起 美国的西进运动,规定说:谁占的地就世世代代归谁,这才让开拓者们有了底气,才有了今天这么大的美国。说到做到,是领导者的基本素质,为什么中国历史上的 改革成功的那么少?因为能像商鞅那样“城门立木”的改革家太少了。昨天才看一个经济频道的新闻,河南某锁厂在市中心广场开擂台,说谁能打开他们研 发的新锁,就当场给50万元奖励,有个中年人上去应战,三下五除二就把锁打开了,猜怎么着?那厂领导叫上保安,把开锁者当场轰走了(坏了他们的好事),瞧,这就 是国内企业的水平,众目睽睽之下都敢反悔!相较之下,往牛奶里加三聚氰胺,那还不是小菜一碟?
小说的结尾尤其让我感叹:一个在文明社会消 失了30年的人,回家乡后居然能找回自己全部的财产!西方人多么注重“契约”这个概念,只要有契约在,不管多少年、发生了多少事,你的财产依然圣神不可侵 犯。以前看《辛巴达七海传奇》,发现人类居然可以和神订立契约,而即使贵为神灵,也要遵守和人的契约,不可打破。这不单是神的戒律,也是西方世界的基本道 德。
说到做到、信守契约。当年英国能够成为殖民地最为广大的国家(日不落帝国),难道完全是偶然吗?西方国家比东方富足,难道也仅仅是因为昔日的掠夺吗?
空难都是故障造成的,和我们平时工作中系统故障一样,但我们还好,顶多就是损失钱或声誉,而对航空界来说,故障就直接要了人的命。
英国的一架BAC 1-11小型客机起飞后,机长解开安全带,喝咖啡,这时驾驶舱前窗突然脱落,强大的吸力直接把机长拉出了飞机!所幸脚被卡住,后被乘务员拉住,于是,机长 昏迷着在飞机外吹了20分钟的零下17度寒风,副驾驶还算冷静,开着敞篷飞机安全降落,降落后,由于后怕和对机长的悲伤,副驾驶都吓哭了。但奇迹是,这次 事故无人伤亡,机长居然活了下来,疗养了几个月后,他老人家又重新坐上了他心爱的飞机(要换我,早拉倒不干了)。
这个事情教育我们:只要飞机还在空中,你最好还是老老实实的系好安全带,别耍酷。
后来的调查发现,驾驶舱前窗的设计居然是从外往内固定,这样,飞行中,机舱内是高压,始终有力量推着前窗。而维修人员在维修这个螺丝老旧的前窗时,是目测选 新螺丝的,而且是在一个备用机棚,光线十分灰暗......可能很多人想不到飞机这种高科技玩意,是在一个多么脏乱的大棚里维修的,原因是航空公司为了节 省成本,让很多维修人员倒班,工棚当然不够用,而维修人员也是瞌睡连天,于是不再遵从规范(规范是查手册找螺丝,不是目测),于是前窗用了尺寸偏小的螺 丝,于是在空中前窗飞去,于是机长被吸,成为了最“拉风”的人......
首先,航空公司混蛋,为了利润不折手段,加大维修人员的工作强度,这就 像某些IT公司,为了所谓业务,拼命压榨程序员,一天从9点到23点,一周从周一到周六,最后人员疲惫、故障百出。其次,设计人员笨蛋,窗户设计有缺陷, 细节疏漏,这种疏忽即使压力测试都不一定能发现(压力测试使用完好的飞机,不是换了螺丝的),所以,设计review很重要(记得叫上中中)。
2002年,俄罗斯乌法市小学的优秀孩子坐图-154客机去西班牙参加一个联合国教科文组织的活动,经过瑞士上空时,和一架货机相撞,乘客全部丧生,包括52个孩子!
我和老婆看完那期的节目,两人都十分愤懑,并一致认为——瑞士空管局要负全部责任!
瑞士空管局的人,由于晚上途经的飞机少,任务也少,居然一部分跑去吃宵夜,一个小伙子只好守着三个显示屏指挥飞机,一架飞机要降落了,他只好全神贯注,殊不 知另一个显示屏上,两架飞机就要相撞了!吃你个头的夜宵!空中几百条鲜活的生命,包括52个活蹦乱跳的孩子,他们身为空管,居然吃夜宵去了!?把任务都丢 给一个人。我对瑞士人谨慎细致的印象一落千丈。以后选航线,尽量远离瑞士。
而且,飞机快要相撞时,德国的空管自动系统发出了报警(这时起码的系统功能),德国空管却不能给飞行员直接去电话,这是国际空管规则......我现在才深刻理解了“要遵守规范,但不能拘泥于规范”的意思。
总之,感触良多,做工程师除了小心,还要耐心,多看几眼,多做点安全检查,虽然出不了人命,少coredump、少损失点钱,也是好的啊。