星期二, 十二月 30, 2008

Python Note: struct for binary protocol

roczhou

30 Dec 2008 ChangeLog:

  • 30 Dec 2008, roczhou, 创建文档

最近在做一个项目的时候,使用了基于文本的 JSON 协议,总的来说,效率还可以。不过后来在做压力测试时,因为协议使用 UDP,因此会有大报文分片的情况,所以服务端只能基于 IP 分配任务,但因为最初没有找到虚拟大量客户端的有效方法(后来通过虚拟 IP 加 iptables 实现),故当时出现的一个情况就是大量请求只压在了一个任务线程(Task Thread)上,服务端不能完全压满。

因此最初提出了一个将协议头改成二进制的方法,这样前端接收线程可以在收到报文后进行一个比较快速的解析,并将内容分配给正确的任务线程。但此时客 户端使用的是 Python 实现,而服务器端使用的是 C/C++ 实现,为了实现这种二进制协议,需要使用 Python 的 struct 模块来进行转换。例子如下:

Python 端:

  #!/usr/bin/env python
# -*- encoding: utf-8 -*-

__author__ = "roczhou"
__date__ = "11 Dec 2008"
__version__ = "0.3"

import time
import struct

headobj = {
"V" : 1,
"C" : 0x01,
"S" : 10000,
"R" : 0,
# "T" : int(time.time()),
"T" : 1111111111,
"c" : 1,
"i" : 0,
"L" : 32,
}

values = []
for k in ["V", "C", "S", "R", "T", "c", "i", "L"]:
values.append(headobj[k])
print "SIZE", struct.calcsize("!2BHIQ2IH")
open("/tmp/head.bin", 'wb').write(struct.pack("!2BHIQ2IH", *values))

C 端:

  #include 
#include
#include

struct _head {
uint8_t version;
uint8_t command;
uint16_t sequence;
uint32_t reserved;
uint64_t timestamp;
uint32_t count;
uint32_t index;
uint16_t length;
} typedef head;

int main(int argc, char *argv[]) {
char buffer[1024];
FILE *fp = fopen("/tmp/head.bin", "rb");
if(fp == NULL) {
printf("File /tmp/head.bin does not exists\n");
return 1;
}
// size_t len = fread(buffer, sizeof(head), 1024, fp);
size_t len = fread(buffer, 1, 1024, fp);
head *hd = (head *)buffer;
printf("version: %d\n", hd->version);
printf("command: %d\n", hd->command);
printf("sequence: %d\n", ntohs(hd->sequence));
printf("reserved: %d\n", ntohl(hd->reserved));
printf("timestamp: %d\n", ntohl(hd->timestamp));
printf("count: %d\n", ntohl(hd->count));
printf("index: %d\n", ntohl(hd->index));
printf("length: %d\n", ntohs(hd->length));
printf("LEN: %d\n", len)
return 0;
};

对于二进制协议,第一是要保证每个字段(元素)的长度固定不变,第二是要保证各个字段(元素)的顺序固定不必。

关于 Python struct 的用法,可以参考官方文档,最重要的是保证格式串所表示的各个字段(元素)的字节长一致,列表如下:

  B, [unsigned] char, 1 bytes
H, [unsigned] short, 2 bytes
I, [unsigned] int, 4 bytes
Q, [unsigned] long long, 8 bytes

l/L(long) 和 i/L(int) 的字节长度一样?

  >>> import struct
>>> struct.calcsize("!i")
4
>>> struct.calcsize("!l")
4

struct.calcsize(format) 会计算这个格式表示的字长,这个字节长必须和 C/C++ 的 sizeof(struct) 所计算出的长度一样。在上面的 C 代码中,最后也打印出了这个长度 LEN。

使用二进制协议另一个问题是字节序。不同的操作系统平台使用的字节序可能不一样,例如 Linux 和 Solaris。使用网络序一般不会有什么问题,所以在 Python 端的 format 使用了 ! 表示使用网络序,而 C 端使用 ntohs/ntohl 表示从网络序转换成整型和长整形,否则在 Linux 和 Solaris 下得到的结果会不同。

星期五, 十二月 26, 2008

自动化文档管理方案

基本思路

  1. 使用简单的 t2t 标记进行文档编写
  2. 使用 subversion 对这些文档进行版本控制
  3. 使用 GNU Make 实现自动化管理
  4. 文档编写之后使用 txt2tags 进行文档转换为其它格式(通过调用相应 Makefile target 实现)
  5. 使用 mutt/msmtp 自动发送转换后文本到某个邮件列表(相应 make target)
  6. 自动同步到在线文档系统?
  7. 自动作图?(目前可用 dia)
  8. 上传图片?
  9. 对项目,自动生成站点层级页面?

目前已实现前面五点,后续功能方案研究中...

我使用的系统环境为

  $ uname
CYGWIN_NT-5.1

所以任何 Linux/UNIX 系统都是合适的。

略去部分

  • txt2tags 比较简单,参考官方文档大概 <20min>
  • subversion 使用广泛,在此也不赘述

mutt/msmtp

mutt/msmtp 在 Cygwin 似乎不太稳定,但基本可以使用:

  mutt-1.4.2.2-2
msmtp-1.4.13-1

mutt 是 MUA,它需要一个 MTA 来为它发送邮件,默认情况下它会使用 sendmail 或 postfix 的 sendmail 命令,但安装和配置一个 sendmail/postfix 太麻烦了,对于这种小应用不合适,所以使用 msmtp,它是一个轻量级的 MTA。

因为只需要在命令行调用 mutt,所以不需要进行太复杂的设置,编写 mutt 和 msmtp 相应的配置文件如下:

  sh$ cat ~/.mutt/private.muttrc
# SMTP
set sendmail="/usr/bin/msmtp -f someone@gmail.com"

sh$ cat ~/.msmtprc
account private
host smtp.gmail.com
port 587
protocol smtp
auth on
from someone@gmail.com
user someone@gmail.com
password "********"
tls on
tls_starttls on
tls_certcheck off

~/.mutt/private.muttrc 指明了使用 msmtp 及其参数,-f 即 .msmtprc 中的 from 内容,用这个来标识要使用哪个账号来发送邮件,因为我们可能要使用多个账号发不同的邮件,比如对工作的内容要使用另一个账号,这也是为什么没有使用标准的 ~/.muttrc 或 ~/.mutt/muttrc 作为 mutt 配置的原因,下面会将到如何使用其他账号。

可以先尝试一下是否发送会成功:

  sh$ echo "testing mutt..." | mutt -s "Mutt" -F ~/.mutt/private.muttrc $mail_address

到另一个邮箱 $mail_address 看看是否确实收到了邮件。

将工作时使用的邮箱加入 ~/.msmtprc 后如下:

  $ cat ~/.msmtprc
account private
host smtp.gmail.com
port 587
protocol smtp
auth on
from someone@gmail.com
user someone@gmail.com
password "********"
tls on
tls_starttls on
tls_certcheck off

account default
host ssl.alibaba-inc.com
port 465
protocol smtp
auth on
from someone@company.com
user someone
password "********"
tls on
tls_starttls off
tls_certcheck off
# tls_force_sslv3 on

注意公司的邮箱使用 ironport,参数 tls_starttls off 与 gmail 邮箱的不同,为了使用这个设置,需要另一个 mutt 配置文件:

  sh$ cat ~/.mutt/work.muttrc
# Header
my_hdr From: someone@company.com
# SMTP
set sendmail="/usr/bin/msmtp -f someone@company.com"

因为公司账号没有域名后缀,所以发出去的邮件 header 部分将没有域名部分,故在 ~/.mutt/work.muttrc 中增加 my_hdr 让 mutt 帮补上。

可再用前述方法试验一下是否能够正确发送邮件。

Makefile

邮件客户端配置成功后,就可以在文档目录下编写一个 Makefile。此时正确的目录结构很重要,方便我们进行管理:

  docs/
index.t2t, 用来生成结构化文档
*.t2t, 生成单独文档到 html/ 和 text/ 下
html/*.html, 转换后的 html 文件
text/*.txt, 转换后的 txt文件
_mail/work, 使用 work 邮箱时的时间戳文件
_mail/private, 使用私有邮箱时的时间戳文件
_release, 发布文档列表,只有在这个列表中的文件发生变更后才会发送邮件和进行在线同步
Makefile -> ../Makefile, 可制成符号链接到父目录 Makefile,这样可以用同一个 Makefile 管理大量分类文档

Makefile 内容如下:

  SA_MAIL := sa@list.company.com

html: *.t2t
for f in $?; do fn=`echo $$f | awk -F. '{print $$1}'` && txt2tags -t html -o html/$$fn.html $$f; done

txt: *.t2t
for f in $?; do fn=`echo $$f | awk -F. '{print $$1}'` && txt2tags -t txt -o text/$$fn.txt $$f; done

_mail/work: *.t2t
for f in $?; do \
grep $$f _release >/dev/null && \
( \
echo "mail $$f ..."; \
fn=`echo $$f | awk -F. '{print $$1}'`; \
title=`sed -n '1p' $$f`; \
cat text/$$fn.txt | mutt -F ~/.mutt/work.muttrc -s "$$title" -a html/$$fn.html $(SA_MAIL); \
) || \
echo "skip $$f ..."; \
done
touch _mail/work

当运行make htmlmake txt这两个 target 时就会分别在 html/ 或 text/ 下生成转换文档,运行make _mail/work后会用工作邮箱发送邮件到邮件列表 SA_MAIL,邮件内容为生成 txt 内容,附件为生成 html 文件。

星期二, 十二月 09, 2008

利用 IFS 变量在 Bash 中进行行处理

有一个文件
$ cat services.txt
锘?name script(basename) args("") mask(1/2) script_type(1/2[d]) dss
#step = "300"
#rra = "RRA:MIN:0.5:1:2016 RRA:MIN:0.5:6:8640 RRA:MIN:0.5:36:2920 RRA:MIN:0.5:288:1825 RRA:AVERAGE:0.5:1:2016 RRA:AVERAGE:0.5:6:8640 RRA:AVERAGE:0.5:36:2920 RRA:AVERAGE:0.5:288:1825 RRA:MAX:0.5:1:2016 RRA:MAX:0.5:6:8640 RRA:MAX:0.5:36:2920 RRA:MAX:0.5:288:1825"

disk adapter "-t alarm /home/testenv/script/check_disk -l -w 10% -c 5% -e" 1 2
inode adapter "-t alarm /home/testenv/script/check_disk -l -W 15% -K 10% -e" 1 2
load adapter "-t alarm /home/testenv/script/check_load -w 4,5,6 -c 6,7,8" 1 2
#load adapter "-t state /home/testenv/script/check_load -w 4,5,6 -c 6,7,8" 2 2
crond adapter "-t alarm /home/testenv/script/check_procs -w 1:1 -c 1:3 -C crond" 1 2
portmap adapter "-t alarm /home/testenv/script/check_procs -w 1:1 -c 1:3 -C portmap" 1 2
syslog adapter "-t alarm /home/testenv/script/check_procs -w 1:1 -c 1:3 -C syslogd" 1 2
snmpd adapter "-t alarm /home/testenv/script/check_procs -w 1:1 -c 1:3 -C snmpd" 1 2
sshd adapter "-t alarm /home/testenv/script/check_procs -w 1:1 -c 1:3 -C sshd" 1 2
#cpu
#mem
#disksp
#diskio
#iftraffic
#ifpackage

现在我需要按行处理,将每一行的结果进行重组并作为参数去调用另一个脚本。所以首先我需要得到每一行的结果,按照以前的做法,我通常是使用 while 循环来做,因为使用 for 得到的结果不对:
$ for line in $(grep -Pv '^$|^#' services.txt);do echo $line;done
锘?name
script(basename)
args("")
mask(1/2)
script_type(1/2[d])
dss
disk
adapter
"-t
alarm
/home/testenv/script/check_disk
-l
-w
10%
-c
5%
-e"
1
2
inode
adapter
"-t
alarm
/home/testenv/script/check_disk
-l
-W
15%
-K
10%
-e"
1
2
load
adapter
"-t
alarm
/home/testenv/script/check_load
-w
4,5,6
-c
6,7,8"
1
2
crond
adapter
"-t
alarm
/home/testenv/script/check_procs
-w
1:1
-c
1:3
-C
crond"
1
2
portmap
adapter
"-t
alarm
/home/testenv/script/check_procs
-w
1:1
-c
1:3
-C
portmap"
1
2
syslog
adapter
"-t
alarm
/home/testenv/script/check_procs
-w
1:1
-c
1:3
-C
syslogd"
1
2
snmpd
adapter
"-t
alarm
/home/testenv/script/check_procs
-w
1:1
-c
1:3
-C
snmpd"
1
2
sshd
adapter
"-t
alarm
/home/testenv/script/check_procs
-w
1:1
-c
1:3
-C
sshd"
1
2

得到的不是每一行的输出!

而使用 while 结果如下:
$ grep -Pv '^$|^#' services.txt | while read line; do echo $line; done
锘? ame script(base ame) args("") mask(1/2) script_type(1/2[d]) dss
disk adapter "-t alarm /home/teste v/script/check_disk -l -w 10% -c 5% -e" 1 2
i ode adapter "-t alarm /home/teste v/script/check_disk -l -W 15% -K 10% -e" 1 2
load adapter "-t alarm /home/teste v/script/check_load -w 4,5,6 -c 6,7,8" 1 2
cro d adapter "-t alarm /home/teste v/script/check_procs -w 1:1 -c 1:3 -C cro d" 1 2
portmap adapter "-t alarm /home/teste v/script/check_procs -w 1:1 -c 1:3 -C portmap" 1 2
syslog adapter "-t alarm /home/teste v/script/check_procs -w 1:1 -c 1:3 -C syslogd" 1 2
s mpd adapter "-t alarm /home/teste v/script/check_procs -w 1:1 -c 1:3 -C s mpd" 1 2
sshd adapter "-t alarm /home/teste v/script/check_procs -w 1:1 -c 1:3 -C sshd" 1 2


但是使用 while 循环会启动一个子 Shell,如果我要在循环后再进行其他操作则会有问题,因为此时 $line 已经不可用了,于是可以用 IFS 来做 for 循环,因为 for 循环不会在子 Shell 中进行:
$ IFS=$'\n' && for line in $(grep -Pv '^$|^#' services.txt);do echo $line;done
锘?name script(basename) args("") mask(1/2) script_type(1/2[d]) dss
disk adapter "-t alarm /home/testenv/script/check_disk -l -w 10% -c 5% -e" 1 2
inode adapter "-t alarm /home/testenv/script/check_disk -l -W 15% -K 10% -e" 1 2
load adapter "-t alarm /home/testenv/script/check_load -w 4,5,6 -c 6,7,8" 1 2
crond adapter "-t alarm /home/testenv/script/check_procs -w 1:1 -c 1:3 -C crond" 1 2
portmap adapter "-t alarm /home/testenv/script/check_procs -w 1:1 -c 1:3 -C portmap" 1 2
syslog adapter "-t alarm /home/testenv/script/check_procs -w 1:1 -c 1:3 -C syslogd" 1 2
snmpd adapter "-t alarm /home/testenv/script/check_procs -w 1:1 -c 1:3 -C snmpd" 1 2
sshd adapter "-t alarm /home/testenv/script/check_procs -w 1:1 -c 1:3 -C sshd" 1 2


注意这个地方必须使用单引号,使用双引号的结果不对:
$ IFS=$"\n" && for line in $(grep -Pv '^$|^#' services.txt);do echo $line;done
锘?
ame script(base
ame) args("") mask(1/2) script_type(1/2[d]) dss
disk adapter "-t alarm /home/teste
v/script/check_disk -l -w 10% -c 5% -e" 1 2
i
ode adapter "-t alarm /home/teste
v/script/check_disk -l -W 15% -K 10% -e" 1 2
load adapter "-t alarm /home/teste
v/script/check_load -w 4,5,6 -c 6,7,8" 1 2
cro
d adapter "-t alarm /home/teste
v/script/check_procs -w 1:1 -c 1:3 -C cro
d" 1 2
portmap adapter "-t alarm /home/teste
v/script/check_procs -w 1:1 -c 1:3 -C portmap" 1 2
syslog adapter "-t alarm /home/teste
v/script/check_procs -w 1:1 -c 1:3 -C syslogd" 1 2
s
mpd adapter "-t alarm /home/teste
v/script/check_procs -w 1:1 -c 1:3 -C s
mpd" 1 2
sshd adapter "-t alarm /home/teste
v/script/check_procs -w 1:1 -c 1:3 -C sshd" 1 2

星期一, 十一月 03, 2008

千与千寻主题曲:永远同在

我心深处有声音在呼唤
时常想做个教心灵跃动的梦
纵有数不尽的悲伤
我确信能在那方遇上你
反复犯了错的旅客
最少也看见过青空的蔚蓝
即使前路茫茫无尽
我的双手仍怀抱着光明
告别的时候静下来的心
归于无有的身体叫耳朵细听
生存的奇妙死亡的不可思议
花与风与城市都同一样
我心深处有声音在呼唤
时常不断在绘画梦想
总有说不清的悲伤
以同一张嘴巴温柔地歌唱
在即将消失的回忆中
听到不能忘怀的微声细语
在破碎的镜子上
反照出新景象
最初的清晨宁静的窗
归于无有的身体不断被充满
不再探求海的另一边
因为光辉就在这里
在我里面找到了

星期五, 十月 31, 2008

内向人如何建立自己的人际网络

人际网络是一项投资而不是浪费。

想想你是否能够打一两个电话就找到你想要的东西。如果你有良好的人际网络,你应该就可以。通过投资时间建立人际网络,你在需要搞定事情的时候就可以节省时间。拥有良好人际网络的人不用耗费时间向不认识的人随机群发电邮、购买线索或行业名单、或者从上百的简历中挑出合适的应聘者。做出选择吧,你想现在花点时间呢还是以后再补?

遇到王子之前,你要吻很多青蛙。

一开始时你可能要盲目地选择参加的社交活动。你可能会在一个不喜欢的场合痛苦地呆上一个小时,但是从中可以学到哪些活动需要参加,哪些活动需要跳过。最终,你会找到一些你喜爱的人和活动。

不要花太多时间。

如果你把自己累坏了,你就再也不想去建立人际网络了。制定一个上限,一个月只参加一至两个活动。关系的建立需要很长时间,所以与少一点的圈子保持长期的关系比参加很多圈子却只能保持短暂关系好。

把酷的想法做出来。

内向的人通常不喜欢谈论自己——我们更喜欢谈论想法。影响自己去谈论一些自己做过的事情。不要自吹自擂,要切题。这样那些外向的人会谈论起你,传说你的成就。这会增加你在一些圈子里的信誉。我知道人们更喜欢你的好点子和知识,但这个世界只看你做了什么。

邀请别人共进午餐。

或者你可以下班之后请他们喝咖啡和啤酒。因为如果对方也是一个内向的人,他或她不会邀请你,所以你要做出邀请。

经常做你喜欢的事情。

我住在太空海岸(译者注:Space Coast)的时候,我参加了一个为企业主和投资者开设的创业者论坛圈子。我在其中学到了很多东西,但是那里的人们直到6个月之后才开始认出我、向我打招呼。作为一个23岁的人,和满屋子中年人在一起并不舒服,但你需要经常地露面,月复一月。

分析你的成果。

内向的人有很好的直觉、善于分析。那就利用这点长处。你的关系里什么是有用的,什么不是?你最大的阻碍是什么?

找出你人际网络中的关键点。

不要找一个销售人员,找一个认识很多销售人员的人,然后请那个人吃午饭。如果人际网络让你感到疲惫,那么找十个认识很多人的关键人物会比找到和保持五十个联系人好很多。找到合适的关键人要花很多时间。找那些因工作原因(无论出自什么理由)而要和很多人保持良好关系的内向的人。那些和你有着共同爱好的外向的人也是很好的选择。

不要只为了社交而去建立人际网络。

有一本书叫《永远不要独自吃饭》(译者注:Never Eat Alone)。对于外向的人来说,书上讲的都不错,但是我们内向的人不能为了社交而社交。随着你认识越来越多的人,专注于那些与你最合得来的人,不必过多的专注于认识新的人。

一开始的几次活动对我来说比较困难。有时我不知道该和新认识的人说些什么。如果有人以诸如“你如何看待以功利的观点做关于伦理的决定?”、“Sarbanes-Oxley法案有没有鼓励公司去做‘交易’。”的问题开始一段谈话,我会感觉好很多。我通常觉得想法观点比人本身有趣。但是,通过坚持,月复一月,我慢慢地懂得说一些巧妙的话,认识陌生人也觉得比以前舒服了。所以相信我,与人交往总会变得简单。

规则总是大多数人制定的,而外向的人占了大多数(我猜有70%)(译者注:MBTI人格理论认为当你比56%的人外向时你就可以算是外向的了,换句话说,该理论认为56%的人可以被认为是内向的)。人际网络是一把释放你潜能的重要钥匙。所以尽你的力去按照规则游戏,不然就不要干坐着抱怨没人把你的奇思妙想化为现实。我希望你可以从我的经验中学点什么,这样你就不用走一些我走过的弯路了。

星期四, 十月 30, 2008

HP总裁孙振耀退休时的一封信

HP总裁孙振耀退休时的一封信(1)

我有个有趣的观察,外企公司多的是25-35岁的白领,40岁以上的员工很少,二三十岁的外企员工是意气风发的,但外企公司40岁附近的经理人是很尴尬的。我见过的40岁附近的外企经理人大多在一直跳槽,最后大多跳到民企,比方说,唐骏。外企员工的成功很大程度上是公司的成功,并非个人的成功,西门子的确比国美大,但并不代表西门子中国经理比国美的老板强,甚至可以说差得很远。而进外企的人往往并不能很早理解这一点,把自己的成功90%归功于自己的能力,实际上,外企公司随便换个中国区总经理并不会给业绩带来什么了不起的影响。好了问题来了,当这些经理人40多岁了,他们的薪资要求变得很高,而他们的才能其实又不是那么出众,作为外企公司的老板,你会怎么选择?只要不高薪水的,要出位的精明强干精力冲沛的年轻人,有的是,为什么还要用你?

  从上面这个例子,其实可以看到我们的工作轨迹,二三十岁的时候,生活的压力还比较小,身体还比较好,上面的父母身体还好,下面又没有孩子,不用还房贷,也没有孩子要上大学,当个外企小白领还是很光鲜的,挣得不多也够花了。但是人终归要结婚生子,终归会老,到了40岁,父母老了,要看病要吃药,要有人看护,自己要还房贷,要过基本体面的生活,要养小孩……那个时候需要挣多少钱才够花才重要。所以,看待工作,眼光要放远一点,一时的谁高谁低并不能说明什么。

从这个角度上来说,我不太赞成过于关注第一份工作的薪水,更没有必要攀比第一份工作的薪水,这在刚刚出校园的学生中间是很常见的。正常人大概要工作35年,这好比是一场马拉松比赛,和真正的马拉松比赛不同的是,这次比赛没有职业选手,每个人都只有一次机会。要知道,有很多人甚至坚持不到终点,大多数人最后是走到终点的,只有少数人是跑过终点的,因此在刚开始的时候,去抢领先的位置并没有太大的意义。刚进社会的时候如果进500强公司,大概能拿到3k -6k/月的工资,有些特别技术的人才可能可以到8k/月,可问题是,5年以后拿多少?估计5k-10k了不起了。起点虽然高,但增幅有限,而且,后面的年轻人追赶的压力越来越大。

我前两天问我的一个销售,你会的这些东西一个新人2年就都学会了,但新人所要求的薪水却只是你的一半,到时候,你怎么办?职业生涯就像一场体育比赛,有初赛、复赛、决赛。初赛的时候大家都刚刚进社会,大多数都是实力一般的人,这时候努力一点认真一点很快就能让人脱颖而出,于是有的人二十多岁做了经理,有的人迟些也终于赢得了初赛,三十多岁成了经理。然后是复赛,能参加复赛的都是赢得初赛的,每个人都有些能耐,在聪明才智上都不成问题,这个时候再想要胜出就不那么容易了,单靠一点点努力和认真还不够,要有很强的坚忍精神,要懂得靠团队的力量,要懂得收服人心,要有长远的眼光……

看上去赢得复赛并不容易,但,还不是那么难。因为这个世界的规律就是给人一点成功的同时让人骄傲自满,刚刚赢得初赛的人往往不知道自己赢得的仅仅是初赛,有了一点小小的成绩大多数人都会骄傲自满起来,认为自己已经懂得了全部,不需要再努力再学习了,他们会认为之所以不能再进一步已经不是自己的原因了。虽然他们仍然不好对付,但是他们没有耐性,没有容人的度量,更没有清晰长远的目光。就像一只愤怒的斗牛,虽然猛烈,最终是会败的,而赢得复赛的人则象斗牛士一样,不急不躁,跟随着自己的节拍,慢慢耗尽对手的耐心和体力。赢得了复赛以后,大约已经是一位很了不起的职业经理人了,当上了中小公司的总经理,大公司的副总经理,主管着每年几千万乃至几亿的生意。

最终的决赛来了,说实话我自己都还没有赢得决赛,因此对于决赛的决胜因素也只能凭自己的猜测而已,这个时候的输赢或许就像武侠小说里写得那样,大家都是高手,只能等待对方犯错了,要想轻易击败对手是不可能的,除了使上浑身解数,还需要一点运气和时间。世界的规律依然发挥着作用,赢得复赛的人已经不只是骄傲自满了,他们往往刚愎自用,听不进去别人的话,有些人的脾气变得暴躁,心情变得浮躁,身体变得糟糕,他们最大的敌人就是他们自己,在决赛中要做的只是不被自己击败,等着别人被自己击败。这和体育比赛是一样的,最后高手之间的比赛,就看谁失误少谁就赢得了决赛。

根源
你工作快乐么?你的工作好么?
有没有觉得干了一段时间以后工作很不开心?有没有觉得自己入错了行?有没有觉得自己没有得到应有的待遇?有没有觉得工作像一团乱麻每天上班都是一种痛苦?有没有很想换个工作?有没有觉得其实现在的公司并没有当初想象得那么好?有没有觉得这份工作是当初因为生存压力而找的,实在不适合自己?你从工作中得到你想要得到的了么?你每天开心么?
  天涯上愤怒的人很多,你有没有想过,你为什么不快乐?你为什么愤怒?其实,你不快乐的根源,是因为你不知道要什么!你不知道要什么,所以你不知道去追求什么,你不知道追求什么,所以你什么也得不到。
我总觉得,职业生涯首先要关注的是自己,自己想要什么?大多数人大概没想过这个问题,唯一的想法只是——我想要一份工作,我想要一份不错的薪水,我知道所有人对于薪水的渴望,可是,你想每隔几年重来一次找工作的过程么?你想每年都在这种对于工作和薪水的焦急不安中度过么?不想的话,就好好想清楚。饮鸩止渴,不能因为口渴就拼命喝毒药。越是焦急,越是觉得自己需要一份工作,越饥不择食,越想不清楚,越容易失败,你的经历越来越差,下一份工作的人看着你的简历就皱眉头。于是你越喝越渴,越渴越喝,陷入恶性循环。最终只能哀叹世事不公或者生不逢时,只能到天涯上来发泄一把,在失败者的共鸣当中寻求一点心理平衡罢了。大多数人都有生存压力,我也是,有生存压力就会有很多焦虑,积极的人会从焦虑中得到动力,而消极的人则会因为焦虑而迷失方向。所有人都必须在压力下做出选择,这就是世道,你喜欢也罢不喜欢也罢。
  一般我们处理的事情分为重要的事情和紧急的事情,如果不做重要的事情就会常常去做紧急的事情。比如锻炼身体保持健康是重要的事情,而看病则是紧急的事情。如果不锻炼身体保持健康,就会常常为了病痛烦恼。又比如防火是重要的事情,而救火是紧急的事情,如果不注意防火,就要常常救火。找工作也是如此,想好自己究竟要什么是重要的事情,找工作是紧急的事情,如果不想好,就会常常要找工作。往往紧急的事情给人的压力比较大,迫使人们去赶紧做,相对来说重要的事情反而没有那么大的压力,大多数人做事情都是以压力为导向的,压力之下,总觉得非要先做紧急的事情,结果就是永远到处救火,永远没有停歇的时候。(很多人的工作也像是救火队一样忙碌痛苦,也是因为工作中没有做好重要的事情。)那些说自己活在水深火热为了生存顾不上那么多的朋友,今天找工作困难是当初你们没有做重要的事情,是结果不是原因。如果今天你们还是因为急于要找一份工作而不去思考,那么或许将来要继续承受痛苦找工作的结果。
  我始终觉得我要说的话题,沉重了点,需要很多思考,远比唐笑打武警的话题来的枯燥乏味,但是,天下没有轻松的成功,成功,要付代价。请先忘记一切的生存压力,想想这辈子你最想要的是什么?所以,最要紧的事情,先想好自己想要什么。

HP总裁孙振耀退休时的一封信(2)

什么是好工作
  当初微软有个唐骏,很多大学里的年轻人觉得这才是他们向往的职业生涯,我在清华bbs里发的帖子被这些学子们所不屑,那个时候学生们只想出国或者去外企,不过如今看来,我还是对的,唐骏去了盛大,陈天桥创立的盛大,一家民营公司。一个高学历的海归在500强的公司里拿高薪水,这大约是很多年轻人的梦想,问题是,每年毕业的大学生都在做这个梦,好的职位却只有500个。

  人都是要面子的,也是喜欢攀比的,即使在工作上也喜欢攀比,不管那是不是自己想要的。大家认为外企公司很好,可是好在哪里呢?好吧,他们在比较好的写字楼,这是你想要的么?他们出差住比较好的酒店,这是你想要的么?别人会羡慕一份外企公司的工作,这是你想要的么?那一切都是给别人看的,你干吗要活得那么辛苦给别人看?另一方面,他们薪水福利一般,并没有特别了不起,他们的晋升机会比较少,很难做到很高阶的主管,他们虽然厌恶常常加班,却不敢不加班,因为“你不干有得是人干”,大部分情况下会找个台湾人香港人新加坡人来管你,而这些人又往往有些莫名其妙的优越感。你想清楚了么?500强一定好么?找工作究竟是考虑你想要什么,还是考虑别人想看什么?

我的大学同学们大多数都到美国了,甚至毕业这么多年了,还有人最近到国外去了。出国真的有那么好么?我的大学同学们,大多数还是在博士、博士后、访问学者地挣扎着,至今只有一个正经在一个美国大学里拿到个正式的教职。国内的教授很难当么?我有几个表亲也去了国外了,他们的父母独自在国内,没有人照顾,有好几次人在家里昏倒都没人知道,出国,真的这么光彩么?就像有人说的“很多事情就像看A片,看的人觉得很爽,做的人未必。”

人总想找到那个最好的,可是,什么是最好的?你觉得是最好的那个,是因为你的确了解,还是因为别人说他是最好的?即使他对于别人是最好的,对于你也一定是最好的么?

对于自己想要什么,自己要最清楚,别人的意见并不是那么重要。很多人总是常常被别人的意见所影响,亲戚的意见,朋友的意见,同事的意见……问题是,你究竟是要过谁的一生?人的一生不是父母一生的续集,也不是儿女一生的前传,更不是朋友一生的外篇,只有你自己对自己的一生负责,别人无法也负不起这个责任。自己做的决定,至少到最后,自己没什么可后悔。对于大多数正常智力的人来说,所做的决定没有大的对错,无论怎么样的选择,都是可以尝试的。比如你没有考自己上的那个学校,没有入现在这个行业,这辈子就过不下去了?就会很失败?不见得。

  我想,好工作,应该是适合你的工作,具体点说,应该是能给你带来你想要的东西的工作,你或许应该以此来衡量你的工作究竟好不好,而不是拿公司的大小,规模,外企还是国企,是不是有名,是不是上市公司来衡量。小公司,未必不是好公司,赚钱多的工作,也未必是好工作。你还是要先弄清楚你想要什么,如果你不清楚你想要什么,你就永远也不会找到好工作,因为你永远只看到你得不到的东西,你得到的,都是你不想要的。

可能,最好的,已经在你的身边,只是,你还没有学会珍惜。人们总是盯着得不到的东西,而忽视了那些已经得到的东西。


普通人

  我发现中国人的励志和国外的励志存在非常大的不同,中国的励志比较鼓励人立下大志愿,卧薪尝胆,有朝一日成富成贵。而国外的励志比较鼓励人勇敢面对现实生活,面对普通人的困境,虽然结果也是成富成贵,但起点不一样,相对来说,我觉得后者在操作上更现实,而前者则需要用999个失败者来堆砌一个成功者的故事。

我们都是普通人,普通人的意思就是,概率这件事是很准的。因此,我们不会买彩票中500万,我们不会成为比尔盖茨或者李嘉诚,我们不会坐飞机掉下来,我们当中很少的人会创业成功,我们之中有30%的人会离婚,我们之中大部分人会活过65岁……

所以请你在想自己要什么的时候,要得“现实”一点,你说我想要做李嘉诚,抱歉,我帮不上你。成为比尔盖茨或者李嘉诚这种人,是靠命的,看我写的这篇文章绝对不会让你成为他们,即使你成为了他们,也绝对不是我这篇文章的功劳。“王侯将相宁有种乎”,但真正当皇帝的只有一个人,王侯将相,人也不多。目标定得高些对于喜欢挑战的人来说有好处,但对于大多数普通人来说,反而比较容易灰心沮丧,很容易就放弃了。

回过头来说,李嘉诚比你有钱大致50万倍,他比你更快乐么?或许。有没有比你快乐50万倍,一定没有。他比你最多也就快乐一两倍,甚至有可能还不如你快乐。寻找自己想要的东西不是和别人比赛,比谁要得更多更高,比谁的目标更远大。虽然成为李嘉诚这个目标很宏大,但你并不见得会从这个目标以及追求目标的过程当中获得快乐,而且基本上你也做不到。你必须听听你内心的声音,寻找真正能够使你获得快乐的东西,那才是你想要的东西。

你想要的东西,或者我们把它称之为目标,目标其实并没有高低之分,你不需要因为自己的目标没有别人远大而不好意思,达到自己的目标其实就是成功,成功有大有小,快乐却是一样的。我们追逐成功,其实追逐的是成功带来的快乐,而非成功本身。职业生涯的道路上,我们常常会被攀比的心态蒙住眼睛,忘记了追求的究竟是什么,忘记了是什么能使我们更快乐。

社会上一夜暴富的新闻很多,这些消息,总会在我们的心里面掀起很多涟漪,涟漪多了就变成惊涛骇浪,心里的惊涛骇浪除了打翻承载你目标的小船,并不会使得你也一夜暴富。“只见贼吃肉,不见贼挨揍。”我们这些普通人既没有当贼的勇气,又缺乏当贼的狠辣绝决,虽然羡慕吃肉,却更害怕挨揍,偶尔看到几个没挨揍的贼就按奈不住,或者心思活动,或者大感不公,真要叫去做贼,却也不敢。

我还是过普通人的日子,要普通人的快乐,至少,晚上睡得着觉。


跳槽与积累

  首先要说明,工作是一件需要理智的事情,所以不要在工作上耍个性,天涯上或许会有人觉得你很有个性而叫好,煤气公司电话公司不会因为觉得你很有个性而免了你的帐单。当你很帅地炒掉了你的老板,当你很酷地挖苦了一番招聘的HR,账单还是要照付,只是你赚钱的时间更少了,除了你自己,没人受损失。

我并不反对跳槽,但跳槽决不是解决问题的办法,而且频繁跳槽的后果是让人觉得没有忠诚度可言,而且不能安心工作。现在很多人从网上找工作,很多找工作的网站常常给人出些馊主意,要知道他们是盈利性企业,当然要从自身盈利的角度来考虑,大家越是频繁跳槽频繁找工作他们越是生意兴隆,所以鼓动人们跳槽是他们的工作。所以他们会常常告诉你,你拿的薪水少了,你享受的福利待遇差了,又是“薪情快报”又是“赞叹自由奔放的灵魂”。至于是否会因此让你不能安心,你跳了槽是否解决问题,是否更加开心,那个,他们就不管了。

要跳槽肯定是有问题,一般来说问题发生了,躲是躲不开的,很多人跳槽是因为这样或者那样的不开心,如果这种不开心,在现在这个公司不能解决,那么在下一个公司多半也解决不掉。你必须相信,90%的情况下,你所在的公司并没有那么烂,你认为不错的公司也没有那么好。就像《围城》里说的,“城里的人拼命想冲出来,而城外的人拼命想冲进去。”每个公司都有每个公司的问题,没有问题的公司是不存在的。换个环境你都不知道会碰到什么问题,与其如此,不如就在当下把问题解决掉。很多问题当你真的想要去解决的时候,或许并没有那么难。有的时候你觉得问题无法解决,事实上,那只是“你觉得”。

人生的曲线应该是曲折向上的,偶尔会遇到低谷但大趋势总归是曲折向上的,而不是象脉冲波一样每每回到起点,我见过不少面试者,30多岁了,四五份工作经历,每次多则3年,少则1年,30多岁的时候回到起点从一个初级职位开始干起,拿基本初级的薪水,和20多岁的年轻人一起竞争,不觉得有点辛苦么?这种日子好过么?

我非常不赞成在一个行业超过3年以后换行业,基本上,35岁以前我们的生存资本靠打拼,35岁以后生存的资本靠的就是积累,这种积累包括人际关系,经验,人脉,口碑……如果常常更换行业,代表几年的积累付之东流,一切从头开始,如果换了两次行业,35岁的时候大概只有5年以下的积累,而一个没有换过行业的人至少有了10年的积累,谁会占优势?工作到2-3年的时候,很多人觉得工作不顺利,好像到了一个瓶颈,心情烦闷,就想辞职,乃至换一个行业,觉得这样所有一切烦恼都可以抛开,会好很多。

其实这样做只是让你从头开始,到了时候还是会发生和原来行业一样的困难,熬过去就向上跨了一大步,要知道每个人都会经历这个过程,每个人的职业生涯中都会碰到几个瓶颈,你熬过去了而别人没有熬过去你就领先了。

跑长跑的人会知道,开始的时候很轻松,但是很快会有第一次的难受,但过了这一段又能跑很长一段,接下来会碰到第二次的难受,坚持过了以后又能跑一段,如此往复,难受一次比一次厉害,直到坚持不下去了。大多数人第一次就坚持不了了,一些人能坚持到第二次,第三次虽然大家都坚持不住了,可是跑到这里的人也没几个了,这点资本足够你安稳活这一辈子了。

一份工作到两三年的时候,大部分人都会变成熟手,这个时候往往会陷入不断的重复,有很多人会觉得厌倦,有些人会觉得自己已经搞懂了一切,从而懒得去寻求进步了。很多时候的跳槽是因为觉得失去兴趣了,觉得自己已经完成比赛了。其实这个时候比赛才刚刚开始,工作两三年的人,无论是客户关系,人脉,手下,和领导的关系,在业内的名气……还都是远远不够的,但稍有成绩的人总是会自我感觉良好的,每个人都觉得自己跟客户关系铁得要命,觉得自己在业界的口碑好得很。其实可以肯定地说,一定不是,这个时候,还是要拿出前两年的干劲来,稳扎稳打,积累才刚刚开始。

你足够了解你的客户吗?你知道他最大的烦恼是什么吗?你足够了解你的老板么?你知道他最大的烦恼是什么吗?你足够了解你的手下么?你知道他最大的烦恼是什么吗?如果你不知道,你凭什么觉得自己已经积累够了?如果你都不了解,你怎么能让他们帮你的忙,做你想让他们做的事情?如果他们不做你想让他们做的事情,你又何来的成功?

等待

  这是个浮躁的人们最不喜欢的话题,本来不想说这个话题,因为会引起太多的争论,而我又无意和人争论这些,但是考虑到对于职业生涯的长久规划,这是一个躲避不了的话题,还是决定写一写,不爱看的请离开吧。

并不是每次闯红灯都会被汽车撞,并不是每个罪犯都会被抓到,并不是每个错误都会被惩罚,并不是每个贪官都会被枪毙,并不是你的每一份努力都会得到回报,并不是你的每一次坚持都会有人看到,并不是你每一点付出都能得到公正的回报,并不是你的每一个善意都能被理解……这个,就是世道。好吧,世道不够好,可是,你有推翻世道的勇气么?如果没有,你有更好的解决办法么?有很多时候,人需要一点耐心,一点信心。每个人总会轮到几次不公平的事情,而通常,安心等待是最好的办法。

有很多时候我们需要等待,需要耐得住寂寞,等待属于你的那一刻。周润发等待过,刘德华等待过,周星驰等待过,王菲等待过,张艺谋也等待过……看到了他们如今的功成名就的人,你可曾看到当初他们的等待和耐心?你可曾看到金马奖影帝在街边摆地摊?你可曾看到德云社一群人在剧场里给一位观众说相声?你可曾看到周星驰的角色甚至连一句台词都没有?每一个成功者都有一段低沉苦闷的日子,我几乎能想象得出来他们借酒浇愁的样子,我也能想象得出他们为了生存而挣扎的窘迫。在他们一生最中灿烂美好的日子里,他们渴望成功,但却两手空空,一如现在的你。没有人保证他们将来一定会成功,而他们的选择是耐住寂寞。如果当时的他们总念叨着“成功只是属于特权阶级的”,你觉得他们今天会怎样?

曾经我也不明白有些人为什么并不比我有能力却要坐在我的头上,年纪比我大就一定要当我的领导么?为什么有些烂人不需要努力就能赚钱?为什么刚刚改革开放的时候的人能那么容易赚钱,而轮到我们的时候,什么事情都要正规化了?有一天我突然想,我还在上学的时候他们就在社会里挣扎奋斗了,他们在社会上奋斗积累了十几二十年,我们新人来了,他们有的我都想要,我这不是在要公平,我这是在要抢劫。因为我要得太急,因为我忍不住寂寞。二十多岁的男人,没有钱,没有事业,却有蓬勃的欲望。

人总是会遇到挫折的,人总是会有低潮的,人总是会有不被人理解的时候的,人总是有要低声下气的时候,这些时候恰恰是人生最关键的时候,因为大家都会碰到挫折,而大多数人过不了这个门槛,你能过,你就成功了。在这样的时刻,我们需要耐心等待,满怀信心地去等待,相信,生活不会放弃你,机会总会来的。至少,你还年轻,你没有坐牢,没有生治不了的病,没有欠还不起的债。比你不幸的人远远多过比你幸运的人,你还怕什么?路要一步步走,虽然到达终点的那一步很激动人心,但大部分的脚步是平凡甚至枯燥的,但没有这些脚步,或者耐不住这些平凡枯燥,你终归是无法迎来最后的那些激动人心。

逆境,是上帝帮你淘汰竞争者的地方。要知道,你不好受,别人也不好受,你坚持不下去了,别人也一样,千万不要告诉别人你坚持不住了,那只能让别人获得坚持的信心,让竞争者看着你微笑的面孔,失去信心,退出比赛。胜利属于那些有耐心的人。

在最绝望的时候,我会去看电影《The Pursuit of Happiness》《Jerry Maguire》,让自己重新鼓起勇气,因为,无论什么时候,我们总还是有希望。当所有的人离开的时候,我不失去希望,我不放弃。每天下班坐在车里,我喜欢哼着《隐形的翅膀》看着窗外,我知道,我在静静等待,等待属于我的那一刻。
原贴里伊吉网友的话我很喜欢,抄录在这里:
每个人都希望,自己是独一无二的特殊者
含着金匙出生、投胎到好家庭、工作安排到电力局拿1w月薪这样的小概率事件,当然最好轮到自己
红军长征两万五、打成右派反革命、胼手胝足牺牲尊严去奋斗,最好留给祖辈父辈和别人
自然,不是每个吃过苦的人都会得到回报
但是,任何时代,每一个既得利益者身后,都有他的祖辈父辈奋斗挣扎乃至流血付出生命的身影
羡慕别人有个好爸爸,没什么不可以
问题是,你的下一代,会有一个好爸爸吗?
至于问到为什么不能有同样的赢面概率?
我只能问:为什么物种竞争中,人和猴子不能有同样的赢面概率?
物竞天择。猴子的灵魂不一定比你卑微,但你身后有几十万年的类人猿进化积淀。

HP总裁孙振耀退休时的一封信(3)

入对行,跟对人
  在中国,大概很少有人是一份职业做到底的,虽然如此,第一份工作还是有些需要注意的地方,有两件事情格外重要,第一件是入行,第二件事情是跟人。第一份工作对人最大的影响就是入行,现代的职业分工已经很细,我们基本上只能在一个行业里成为专家,不可能在多个行业里成为专家。很多案例也证明即使一个人在一个行业非常成功,到另外一个行业,往往完全不是那么回事情,“你想改变世界,还是想卖一辈子汽水?”是乔布斯邀请百事可乐总裁约翰·斯考利加盟苹果时所说的话,结果这位在百事非常成功的约翰,到了苹果表现平平。其实没有哪个行业特别好,也没有哪个行业特别差,或许有报道说哪个行业的平均薪资比较高,但是他们没说的是,那个行业的平均压力也比较大。看上去很美的行业一旦进入才发现很多地方其实并不那么完美,只是外人看不见。

说实话,我自己都没有发大财,所以我的建议只是让人快乐工作的建议,不是如何发大财的建议,我们只讨论一般普通打工者的情况。我认为选择什么行业并没有太大关系,看问题不能只看眼前。比如,从前年开始,国家开始整顿医疗行业,很多医药公司开不下去,很多医药行业的销售开始转行。其实医药行业的不景气是针对所有公司的,并非针对一家公司,大家的日子都不好过,这个时候跑掉是非常不划算的,大多数正规的医药公司即使不做新生意撑个两三年总是能撑的,大多数医药销售靠工资撑个两三年也是可以撑的,国家不可能永远捏着医药行业不放的,两三年以后光景总归还会好起来的,那个时候别人都跑了而你没跑,那时的日子应该会好过很多。有的时候觉得自己这个行业不行了,问题是,再不行的行业,做得人少了也变成了好行业,当大家都觉得不好的时候,往往却是最好的时候。大家都觉得金融行业好,金融行业门槛高不说,有多少人削尖脑袋要钻进去,竞争激励,进去以后还要时时提防,一个疏忽,就被后来的人给挤掉了,压力巨大,又如何谈得上快乐?也就未必是“好”工作了。

太阳能这个东西至今还不能进入实际应用的阶段,但是中国已经有7家和太阳能有关的公司在纽交所上市了,国美苏宁永乐其实是贸易型企业,也能上市,鲁泰纺织连续10年利润增长超过50%,卖茶的一茶一座,卖衣服的海澜之家都能上市……其实选什么行业真的不重要,关键是怎么做。事情都是人做出来的,关键是人。

有一点是需要记住的,这个世界上,有史以来直到我们能够预见得到的未来,成功的人总是少数,有钱的人总是少数,大多数人是一般的,普通的,不太成功的。因此,大多数人的做法和看法,往往都不是距离成功最近的做法和看法。因此大多数人说好的东西不见得好,大多数人说不好的东西不见得不好。大多数人都去炒股的时候说明跌只是时间问题,大家越是热情高涨的时候,跌的日子越近。大多数人买房子的时候,房价不会涨,而房价涨的差不多的时候,大多数人才开始买房子。不会有这样一件事情让大家都变成功,发了财,历史上不曾有过,将来也不会发生。有些东西即使一时运气好得到了,还是会在别的时候别的地方失去的。

年轻人在职业生涯的刚开始,尤其要注意的是,要做对的事情,不要让自己今后几十年的人生总是提心吊胆,更不值得为了一份工作赔上自己的青春年华。我的公司是个不行贿的公司,以前很多人不理解,甚至自己的员工也不理解,不过如今,我们是同行中最大的企业,客户乐意和我们打交道,尤其是在国家打击腐败的时候,每个人都知道我们做生意不给钱的名声,都敢于和我们做生意。而勇于给钱的公司,不是倒了,就是跑了,要不就是每天睡不好觉,人还是要看长远一点。很多时候,看起来最近的路,其实是最远的路,看起来最远的路,其实是最近的路。

跟对人是说,入行后要跟个好领导好老师,刚进社会的人做事情往往没有经验,需要有人言传身教。对于一个人的发展来说,一个好领导是非常重要的。所谓“好”的标准,不是他让你少干活多拿钱,而是以下三个。

首先,好领导要有宽广的心胸,如果一个领导每天都会发脾气,那几乎可以肯定他不是个心胸宽广的人,能发脾气的时候却不发脾气的领导,多半是非常厉害的领导。中国人当领导最大的毛病是容忍不了能力比自己强的人,所以常常可以看到的一个现象是,领导很有能力,手下一群庸才或者手下一群闲人。如果看到这样的环境,还是不要去的好。

其次,领导要愿意从下属的角度来思考问题,这一点其实是从面试的时候就能发现的,如果这位领导总是从自己的角度来考虑问题,几乎不听你说什么,这就危险了。从下属的角度来考虑问题并不代表同意下属的说法,但他必须了解下属的立场,下属为什么要这么想,然后他才有办法说服你,只关心自己怎么想的领导往往难以获得下属的信服。

第三,领导敢于承担责任,如果出了问题就把责任往下推,有了功劳就往自己身上揽,这样的领导不跟也罢。选择领导,要选择关键时刻能抗得住的领导,能够为下属的错误买单的领导,因为这是他作为领导的责任。

有可能,你碰不到好领导,因为,中国的领导往往是屁股决定脑袋的领导,因为他坐领导的位置,所以他的话就比较有道理,这是传统观念官本位的误区,可能有大量的这种无知无能的领导,只是,这对于你其实是好事,如果将来有一天你要超过他,你希望他比较聪明还是比较笨?相对来说这样的领导其实不难搞定,只是,你要把自己的身段放下来而已。多认识一些人,多和比自己强的人打交道,同样能找到好的老师,不要和一群同样郁闷的人一起控诉社会,控诉老板,这帮不上你,只会让你更消极。和那些比你强的人打交道,看他们是怎么想的,怎么做的,学习他们,然后跟更强的人打交道。

选择

  我们每天做的最多的事情,其实是选择,因此在谈职业生涯的时候不得不提到这个话题。

我始终认为,在很大的范围内,我们究竟会成为一个什么样的人,决定权在我们自己,每天我们都在做各种各样的选择,我可以不去写这篇文章,去别人的帖子拍拍砖头,也可以写下这些文字,帮助别人的同时也整理自己的思路,我可以多注意一下格式让别人易于阅读,也可以写成一堆,我可以就这样发上来,也可以在发以前再看几遍,你可以选择不刮胡子就去面试,也可以选择出门前照照镜子……每天,每一刻我们都在做这样那样的决定,我们可以漫不经心,也可以多花些心思,成千上万的小选择累计起来,就决定了最终我们是个什么样的人。

从某种意义上来说我们的未来不是别人给的,是我们自己选择的,很多人会说我命苦啊,没得选择啊,如果你认为“去微软还是去IBM”“上清华还是上北大”“当销售副总还是当厂长”这种才叫选择的话,的确你没有什么选择,大多数人都没有什么选择。但每天你都可以选择是否为客户服务更周到一些,是否对同事更耐心一些,是否把工作做得更细致一些,是否把情况了解得更清楚一些,是否把不清楚的问题再弄清楚一些……你也可以选择在是否在痛苦中继续坚持,是否抛弃掉自己的那些负面的想法,是否原谅一个人的错误,是否相信我在这里写下的这些话,是否不要再犯同样的错误……生活每天都在给你选择的机会,每天都在给你改变自己人生的机会,你可以选择赖在地上撒泼打滚,也可以选择咬牙站起来。你永远都有选择。有些选择不是立竿见影的,需要累积,比如农民可以选择自己常常去浇地,也可以选择让老天去浇地,诚然你今天浇水下去苗不见得今天马上就长出来,但常常浇水,大部分苗终究会长出来的,如果你不浇,收成一定很糟糕。

每天生活都在给你机会,他不会给你一叠现金也不会拱手送你个好工作,但实际上,他还是在给你机会。我的家庭是一个普通的家庭,没有任何了不起的社会关系,我的父亲在大学毕业以后就被分配到了边疆,那个小县城只有一条马路,他们那一代人其实比我们更有理由抱怨,他们什么也没得到,年轻的时候文化大革命,书都没得读,支援边疆插队落户,等到老了,却要给年轻人机会了。他有足够的理由像成千上万那样的青年一样坐在那里抱怨生不逢时,怨气冲天。然而在分配到边疆的十年之后,国家恢复招研究生,他考回了原来的学校。研究生毕业,他被分配到了安徽一家小单位里,又是3年以后,国家第一届招收博士生,他又考回了原来的学校,成为中国第一代博士,那时的他比现在的我年纪还大。生活并没有放弃他,他也没有放弃生活。10年的等待,他做了他自己的选择,他没有放弃,他没有破罐子破摔,所以时机到来的时候,他改变了自己的人生。你最终会成为什么样的人,就决定在你的每个小小的选择之间。

你选择相信什么?你选择和谁交朋友?你选择做什么?你选择怎么做?……我们面临太多的选择,而这些选择当中,意识形态层面的选择又远比客观条件的选择来得重要得多,比如选择做什么产品其实并不那么重要,而选择怎么做才重要。选择用什么人并不重要,而选择怎么带这些人才重要。大多数时候选择客观条件并不要紧,大多数关于客观条件的选择并没有对错之分,要紧的是选择怎么做。一个大学生毕业了,他要去微软也好,他要卖猪肉也好,他要创业也好,他要做游戏代练也好,只要不犯法,不害人,都没有什么关系,要紧的是,选择了以后,怎么把事情做好。

除了这些,你还可以选择时间和环境,比如,你可以选择把这辈子最大的困难放在最有体力最有精力的时候,也可以走一步看一步,等到了40岁再说,只是到了40多岁,那正是一辈子最脆弱的时候,上有老下有小,如果在那个时候碰上了职业危机,实在是一件很苦恼的事情。与其如此不如在20多岁30多岁的时候吃点苦,好让自己脆弱的时候活得从容一些。你可以选择在温室里成长,也可以选择到野外磨砺,你可以选择在办公室吹冷气的工作,也可以选择40度的酷热下,去见你的客户,只是,这一切最终会累积起来,引导你到你应得的未来。

我不敢说所有的事情你都有得选择,但是绝大部分事情你有选择,只是往往你不把这当作一种选择。认真对待每一次选择,才会有比较好的未来。


选择职业

职业的选择,总的来说,无非就是销售、市场、客服、物流、行政、人事、财务、技术、管理几个大类,有个有趣的现象就是,500强的CEO当中最多的是销售出身,第二多的人是财务出身,这两者加起来大概超过95%。现代IT行业也有技术出身成为老板的,但实际上,后来他们还是从事了很多销售和市场的工作,并且表现出色,公司才获得了成功,完全靠技术能力成为公司老板的,几乎没有。这是有原因的,因为销售就是一门跟人打交道的学问,而管理其实也是跟人打交道的学问,这两者之中有很多相通的东西,他们的共同目标就是“让别人去做某件特定的事情。”而财务则是从数字的层面了解生意的本质,从宏观上看待生意的本质,对于一个生意是否挣钱,是否可以正常运作有着最深刻的认识。

公司小的时候是销售主导公司,而公司大的时候是财务主导公司,销售的局限性在于只看人情不看数字,财务的局限性在于只看数字不看人情。公司初期,运营成本低,有订单就活得下去,跟客户也没有什么谈判的条件,别人肯给生意做已经谢天谢地了,这个时候订单压倒一切,客户的要求压倒一切,所以当然要顾人情。公司大了以后,一切都要规范化,免得因为不规范引起一些不必要的风险,同时运营成本也变高,必须提高利润率,把有限的资金放到最有产出的地方。对于上市公司来说,股东才不管你客户是不是最近出国,最近是不是那个省又在搞严打,到了时候就要把业绩拿出来,拿不出来就抛股票,这个时候就是数字压倒一切。

前两天听到有人说一句话觉得很有道理,开始的时候我们想“能做什么?”,等到公司做大了有规模了,我们想“不能做什么。”很多人在工作中觉得为什么领导这么保守,这也不行那也不行,错过很多机会。很多时候是因为,你还年轻,你想的是“能做什么”,而作为公司领导要考虑的方面很多,他比较关心“不能做什么”。

我并非鼓吹大家都去做销售或者财务,究竟选择什么样的职业,和你究竟要选择什么样的人生有关系,有些人就喜欢下班按时回家,看看书听听音乐,那也挺好,但就不适合找个销售的工作了,否则会是折磨自己。有些人就喜欢出风头,喜欢成为一群人的中心,如果选择做财务工作,大概也干不久,因为一般老板不喜欢财务太积极,也不喜欢财务话太多。先想好自己要过怎样的人生,再决定要找什么样的职业。有很多的不快乐,其实是源自不满足,而不满足,很多时候是源自于心不定,而心不定则是因为不清楚究竟自己要什么,不清楚要什么的结果就是什么都想要,结果什么都没得到。

我想,我们还是因为生活而工作,不是因为工作而生活,生活是最要紧的,工作只是生活中的一部分。我总是觉得生活的各方面都是相互影响的,如果生活本身一团乱麻,工作也不会顺利。所以要有娱乐、要有社交、要锻炼身体,要有和睦的家庭……最要紧的,要开心,我的两个销售找我聊天,一肚子苦水,我问他们,2年以前,你什么都没有,工资不高,没有客户关系,没有业绩,处于被开的边缘,现在的你比那时条件好了很多,为什么现在却更加不开心了?如果你做得越好越不开心,那你为什么还要工作?首先的首先,人还是要让自己高兴起来,让自己心态好起来,这种发自内心的改变会让你更有耐心,更有信心,更有气质,更能包容……否则,看看镜子里的你,你满意么?

有人会说,你说得容易,我每天加班,不加班老板就会把我炒掉,每天累得要死,哪有时间娱乐、社交、锻炼?那是人们把目标设定太高的缘故,如果你还在动不动就会被老板炒掉的边缘,那么你当然不能设立太高的目标,难道你还想每天去打高尔夫?你没时间去健身房锻炼身体,但是上下班的时候多走几步可以吧,有楼梯的时候走走楼梯不走电梯可以吧?办公的间隙扭扭脖子拉拉肩膀做做俯卧撑可以吧?谁规定锻炼就一定要拿出每天2个小时去健身房?你没时间社交,每月参加郊游一次可以吧,周末去参加个什么音乐班,绘画班之类的可以吧,去尝试认识一些同行,和他们找机会交流交流可以吧?开始的时候总是有些难的,但迈出这一步就会向良性循环的方向发展。而每天工作得很苦闷,剩下的时间用来咀嚼苦闷,只会陷入恶性循环,让生活更加糟糕。

星期三, 十月 15, 2008

"UNIX 网络编程"学习笔记

有点潦草,但总比没有强,放在博客上也好找,而且不用担心丢失:

(一) socket:

在 TCP 和 IP 之上的一个层

可靠:读出来一定是对的,但写入不一定能被对方收到。

while(1) {
ret = read(socket, fd, ...);
if(ret == 0 || ret == -1) {
break;
}
write(socket, fd, ...)
}
close(socket)

write BUG,部分写入,因为有 buffer
应该:
len = write(socket, fd ...)

close BUG, buffer 里面可能还有数据,如果此时就 close,就可能造成传输不完整。

BUFSIZE 不一致:
close 在 FIN 发出后就返回,不等 FIN+ACK 返回,对普通文件没问题,对套接字有风险
shutdown
LINGER 选项
用户层确认(应用层 ACK) 设计时精细点

STREAM 字节流
DGRAM 分组流,一次就是一个分组

UDP: SOCK_DGRAM
unreliable 对一个分组内部的数据还是可靠的,分组之间顺序不能确定

TCP Nagle 算法
小包消耗网络性能,所以小包先攒在 buffer 中,直到得到一个分段(MSS)或对方发出了一个确认

取消 Nagle: NODELAY 选项

writer
sendmsg


SOCK_SEQPACKET 是有序、双工的分组流

AF(Address Family) -> PF(Protocol Faminly) 为将来可能一个 Protocal 对应多个 Address
PF_LOCAL
PF_INET
PF_INET6

tcp_socket = socket(PF_INET, SOCK_STREAM, 0);
0 - protocol: IPPROTO_TCP


ADDRESS FORMAT (man 7 ip)


字节序 对齐 问题
字节序:
大端(先高后低) 0x00000005
小端 x86 0x05000000

主机序/网络序

四字节对齐:
struct {
char c[5];
int k;
int n;
}
sizeof() 返回 16

struct {
char c[5];
int k;
int n;
}__attribute__(packet()); GCC 扩展 limitation

回避字节序和对齐问题:用流式套接字 ASCII 码通信——仿照 HTTP 协议
\r\n
fprintf
fputs
fgets
fread
fwrite
用标准 IO
fp = fdopen(sd, "r+")
把一个 sd 封装在 fd 中,
要记得通信完之后用 fflush()
效率会低一点,但网络效率不会比 CPU 高,CPU 不会成为网络的瓶颈

解决半连接:
SYNCOOKIE
取消 backlog 池

C --- SYN s=m-1 --> S
C <-- ACK a=m --- S
<-- SYN s=m-1 ---
C --- ACK A=m --> S

以前的 m 是随即的,现在用 SIP/DIP/SP/DP/协议号 等做一个线性变换做一个哈希来生成 m(SHA1),一分钟增一

但是 SYNCOOKIE 一些 TCP 的高级功能就不能用了,不符合 RFC 了。

Linux 策略:当半连接池耗尽时再使用 SYNCOOKIE 策略。

(二) TCP/IP
以太网(10M 共享) 快速交换式以太网 协议(通信格式和通信规程——协议设计)
格式:以太帧 MTU==1500(由硬件规定的),以太帧最长 1500+18=1518
规程:CSMA/CD——半双工 交换式——全双工

dst(6 bytes), src(6 bytes), protocol(2 bytes, 0800 IP)

单播
广播 0xFFFFFF
多播 0x01.... 0x010053..(IPv4 多播)

IP 层(RFC791 文档):
PP 点到点
MA: BMA, NBMA 广播型多点访问接口(以太)

格式:
/usr/include/netinet/ip.h
/usr/include/linux/ip.h
TOS: 最小 DELAY(telnet), 最大吞吐(ftp), 最低。。 最高可靠 虽然 TCP 端口号有此信息,但为了路由器设计的复杂度的降低,需要此冗余信息
tot_len: 16^2=64K,但以太网只能达到 1500 bytes,FDDI 也只能达到 4K
ttl: 最大跳数
protocol: (可以看 /etc/protocols)
check:

规程:编址、路由(如果两条路由规则都匹配,精确优先——路由表已经排过序)

ip ro sh/show
ip ro add x.x.x.x/m { via GATEWAY | dev NIC }

IP 单播/广播/多播
主机模式/网关模式 ip_forward

多播包默认 TTL 是 1,即默认多播不穿过路由器

TCP UDP
UDP: 多点通讯(广播 DHCP/多播 netmeeting) 单报(DNS)
流控、错序、丢报及其重传

TCP 32 位序号(字节序号,不是分组序号)/ 32 位 ACK 号(我下面希望收到你哪个字节!) 不是一个分组一个 ACK,可以几个连续一起 ACK
SACK(selected ACK, 选择性 ACK)

停等式流控(适合于小时延小传输局域网):
Bps——峰值流量 Pps(packet data量/时间)
Pps = t传/(t传 + Rtt)
Round Trap Time(RTT)
t传:传输时间
减小 RTT,增加 T传

滑动窗口(适合于大时延大传输网络):
一次多传点,则相当于 t传 增加了,这些报文在路由器 buffer 中: size(网络容量) = Rtt * Bandwidth(瓶颈带宽),则滑动窗口设置为一个网络容量最合适
TCP 探测网络容量——动态滑动窗口
接收方:大小 = 剩余网络容量/2
发送方:测出拥塞窗口 慢启技术(滑动窗口逐渐增大,一旦发现丢报则减半:加性增加,乘性减小)

点到多点:超时窗口而不是确认窗口

流媒体:开环无反馈控制!均匀速率

TCP 状态: flags


域名解析:
gethostbyname() —— 线程不安全
getaddrinfo() 取代 gethostbyname() 和 getservicebyname()

格式转换:
inet_ntop()
inet_pton()


man 7 setsockopt
getsockopt()
setsockopt()
SO_REUSEADDR 可解决 TIME_WAIT 状态
SO_RCVTIMEO SO_SNDTIMEO 否则用 alarm() 来处理超时(普世方法),或使用 select

使用 alarm() 处理超时:
sigsetjmp();
alarm(...);
...
recv()
...
alarm(0);

SO_RCVLOWAT/SO_SNDLOWAT
当 select 时可以设置最少有多少字节可读/可写时再从 select()/poll() 返回

SO_RCVBUF 不能设置得比 SO_RCVLOWAT 小!

SO_LINGER 都应该设置成 LINGER: 防止 close() 时还有数据,尽可能确保缓冲中的数据清空——控制 close() 的返回时机
在对端给出 FIN ACK 后才 close(),这时基本上可避免 TIME_WAIT 状态
完美解决:用户态确认,否则 LINGER + shutdown()

两个常规选项:SO_REUSEADDR/SO_LINGER

网络程序设置!!!
模拟大时延网络:利用 Linux 的 tc 程序(bps)
模拟丢包率:iptables(pps)
www.netfilter.org
patch-o-magic-ng*.tar.bz2 只要 random 功能
补丁:random,打在内核和 iptables 命令中
iptables -t filter -A FORWARD -m random --random-average 3 -j DROP

SO_REUSEPORT(BSD) 对数据报套接字有意义 多播/广播通信

man 7 tcp
TCP 层的 setsockopt 选项:
level = IPPROTO_TCP
TCP_NODELAY: 禁止 Nagle 算法,提高响应速度,但传输性能下降
TCP_QUICKACK(非标准): 快速确认,默认接受方滑动窗口多收几个再确认
TCP_CORK(非标准 Linux): 往吞吐率方向优化,响应速度下降 相当与一个塞子/闸门!
TCP_DEFER_ACCEPT(Linux): 推迟接受,当有 data 过来时 accept() 才返回!这样在 accept() 之后的 read() 不会阻塞,但可以用 select() 实现
TCP_WINDOW_CLAMP(Linux): 确认窗口大小 拥塞窗口

man 7 ip
IP 层 setsockopt 选项:
level = IPPROTO_IP
IP_TOS: 互联网不支持 TOS,永远是先来先服务,在内部网关有用
IP_TTL: 默认 64
/proc/sys/net/ipv4/ip_default_ttl
IP_HDRINCL: 原始套接字
IP_RECVERR:
IP_MTU_DISCOVER: 避免分片 P(Path)MTU_DISCOVER,会利用 ICMP
/proc/sys/net/ipv4/ip_no_pmtu_disc
IP_MTU
IP_MULTICAST_TTL
IP_MULTICAST_LOOP

man 7 packet
链路层套接字


数据报:不明确 C S
UDP 也可以 connect(),但是是过滤的意思

变长分组(DNS) 例如 struct
发方:struct, 利用 malloc() 分配堆
struct {
int len;
int math;
int liter;
char name[1]; # 比着实际长度再去分配堆
};
收方:
man 2 recvfrom
MSG_PEEK 收报先仅仅拷贝一次,获得报头,从而先知道 len,再根据 len 去 malloc()
UDP 报头有一个长度段,可以用 ioctl() 获取(man 7 udp),可以不用 int len; ioctl() 非阻塞,所以可借助于 select()


多播/广播,多点通信必须使用 UDP
man 7 ip
广播地址:全网 255.255.255.255
子网 ip ! ~netmask
只能作为目标地址出现
man 7 socket
SO_BROADCAST
令牌桶-->漏桶,首先利用内核的 TC 做流量控制,并把进程设置为实时(但只能尽力,因为是分时)

多播:D 类 224.0.0.0/4 28 bit 多播组号
RFC -- 如果分配多播地址
224.0.2.0 ~ 224.0.254.255

ping 224.0.0.1

互联网不支持多播,只能用隧道,并配置多播路由

多播 TTL 默认是 1
IP_MULTICAST_TTL
IP_MULTICAST_LOOP 是否自己要收到
IP_DROP_MEMBERSHIP
IP_ADD_MEMBERSHIP


IPv6 没有广播的概念,只有多播,广播只是多播的特例

在内网中发信息用多播比较方便,效率和网络利用率更高


(三) 信号:BSD 模型
每个进程有两个标志:mask/pending(32 位,每个位代表一个信号),mask 全为 1,pending 全为 0,调用 kill() 时内核将相应的 pending 位置 1,处理的时候将相应的 mask 置 0。信号在下次进程被内核调度的时候才响应(分时)

信号丢失:在响应处理之前两次发送了同一个信号
信号不排队

新的接口函数:sigqueue() 可排队

sigprocmask(); sigsuspend(); 原子操作
pause();

一个进程何时被调度:
1. 休眠
2. 从内核回到用户态的过程中
3. 时钟终端响应

实时信号是按照到来顺序相应的,并且内核会先相应标准信号
/usr/include/signal.h
/usr/include/bites/signum.h
#define SIGRTMIN
#define SIGRTMAX

signal 最好不要和线程同时使用!!!因为 signal 是针对进程的,所有线程都会收到信号,而哪个线程来处理是随机的,除非所有子线程都显示的关闭对信号的处理(mask()),只由一个线程来处理。

每个线程都有自己的 mask/pending,向该进程发信号会置所有线程的 pending,一个线程响应的时候会把所有的 mask 置位!
man pthread_sigmask
sigprocmask

硬件层中断

sig_automic_t
但并不能保证线程安全,线程仍然需要互斥(mutex),因为可能有多个 CPU!

平台相关:
不要用 signal(),用 sigaction()
setjmp()/longjmp(),用 sigjmp()

Python GIL?


(四) 高级 I/O
非阻塞 fcntl()
多路 select(), poll(), (Linux epoll() 原生态调用)
信号驱动 SIGIO fnctl()
异步 aio

select(),休眠最好使用 select(), sleep() 可能会影响 alarm() (非 UNIX 平台)——因为只有一个信号!在有些平台上 sleep() 很可能就是用 alarm() 实现的!看 man 3 sleep。要么调用 nanosleep()

pselect(), const sigset_t *sigmask 参数将:
sigprocmask() 和 select() 原子化了!

一次系统调用可看做一个原子操作!在用户态完全可以这样认为。系统调用的原子性不是靠锁来保证的。
最新:内核可抢占!
服务器编译内核:如果可能应该关闭内核可抢占功能, 参数 preemtive
i++ 的操作不会被中断打断,是比系统调用更微观尺度上原子操作

poll()
ppoll()

epoll() epfd 也可以用 epoll() 监视,因此可以构造一个链式的 epoll()

异步:之前都考虑是阻塞和非阻塞,即等待时不阻塞,但读写的时候还是可能会阻塞,这里是纯异步
适合于需要大量操作文件描述符的情况

man 3 aio_read
aio_read()/aio_write()
aio_error()
aio_return()
Linux IO 调度器(块设备 BIO)

aio_error() 会盲等!但很多时候不是问题。

/usr/include/aio.h
struct aiocb{
...
struct sigevent aio_sigevent;
}

这样就可以用 sigsuspend() 去等待信号了!

/usr/include/bits/siginfo.h
sigevent 可以有三种通知方式 notify
不通知
信号通知
线程通知: 一个函数指针
SIGEV_SIGNAL
SIGEV_THREAD

aio_cancel()
aio_fsync()
aio_suspend()

要连接动态库 lrt.so

man -k aio_

(五) OpenSSL
单钥加密
公钥加密: RSA

一般先交换公钥,然后商定一个单钥通道

证书解决中间人攻击

服务器证书
openssl genrsa -out server.key
openssl req -new -key server.key -out server.csr
openssl req -new -x509 -days 365 -key server.key -out server.crt

SSL/BIO 接口

BIO: man bio
bio_read()
bio_write()


Kerberos GSSAPI

RFC/ANSI/POSIX

Questions:
1. SSL?
2. 应用协议设计规范?
3. select 和标准 IO 问题?
4. 多进程、线程选择?
5. 有没有嗅探器可以直接看到应用协议(基于行的)? wireshark
6. 书上下两册?

wget ref 参数?

应用层协议设计方法:使用状态机!!!!!!

C 语言名字空间问题?


client_ssl.c
#include
#include

int main() {
SSL_CTX *myctx;
SSL *myssl;

myctx = SSL_CTX_new(SSLv3_client_method());
if () {

}

// SSL_use_RSAPrivateKey_file();
// SSL_use_certificate_file();

myssl = SSL_new(myctx);
if ()
{
}

SSL_set_fd(myssl, newsd);
// 绑定到 socket sd
ret = SSL_connect(myssl);

/* Verify certificate */

if (ret...) {
SSL_read();
SSL_write();
}

SSL_free(myssl);
SSL_CTX_free(myctx);
}


server_ssl.c
#include
#include

int main() {
SSL_CTX *myctx;
SSL *myssl;

myctx = SSL_CTX_new(SSLv3_server_method());
if () {

}

SSL_use_RSAPrivateKey_file();
SSL_use_certificate_file();

myssl SSL_new(myctx);
if ()
{
}

SSL_set_fd(myssl, newsd);
// 绑定到 socket sd
ret = SSL_accept(myssl);

if (ret...) {
SSL_read();
SSL_write();
}

SSL_free(myssl);
SSL_CTX_free(myctx);
}


test.c
#include
#define BUFSIZE 1024

int send_data(int sd, int fd)
{
int ret, len, pos;

len = 0;
pos = 0
while(1) {
if (len == 0) {
len = read(fd, buf, BUFSIZE);
pos = 0;
if (ret == -1) {
...
}
if (ret == 0) {
break;
}
}
ret = write(sd, buf+pos, ret);
len -= ret;
pos += ret;
}
close(sd);
}


multicast_test.c
#include

#include

int xx() {
...
int val = 1;
struct ip_mreqn mreq;

ret = setsockopt(sd, IPPROTO_IP, IP_MULTICAST_LOOP, &val, sizeof(val));

inet_pton(AF_INET, "224.0.2.2", &mreq.imr_multiaddr);
inet_pton(AF_INET, "0.0.0.0", &mreq.imr_address);
mreq.imr_ifindex = if_nametoindex("eth0");
// 索引号: ip ad sh 时最前面显示的 id 值
ret = setsockopt(sd, IPPROTO_IP, IP_MULTICAST_IF, &mreq, sizeof(mreq));

ret = setsockopt(sd, IPPROTO_IP, IP_ADDMEMBERSHIP, &mreq, sizeof(mreq));
...
}

星期日, 九月 21, 2008

自省

不自见故明,不自是故彰,不自伐故有功,不自矜故长。夫唯不争,故天下莫能与之争。

过犹不及,做回我自己。

《内向者优势》这本书给我不少启发,发现年来费尽心思,却只是在做另一个人。很累,在另一条线上也不成功。

该换换思路了。

To be yourself.

当然,该做的事情还是得做,只是不要在用这些不恰当的方法了,只会使自己陷入更糟的境况。原则必须坚持,方法必须灵活。

当务之急,首先是尽快恢复精力。

星期三, 八月 27, 2008

工作进展

忙,实在没什么什么时间,挤点巴点巴一点点写吧。来杭州 5 个多月了,其他不论,在工作专业方面,还是有不少进展,总结下来大致有以下几点:

1. saunit 项目:第一天上岗,说要写一些 Trouble Shooting 自动化脚本,没人做,我说我来做吧,然后弄了个框架。不想越做越大,现在变成了整个公司监控项目中的一部分,现在我在忙活的就是这玩意... saunit-0.2.2 已经发布,可以做自动化检查(当然检查脚本还是要自己去扩展,只不过写起来就很方便也很规范了)。saunit-0.3 正在开发,需要支持并发和更强的调度功能,而且是作为整个分布式系统的代理端运行。

2. 设计模式:其实设计模式我之前没有系统学过,到实际用的时候就是野路子,而后来看来书后才发现我自己用的就是这么个东西——真是有悟性呀——自夸一下 ^^D。因为做项目的原因,实际使用时学习的效果是比较不错的。后来参加过三天外部讲师做的设计模式的培训,当时与大家讨论我的一些设计的时候,老师的评价是 cool,嘿嘿~~~ 而我那是基本上才刚刚看设计模式而已,嘿嘿~~~

现在看得比较多,用的比较好的有:策略模式、工厂方法、命令模式和单件模式等,而且因为 Python 的原因,多态非常好用。

上设计模式课的时候提到的一些企业应用的框架也蛮有启发性的,有时间还要好好看看。

3. 分布式系统:当 saunit 开始要扩大为监控系统的时候,我就开始考虑整个设计,并且希望能够做成分布式的维护系统而不仅仅是监控——这些灵感和想法最初就来自于弗洛.文奇的《天渊》。

所以去图书馆找了一些分布式系统的数来看,再次发现自己有悟性(^^P)——我无意中考虑使用了消息队列这种模式。另外,我也考虑了关于主机认证、协议设计等一些问题。

后来,虫虫介绍了一个叫 func(别看错了 呵呵 ^^$) 的东西,好像是 redhat 刚出的新东东,讨论过后,发现很多东西和我之前的想法比较接近,它的协议使用 XMLRPC + JSON,具体是如何还得有时间再研究。

4. 架构设计(控制和监视系统):做监控系统的时候,一开始没人,只能我来搞,文档都写好了,甚至已经开始动手做一些东西了,虽然可能也不咋地。好在后来有专人来负责了,也算是搞到了资源,虽然很多想法被裁剪了,有些甚至面目全非,让我觉得有些别扭,特别是现在做 saunit-0.3 实现的时候要按照那种思路,有时候觉得逻辑挺复杂的。

不过就当学习吧,实际上也有很多好的想法的确也是我以前没想到的。更多灵感。

5. 协议设计:主要一是应用协议设计中需要考虑哪些问题,如确认和断点续传、消息队列及其缓冲。另一个就是接触到 JSON 这个东西,感觉非常好,比较符合我这种讨厌 XML 的人的思维系统,而且我在考虑是否能和 Tree 解析,将 FORMAT 和 CONTENT 分离实现更大的数据传输吞吐效率,这样可以将更多网络 I/O 转变为 CPU/MEN 的操作。

google protocol buffers 太晦涩,用起来也不太方便,先不管了。呵呵。

另外就是现在因为项目的原因,需要对使用 UDP 做应用协议有更深入的学习,也不错。

6. TCP/IP 和网络编程,对 TCP 的一些东西了解得更多,比如停等时间、滑动窗口等,上次也参加了《UNIX 网络编程》的培训,还好,大部分内容还能懂,虽然那些 C 语言现在对我来说实在是有些艰深晦涩了... 我何时有时间再真正捡起来呢?

......

星期二, 七月 15, 2008

持之以恒

世界上没有什么东西能取代持之以恒的精神。
才华不能,有才华但不成功的人随处可见。
天赋不能,天赋无回报几乎是一句谚语。
教育不能,这个世界挤满了受过教育的被遗弃者。
只有毅力和决心,才是万能的。
------Woodrow.Wilson
(美国第28届总统\1919年诺贝尔国际和平奖获得者)

星期五, 七月 11, 2008

迟暮鸟语

迟暮鸟语
莎士比亚

在我身上你或许会看见秋天,
当黄叶,或尽脱,或只三三两两
挂在瑟缩的枯枝上索索抖颤——
荒废的歌坛,那里百鸟曾合唱。

在我身上你或许会看见暮霭,
它在日落后向西方徐徐消退:
黑夜,死的化身,渐渐把它赶开,
严静的安息笼住纷纭的万类。

在我身上你或许会看见余烬,
它在青春的寒灰里奄奄一息,
在惨淡灵床上早晚总要断魂,
给那滋养过它的烈焰所销毁。

看见了这些,你的爱就会加强,
因为他转瞬要辞你溘然长往。

星期五, 六月 20, 2008

指导性架构设计原则

  下面的指导性设计原则描述了我们的设计理念

* 只要某一功能的缺失不会导致无法完成某个实际的应用程序, 就不新增该功能。

* 决定系统不做成什么样子, 与决定将它做成什么样子同样重要。 不去满足所有的需要,而是让系统具备可扩展性, 使其能够向上兼容。

* 尽可能抽象代码中的通用部分, 除非没有可以用来抽象的实例。

* 如果没有完全理解一个问题, 最好干脆不提供任何解决方案。

* 如果能用 10% 的工作完成 90% 的工作, 则选择较简单的解决方案。

* 尽可能隔离复杂性。

* 提供机制而非策略。 具体而言, 将用户界面策略交由客户去选定。

  摘自 Scheifler & Gettys: "X Window System"

FreeBSD 开发手册

星期一, 六月 16, 2008

应用于 Python 的 vim 配置点滴

应用于 Python 的 vim 配置点滴(windows平台下)

http://blog.bobobook.cn/?p=42

  Python是我最喜欢的编程语言,而vim也是我最常用的编辑器,所以更好的配置vim来编辑Python源程序是很有必要的。下面谈谈偶学来的一点配置技巧。以下的大部分配置都是通过修改_vimrc文件来实现的,在Windows下它存在于vim的安装目录。


1、快捷调用使用的环境变量

  我习惯于把vim的可执行文件路径添加到PATH环境变量,这样平时再控制台下编程的时候可以很方便的启动vim。另外,vim使用的文件名还是略微长了一点。所以我一般将gvim.exe复制后改名为gi.exe。将vim.exe复制后改名为vi.exe。这样直接在控制台下执行gi和vi就可以分别启动vim的GUI版本和Console版本了。

2、在当前路径启动控制台

  很多时候需要在控制台下调试程序需要在当前路径启动一个cmd窗口,而有些路径相当的深或者含有大量中文字符的路径让我们很恼火。所以可以写如下一个简单的批处理文件来在当前文件夹下启动cmd窗口。

@echo off
cmd .

  保存为cmdhere.bat。这样将cmdhere.bat复制到想要的文件夹,直接双击这个批处理文件就可以打开想要的cmd窗口了,而且当前路径也是所在文件夹的路径,非常的方便。

3、关闭备份

  安装后的vim自动是具备备份功能的,一旦一个文件被修改就会生成~filename的备份文件。尽管对发生错误修改时的恢复有用,但是偶还是很不爽。所以关掉它才比较符合偶的习惯。

  在_vimrc文件末尾添加

set nobackup

  即可。

4、自动开启行号

  对编程的人来说行号实在是太重要的,因为很多时候调试中的错误定位就是需要行号的。在vim下直接输入:set nu可以开启行号功能,但是对于习惯于每次都开启行号功能的人来说,还是自动一些为好。编辑_vimrc文件,在末尾添加

set nu

  即可。

5、运行Python脚本的键盘映射

  可以在vim下按下一个快捷键来直接运行当前的Python脚本。如下的设置是使用F12键,且环境变量中已经添加了python.exe的情况。具体习惯和路径可以自己修改。这一行添加到_vimrc文件中。

map :!python.exe %

  这样就可以编辑Python脚本时,随时按下F12,再按下回车键来执行当前脚本。这里的执行时还有一个很贴心的功能,就是对于控制台脚本,执行后并不是直接退出,而是请求按下任意键后才退出。

6、使用ctags功能

  这个功能太强大了,只能这么说了,具体还有什么贴心功能可以自己探索。先讲讲安装。去sf.net下载一个ctags的Windows版本。注意,最新版本可能没有for win32版本的,所以需要向上推一个版本找找看。
下载后将其中解压出的ctags.exe文件放入vim的文件夹,当然,前提是vim的执行文件路径已经添加到PATH变量了。这时,按下gvim工具菜单的[建立],(by gashero)或者在普通模式输入

:!ctags -R

  这样可以在当前路径下建立一个tags文件,然后就可以在vim中使用跳转功能了。其中的-R选项是递归搜索子目录下的相关符号。所谓跳转就是可以随时转到自己想要的函数、类型、结构体、类等等的定义处。比如光标处于任意位置时可以普通模式下输入

:ta func_name

  就可以直接将光标定位到func_name函数处。当然,其他的命名也可以使用这种方法进行快速定位。另外就是可以在光标指向一个函数名时按下Ctrl+],马上跳转到这个函数的定义。在跳转到定义位置之后,可以按下Ctrl+o快速返回原来的编辑位置。

  需要明确以下ctags和tags的区别。ctags是一个程序用于生成tags文件。tags文件是当前目录下所有源文件的标签链接文件。在一个没有ctags的vim中只是无法生成新的tags文件,但是已有的tags文件仍然可以使用。

  如果tags文件不再当前目录下则用

:set tags=filename

  来指定。

7、开启代码折叠

  下载python_fold插件,解压后是python_fold.vim文件,放入plugin目录下。即可实现 Python代码的折叠支持。再次打开Python脚本时会发现所有的代码已经折叠了,其中还现了折叠部分拥有的行数。在折叠的行按下zo可以打开折叠,按下zc会折叠上代码。(by gashero)

  使用了代码折叠以后是否感觉到了vim已经成为了一种相当现代化的编辑器了。呵呵,后面还有精彩。

8、开启taglist功能

  taglist功能是在vim窗口左侧开启一个列表,提供ctags生成的tags文件中的所有符号。此时在 taglist窗口中找到需要跳转到的符号,定位光标,按下回车,即可将光标定位到右侧窗口的相关代码位置。使用十分的方便。当然要确保tags文件已经存在的情况下。另外,对于两个窗口之间的切换,我是习惯于按两次Ctrl+w的。
taglist是需要下载的一个taglist.vim文件,可以到sf.net上搜索及下载。

默认情况下taglist是不打开的。不同的是python_fold是默认打开的。手动打开,在普通模式下输入

:Tlist

  默认打开taglist的方法:_vimrc中加入

let Tlist_Auto_Open=1

星期日, 六月 15, 2008

The Road Not Taken

The Road Not Taken
Frost, Robert

TWO roads diverged in a yellow wood,
And sorry I could not travel both
And be one traveler, long I stood
And looked down one as far as I could
To where it bent in the undergrowth;

Then took the other, as just as fair,
And having perhaps the better claim,
Because it was grassy and wanted wear;
Though as for that the passing there
Had worn them really about the same,

And both that morning equally lay
In leaves no step had trodden black.
Oh, I kept the first for another day!
Yet knowing how way leads on to way,
I doubted if I should ever come back.

I shall be telling this with a sigh
Somewhere ages and ages hence:
Two roads diverged in a wood, and I—
I took the one less traveled by,
And that has made all the difference.

未选择的路

黄色的树林里分出两条路,
可惜我不能同时去涉足,
我在那路口久久伫立,
我向着一条路极目望去,
直到它消失在丛林深处。

但我却选了另外一条路,
它荒草萋萋,十分幽寂,
显得更诱人、更美丽,
虽然在这两条小路上,
都很少留下旅人的足迹,

虽然那天清晨落叶满地,
两条路都未经脚印污染。
呵,留下一条路等改日再见!
但我知道路径延绵无尽头,
恐怕我难以再回返。

也许多少年后在某个地方,
我将轻声叹息把往事回顾,
一片树林里分出两条路,
而我选了人迹更少的一条,
从此决定了我一生的道路。

刹那得永恒

Auguries of Innocence
William Blake

To see a world in a grain of sand,
And a heaven in a wild flower,
Hold infinity in the palm of your hand,
And eternity in an hour

一沙一世界
一花一天堂
掌中握无限
刹那得永恒

星期五, 六月 13, 2008

Python 用 list([...]) 后的 identify

>>> L1 = ['a', 'b', 'c']
>>> L2 = list(L1)
>>> L2 is L1
False
>>> L2[1] is L1[1]
True

L1 不是 L2,不过其元素一样。

ssh 避免密码提示的参数

ssh -n -o StrictHostKeyChecking=no -o PasswordAuthentication=no -o ConnectTimeout=1 $ip "uname -sr" | sed 's/^/ /g'

星期日, 五月 25, 2008

突然发现“行路难”里有“弹剑作歌”几个字

当时想到用“弹剑而歌”几个字做博客的名字,是因为看一个电视节目,说古人造剑可以发出铿铿的悠远的声音,是因为当时铸造技术比较成熟了,可以将剑身和剑柄铸造在一起,所以声音传播没有阻隔,故能悠远而轻扬。刚才却突然发现李白在一首《行路难》里面有“弹剑作歌”几个字!

行路难

李 白

大道如青天,我独不得出。
羞逐长安社中儿,赤鸡白雉赌梨栗。
弹剑作歌奏苦声,曳裾王门不称情。
淮阴市井笑韩信,汉朝公卿忌贾生。
君不见昔时燕家重郭隗,拥篲折节无嫌猜。
剧辛乐毅感恩分,输肝剖胆效英才。
昭王白骨萦蔓草,谁人更扫黄金台?
行路难,归去来!

不过我心中的形象却不是“奏苦歌”,而是那种提三尺剑,边走边唱的高亢,是“永远自由自我,永远高唱我歌”的潇洒,是“仰天长笑出门去,我辈岂是蓬蒿人”的豪迈和决绝。

风雨如磐暗故园

住处没有电视机,平时也是个心无旁骛不怎么关心时事政治的人,所以网上的新闻看得也少,而且因为刚刚入职需要尽快融入环境,又接手了项目和临时的事情,这样手头的事情也多了,最近心中的思虑其实也颇多的,对地震的事情,虽然也捐了几百大圆,但一直都没有多少感性的认识,每天就是上下班,沉浸在自己不断演进的设想中,虽然 19 号也默哀过了,但心中的触动其实只怕也有限。

昨天从网侠会回来,倒头先睡了两个小时,起来还是觉得有些头痛,不想写东西了,想想上上网吧,看看与地震相关的新闻,那些照片、那些报导这才触动内心感性的东西,查了查伤亡人数,与非典和 98 年洪水对比了一下,发现确实相当很严重,因为这两次印象还是蛮深刻的。今天也看了很久,最新报导说下午青川又有6.4级的余震。不禁想起鲁迅的诗:

灯下漫笔

灵台无计逃神矢,
风雨如磐暗故园。
寄意寒星荃不察,
我以我血荐轩辕。

虽然表面上是温和内敛的人,但实际上却自知内心的刚硬火烈、狂放不羁,今天去理发,老板说我头发硬、头发硬的人心也硬,想想可能也说得不差,但到底硬在什么地方呢?我想也许还是那种不达目的不罢休的二杆子倔劲吧。其实又何尝不曾多次感动、流泪和痛哭呢,只是决不愿示于人前。当自己还是一个倍感绝望的少年的时候,第一次听到黄家驹的歌,我曾经哭泣;《牛虻》曾经使我痛哭;《肖申克的救赎》和《闻香识女人》也曾令我数次感动;常读历史,每每掩卷,常常感慨,有时也会觉得眼角湿润;读林达的《近看美国》的时候也是;在上海的第一个夜晚,何尝不曾绝望;在百阿培训的时候,听讲马云的创业史以及听马云版的《在路上》的时候,我都是躲在角落里抹眼角... 而今天,我觉得,鼻子发酸。

这个民族实在是多灾多难,真的不知道还能坚持多少次?这振聋发聩的响动,不知道能惊醒多少人,能否激起某种精神和信念?或者最终我们只是回到老路上,继续浑浑噩噩的过日子?我们还剩多少血性?我们还剩下多少理想?

不敢说能够“血荐轩辕”,不敢说像于谦那样“风孰于高”,但至少应该“血仍未冷”。我想我现在能做的也就是把手头的这些想法尽力去实现,而现在对我来说也正是这样一个关键时期。本来第一天上岗的时候开周例会,说有个 trouble shooting 的脚本项目,我本来对编程设计、系统架构这块就比较感兴趣,就说我来做吧。因为以前也想到过能否把 trouble shooting 做得像 xUnit 那样,所以这次就正好利用这个机会,建立了这样一个框架,将实际的 Check Points 放到框架下,只要返回结果就可以了,这样就利于将来的扩展,而且可以不仅仅使用 Python,将也可以使用 Shell/Perl 甚至 C 语言来写检查点。

写出来,在上周六加班的时候实际上部署了一次,当然还是有很多问题。而之前,冯大师也表示出兴趣,说能否和监控结合起来,包括和监控负责人和主管这些人讨论下来,甚至希望能够将现在的 Nagios 系统替换掉,因为其性能和可维护性太差。

所以上周又赶了一下具体的需求,以及结合我以前已经产生的设想、在最近 Intel 交流的时候产生的一些新的设想都加进去,实际上在考虑整合的分布式管理系统架构了。当然,事情只能一步步做,饭只能一口口吃,必须把目标步子定得小有点,这样成功的希望才更大。

所以,最近就是要把这个 SaUnit 项目完成,包括新版本的框架,和所有现在需要有的、至少在 Trouble Shooting 上的检查点,我还希望能够乘此把 caxes 的 Tree/Table 完成并集成进去,实际上这周基本上已经完成了 Tree 的重构,去掉了 treedict 这个多余的设计并重新选择数据结构后,发现效率提高了 150 倍!这个要在下个月中旬之前完成,必须考虑到各种困难,还要保证所有的测试能够通过,所以其实时间也应该是挺紧张的。

不管怎么样,我觉得我找到了这样一个可以施展拳脚的地方,一个可能实现自己理想的机会,我知道,我心中的激情仍然没有熄灭,包括昨天在网侠会,虽有有些东西听的也是一头雾水,但毕竟还是能获得新的想法和灵感,甚至可以说是更宏大的设计。虽然这些年也走了很多弯路,也发生过动摇,但最终的抉择还是把我带到了这里——历史决定了我们是谁,我们在其中的幸运和遭遇以及做出的选择,决定了我们今天是怎样的人!不是吗?

吟唱李白的那首行路难吧:
金樽清酒斗十千, 玉盘珍羞直万钱。
停杯投箸不能食, 拔剑四顾心茫然。
欲渡黄河冰塞川, 将登太行雪满山。
闲来垂钓碧溪上, 忽复乘舟梦日边。
行路难,行路难, 多歧路,今安在?
长风破浪会有时, 直挂云帆济沧海。

星期四, 五月 22, 2008

Python 从生成器 "return" 而不是 "StopIteration"

在 ulfs/caxes 项目中的 tree.py 的 __revision__ = 268 版本中,我打算将 __traverse__() 方法做改进,以前返回一个 treedict,所以需要使用 tuple 作为其 pathseq,但现在打算完全取消 treedict,这样用 list 作为 pathseq 要合理得多,此时 __traverse__() 应该作为 generator 不断 yield 数据。

一开始编码如下:
 def __traverse__(self)
"""...
Comments
...
"""
id_self = id(self)
if self.__id_visited.has_key(id_self): return {}
# To avoid cyclic link problem!
Tree.__id_visited[id_self] = None
pathseq = self.__path_stack
yield pathseq, self
...
然后我在 Python 中尝试导入 tree 模块:
>>> import tree
Traceback (most recent call last):
File "", line 1, in
File "tree.py", line 399
yield pathseq, self
SyntaxError: 'return' with argument inside generator
这个错误实际上就是由于上面使用了 return,对 iterator/generator 来说,必须使用 StopIteration 异常处理来终止!
 def __traverse__(self):
id_self = id(self)
if self.__id_visited.has_key(id_self):
# return {}
raise StopIteration
# To avoid cyclic link problem!
Tree.__id_visited[id_self] = None
pathseq = self.__path_stack
yield pathseq, self
......

Python 迭代器和递归调用

如果不使用 for 循环,则只能返回第一次的数据!
#!/usr/bin/env python
# -*- encoding: utf-8 -*-

def iter(x=0):
if x < 100:
yield x * x
for y in iter(x + 1): yield y

for y in iter(): print y

星期一, 四月 21, 2008

cygwin rxvt 及其中文支持配置

安装 cygwin 本身是比较简单的,按照提示做就好了。但安装好之后,默认启用 Windows 的 cmd.exe 作为命令行功能是比较弱的,可以启动后再运行 rxvt,但发现中文输入输出有问题。

首先,设置 cygwin 使用 rxvt 作为默认的 CLI,编辑 cygwin.bat(C:\cygwin\cygwin.bat)文件,最后文件是这样的:
@echo off

C:
chdir C:\cygwin\bin

set CYGWIN=codepage:oem tty binmode title
rxvt -e bash --login -i
主要看最后两行。

为支持中文,编辑 ~/.bashrc 如下:
export LESSCHARSET=latin1
alias less='/bin/less -r'
alias ls='/bin/ls -F --color=tty --show-control-chars'
export LC_ALL=zh_CN.GBK
export LC_CTYPE=zh_CN.GBK
export LANG=zh_CN.GBK
# export XMODIFIERS="@im=Chinput"
# stty cs8-istrip
# stty pass8
export OUTPUT_CHARSET="GBK"
编辑 ~/.inputrc 如下:
set meta-flag on
set input-meta on
set convert-meta off
set output-meta on
set completion-ignore-case on
此时,如果使用的是 cmd.exe 作为 CLI,则已经可以输入输出中文了,但如果是 rxvt,则还需要修改 ~/.Xdefaults,因为 rxvt 中文显示和字体有关:
! ~/.Xdefaults - X default resource settings 
Rxvt*loginShell: True
Rxvt*font: -*-courier-medium-r-*-*-16-140-*-75-*-*-*-*
Rxvt*boldfont: -*-courier-bold-r-*-*-16-140-*-75-*-*-*-*
! Rxvt*font: -*-nsimsun-medium-r-*-*-16-140-*-75-*-*-*-*
! Rxvt*boldfont: -*-nsimsun-bold-r-*-*-16-140-*-75-*-*-*-*
Rxvt*SaveLines: 2000
Rxvt*background: #000020
Rxvt*foreground: #ffffbf
目前我只发现 courier 和 nsimsun(新宋) 字体可用。

另外,如果安装的是 native 版本的 gvim,可以直接调用,前提是已经将 gvim 加入到了 Windows 的 %PATH% 环境变量中了。因为 Windows 文件名的原因,如果直接运行 gvim ~/.Xdefaults 是不行的,但可以运行 gvim .Xdefaults。

星期一, 四月 14, 2008

Python open() 的 'U' 参数

看下面的一个 Python 脚本:
#!/usr/bin/python
# -*- encoding: utf-8 -*-

__author__ = "周鹏 "
__date__ = "14 April 2008"
__version__ = "0.1"

import re
import os,sys
import subprocess
from subprocess import PIPE

class CheckFail:
def __init__(self, message):
self.message = message

class CheckPoint:
"""
过滤 dmesg 输出以检查是否有硬件故障
"""
def __init__(self, args, info, config):
self.args = args
self.info = info
self.config = config
self.status = "OK"
self.strerr = ""
self.L_regexp = []
re_empty_line = re.compile("^\s*$")
for line in open(config, 'rU'):
if line.startswith("#"): continue
elif re_empty_line.match(line): continue
line = r"%s" % line.strip("\n")
self.L_regexp.append(re.compile(line))

def run(self):
pipe = subprocess.Popen(["dmesg"], stdout=PIPE, stderr=PIPE)
for line in pipe.stdout.readlines():
line = line.strip("\n")
for ro in self.L_regexp:
mo = ro.search(line)
if mo:
return "FAIL", "dmesg 硬件或核心故障: %s" % line
return self.status, self.strerr
这里使用 open(fname, 'rU') 从一个配置文件 config 中读取需要的过滤模式,然后对 dmesg 的输出进行匹配,因为开始编辑这个 config 文件的时候时在 Windows 下面,所以如果仅仅使用 'r',则读到的将是带 \r 作为换行符的串,那么当与在 Linux 下的文本进行过滤时,则完全不可能真正进行匹配!

所以这里使用了一个 'U' 参数。

星期日, 四月 06, 2008

较安全的 outlook 基本设置

安全性首先取决于对服务器的设置,即对于 SMTP 和 POP3 等都有必要的验证,同时通信使用加密连接如 SSL,此时对于 outlook 的设置如下:

"工具"->"电子邮件帐户..."->"添加/更改"

"用户信息"是出现在邮件头中的信息,"登陆信息"是指接收邮件使用 POP3 服务器时的登陆信息,"服务器信息"包括发送和接收的 POP3/SMTP 服务器,很可能是同一台服务器,例如:ssl.example.com。

然后进入"其他设置",在"发送服务器"标签中设置"我的发送服务器(SMTP)需要验证"以及"使用与接收邮件服务器(即 POP3)相同的设置",如不设置后者,则通常意味着 SMTP 和 POP3 使用了不同的服务器,或至少没有使用统一的账号系统。

在"高级"标签中,因为需要使用 SSL 加密,因此必须勾选相应选项,同时需要设定正确的端口,SMTP SSL 通常为 465,POP3 SSL 为 995;通常情况下,你可能需要在不同的主机上处理邮件,因此应该选择"在服务器上保留邮件的副本",这样就不至于导致在一台客户机上接收邮件后在另外一台客户机上无法处理。但此时无法保证设置和对邮件的处理结果保持一致,如何能使用 IMAP 才好!

星期六, 三月 08, 2008

Vienna

歌手:Billy Joel 专辑:Stranger

Slow down you crazy child
You're so ambitious for a juvenile
But then if you're so smart tell me why
You are still so afraid?
Where's the fire, what's the hurry about?
You better cool it off before you burn it out
You got so much to do and only so many hours in a day
But you know that when the truth is told
That you can get what you want
Or you an just get old
You're gonna kick off before you even get halfway through
When will you realize...Vienna waits for you

Slow down you're doing fine
You can't be everything you want to be before your time
Although it's so romantic on the borderline tonight
Too bad but it's the life you lead
You're so ahead of yourself
That you forget what's you need
Though you can see when you're wrong
But you know you can't always see when you're right
You got your passion you got your pride
But don't you know only fools are satisfied?
Dream on but don't imagine they'll all come true
When will you realize
Vienna waits for you

Slow down you crazy child
Take the phone off the hook and disappear for a while
It's alright you can afford to lose a day or two
When will you realize...
Vienna waits for you.

星期二, 一月 22, 2008

C static && dynamic library

首先看如下几个 C 源文件:
roc@ulfs:~/c$ cat main.c
#include
// #include "mod1.h"

void main() {
printf("Hello world: %d\n", func(8));
return;
}

roc@ulfs:~/c$ cat mod1.h
#include
// #include "mod2.c"
#include "mod2.h"

roc@ulfs:~/c$ cat mod2.h
int func(int i);

roc@ulfs:~/c$ cat mod2.c
#include

int func(int i) {
int I = i*i;
printf("i == %d\n", i);
return I;
}
然后进行编译并运行:
roc@ulfs:~/c$ gcc main.c mod2.c
roc@ulfs:~/c$ gcc main.c mod2.c
main.c: In function 'main':
main.c:4: warning: return type of 'main' is not 'int'
roc@ulfs:~/c$ ./a.out
i == 8
Hello world: 64
此时完全不需要头文件,编译器直接链接了。那么究竟何时需要头文件呢?

是不是和动态连接库有关呢?首先了解一下动态链接库:
roc@ulfs:~/c$ gcc -c main.c
roc@ulfs:~/c$ gcc -c mod2.c
roc@ulfs:~/c$ gcc -shared -o libmod2.so mod2.o
roc@ulfs:~/c$ gcc -shared -o libmod2.so.1 mod2.c
roc@ulfs:~/c$ diff libmod2.so libmod2.so.1
// 显示两个文件是完全一样的
roc@ulfs:~/c$ file libmod2.so
libmod2.so: ELF 32-bit LSB shared object, Intel 80386, version 1 (SYSV), not stripped
roc@ulfs:~/c$ file mod2.o
mod2.o: ELF 32-bit LSB relocatable, Intel 80386, version 1 (SYSV), not stripped
// 此时是两种不同的文件
然后尝试用两种方式编译:
roc@ulfs:~/c$ gcc -o dynamic.out main.o libmod2.so
roc@ulfs:~/c$ gcc -o dynamic.out.1 main.c libmod2.so
main.c: In function 'main':
main.c:4: warning: return type of 'main' is not 'int'
roc@ulfs:~/c$ diff dynamic.out dynamic.out.1
// 两个文件也是一样的
然后运行:
roc@ulfs:~/c$ ./dynamic.out
./dynamic.out: error while loading shared libraries: libmod2.so: cannot open shared object file: No such file or directory
roc@ulfs:~/c$ sudo vi /etc/ld.so.conf
...
/home/roc/c
# ADD THIS LINE
# 以确保运行时可以找到这个共享库,应该也可以使用 LD_LIBRARY_PATH 环境变量
roc@ulfs:~/c$ sudo ldconfig
roc@ulfs:~/c$ ./dynamic.out
i == 8
Hello world: 64
// 可以正确运行
// 此时仍然不需要使用头文件


# ******
roc@ulfs:~/c$ gcc -static -o static.out main.o libmod2.so
/usr/bin/ld: attempted static link of dynamic object `libmod2.so'
collect2: ld returned 1 exit status

# ****** ???

如果我不使用 libmod2.so 文件名,而使用 mod2.so 文件名会如何呢?
roc@ulfs:~/c$ gcc -shared -o mod2.so mod2.o
roc@ulfs:~/c$ diff mod2.so libmod2.so
roc@ulfs:~/c$ gcc -o dy.out main.o mod2.so
roc@ulfs:~/c$ ./dy.out
./dy.out: error while loading shared libraries: mod2.so: cannot open shared object file: No such file or directory
roc@ulfs:~/c$ LD_LIBRARY_PATH=. ./dy.out
i == 8
Hello world: 64
使用 libmod2.so 这种形式的文件名是为了在 gcc 上使用 -l 参数时更方便,能够直接找到相关的库而不需要指定具体的文件:

roc@ulfs:~/c$ gcc -o dynamic.out main.o -lmod2
/usr/bin/ld: cannot find -lmod2
collect2: ld returned 1 exit status
roc@ulfs:~/c$ gcc -o dynamic.out main.o -L. -lmod2
# 或者:
roc@ulfs:~/c$ LIBRARY_PATH=. gcc -o dynamic.out main.o -lmod2
使用 -L 或 LIBRARY_PATH 是为了保证在编译的时候能够找到需要的共享库文件。

到目前为止,没有看到必须要使用头文件的地方!

另外,可以了解一下 lib*.a 文件,它实际上是(archive)的简写,和 tar 文件非常类似,不过 tar 最初是使用磁带的,而 ar 直接将磁盘上的 *.o 对象文件打包到 *.a 档案文件中,因此可以供静态链接使用。可以用 ar -t 命令查看一个 *.la 文件。

gcc -o dynamic.out main.c libmod2.a ??????

星期二, 一月 08, 2008

Python bound/unbound methods

python class methods identity 中讨论过 instances 的 attributes/methods 的内存使用的问题,关于这一点,有另外一个说法,就是 Python 的 bound/unbound methods。

instances 的 methods 都是 bound methods,这些 methods 只有在实际调用这个方法的时候才会创建具体的 method 对象,执行结束后就会 destroy。从 python-list 上得到的回复是,虽然两个 id() 给出的值是一样的,但那只不过是一个误解,因为在前一个 instance 的 method 调用结束后,后一个 instance 的 method 会重复利用这段内存,结果造成 id() 的返回值一样。而在前面的讨论中,我们已经知道 is 是利用 im_self 来查看标识的,所以 is 测试返回 False。

一个 iptables 的奇怪需求和 TCP/IP netmask 问题

前几天 xuni 说能不能设置 iptables 控制某个网段,比如 192.168.0.10-100 的 IP 禁止掉,因为如果一个一个去增加,似乎效率太低,无论是对人还是对机器来说。

但似乎不太容易实现,首先是如何划分网段,另外这样划分和实际的网段设置(192.168.0.0/24)不同,是不是能够有效,特别是,中间作为网络地址和广播地址的 IP 能否生效?

首先解决第二个问题。随便找个计算子网及其掩码的工具,可以得到划分子网的网络地址和掩码,例如:192.168.0.96/255.255.255.224,也就是 192.168.0.96/28,则主机 IP 范围是 192.168.0.97-126,广播地址是 192.168.0.127,因为我自己的主机是 192.168.0.125,在这个网段之内。我在 iptables 中禁掉这个网段,发现有效,所以虽然网络配置是 192.168.0.0/24,但 iptables 并不受此影响。

再查看边界,将自己的 IP 改为 192.168.0.96 和 192.168.0.127,发现都可以有效的被禁止,所以不存在空洞的问题。

know IP/netmask, ask for net address 中讨论过从 IP/netmask 计算网络地址的问题,但这里第一个问题涉及到划分子网段时能否使用向 192.168.0.96/255.255.255.249 这样的掩码。这比较难于理解,所以考虑一个比较极端的例子,在《TCP/IP 详解 卷一 协议》中关于 TCP/IP 有一个这样的习题,即 255.255.0.255 这样的子网掩码是否合法。

答案是合法的,不过不符合习惯,因为难于理解,最主要的问题是其主机地址被分割在不连续的空间中了。例如:192.168.0.2/255.255.0.255,其结果是主机地址的范围是:192.168.[1-254].2。

所以如果按照习惯用法分配在一个连续地址空间中,则 10-100 这样的需求不可能直接实现,因为子网划分总是固定的,只能间接地通过若干子网和 IP 组合来实现。

但习题上说有 16 位的主机地址空间,但不是只有 8 位吗?