星期一, 五月 11, 2009

uuid of subversion

uuid of subversion

在做 subversion 迁移的时候,使用了 svnsync 来同步 repository 数据(编写的 svnsync.sh 脚本调用 svnadmin 和 svnsync init/sync 过程并日志),服务端的迁移没有什么问题,而客户端的重定位,直接使用 sw 不行:

  [root@vxy8_cfengine2 /tmp/cfengine.svn]
#svn sw http://172.22.35.10/svn/trunk/KS_CFENGINE/cfengine
svn: 'http://172.22.35.10/svn/trunk/KS_CFENGINE/cfengine'
is not the same repository as
'http://172.22.35.12/svn'

这就是说,直接使用 sw 需要在同一个主机的同一个 repository 下才行。

但 sw 有一个 --relocate 参数,可以切换 repository:

  [root@vxy8_cfengine2 /tmp/cfengine.svn]
#svn sw --relocate http://172.22.35.12/svn/trunk/KS_CFENGINE/cfengine http://172.22.35.10/svn/trunk/KS_CFENGINE/cfengine
svn: The repository at 'http://172.22.35.10/svn' has uuid '0ce980e2-6b68-4f57-841b-e7c3a84044d9', but the WC has '92f65df4-df84-11dc-ae99-89d8fa8a2030'

这个报错和 subversion 的 uuid 有关,这是由 svnsync 同步数据遗留的问题,源 repos 和新 repos 的 uuid 不一致,看看工作目录的情况:

  [root@vxy8_cfengine2 /tmp/cfengine.svn]
#svn info
Path: .
URL: http://172.22.35.12/svn/trunk/KS_CFENGINE/cfengine
Repository Root: http://172.22.35.12/svn
Repository UUID: 0ce980e2-6b68-4f57-841b-e7c3a84044d9
Revision: 2837
Node Kind: directory
Schedule: normal
Last Changed Author: roczhou.zhoup
Last Changed Rev: 2835
Last Changed Date: 2009-05-11 10:50:24 +0800 (Mon, 11 May 2009)

使用 svnlook uuid 可以查看 repos 的 uuid。下面是源 repos

  [root@vxy8_cfengine2 /tmp/cfengine.svn]
#svnlook uuid /home/admin/data/svn_repo/
0ce980e2-6b68-4f57-841b-e7c3a84044d9

下面是目标 repos

  [root@vxy7_cfengine1 /var/cfengine.svn]
#svnlook uuid /home/admin/data/svn_repo
92f65df4-df84-11dc-ae99-89d8fa8a2030

解决办法是,对于 subversion-1.5 以上,可直接在运行 svnadmin setuuid 来设置源或目标 repos 的 uuid 使其保持一致,对低于 subversion-1.5 的系统,示例如下(更改源 repos):

  [root@vxy8_cfengine2 /tmp/cfengine.svn]
#vi /tmp/x

SVN-fs-dump-format-version: 2

UUID: 92f65df4-df84-11dc-ae99-89d8fa8a2030

[root@vxy8_cfengine2 /tmp/cfengine.svn]
#cat /tmp/x | svnadmin load --force-uuid /home/admin/data/svn_repo/

#svnlook uuid /home/admin/data/svn_repo/
92f65df4-df84-11dc-ae99-89d8fa8a2030

[root@vxy8_cfengine2 /tmp/cfengine.svn]
#svn info
Path: .
URL: http://172.22.35.12/svn/trunk/KS_CFENGINE/cfengine
Repository Root: http://172.22.35.12/svn
Repository UUID: 0ce980e2-6b68-4f57-841b-e7c3a84044d9
Revision: 2837
Node Kind: directory
Schedule: normal
Last Changed Author: roczhou.zhoup
Last Changed Rev: 2835
Last Changed Date: 2009-05-11 10:50:24 +0800 (Mon, 11 May 2009)

# 此时还没变

[root@vxy8_cfengine2 /tmp/cfengine.svn]
#svn sw --relocate http://172.22.35.12/svn/trunk/KS_CFENGINE/cfengine http://172.22.35.10/svn/trunk/KS_CFENGINE/cfengine
svn: The repository at 'http://172.22.35.10/svn' has uuid '51c9629f-21a9-4eaa-825b-20ccd5c61100', but the WC has '92f65df4-df84-11dc-ae99-89d8fa8a2030'

[root@vxy8_cfengine2 /tmp/cfengine.svn]
#svn up

[root@vxy8_cfengine2 /tmp/cfengine.svn]
#svn info
Path: .
URL: http://172.22.35.12/svn/trunk/KS_CFENGINE/cfengine
Repository Root: http://172.22.35.12/svn
Repository UUID: 92f65df4-df84-11dc-ae99-89d8fa8a2030
Revision: 2837
Node Kind: directory
Schedule: normal
Last Changed Author: roczhou.zhoup
Last Changed Rev: 2835
Last Changed Date: 2009-05-11 10:50:24 +0800 (Mon, 11 May 2009)

# 此时已经改变

[root@vxy8_cfengine2 /tmp/cfengine.svn]
#svn sw --relocate http://172.22.35.12/svn/trunk/KS_CFENGINE/cfengine http://172.22.35.10/svn/trunk/KS_CFENGINE/cfengine
svn: The repository at 'http://172.22.35.10/svn' has uuid '51c9629f-21a9-4eaa-825b-20ccd5c61100', but the WC has '92f65df4-df84-11dc-ae99-89d8fa8a2030'

但运行还是出错,这说明,更改源 repos 的 uuid 无效!

那么尝试更改目标的 uuid(下面让 vxy8_cfengine2 作为目标,vxy7_cfengine1 作为源)

  [root@vxy8_cfengine2 /tmp/cfengine.svn]
#!286
cat /tmp/y | svnadmin load --force-uuid /home/admin/data/svn_repo/

[root@vxy8_cfengine2 /tmp/cfengine.svn]
#svnlook uuid /home/admin/data/svn_repo/
0ce980e2-6b68-4f57-841b-e7c3a84044d9

[root@vxy7_cfengine1 /tmp/cfengine.svn]
#svn info
Path: .
URL: http://172.22.35.10/svn/trunk/KS_CFENGINE/cfengine
Repository Root: http://172.22.35.10/svn
Repository UUID: 92f65df4-df84-11dc-ae99-89d8fa8a2030
Revision: 2842
Node Kind: directory
Schedule: normal
Last Changed Author: roczhou.zhoup
Last Changed Rev: 2842
Last Changed Date: 2009-05-11 14:52:36 +0800 (Mon, 11 May 2009)

[root@vxy7_cfengine1 /tmp/cfengine.svn]
#svn sw --relocate http://172.22.35.10/svn/trunk/KS_CFENGINE/cfengine http://172.22.35.12/svn/trunk/KS_CFENGINE/cfengine
svn: The repository at 'http://172.22.35.12/svn' has uuid '92f65df4-df84-11dc-ae99-89d8fa8a2030', but the WC has '0ce980e2-6b68-4f57-841b-e7c3a84044d9'

[root@vxy8_cfengine2 /tmp/cfengine.svn]
#cat /tmp/x | svnadmin load --force-uuid /home/admin/data/svn_repo/

[root@vxy8_cfengine2 /tmp/cfengine.svn]
#svnlook uuid /home/admin/data/svn_repo/
92f65df4-df84-11dc-ae99-89d8fa8a2030

[root@vxy7_cfengine1 /tmp/cfengine.svn]
#svn sw --relocate http://172.22.35.10/svn/trunk/KS_CFENGINE/cfengine http://172.22.35.12/svn/trunk/KS_CFENGINE/cfengine

[root@vxy7_cfengine1 /tmp/cfengine.svn]
#svn info
Path: .
URL: http://172.22.35.12/svn/trunk/KS_CFENGINE/cfengine
Repository Root: http://172.22.35.12/svn
Repository UUID: 92f65df4-df84-11dc-ae99-89d8fa8a2030
Revision: 2848
Node Kind: directory
Schedule: normal
Last Changed Author: roczhou.zhoup
Last Changed Rev: 2847
Last Changed Date: 2009-05-11 16:15:01 +0800 (Mon, 11 May 2009)

这里已经完成了切换。所以,要统一 uuid,需要更改目标 repos 的 uuid。

另外,从上面的例子可以看出,修改了 repos 的 uuid 之后,对于已经检查(checkout/co)的工作目录不会有影响,当其下一次 svn up 之后,其 info 中的 uuid 即进行了更新。

星期二, 十二月 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年诺贝尔国际和平奖获得者)