Skip to content

生活琐碎

当开幕式的火炬点燃的时候,我正在公司的角落里跟 iebook 搏斗着——本来闹运开幕这天是可以不必来公司,可以在家上班的,但为了挣一点零花钱,答应帮别人做一个电子书,于是就不辞辛苦的又赶到公司,从早上11点一直做到晚上10点。

原以为家里的本本太慢,公司的台式机应该会好一些,哪知道 iebook 程序做的那么烂,当页数超过 50 页的时候,就卡的不行了:时不时的报 “out of memory”(程序都没有处理一下报错信息,直接把 window 函数的返回信息给打印了出来),打开一个元素需要30秒,插入一副图片需要50秒,还有无法保存项目文件,这才是最致命的。

央视的开幕式,看起来烂的不行,一如每年的春晚。但后来看了 NBC 的版本才知道,原来开幕式本身,确实不错。开幕式是一个不错的造星机会,不管是唱歌的小女孩,还是领队的小男孩,“出世十年无人问,一夜闹运天下知”。

9号,去唐智星暂时住的地方,在委托方的要求下对“朗诵古诗文对高中生心理的影响” 电子书作最后的润色定稿。2G内存的机器,跑 iebook 还是那么慢,一边做电子杂志,一边看中国队在闹运上争金夺银。好不容易才算最终完成,外面已经是万家灯火了。

10号电子书交付通过,去拿钱,却刚好碰到北京的暴雨。大约因为开幕式的时候,所谓的人工干预降雨,虽然保证了当时没有下雨,不为人知的副作用却还是有的——河北暴雨,北京暴雨,接连几天,四处天象大乱。手握未知的力量,虽然强大,却不受控制,一旦释放,看得见的利与看不见的害,如何权衡取舍?

周一上班,与源源,志勇一同搬下到了17层。搬来搬去,按源源的话说,是已经麻木了。这回搬到的位置却还不错,正朝西:中国化工楼顶的草坪似乎就在眼前,小树,假山,静默的凉亭;稍远是更高的第三极;越过第三极的玻璃墙面,远远的西山绵延起伏。

搬到这边,工作环境从 CentOS 换到 FreeBSD ,Bash 也换成了 Csh 。继续用 CSF (Common Server Framework),搭建 Sina S3 (Simple Storage Service)的 Dispatcher 。

大量小文件的实时同步方案

 传统的文件同步方案有rsync(单向) 和 unison(双向)等,它们需要扫描所有文件后进行比对,差量传输。如果文件数量达到了百万甚至千万量级,扫描所有文件将非常耗时。而且正在发生变化的往往是其中很少的一部分,这是非常低效的方式。

之前看了Amazon的Dynamo的设计文档, 它们每个节点的数据是通过Hash Tree来实现同步,既有通过日志来同步的软实时特点(msyql, bdb等),也可以保证最终数据的一致性(rsync, unison等)。Hash Tree的大体思路是将所有数据存储成树状结构,每个节点的Hash是其所有子节点的Hash的Hash,叶子节点的Hash是其内容的Hash。这样一 旦某个节点发生变化,其Hash的变化会迅速传播到根节点。需要同步的系统只需要不断查询跟节点的hash,一旦有变化,顺着树状结构就能够在logN级 别的时间找到发生变化的内容,马上同步。

文件系统天然的是树状结构,尽管不是平衡的数。如果文件的修改时间是可靠的,可以表征文件的变化,那就可以用它作为文件的Hash值。另一方面,文 件的修改通常是按顺序执行的,后修改的文件比早修改的文件具有更大的修改时间,这样就可以把一个目录内的最大修改时间作为它的修改时间,以实现Hash Tree。这样,一旦某个文件被修改,修改时间的信息就会迅速传播到根目录。

一般的文件系统都不是这样做的,目录的修改时间表示的是目录结构最后发生变化的时间,不包括子目录,否则会不堪重负。因为我们需要自己实现这个功 能,利用Linux 2.6内核的新特性inotify获得某个目录内文件发生变化的信息,并把其修改时间传播到它的上级目录(以及再上级目录)。Python 有 pyinotify,watch.py的代码如下:

  1. #!/usr/bin/python  
  2.   
  3. from pyinotify import *  
  4. import osos.path  
  5.   
  6. flags = IN_CLOSE_WRITE|IN_CREATE|IN_Q_OVERFLOW  
  7. dirs = {}  
  8. base = ’/log/lighttpd/cache/images/icon/u241′  
  9. base = ’tmp’  
  10.   
  11. class UpdateParentDir(ProcessEvent):  
  12.     def process_IN_CLOSE_WRITE(self, event):  
  13.         print ’modify’, event.pathname  
  14.         mtime = os.path.getmtime(event.pathname)  
  15.         p = event.path  
  16.         while p.startswith(base):  
  17.             m = os.path.getmtime(p)  
  18.             if m < mtime:  
  19.                 print ’update‘, p  
  20.                 os.utime(p, (mtime,mtime))  
  21.             elif m > mtime:  
  22.                 mtime = m  
  23.             p = os.path.dirname(p)  
  24.       
  25.     process_IN_MODIFY = process_IN_CLOSE_WRITE  
  26.   
  27.     def process_IN_Q_OVERFLOW(self, event):  
  28.         print ’over flow’  
  29.         max_queued_events.value *= 2  
  30.   
  31.     def process_default(self, event):  
  32.         pass  
  33.   
  34. wm = WatchManager()  
  35. notifier = Notifier(wm, UpdateParentDir())  
  36. dirs.update(wm.add_watch(base, flags, rec=True, auto_add=True))  
  37.   
  38. notifier.loop()  

在已经有Hash Tree的时候,同步就比较简单了,不停地获取根目录的修改时间并顺着目录结构往下找即可。需要注意的是,在更新完文件后,需要设置修改时间为原文件的修改时间,目录也是,保证Hash Tree的一致性,否则没法同步。mirror.py的代码如下

#!/usr/bin/python  

  1.   
  2. import sys,time,re,urllib  
  3. import os,os.path  
  4. from os.path import exists, isdir, getmtime  
  5.   
  6. src = sys.argv[1]  
  7. dst = sys.argv[2]  
  8.   
  9. def local_mirror(src, dst):  
  10.     if exists(dst) and mtime == getmtime(dst):  
  11.         return  
  12.     if not isdir(src):  
  13.         print ’update:’, dst  
  14.         open(dst,’wb’).write(open(src).read())  
  15.     else:  
  16.         if not exists(dst):  
  17.             os.makedirs(dst)  
  18.         for filename in os.listdir(src):  
  19.             local_mirror(os.path.join(src,filename), os.path.join(dst,filename))  
  20.     os.utime(dst, (mtime,mtime))  
  21.   
  22. def get_info(path):  
  23.     f = urllib.urlopen(path)  
  24.     mtime = f.headers.get(‘Last-Modified’)  
  25.     if mtime:  
  26.         mtime = time.mktime(time.strptime(mtime, ’%a, %d %b %Y %H:%M:%S %Z’))  
  27.     content = f.read()  
  28.     f.close()  
  29.     return int(mtime), content  
  30.   
  31. p = re.compile(r’([\d.]+?) +([\w/]+)’)  
  32.   
  33. def remote_mirror(src, dst):  
  34.     mtime, content = get_info(src)  
  35.     if exists(dst) and mtime == int(getmtime(dst)):  
  36.         return  
  37.     print ’update:’, dst, src  
  38.     if not src.endswith(‘/’):  
  39.         open(dst,’wb’).write(content)  
  40.     else:  
  41.         if not exists(dst):  
  42.             os.makedirs(dst)  
  43.         for mt,filename in p.findall(content):  
  44.             mt = int(float(mt))  
  45.             lpath = dst+filename  
  46.             if not exists(lpath) or int(getmtime(lpath)) != mt:  
  47.                 remote_mirror(src+filename, lpath)  
  48.     os.utime(dst, (mtime,mtime))  
  49.   
  50. if src.startswith(‘http://’):  
  51.     mirror = remote_mirror  
  52. else:  
  53.     mirror = local_mirror  
  54.   
  55. while True:  
  56.     mirror(src, dst)  
  57.     time.sleep(1)  

如果源文件不在同一台机器上,可以通过NFS等共享过来。或者可以通过支持列目录的HTTP服务器来访问远程目录,mirror.py 已经支持这种访问方式。server.py 是用webpy做的一个简单的只是列目录的文件服务器。由于瓶颈在IO上,它的性能不是关键。server.py的代码如下:

#!/usr/bin/python  

  1.   
  2. import os,os.path  
  3. import web  
  4. import time  
  5.   
  6. root = ’tmp’  
  7.   
  8. HTTP_HEADER_TIME = ’%a, %d %b %Y %H:%M:%S %Z’  
  9.   
  10. class FileServer:  
  11.     def GET(self, path):  
  12.         path = root + path  
  13.         if not os.path.exists(path):  
  14.             return 404  
  15.         mtime = time.localtime(os.path.getmtime(path))  
  16.         web.header(‘Last-Modified’, time.strftime(HTTP_HEADER_TIME, mtime))  
  17.         if os.path.isdir(path):  
  18.             for file in os.listdir(path):  
  19.                 if file.startswith(‘.’): continue  
  20.                 p = os.path.join(path,file)  
  21.                 m = os.path.getmtime(p)  
  22.                 if os.path.isdir(p):  
  23.                     file += ’/'  
  24.                 print m, file  
  25.         else:  
  26.             print open(path,’rb’).read()  
  27.   
  28. urls = (  
  29.    ”(/.*)”, ”FileServer”,  
  30. )  
  31.   
  32. if __name__ == ’__main__‘:  
  33.     web.run(urls, globals())  


为了获得更好性能,以达到更好的实时性,Hash Tree最好是平衡的,比如BTree。如果一个文件发生变化,同步它需要进行的IO操作为N*M,其中N为数的层数,M为每层的文件数目。现在我们N为 2,M最大为10000,适当减少它可以获得更好的性能,比如N为4,M为100。在以后创建目录结构时,最好能够考虑这方面的因素。

参考:灵感点滴 (http://blog.daviesliu.net/)

sysctl.conf 参考配置及说明

###################
所有rfc相关的选项都是默认启用的
###################

net.inet.ip.sourceroute=0
net.inet.ip.accept_sourceroute=0
#############################
通过源路由,攻击者可以尝试到达内部IP地址 –包括RFC1918中的地址,所以
不接受源路由信息包可以防止你的内部网络被探测。
#################################

net.inet.tcp.drop_synfin=1
###################################
安全参数,编译内核的时候加了options TCP_DROP_SYNFIN才可以用,可以阻止某些OS探测。
##################################

kern.maxvnodes=8446
###########################
vnode 是对文件或目录的一种内部表达。 因此, 增加可以被操作系统利用的 vnode 数量将降低磁盘的 I/O。
一般而言, 这是由操作系统自行完成的,也不需要加以修改。但在某些时候磁盘 I/O 会成为瓶颈,
而系统的 vnode 不足, 则这一配置应被增加。此时需要考虑是非活跃和空闲内存的数量。
要查看当前在用的 vnode 数量:
# sysctl vfs.numvnodes
vfs.numvnodes: 91349
要查看最大可用的 vnode 数量:
# sysctl kern.maxvnodes
kern.maxvnodes: 100000
如果当前的 vnode 用量接近最大值,则将 kern.maxvnodes 值增大 1,000 可能是个好主意。
您应继续查看 vfs.numvnodes 的数值, 如果它再次攀升到接近最大值的程度,
仍需继续提高 kern.maxvnodes。 在 top(1) 中显示的内存用量应有显著变化,
更多内存会处于活跃 (active) 状态。
####################################

kern.maxproc: 964
###########################
Maximum number of processes
####################################
kern.maxprocperuid: 867
###########################
Maximum processes allowed per userid
####################################
因为我的maxusers设置的是256,20+16*maxusers=4116。
maxprocperuid至少要比maxproc少1,因为init(8) 这个系统程序绝对要保持在运作状态。
我给它设置的2068。

kern.maxfiles: 1928
###########################
系统中支持最多同时开启的文件数量,如果你在运行数据库或大的很吃描述符的进程,那么应该设置在20000以上,
比如kde这样的桌面环境,它同时要用的文件非常多。
一般推荐设置为32768或者65536。
####################################

kern.argmax: 262144
###########################
maximum number of bytes (or characters) in an argument list.
命令行下最多支持的参数,比如你在用find命令来批量删除一些文件的时候
find . -name “*.old” -delete,如果文件数超过了这个数字,那么会提示你数字太多的。
可以利用find . -name “*.old” -ok rm {} \;来删除。
默认的参数已经足够多了,因此不建议再做修改。
####################################

kern.securelevel: -1
###########################
-1:这是系统默认级别,没有提供任何内核的保护错误;
0:基本上作用不多,当你的系统刚启动就是0级别的,当进入多用户模式的时候就自动变成1级了。
1:在这个级别上,有如下几个限制:
a. 不能通过kldload或者kldunload加载或者卸载可加载内核模块;
b. 应用程序不能通过/dev/mem或者/dev/kmem直接写内存;
c. 不能直接往已经装在(mounted)的磁盘写东西,也就是不能格式化磁盘,但是可以通过标准的内核接口执行写操作;
d. 不能启动X-windows,同时不能使用chflags来修改文件属性;
2:在 1 级别的基础上还不能写没装载的磁盘,而且不能在1秒之内制造多次警告,这个是防止DoS控制台的;
3:在 2 级别的级别上不允许修改IPFW防火墙的规则。
如果你已经装了防火墙,并且把规则设好了,不轻易改动,那么建议使用3级别,如果你没有装防火墙,而且还准备装防火墙的话,不建议使用。
我们这里推荐使用 2 级别,能够避免比较多对内核攻击。
####################################

kern.maxfilesperproc: 1735
###########################
每个进程能够同时打开的最大文件数量,网上很多资料写的是32768
除非用异步I/O或大量线程,打开这么多的文件恐怕是不太正常的。
我个人建议不做修改,保留默认。
####################################

kern.ipc.maxsockbuf: 262144
###########################
最大的套接字缓冲区,网上有建议设置为2097152(2M)、8388608(8M)的。
我个人倒是建议不做修改,保持默认的256K即可,缓冲区大了可能造成碎片、阻塞或者丢包。
####################################

kern.ipc.somaxconn: 128
###########################
最大的等待连接完成的套接字队列大小,即并发连接数。
高负载服务器和受到Dos攻击的系统也许会因为这个队列被塞满而不能提供正常服务。
默认为128,推荐在1024-4096之间,根据机器和实际情况需要改动,数字越大占用内存也越大。
####################################

kern.ipc.nmbclusters: 4800
###########################
这个值用来调整系统在开机后所要分配给网络 mbufs 的 cluster 数量,
由于每个 cluster 大小为 2K,所以当这个值为 1024 时,也是会用到 2MB 的核心内存空间。
假设我们的网页同时约有 1000 个联机,而 TCP 传送及接收的暂存区大小都是 16K,
则最糟的情况下,我们会需要 (16K+16K) * 1024,也就是 32MB 的空间,
然而所需的 mbufs 大概是这个空间的二倍,也就是 64MB,所以所需的 cluster 数量为 64MB/2K,也就是 32768。
对于内存有限的机器,建议值是 1024 到 4096 之间,而当拥有海量存储器空间时,我们可以将它设定为 4096 到 32768 之间。
我们可以使用 netstat 这个指令并加上参数 -m 来查看目前所使用的 mbufs 数量。
要修改这个值必须在一开机就修改,所以只能在 /boot/loader.conf 中加入修改的设定
kern.ipc.nmbclusters=32768
####################################

kern.ipc.shmmax: 33554432
###########################
共享内存和信号灯(“System VIPC”)如果这些过小的话,有些大型的软件将无法启动
安装xine和mplayer提示的设置为67108864,即64M,
如果内存多的话,可以设置为134217728,即128M
####################################

kern.ipc.shmall: 8192
###########################
共享内存和信号灯(“System VIPC”)如果这些过小的话,有些大型的软件将无法启动
安装xine和mplayer提示的设置为32768
####################################

kern.ipc.shm_use_phys: 0
###########################
如果我们将它设成 1,则所有 System V 共享内存 (share memory,一种程序间沟通的方式)部份都会被留在实体的内存 (physical memory) 中,
而不会被放到硬盘上的 swap 空间。我们知道物理内存的存取速度比硬盘快许多,而当物理内存空间不足时,
部份数据会被放到虚拟的内存上,从物理内存和虚拟内存之间移转的动作就叫作 swap。如果时常做 swap 的动作,
则需要一直对硬盘作 I/O,速度会很慢。因此,如果我们有大量的程序 (数百个) 需要共同分享一个小的共享内存空间,
或者是共享内存空间很大时,我们可以将这个值打开。
这一项,我个人建议不做修改,除非你的内存非常大。
####################################

kern.ipc.shm_allow_removed: 0
###########################
共享内存是否允许移除?这项似乎是在fb下装vmware需要设置为1的,否则会有加载SVGA出错的提示
作为服务器,这项不动也罢。
####################################

kern.ipc.numopensockets: 12
###########################
已经开启的socket数目,可以在最繁忙的时候看看它是多少,然后就可以知道maxsockets应该设置成多少了。
####################################

kern.ipc.maxsockets: 1928
###########################
这是用来设定系统最大可以开启的 socket 数目。如果您的服务器会提供大量的 FTP 服务,
而且常快速的传输一些小档案,您也许会发现常传输到一半就中断。因为 FTP 在传输档案时,
每一个档案都必须开启一个 socket 来传输,但关闭 socket 需要一段时间,如果传输速度很快,
而档案又多,则同一时间所开启的 socket 会超过原本系统所许可的值,这时我们就必须把这个值调大一点。
除了 FTP 外,也许有其它网络程序也会有这种问题。
然而,这个值必须在系统一开机就设定好,所以如果要修改这项设定,我们必须修改 /boot/loader.conf 才行
kern.ipc.maxsockets=”16424″
####################################

kern.ipc.nsfbufs: 1456
###########################
经常使用 sendfile(2) 系统调用的繁忙的服务器,
有必要通过 NSFBUFS 内核选项或者在 /boot/loader.conf (查看 loader(8) 以获得更多细节) 中设置它的值来调节 sendfile(2) 缓存数量。
这个参数需要调节的普通原因是在进程中看到 sfbufa 状态。sysctl kern.ipc.nsfbufs 变量在内核配置变量中是只读的。
这个参数是由 kern.maxusers 决定的,然而它可能有必要因此而调整。
在/boot/loader.conf里加入
kern.ipc.nsfbufs=”2496″
####################################

kern.maxusers: 59
###########################
maxusers 的值决定了处理程序所容许的最大值,20+16*maxusers 就是你将得到的所容许处理程序。
系统一开机就必须要有 18 个处理程序 (process),即便是简单的执行指令 man 又会产生 9 个 process,
所以将这个值设为 64 应该是一个合理的数目。
如果你的系统会出现 proc table full 的讯息的话,可以就把它设大一点,例如 128。
除非您的系统会需要同时开启很多档案,否则请不要设定超过 256。

可以在 /boot/loader.conf 中加入该选项的设定,
kern.maxusers=256
####################################

kern.coredump: 1
###########################
如果设置为0,则程序异常退出时不会生成core文件,作为服务器,不建议这样。
####################################

kern.corefile: %N.core
###########################
可设置为kern.corefile=”/data/coredump/%U-%P-%N.core”
其中 %U是UID,%P是进程ID,%N是进程名,当然/data/coredump必须是一个实际存在的目录
####################################

vm.swap_idle_enabled: 0
vm.swap_idle_threshold1: 2
vm.swap_idle_threshold2: 10
#########################
在有很多用户进入、离开系统和有很多空闲进程的大的多用户系统中很有用。
可以让进程更快地进入内存,但它会吃掉更多的交换和磁盘带宽。
系统默认的页面调度算法已经很好了,最好不要更改。
########################

vfs.ufs.dirhash_maxmem: 2097152
#########################
默认的dirhash最大内存,默认2M
增加它有助于改善单目录超过100K个文件时的反复读目录时的性能
建议修改为33554432(32M)
#############################

vfs.vmiodirenable: 1
#################
这个变量控制目录是否被系统缓存。大多数目录是小的,在系统中只使用单个片断(典型的是1K)并且在缓存中使用的更小 (典型的是512字节)。
当这个变量设置为关闭 (0) 时,缓存器仅仅缓存固定数量的目录,即使您有很大的内存。
而将其开启 (设置为1) 时,则允许缓存器用 VM 页面缓存来缓存这些目录,让所有可用内存来缓存目录。
不利的是最小的用来缓存目录的核心内存是大于 512 字节的物理页面大小(通常是 4k)。
我们建议如果您在运行任何操作大量文件的程序时保持这个选项打开的默认值。
这些服务包括 web 缓存,大容量邮件系统和新闻系统。
尽管可能会浪费一些内存,但打开这个选项通常不会降低性能。但还是应该检验一下。
####################

vfs.hirunningspace: 1048576
############################
这个值决定了系统可以将多少数据放在写入储存设备的等候区。通常使用默认值即可,
但当我们有多颗硬盘时,我们可以将它调大为 4MB 或 5MB。
注意这个设置成很高的值(超过缓存器的写极限)会导致坏的性能。
不要盲目的把它设置太高!高的数值会导致同时发生的读操作的迟延。
#############################

vfs.write_behind: 1
#########################
这个选项预设为 1,也就是打开的状态。在打开时,在系统需要写入数据在硬盘或其它储存设备上时,
它会等到收集了一个 cluster 单位的数据后再一次写入,否则会在一个暂存区空间有写入需求时就立即写到硬盘上。
这个选项打开时,对于一个大的连续的文件写入速度非常有帮助。但如果您遇到有很多行程延滞在等待写入动作时,您可能必须关闭这个功能。
############################

net.local.stream.sendspace: 8192
##################################
本地套接字连接的数据发送空间
建议设置为65536
###################################
net.local.stream.recvspace: 8192
##################################
本地套接字连接的数据接收空间
建议设置为65536
###################################

net.inet.ip.portrange.lowfirst: 1023
net.inet.ip.portrange.lowlast: 600
net.inet.ip.portrange.first: 49152
net.inet.ip.portrange.last: 65535
net.inet.ip.portrange.hifirst: 49152
net.inet.ip.portrange.hilast: 65535
###################
以上六项是用来控制TCP及UDP所使用的port范围,这个范围被分成三个部份,低范围、预设范围、及高范围。
这些是你的服务器主动发起连接时的临时端口的范围,预设的已经1万多了,一般的应用就足够了。
如果是比较忙碌的FTP server,一般也不会同时提供给1万多人访问的,
当然如果很不幸,你的服务器就要提供很多,那么可以修改first的值,比如直接用1024开始
#########################

net.inet.ip.redirect: 1
#########################
设置为0,屏蔽ip重定向功能
###########################

net.inet.ip.rtexpire: 3600
net.inet.ip.rtminexpire: 10
########################
很多apache产生的CLOSE_WAIT状态,这种状态是等待客户端关闭,但是客户端那边并没有正常的关闭,于是留下很多这样的东东。
建议都修改为2
#########################

net.inet.ip.intr_queue_maxlen: 50
########################
Maximum size of the IP input queue,如果下面的net.inet.ip.intr_queue_drops一直在增加,
那就说明你的队列空间不足了,那么可以考虑增加该值。
##########################
net.inet.ip.intr_queue_drops: 0
####################
Number of packets dropped from the IP input queue,如果你sysctl它一直在增加,
那么增加net.inet.ip.intr_queue_maxlen的值。
#######################

net.inet.ip.fastforwarding: 0
#############################
如果打开的话每个目标地址一次转发成功以后它的数据都将被记录进路由表和arp数据表,节约路由的计算时间
但会需要大量的内核内存空间来保存路由表。
如果内存够大,打开吧,呵呵
#############################

net.inet.ip.random_id: 0
#####################
默认情况下,ip包的id号是连续的,而这些可能会被攻击者利用,比如可以知道你nat后面带了多少主机。
如果设置成1,则这个id号是随机的,嘿嘿。
#####################

net.inet.icmp.maskrepl: 0
############################
防止广播风暴,关闭其他广播探测的响应。默认即是,无须修改。
###############################

net.inet.icmp.icmplim: 200
##############################
限制系统发送ICMP速率,改为100吧,或者保留也可,并不会给系统带来太大的压力。
###########################
net.inet.icmp.icmplim_output: 1
###################################
如果设置成0,就不会看到提示说Limiting icmp unreach response from 214 to 200 packets per second 等等了
不过禁止输出容易让我们忽视攻击的存在。这个自己看着办吧。
######################################

net.inet.icmp.drop_redirect: 0
net.inet.icmp.log_redirect: 0
###################################
设置为1,屏蔽ICMP重定向功能
###################################
net.inet.icmp.bmcastecho: 0
############################
防止广播风暴,关闭广播ECHO响应,默认即是,无须修改。
###############################

net.inet.tcp.mssdflt: 512
net.inet.tcp.minmss: 216
###############################
数据包数据段最小值,以上两个选项最好不动!或者只修改mssdflt为1460,minmss不动。
原因详见#/security/2007/1211/article_4.html
#############################

net.inet.tcp.keepidle: 7200000
######################
TCP的套接字的空闲时间,默认时间太长,可以改为600000(10分钟)。
##########################

net.inet.tcp.sendspace: 32768
###########################
最大的待发送TCP数据缓冲区空间,应用程序将数据放到这里就认为发送成功了,系统TCP堆栈保证数据的正常发送。
####################################
net.inet.tcp.recvspace: 65536
###################################
最大的接受TCP缓冲区空间,系统从这里将数据分发给不同的套接字,增大该空间可提高系统瞬间接受数据的能力以提高性能。
###################################
这二个选项分别控制了网络 TCP 联机所使用的传送及接收暂存区的大小。预设的传送暂存区为 32K,而接收暂存区为 64K。
如果需要加速 TCP 的传输,可以将这二个值调大一点,但缺点是太大的值会造成系统核心占用太多的内存。
如果我们的机器会同时服务数百或数千个网络联机,那么这二个选项最好维持默认值,否则会造成系统核心内存不足。
但如果我们使用的是 gigabite 的网络,将这二个值调大会有明显效能的提升。
传送及接收的暂存区大小可以分开调整,
例如,假设我们的系统主要做为网页服务器,我们可以将接收的暂存区调小一点,并将传送的暂存区调大,如此一来,我们就可以避免占去太多的核心内存空间。

net.inet.udp.maxdgram: 9216
#########################
最大的发送UDP数据缓冲区大小,网上的资料大多都是65536,我个人认为没多大必要,
如果要调整,可以试试24576。
##############################
net.inet.udp.recvspace: 42080
##################
最大的接受UDP缓冲区大小,网上的资料大多都是65536,我个人认为没多大必要,
如果要调整,可以试试49152。
#######################
以上四项配置通常不会导致问题,一般说来网络流量是不对称的,因此应该根据实际情况调整,并观察其效果。
如果我们将传送或接收的暂存区设为大于 65535,除非服务器本身及客户端所使用的操作系统都支持 TCP 协议的 windows scaling extension (请参考 RFC 1323 文件)。
FreeBSD默认已支持 rfs1323 (即 sysctl 的 net.inet.tcp.rfc1323 选项)。
###################################################

net.inet.tcp.log_in_vain: 0
##################
记录下任何TCP连接,这个一般情况下不应该更改。
####################

net.inet.tcp.blackhole: 0
##################################
建议设置为2,接收到一个已经关闭的端口发来的所有包,直接drop,如果设置为1则是只针对TCP包
#####################################

net.inet.tcp.delayed_ack: 1
###########################
当一台计算机发起TCP连接请求时,系统会回应ACK应答数据包。
该选项设置是否延迟ACK应答数据包,把它和包含数据的数据包一起发送。
在高速网络和低负载的情况下会略微提高性能,但在网络连接较差的时候,
对方计算机得不到应答会持续发起连接请求,反而会让网络更加拥堵,降低性能。
因此这个值我建议您看情况而定,如果您的网速不是问题,可以将封包数量减少一半
如果网络不是特别好,那么就设置为0,有请求就先回应,这样其实浪费的网通、电信的带宽速率而不是你的处理时间:)
############################

net.inet.tcp.inflight.enable: 1
net.inet.tcp.inflight.debug: 0
net.inet.tcp.inflight.rttthresh: 10
net.inet.tcp.inflight.min: 6144
net.inet.tcp.inflight.max: 1073725440
net.inet.tcp.inflight.stab: 20
###########################
限制 TCP 带宽延迟积和 NetBSD 的 TCP/Vegas 类似。
它可以通过将 sysctl 变量 net.inet.tcp.inflight.enable 设置成 1 来启用。
系统将尝试计算每一个连接的带宽延迟积,并将排队的数据量限制在恰好能保持最优吞吐量的水平上。
这一特性在您的服务器同时向使用普通调制解调器,千兆以太网,乃至更高速度的光与网络连接 (或其他带宽延迟积很大的连接) 的时候尤为重要,
特别是当您同时使用滑动窗缩放,或使用了大的发送窗口的时候。
如果启用了这个选项,您还应该把 net.inet.tcp.inflight.debug 设置为 0 (禁用调试),
对于生产环境而言, 将 net.inet.tcp.inflight.min 设置成至少 6144 会很有好处。
然而, 需要注意的是,这个值设置过大事实上相当于禁用了连接带宽延迟积限制功能。
这个限制特性减少了在路由和交换包队列的堵塞数据数量,也减少了在本地主机接口队列阻塞的数据的数量。
在少数的等候队列中、交互式连接,尤其是通过慢速的调制解调器,也能用低的 往返时间操作。
但是,注意这只影响到数据发送 (上载/服务端)。对数据接收(下载)没有效果。
调整 net.inet.tcp.inflight.stab 是 不 推荐的。
这个参数的默认值是 20,表示把 2 个最大包加入到带宽延迟积窗口的计算中。
额外的窗口似的算法更为稳定,并改善对于多变网络环境的相应能力,
但也会导致慢速连接下的 ping 时间增长 (尽管还是会比没有使用 inflight 算法低许多)。
对于这些情形, 您可能会希望把这个参数减少到 15, 10, 或 5;
并可能因此而不得不减少 net.inet.tcp.inflight.min (比如说, 3500) 来得到希望的效果。
减少这些参数的值, 只应作为最后不得已时的手段来使用。
############################

net.inet.tcp.syncookies: 1
#########################
SYN cookies是一种用于通过选择加密的初始化TCP序列号,可以对回应的包做验证来降低SYN’洪水’攻击的影响的技术。
默认即是,不需修改
########################

net.inet.tcp.msl: 30000
#######################
这个值网上很多文章都推荐的7500,
还可以改的更小一些(如2000或2500),这样可以加快不正常连接的释放过程(三次握手2秒、FIN_WAIT4秒)。
#########################
net.inet.tcp.always_keepalive: 1
###########################
帮助系统清除没有正常断开的TCP连接,这增加了一些网络带宽的使用,但是一些死掉的连接最终能被识别并清除。
死的TCP连接是被拨号用户存取的系统的一个特别的问题,因为用户经常断开modem而不正确的关闭活动的连接。
#############################

net.inet.udp.checksum: 1
#########################
防止不正确的udp包的攻击,默认即是,不需修改
##############################

net.inet.udp.log_in_vain: 0
#######################
记录下任何UDP连接,这个一般情况下不应该修改。
#######################

net.inet.udp.blackhole: 0
####################
建议设置为1,接收到一个已经关闭的端口发来的所有UDP包直接drop
#######################

net.inet.raw.maxdgram: 8192
#########################
Maximum outgoing raw IP datagram size
很多文章建议设置为65536,好像没多大必要。
######################################
net.inet.raw.recvspace: 8192
######################
Maximum incoming raw IP datagram size
很多文章建议设置为65536,好像没多大必要。
#######################

net.link.ether.inet.max_age: 1200
####################
调整ARP清理的时间,通过向IP路由缓冲填充伪造的ARP条目可以让恶意用户产生资源耗竭和性能减低攻击。
这项似乎大家都未做改动,我建议不动或者稍微减少,比如300(HP-UX默认的5分钟)
#######################

net.inet6.ip6.redirect: 1
###############################
设置为0,屏蔽ipv6重定向功能
###########################

net.isr.direct: 0
###########################
所有MPSAFE的网络ISR对包做立即响应,提高网卡性能,设置为1。
####################################

hw.ata.wc: 1
#####################
这个选项用来打开 IDE 硬盘快取。当打开时,如果有数据要写入硬盘时,硬盘会假装已完成写入,并将数据快取起来。
这种作法会加速硬盘的存取速度,但当系统异常关机时,比较容易造成数据遗失。
不过由于关闭这个功能所带来的速度差异实在太大,建议还是保留原本打开的状态吧,不做修改。
###################

security.bsd.see_other_uids: 1
security.bsd.see_other_gids: 1
#####################
不允许用户看到其他用户的进程,因此应该改成0,
#######################

生活琐碎

几个小事,记录一下:

1. 换了一个手机,多普达 575 (新浪 | ZOL) ,试用了几天才发现,原来射频的很厉害。

2. 18号到20号,互动博客,系统开发部等一起出去玩,去了月陀岛(百度百科),一些照片:http://picasaweb.google.com/birdring/JUOKaE

3. 新浪互动全面向亚马逊看齐:S3 (简单存储服务,Simple Storage Service ),SDB (简单数据库,Simple Database),EC2 (虚拟化云计算平台,Elastic Compute Cloud),SQS (简单队列服务,Simple Queue Service),还有已经发布 1.0 版本的 SDD (简单的分布式 key value 存储系统,Simple Dynamo Daemon),继续 2.0 版之旅。

4. 新版 SSO 系统,我负责的在线状态 bug 修正的差不多了,庆祝一下。(提供UDP和HTTP访问,支持简单的聚合查询,高负载(每台前端机器可支持 1.5 万/秒的并发访问),可扩展,就是可维护性差点,后期需要定制一些监控工具,错误处理流程等等)

64位下C程序的可移植性(64-bit Portability)


代码在64位和32位的系统中,原则上应该都比较友好,尤其对于输出、比较、结构对齐(structure alignment)来说:

1) printf()指定的一些类型在32位和64位系统上可移植性不是很好,C99标准定义了一些可移植的格式。不幸的是,MSVC 7.1并非全部支持,而且标准中也有所遗漏。所以有时我们就不得不自己定义丑陋的版本(使用标准风格要包含文件inttypes.h):

// printf macros for size_t, in the style of inttypes.h
#ifdef _LP64
#define __PRIS_PREFIX "z"
#else
#define __PRIS_PREFIX
#endif

// Use these macros after a % in a printf format string
// to get correct 32/64 bit behavior, like this:
// size_t size = records.size();
// printf("%"PRIuS"\n", size);

#define PRIdS __PRIS_PREFIX "d"
#define PRIxS __PRIS_PREFIX "x"
#define PRIuS __PRIS_PREFIX "u"
#define PRIXS __PRIS_PREFIX "X"
#define PRIoS __PRIS_PREFIX "o"

类型 不要使用 使用 备注
void *(或其他指针类型) %lx %p
int64_t %qd, %lld %"PRId64"
uint64_t %qu, %llu, %llx %"PRIu64", %"PRIx64"
size_t %u %"PRIuS", %"PRIxS" C99指定%zu
ptrdiff_t %d %"PRIdS" C99指定%zd

注意宏PRI*会被编译器扩展为独立字符串,因此如果使用非常量的格式化字符串,需要将宏的值而不是宏名插入格式中,在使用宏PRI*时同样可以在%后指定长度等信息。例如,printf("x = %30"PRIuS"\n", x)在32位Linux上将被扩展为printf("x = %30" "u" "\n", x),编译器会处理为printf("x = %30u\n", x)

2) 记住sizeof(void *) != sizeof(int),如果需要一个指针大小的整数要使用intptr_t

3) 需要对结构对齐加以留心,尤其是对于存储在磁盘上的结构体。在64位系统中,任何拥有int64_t/uint64_t成员的类/结构体将默认被处理为8字节对齐。如果32位和64位代码共用磁盘上的结构体,需要确保两种体系结构下的结构体的对齐一致。大多数编译器提供了调整结构体对齐的方案。gcc中可使用__attribute__((packed)),MSVC提供了#pragma pack()__declspec(align())

4) 创建64位常量时使用LLULL作为后缀,如:

int64_t my_value = 0x123456789LL;
uint64_t my_mask = 3ULL << 48;

5) 如果你确实需要32位和64位系统具有不同代码,可以在代码变量前使用。(尽量不要这么做,使用时尽量使修改局部化)。

密码保护:新浪:关于开放API平台的思考

这是一篇受密码保护的文章。您需要提供访问密码:


转:时间是什么

转自:atppp (http://blog.wuxinan.net/)

序言

时间是什么?物理学家是不会研究这么无聊的哲学问题的。先看看费曼大师在《QED: The Strange Theory of Light and Matter》书中对精细结构常数的看法:

It’s one of the greatest damn mysteries of physics: a magic number that comes to us with no understanding by man. You might say the “hand of God” wrote that number, and “we don’t know how He pushed His pencil.” We know what kind of a dance to do experimentally to measure this number very accurately, but we don’t know what kind of a dance to do on a computer to make this number come out — without putting it in secretly!

再来看看 Fang 的《中华第一系物理讲义页边集》第二卷之林宗涵热力学统计物理讲义:

58 年林宗涵到一个很土的化工工厂劳动,问那里的一个人如何理解熵,那人答,查一查表不就得了?

说明什么道理呢?说明很多东西,知其然不知其所以然就可以了,没必要深究到底是什么。时间这个东西,也是一样的,物理学家关心的问题,只是怎么精确的测量时间,怎么有一个时间单位的共识;至于时间到底是啥子东西,那就让吃饱了饭没事干的哲学家们去意淫好了……

那么首先,时间如何测量呢?最直接的方法就是要有一个稳定周期的东西,这样只要数周期数目就可以了。以前的单摆就是数那个摆来回的次数,现代的石英 钟呢,本质就是数那个石英振荡的次数。可是这些玩意儿每个人作出来都不一样,难以有世界范围的共识,那么怎么达成共识呢?人们想到了天体运动,这个玩意儿 是不以人的意志而转移的。第一个真正意义上世界公认的时间单位标准就是 1960 年规定的地球绕太阳跑一圈儿的周期(一太阳年)为三千一百五十多万秒。(这个年到秒的换算关系我的记忆方法是,一纳世纪约等于圆周率秒:1 nano-century = π seconds 。这个记忆方法普遍认为是贝尔实验室的 Tom Duff 最先提出来的。)

其实,早在几百年前,人们就已经发现天体运动周期并不是很稳定。在太阳年规定之前,世界范围基本公认但没有达成真正共识的秒定义是用太阳日(地球自 转周期)规定的。地球是个很无厘头的东西,一会儿跑得快,一会儿跑得慢,所以后来 1960 年开会,大家还是用稍微更稳定一些的太阳年来规定时间单位标准。当然,当时大家都知道地球公转是越来越慢了,所以那时候规定的太阳年,是用 1900 年那一年的太阳年。初初看来,这是一个无比弱智的定义,过都过去了,谁还能测量几十年前的太阳年。事实上,几百年前的天文测量已经相当精确,所以一百多年 前人们就已经能预测出二十世纪太阳年长度的变化规律了。因此,要实践这个太阳年的标准,只要测量当年的太阳年并换算回 1900 年的就可以了。

无论如何,这种太阳年标准的规定还是很土鳖,抗议声也此起彼伏,于是原子物理学家就开始浑水摸鱼了。当时,原子物理的发展让人们有足够的能力驾驭原 子。人们发现铯原子两个稳定基态之间的振荡频率是很稳定的、不受人的意志而转移的、可以很精确的测量的、而且更重要的是不会像地球一样无厘头会越振越慢 的……于是 1967 年的时候开了一次大会,把这个振荡频率规定为了 9192631770 Hz。这个规定一直沿用至今,而且越来越多的研究表明,当初这个规定的选择极具前瞻性,现在的时间测量精度已经比当初进步了四五个数量级,而铯原子就是这 么给面子,这么高的测量精度下仍旧看不出它那个振荡频率有什么不稳定……

一 。

上次说到最近几个世纪以来,人类对计时的要求越来越高了,那么到底是什么地方对计时有如此高的要求呢?这个问题问懂行的人,恐怕十个有九个会首先想到导航。

十五世纪左右,探险者开始出海远航,给自己的航船定位是一个很重要的问题。用仪器观测天象(太阳,月亮或者星星) 可以精确的测量纬度,可是由于地球自转,测量经度不但需要精确的天象观测,还需要一个精确的钟。在那个没有好钟的年代里面,海上导航是很困难的,导航失误 常常导致海难。1707 年 Cloudesley Shovell 因为算错了自己的位置,和另外三艘军舰相撞,发生大海难,死了两千多个人,人们开始重视海上导航的问题。其实这次海难的原因主要是因为有雾看不见天象而算 错了纬度,不过经度测量一直是最大的问题。牛顿在 1714 年指出:

for determining the Longitude at Sea, there have been several Projects, true in the Theory, but difficult to execute: One is, by a Watch to keep Time exactly: But, by reason of the Motion of a Ship, the Variation of Heat and Cold, Wet and Dry, and the difference of Gravity in different Latitudes, such a Watch hath not yet been made.

制造钟表的人当然也不是吃素的。伽利略很早就制作出了单摆,并提出了用单摆做钟的可能性。1657 年 Christiaan Huygens 发明了第一个单摆钟,一天只走差 10 秒。我们一般用相对误差来表示钟表的准确度,这个钟的准确度就是 10秒/1天(86400秒)= 10-4。后来 1726 年 John Harrison 造了一个一个月只差一秒(4 x 10-7) 的钟,不过正如牛顿指出的,船的运动和温湿度变化导致这些基于单摆的钟在船上都是不可能精确的。1714 年,英国政府的 The Board of Longitude 悬赏两万英镑奖励精度 30 海里的导航方案(Longitude Act)。这需要一个在船上一天只差三秒(3 x 10-5)的钟。结果还是这个 John Harrison,把他后半辈子的心血都用在了研制精确钟表上,最终造出了基于发条的计时器,1761 年在船上试用,一天只差一秒(10-5),并拿走了这个大奖。

在后来的几百年里,导航技术有了长足的进步,钟表制造虽然也发展了很多,但是在导航中的地位却越来越不重要了。历史总是会反复的,如今最先进的民用 导航系统——全球定位系统(GPS),其核心技术之一就是卫星上的原子钟(主要是铯原子和铷原子做的钟)。这些原子钟的精度达到 10-13, 比前面提到的几百年前的技术提高了好多个数量级。为什么又需要这么精确的钟?GPS 的基本原理就是三边定位,如果卫星位置已知,那么接收器只要测出到三颗卫星的距离就可以列三个方程把经度、纬度、高度三个未知数解出来。可是这里的问题是 测量到卫星的距离是通过卫星广播信号的时间差来测的,这就需要所有卫星和地面接收器的时间高度同步。卫星还好说,可每个接收器都带一个原子钟那就太土鳖 了。所以 GPS 真正的方案是,把 GPS 卫星上的时间也当作未知数,用四颗卫星信号列四个方程把经度、纬度、高度和 GPS 时间都算出来,这样接收器的成本就低很多了。可以看到,GPS 的核心除了高度精确的卫星轨道外,还有各个 GPS 卫星之间高度同步的时间。这个时间同步需要什么样的精度呢?GPS 的设计定位误差在 10 米左右,除以光速等于 30 纳秒,也就是说卫星之间的时间同步至少需要保持在 30 纳秒之内。现代 GPS 卫星的设计标准是可以几天才和地面对一次时,这样算下来就很明白了,30 纳秒除以 3 天,卫星上的原子钟的相对误差需要在 10-13 的量级。

10-13 是个什么概念?一百万年才差三秒钟……如果光看这句话,恐怕十个人有十个人会嘲笑说物理学家吃饱了撑的,要这么高的精度干啥?可是这恰恰就是 GPS 十米定位误差的核心。现在最先进的原子钟,也不过就是比 10-15 的精度稍强一些。历史证明,最近的几十年每十年原子钟的精度就提高十倍左右,而人们对计时标准的要求也是按照这个速度在发展。科学的前瞻性很深刻的体现出来了——这一代的科学就是下一代的技术。

不过,难道光一个 GPS 就可以把原子钟的研究捧上天了吗?其实,确实是的,GPS 已经深刻的改变了这个世界,远程通讯、航空摄影、交通工具跟踪控制、海陆空民用导航、捕捞搜救、地震监测、矿产勘探、资源管理、气象学、地质学、水文学、 海洋学、时间控制、仪器校准……现代已经太多的科学技术依赖于 GPS 了。不知道依赖于这个米国国防部控制的系统是不是一件好事?

当然了,除了 GPS,精确的时间系统也在别的地方有很多应用,比方现代电力网的控制,通讯,医药,互联网控制,还有各类科学研究。就科学研究而言,现代科学技术能够最 精确测量的物理量就是时间或者频率了,所以很多科学测量都转化成时间频率测量。另外,在可预见的将来,国际单位制系统也会全部基于时间频率测量来定义。长 度单位“米”在 1983 年被定义为光在 1/299792458 秒内在真空中跑过的距离。如果没有精确的时间测量,长度测量的精度那就无从谈起了。

二。

以前讲到计时的基本方法就是找一个有稳定周期的东西,然后数周期。天体的周期运动因为每个人都看得见,也无法改变,所以很容易有共识,也几乎个个周期都有对应的时间单位。

首先是“天”。这个是地球自转一周,白天黑夜交替的周期。 一天是 24 小时,或者 1440 分钟。不过一个小细节是通常所说的“天”是指“太阳日”(Solar Day),也即太阳出现在同一视线的周期,这和另一种“恒星日”(Sidereal Day,遥远恒星出现在同一视线的周期)有微小的区别。由于地球公转方向和自转方向一致,恒星日比太阳日要稍短一些,具体短多少是一个初等物理习题,留作大家练习好了,哈哈。答 案是大约要短 4 分钟。一般卫星参数都是用恒星座标系统,所以地球同步卫星的周期一般都是标示为 1436 分钟的。

然后是“年” 。这个是地球绕太阳跑一圈,四季变化的周期。很久很久以前,人们就发现一年的时间里地球并不自转整数圈,而大约是 365.25 圈。聪明的祖先们想出了每四年插一个闰年补一天的办法来解决这个分数问题。

还有一个是“月” ,这个最开始是从月亮圆缺变化来的,也就是月亮绕地球跑一圈的周期。这个周期有各类定义方法,比如恒星月(Sidereal Month,27.3 天),不过通常只有朔望月(Synodic month)是有历法意义的,因为它是月亮在地球看来两次对准太阳的周期,也是月亮圆缺变化的周期。很不巧,朔望月不是整数天,大概是 29.53 天。中国的农历历法是描述月亮圆缺的,所以只好有的月 30 天,有的月 29 天。更不巧的是,这样下来 12 个农历月,到一年还差大概 11 天,怎么办呢?只好每两三年插一个农历闰月来补缺这相差的天数,每 19 年大概有 7 个农历闰月。另外,农历闰月的位置是根据节气的一些特征来算的。其实这些都是有规律可循的,只不过这个农历闰月实在太难算所以一般人都认为是紫金山天文台 随便规定的……

无论如何,这样农历闰月的方法还是比较难搞,估计也只有少数中国人搞得清楚。后来从西方通行起来的公历历法,就完全不管什么月亮圆缺了,直接就每个 月搞 30 天或者 31 天了事。至于闰年多的那一天为什么放在二月底,七月八月为什么连续两个大月之类的诡异问题,都可以去查 Gregorian calendar 的历史。好了,年月日讲完了,还有一个时间周期单位:星期。这个似乎就没有天体周期对应了,估计是从圣经来的,上帝造世界造了六天,然后休息了一天……当 然也有别的渊源说法。

 三。

第一篇讲到 1967 年国际通过了新的时间标准,使用原子钟来规定一秒钟的长度,直到现在这仍旧是最精确的计时方法。不过,规定了这个,谁来统一全世界的时间呢?换句话说,哪 一个原子钟作为世界标准时间呢?在如此多元化的世界里,当然谁也不可能服谁的。当初格林威治天文台被搞成了标准 0 度经线,大家就憋了一肚子气,凭什么一个小英国就是地球经线标准……原子钟这里,大家也都学乖了,说哪里的原子钟都不是标准,世界上所有到一定级别的原子 钟都合起来做一个加权平均,这样算出来的时间作为世界原子钟时间标准(International Atomic Time,缩写为 TAI,缩写来自法语 Temps Atomique International)。目前世界上有 300 多个原子钟参与 TAI。这样的好处是原子钟可以互相比较剔除不准的钟,坏掉几个原子钟也不太会影响人类计时标准。

TAI 建立以后,很多物理学家都很高兴,人类终于有很准确的钟了。就像二十世纪初的物理学一样,当时有些人认为人类已经找到了终极的计时方法,这个领域已经没什 么好研究的了,将来要做的也就是修修补补,利用新技术提高精度而已。到了 80 年代,有人发了一篇论文,说原子钟因为不是工作在绝对零度,所以原子钟本身的黑体辐射会造成一定的误差。结果大家一算,好家伙,所有的原子钟都要作巨大的 修正。到了 90 年代,随着原子钟精度的提高,人们发现这个黑体辐射修正还是相当难算的,有一些可怜的原子钟因为设计不良算不准这个修正就被踢出了 TAI。如今,这个黑体辐射修正已经成为铯原子钟的主要误差之一。

TAI 的计时精度比天文测量要高很多,而且测量也很快。天文方法校正时间需要测量一年的天象,而铯原子钟只要测量几个星期就可以到极高的精度。大部分人对 TAI 很满意,但天文学家们就很不爽了,凭什么他们研究了几百年的计时标准突然就被一个才十多年的年轻领域给取代了。于是他们扯皮、抬杠,开国际会议大吵大闹, 并举例说因为地球越跑越慢了,如果按照当时的 TAI 标准,过几千年后太阳到天顶的时间就会是子夜而不是正午了。天文学家这么吵起架来还是很难搞的,于是最后大家只好妥协一下,搞出一个协调世界时(也叫国际 协调时)。规定协调世界时缩写的时候,英语国家的人说用 CUT (coordinated universal time),法语国家的人说用 TUC (temps universel coordonné),结果这个事情照样也是谁也打不赢谁,只要再妥协一次,把两个中和一下,缩写叫做 UTC。

当时根据天文观测规定的时间叫做 UT1 (Universal Time),UTC 就是把 TAI 和 UT1 综合一下。UTC 在 1958 年对准到 UT1,这之后 UTC 平时走时按照 TAI 来走,但是一个叫国际地球自转服务(International Earth Rotation and Reference Systems Service ,IERS)的组织有权在适当的时候在 UTC 里面加入闰秒,以保证 UTC 和 UT1 的差别永远在 1 秒钟之内。这个天文学家和原子物理学家协调出来的 UTC 时间,也就是我们日常所用的标准时间。闰秒一般是加在 6 月或者 12 月最后,加了这么多闰秒,如今 2007 年 UTC 已经比 TAI 落后 33 秒了。

还有一个常听到的缩写是 GMT (Greenwich Mean Time),这个理论上应该是 UT1 的前身,但是因为现在概念混淆,很多时候 GMT 被认为是 UTC 的同义词。另外,GPS 时间的精度要求在纳秒级,随便跳一个闰秒那就完全乱套了,70 年代末第一颗 GPS 卫星上天,GPS 时间对准了当时的 UTC(当时比 TAI 慢 19 秒),从那以后 GPS 时间走时按照 TAI 来走,完全不考虑闰秒,所以 GPS 时间永远比 TAI 慢 19 秒。由于 GPS 时间不会随便跳闰秒,而且也可以很方便的获得,所以现在有一些对时间序列要求高的系统采用的是 GPS 时间而不是 UTC,比如某些手机网络。不过,现代 GPS 信号也会很厚道的包含 UTC 和 GPS 时间相差的秒数,所以地面接收器可以用 GPS 信号来对 UTC 时间,实际上,这就是目前最准确的 UTC 时间传播方式。

<完>

C indent

在unix痛恨者手册中, indent 被当作一个臭名昭著的反面例子给出.不过,从客观角度讲,目前的indent,还是挺讨人喜欢的.
对于indent的参数,建议使用:

-bad -bap -bbb -bbo -nbc -bl -bli0 -bls -c33 -cd33 -ncdb -ncdw -nce -cli0 -cp33 -cs -d0 -nbfda -di2 -nfc1 -nfca -hnl -ip5 -l75 -lp -pcs -nprs -psl -saf -sai -saw -nsc -nsob -nss -i4 -ts4 -ut

这些参数可写入用户目录下的文件,作为运行indent的缺省参数:

echo “-bad -bap -bbb -bbo -nbc -bl -bli0 -bls -c33 -cd33 -ncdb -ncdw -nce -cli0 -cp33 -cs -d0 -nbfda -di2 -nfc1 -nfca -hnl -ip5 -l75 -lp -pcs -nprs -psl -saf -sai -saw -nsc -nsob -nss -i4 -ts4 -ut”
> ~/.indent.pro

indent参数详细说明:
使用的indent长参数                             值         含义
–blank-lines-after-declarations               bad   变量声明后加空行
–blank-lines-after-procedures                bap   函数结束后加空行
–blank-lines-before-block-comments   bbb   块注释前加空行
–break-before-boolean-operator           bbo   较长的行,在逻辑运算符前分行
–blank-lines-after-commas                    nbc   变量声明中,逗号分隔的变量不分行
–braces-after-if-line                                bl   “if”和”{“分做两行
–brace-indent 0                                       bli0   “{“不继续缩进
–braces-after-struct-decl-line              bls   定义结构,”struct”和”{“分行
–comment-indentationn                        c33   语句后注释开始于行33
–declaration-comment-columnn          cd33   变量声明后注释开始于行33
–comment-delimiters-on-blank-lines   ncdb   不将单行注释变为块注释
–cuddle-do-while                             ncdw   “do — while”的”while”和其前面的”}”另起一行
–cuddle-else                                     nce   “else”和其前面的”}”另起一行
–case-indentation 0                       cli0   switch中的case语句所进0个空格
–else-endif-columnn                     cp33   #else, #endif后面的注释开始于行33
–space-after-cast                          cs   在类型转换后面加空格
–line-comments-indentation n   d0   单行注释(不从1列开始的),不向左缩进
–break-function-decl-args          nbfda   关闭:函数的参数一个一行
–declaration-indentationn          di2   变量声明,变量开始于2行,即不必对齐
–format-first-column-comments   nfc1   不格式化起于第一行的注释
–format-all-comments              nfca   不开启全部格式化注释的开关
–honour-newlines             hnl   Prefer to break long lines at the position of newlines in the input.
–indent-leveln          i4   设置缩进多少字符,如果为tab的整数倍,用tab来缩进,否则用空格填充。
–parameter-indentationn   ip5   旧风格的函数定义中参数说明缩进5个空格
–line-length                                   75   l75   非注释行最长75
–continue-at-parentheses          lp   续行从上一行出现的括号开始
–space-after-procedure-calls    pcs   函数和”(“之间插入一个空格
–space-after-parentheses         nprs   在”(”后”)”前不插入空格
–procnames-start-lines             psl   将函数名和返回类型放在两行定义
–space-after-for                         saf   for后面有空格
–space-after-if                            sai   if后面有空格
–space-after-while                     saw   while后面有空格
–start-left-side-of-comments   nsc   不在生成的块注释中加*
–swallow-optional-blank-lines   nsob   不去掉可添加的空行
–space-special-semicolon            nss   一行的for或while语句,在”;”前不加空。
–tab-size                                      ts4   一个tab为4个空格(要能整除”-in”)
–use-tabs                                    ut   使用tab来缩进

对于不同编码风格,如GNU风格,KR风格和BSD风格,不同的使用者可能会需要做不同的参数配置.

使用gprof分析程序

gprof介绍
gprof是一个GNU profiler工具。可以显示程序运行的“flat profile”,包括每个函数的调用次数,每个函数消耗的处理器时间,也可以显示“调用图”,包括函数的调用关系,每个函数调用花费了多少时间。还可以 显示“注释的源代码”--是程序源代码的一个复本,标记有程序中每行代码的执行次数。

基本用法:
1. 使用-pg选项编译和链接你的应用程序。
2. 执行你的应用程序,使之运行完成后生成供gprof分析的数据文件(默认是gmon.out)。
3. 使用gprof程序分析你的应用程序生成的数据,例如:gporf a.out gmon.out。

gprof 实现原理
gprof并不神奇,在编译和链接程序的时 候(使用 -pg 编译和链接选项),gcc 在你应用程序的每个函数中都加入了一个名为mcount(or“_mcount”, or“__mcount”)的函数,也就是说-pg编译的应用程序里的每一个函数都会调用mcount, 而mcount会在内存中保存一张函数调用图,并通过函数调用堆栈的形式查找子函数和父函数的地址。这张调用图也保存了所有与函数相关的调用时间,调用次 数等等的所有信息。

1. 在内存中分配一些内存,存储程序执行期间的统计数据
2. 在GCC使用-pg选项编译后,gcc会在程序的入口处(main 函数之前)调用

void monstartup(lowpc, highpc)

在每个函数的入口处调用

void _mcount()

在程序退出时(在 atexit () 里)调用

void _mcleanup()

  • monstartup:负责初始化profile环境,分配内存空间
  • _mcount:       记录每个函数代码的caller和callee的位置
  • _mcleanup:清除profile环境,保存结果数据为gmon.out,供gprof分析结果

3.在_mcount函数中跟踪程序的执行状况,记录程序代码的执行次数,时间等数据。

常用的gprof命令选项:
-b                 不再输出统计图表中每个字段的详细描述。
-p                 只输出函数的调用图(Call graph的那部分信息)。
-q                 只输出函数的时间消耗列表。
-e Name       不再输出函数Name 及其子函数的调用图(除非它们有未被限制的其它父函数)。可以给定多个 -e 标志。一个 -e 标志只能指定一个函数。
-E Name       不再输出函数Name 及其子函数的调用图,此标志类似于 -e 标志,但它在总时间和百分比时间的计算中排除了由函数Name 及其子函数所用的时间。
-f Name        输出函数Name 及其子函数的调用图。可以指定多个 -f 标志。一个 -f 标志只能指定一个函数。
-F Name       输出函数Name 及其子函数的调用图,它类似于 -f 标志,但它在总时间和百分比时间计算中仅使用所打印的例程的时间。可以指定多个 -F 标志。一个 -F 标志只能指定一个函数。-F 标志覆盖 -E 标志。
-z                  显示使用次数为零的例程(按照调用计数和累积时间计算)。

使用注意:
1) 一般gprof只能查看用户函数信息。如果想查看库函数的信息,需要在编译是再加入“-lc_p”编译参数代替“-lc”编译参数,这样程序会链接libc_p.a库,才可以产生库函数的profiling信息。
2) gprof只能在程序正常结束退出之后才 能生成程序测评报告,原因是gprof通过在atexit()里注册了一个函数来产生结果信息,任何非正常退出都不会执行atexit()的动作,所以不 会产生gmon.out文件。如果你的程序是一个不会退出的服务程序,那就只有修改代码来达到目的。如果不想改变程序的运行方式,可以添加一个信号处理函 数解决问题(这样对代码修改最少),例如:

static void sighandler( int sig_no )
{
exit(0);
}
signal( SIGUSR1, sighandler );

当使用kill -USR1 pid 后,程序退出,生成gmon.out文件。

转:内存泄漏的检测

一.内存泄漏的介绍:

  • 内存泄漏以发生的方式来分类,内存泄漏可以分为4类:
  • 1.常发性内存泄漏。发生内存泄漏的代码会被多次执行到,每次被执行的时候都会导致一块内存泄漏。
  • 2.偶发性内存泄漏。发生内存泄漏的代码只有在某些特定环境或操作过程下才会发生。常发性和偶发性是相对的。对于特定的环境,偶发性的也许就变成了常发性的。所以测试环境和测试方法对检测内存泄漏至关重要。
  • 3.一次性内存泄漏。发生内存泄漏的代码只会被执行一次,或者由于算法上的缺陷,导致总会有一块仅且一块内存发生泄漏。

二.C程序内存泄漏和其它内存常见错误及例子

  • 1.malloc之后没有free引起的内存泄漏
  • 2.指针访问越界
  • ##############Explame1###########################
    • p = (char *)malloc(10);
      • p[24] = ’0′; free(p);
        #################################################
  • 3.两次对同一个地址使用free() ##############Explame2###########################
    char *p = NULL;
    char *q = NULL;
    p = (char *)malloc( 2 );
    free(p);
    free(p); 可以检测出这一行
    free(p+2)这样写也可以检测出
    free(q); 不能检测出
    #################################################
  • 4.让未初始化数据作为赋值量
  • ##############Explame1###########################
    • int n,m;
      m = n;
      #################################################

    5.将未初始化值作为判断条件 ##############Explame1###########################
    int n;
    if(n){
    n++;
    }
    #################################################
    6.memcpy引起的内存重叠 ##############Explame1###########################
    char szStr[10];
    memcpy(szStr,szStr+3,6);
    #################################################

  • 7.栈溢出
    ##############Explame1###########################
    fun(){
    fun();
    }
    main(){
    fun();
    }
    #################################################
    8.new , delete , malloc free 搭配错误 ##############Explame1###########################
    char *p = (char *)malloc(2);
    delete p;
    #################################################

三.Linux下内存检测方法介绍

  • 1.确定要检查的范围。
    • 确定要对哪些内存错误进行检测。基本上都是上面提到的内存错误。

    2.使用内存检测工具对程序进行检测

    • 目前我们使用了Linux下的开源工具Valgrind 进行内存泄漏和各种工具的检测,类似的工具,都是要求再程序在GCC编译时加入-g参数,然后可以动态分析程序的内存分配情况,和各种内存错误。使用一般得到得结果为。(1)错误类型(2)错误所在文件(3)错误所在行。

    3.深入源代码进行错误得分析和猜测。

    • 根据工具得到得错误提示深入源代码进行错误得分析。找到程序自身代码的最底一层调用。

四.Valgrind 工具介绍

        Valgrind是一个GPL的软件,用于Linux(For x86, amd64 and ppc32)程序的内存调试和代码剖析。你可以在它的环境中运行你的程序来监视内存的使用情况,比如C 语言中的malloc和free或者 C++中的new和 delete。使用Valgrind的工具包,你可以自动的检测许多内存管理和线程的bug,避免花费太多的时间在bug寻找上,使得你的程序更加稳固。

Valgrind的主要功能

Valgrind工具包包含多个工具,如Memcheck,Cachegrind,Helgrind, Callgrind,Massif。下面分别介绍个工具的作用:

Memcheck 工具主要检查下面的程序错误:

  • 使用未初始化的内存 (Use of uninitialised memory)

  • 使用已经释放了的内存 (Reading/writing memory after it has been free’d)

  • 使用超过 malloc分配的内存空间(Reading/writing off the end of malloc’d blocks)

  • 对堆栈的非法访问 (Reading/writing inappropriate areas on the stack)

  • 申请的空间是否有释放 (Memory leaks – where pointers to malloc’d blocks are lost forever)

  • malloc/free/new/delete申请和释放内存的匹配(Mismatched use of malloc/new/new [] vs free/delete/delete [])

  • src和dst的重叠(Overlapping src and dst pointers in memcpy() and related functions)

Callgrind

Callgrind收集程序运行时的一些数据,函数调用关系等信息,还可以有选择地进行cache 模拟。在运行结束时,它会把分析数据写入一个文件。callgrind_annotate可以把这个文件的内容转化成可读的形式。

Cachegrind

它模拟 CPU中的一级缓存I1,D1和L2二级缓存,能够精确地指出程序中 cache的丢失和命中。如果需要,它还能够为我们提供cache丢失次数,内存引用次数,以及每行代码,每个函数,每个模块,整个程序产生的指令数。这对优化程序有很大的帮助。

Helgrind

它主要用来检查多线程程序中出现的竞争问题。Helgrind 寻找内存中被多个线程访问,而又没有一贯加锁的区域,这些区域往往是线程之间失去同步的地方,而且会导致难以发掘的错误。Helgrind实现了名为” Eraser” 的竞争检测算法,并做了进一步改进,减少了报告错误的次数。

Massif

堆栈分析器,它能测量程序在堆栈中使用了多少内存,告诉我们堆块,堆管理块和栈的大小。Massif能帮助我们减少内存的使用,在带有虚拟内存的现代系统中,它还能够加速我们程序的运行,减少程序停留在交换区中的几率。

Valgrind 安装

 

1、 到www.valgrind.org下载最新版 http://www.valgrind.org/downloads/current.html2、 解压安装包:tar –jxvf valgrind-3.2.3.tar.bz23、 解压后生成目录valgrind-3.2.34、 cd valgrind-3.2.35、 ./configure6、 Make;make install

Valgrind 使用

用法: valgrind [options] prog-and-args [options]: 常用选项,适用于所有Valgrind工具

  1. -tool=<name> 最常用的选项。运行 valgrind中名为toolname的工具。默认memcheck。

  2. h –help 显示帮助信息。

  3. -version 显示valgrind内核的版本,每个工具都有各自的版本。

  4. q –quiet 安静地运行,只打印错误信息。

  5. v –verbose 更详细的信息, 增加错误数统计。

  6. -trace-children=no|yes 跟踪子线程? [no]

  7. -track-fds=no|yes 跟踪打开的文件描述?[no]

  8. -time-stamp=no|yes 增加时间戳到LOG信息? [no]

  9. -log-fd=<number> 输出LOG到描述符文件 [2=stderr]

  10. -log-file=<file> 将输出的信息写入到filename.PID的文件里,PID是运行程序的进行ID

  11. -log-file-exactly=<file> 输出LOG信息到 file

  12. -log-file-qualifier=<VAR> 取得环境变量的值来做为输出信息的文件名。 [none]

  13. -log-socket=ipaddr:port 输出LOG到socket ,ipaddr:port

LOG信息输出

  1. -xml=yes 将信息以xml格式输出,只有memcheck可用

  2. -num-callers=<number> show <number> callers in stack traces [12]

  3. -error-limit=no|yes 如果太多错误,则停止显示新错误? [yes]

  4. -error-exitcode=<number> 如果发现错误则返回错误代码 [0=disable]

  5. -db-attach=no|yes 当出现错误,valgrind会自动启动调试器gdb。[no]

  6. -db-command=<command> 启动调试器的命令行选项[gdb -nw %f %p]

适用于Memcheck工具的相关选项:

  1. -leak-check=no|summary|full 要求对leak给出详细信息? [summary]

  2. -leak-resolution=low|med|high how much bt merging in leak check [low]

  3. -show-reachable=no|yes show reachable blocks in leak check? [no]

使用Valgrind

  • 对被测程序的要求
  1. 要求用GCC编译时使用-g选项
  2. 例如对test.c程序进行编译:gcc -o test -g test.c
  • 基本用法:
  1. valgrind [option] exefile arg…
  2. [option]:参数选项
  3. exefile:可执行程序
  4. arg…:程序参数
  • 使用内存检测
  1. option参数必须加上–leak-check=yes
  2. 举例:以程序test为例
  3. 检查test程序的内存错误:valgrind –leak-check=yes ./test
  • 1.malloc/free: in use at exit 内存在退出前没有被释放
  • 2.Invalid write of size 非法写内存:一般为数组越界
  • 3.Invalid read of size 非法读内存:一般为数组越界
  • 4.definitely lost/possibly lost/still reachable in loss record 内存未释放
  • 5.Invalid free() / delete / delete[] 同一指针被多次释放
  • 6.Source and destination overlap 内存重叠(一般是使用strncpy,memcpy引起)
  • 7.Syscall param contains uninitialised byte 调用系统函数时传入了未初始化的变量
  • 8.Conditional jump or move depends on uninitialised value 条件判断时使用了未初始化的变量
  • 9.栈溢出Access not within mapped region/Stack overflow 栈溢出
  • 10.Mismatched free() / delete / delete [] new–delete malloc–free搭配错误
  • thanks to http://blog.sina.com.cn/u/1195316944 : http://blog.sina.com.cn/s/blog_473f16d00100044w.html