Skip to content

{ Category Archives } 技术资料

Tiny CC

转自 IBM developerWorks 中国  Tiny CC 本 文介绍 GNU/Linux 系统上最小的 C 语言编译器 Tiny C 编译器。Tiny C 编译器不仅仅是一个常规意义上的 C 语言编译器,它还使得用户可以像使用脚本语言一样使用 C 语言进行快捷的脚本编程。我们着重介绍用 C 语言进行脚本程序开发的魅力。这个系列将由三篇文章组成,这是第一篇,介绍;在第二篇中,我们将说明如何用标准 C 语言完成通常用 sed 和 awk 完成的字符串处理的工作;在第三篇中,我们将说明如何在自己的编译器项目中使用 TCC 作为机器代码生成器。 TCC 介绍 在 下文中,我们说 Tiny C 编译器、Tiny CC、或者 TCC 都是指的这个 Fabrice Bellard 发明的 GNU/Linux 环境下最小的 ANSI C 语言编译器。TCC 的主页在文后的参考资料中列出。在 Debian GNU/Linux 系统中,可以方便的用 apt-get install [...]

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

 传统的文件同步方案有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的代码如下: #!/usr/bin/python      from pyinotify import *   import os, os.path      flags = IN_CLOSE_WRITE|IN_CREATE|IN_Q_OVERFLOW   dirs = {}   base = ’/log/lighttpd/cache/images/icon/u241′   base = ’tmp’      class UpdateParentDir(ProcessEvent):       def process_IN_CLOSE_WRITE(self, event):           print ’modify’, event.pathname           mtime = os.path.getmtime(event.pathname)           p = event.path           while p.startswith(base):               m = os.path.getmtime(p)               if m < mtime:                   print ’update‘, p                   os.utime(p, (mtime,mtime))               elif m > mtime:                   mtime = m               p = os.path.dirname(p)              process_IN_MODIFY = process_IN_CLOSE_WRITE          def process_IN_Q_OVERFLOW(self, event):           print ’over flow’           max_queued_events.value *= 2          def process_default(self, event):           pass      wm = WatchManager()   notifier = Notifier(wm, UpdateParentDir())   dirs.update(wm.add_watch(base, flags, rec=True, auto_add=True))   [...]

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 [...]

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 [...]

使用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的那部分信息)。 [...]

转:内存泄漏的检测

一.内存泄漏的介绍: 内存泄漏以发生的方式来分类,内存泄漏可以分为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++; [...]

转:MySQL参数说明

1. back_log 指定MySQL可能的连接数量。当MySQL主线程在很短的时间内得到非常多的连接请求,该参数就起作用,之后主线程花些时间(尽管很短)检查连接并且启动一个新线程。 back_log参数的值指出在MySQL暂时停止响应新请求之前的短时间内多少个请求可以被存在堆栈中。如果系统在一个短时间内有很多连接,则需要增大 该参数的值,该参数值指定到来的TCP/IP连接的侦听队列的大小。不同的操作系统在这个队列大小上有它自己的限制。试图设定back_log高于你的操 作系统的限制将是无效的。 当观察MySQL进程列表,发现大量 264084 | unauthenticated user | xxx.xxx.xxx.xxx | NULL | Connect | NULL | login | NULL 的待连接进程时,就要加大 back_log 的值。back_log默认值为50。 2. basedir MySQL主程序所在路径,即:–basedir参数的值。 3. bdb_cache_size 分配给BDB类型数据表的缓存索引和行排列的缓冲区大小,如果不使用DBD类型数据表,则应该在启动MySQL时加载 –skip-bdb 参数以避免内存浪费。 4.bdb_log_buffer_size 分配给BDB类型数据表的缓存索引和行排列的缓冲区大小,如果不使用DBD类型数据表,则应该将该参数值设置为0,或者在启动MySQL时加载 –skip-bdb 参数以避免内存浪费。 5.bdb_home 参见 –bdb-home 选项。 6. bdb_max_lock 指定最大的锁表进程数量(默认为10000),如果使用BDB类型数据表,则可以使用该参数。如果在执行大型事物处理或者查询时发现 bdb: Lock table is out of available locks or Got [...]

BDB Replication 三

Network partitions bdb replication 的实现可能被网络隔离的问题影响。 例如,考虑replication组有n个成员。网络隔离让master在一边,多于一半(n/2)的站点在另外一边。和master在一边的站点将继续 前进,master继续接受数据库的写请求。不幸的是,隔离在另一边的站点,意思到他们的master不在了,将举行一个选举。这个选举将取得成功,因为 这儿有总数n/2以上的站点在这边,然后这个组内将会有两个master。既然两个master都可能潜在地接受写请求,那么数据库将可能产生分歧,使得 数据不一致。 如果曾经在一个组内发现了多个master,一个master检测到这个问题的时候将会返回DB_REP_DUPMASTER。如果一个应用程序看到这个 返回,它应该重新配置自己作为一个client(通过调用ENV->rep_start),然后发起一场选举(通过调用 DB_ENV->rep_elect)。赢得这次选举的可能是先前的两个master之一,也可能完全就是另外的站点。无论如何,这个胜出的系统将 引导其它系统达到一致。 作为另外一个例子,考虑一个replication组有一个master环境和两个client,A和B,在那A可能会升级为master地位而B不可能。然后,假设client A从其他的两个数据库环境中被隔离出来了,它的数据变的过期。然后假设这个master倒掉了,而且不再上线。随后,网络隔离被修复了,client A和B进行了一次选举。因为client B不能赢得选举,client A将会默认地赢得这次选举,为了重新和B同步,可能在B上提交的事务将不能回滚直到这两个站点能再次地一起前进。 在这两个例子中,都有一步就是新选举出的master引导组内的成员和它自己一致,以便它可以开始发送新信息给它们。这可能会丢失信息,因为以前提交的事务没有回滚。 在体系结构上网络隔离是个问题,应用程序可能想实现一个心跳协议以最小化一个糟糕的网络隔离的影响。只要一个master至少可以和组内一半的站点通信的 时候,就不可能出现两个master。如果一个master不再能和足够的站点取得联系的时候,它应该重新配置自己作为一个client,和举行一次选 举。 这儿有另外一个工具应用程序可以用来最小化网络隔离情况下的损失。通过指定一个 nsites 参数给DB_ENV->rep_elect ,也就是说,比组内的实际成员的数目大,应用程序可以阻止系统宣布他们自己成为master,除非它们可以和组内绝大部分站点通话。例如,如果组内有20 个数据库环境,把参数30指定给DB_ENV->rep_elect方法,那么这个系统至少要和16个站点通话才可以宣布自己为master。 指定一个小于组内世界成员数目的nsites参数给DB_ENV->rep_elect,也有它的用处。例如,考虑一个组有只有两个数据库环境。如果他们被隔离了,其中任何一个都不能取得足够的选票数成为master。一个合理的选择是,指定一个系统的nsites 参数为2,另一个为1。那样,当被隔离的时候,其中一个系统可以赢得选举权,而另一个不能。这能允许当网络被隔离的时候其中一个系统能继续接受写请求。 这些关卡强调了bdb replicated环境中好的网络底层构造的重要性。当replicating数据库环境在严重丢包的网络环境中,最好的解决可能是拣选一个单一的master,只有当人工干涉决定这个被选择的master不能再恢复上线时,才举行选举。 Replication FAQ Does Berkeley DB provide support for forwarding write queries from clients to masters? No, it does not. The Berkeley DB RPC server [...]

BDB Replication 二

Synchronizing with a master 当一个client探测到replication组内一个新的master后,在它能去处理新的数据库变化之前,这个client必须去同步这个新的master。同步是一个重量级操作,它能同时给这个client和master增加负担。这儿有一些措施,一个应用程序可以用来减轻同步的负担: 延迟client同步: 当组内有了一个新的master,不论是被应用程序指定的还是因为选举的结果,所有的clients必须去同步这个新的master。这会使新的 master的资源过度损耗,因为很多clients可能都试图去和它通信和从它那儿取得记录。clients应用程序如果想延迟client的同步,应 该用DB_REP_CONF_DELAYCLIENT标志去调用DB_ENV->rep_set_config 方法。这个配置使得client总从DB_ENV->rep_process_message方法返回DB_REP_NEWMASTER,但是这个 client将不会继续去同步这个新的master。Client端应用程序选择延迟同步,用这种方法,那么将来再用 DB_ENV->rep_sync方法去同步将是可靠的。 client-to-client的同步: Clients可以接受和服务其他clients的请求。Clients请求记录调用这个Client应用程序的传输回调函数。可以由其他Clients 满足的请求,传输函数的标志被设置成DB_REP_ANYWHERE。应用程序可以选择发送这些请求到任意client,或者忽略这个标志,而把请求发送 到这个消息的环境id所指定的站点。 Client应用程序可以用不管什么算法,它们选择来负载均衡到其它clients的请求。任何client接受到一个它不能满足的请求,都将回复给请求的client,告诉它,自己不能够提供请求的信息,而那个最初的请求者,将重新请求这个信 息。另外,如果这个最初的请求没有到达发要发送给的那个目标client端,这个最初的发送请求的client也将重新请求这个信息。这这些任意一种情况 下,这个重新的请求将把传输函数的标志设置成DB_REP_REREQUEST 。应用程序可能通过把这个请求进一步传递给naster来响应这个DB_REP_REREQUEST 标志(因为是被这个消息的环境id指定的),或继续把这个请求传送给另一个client。 这延迟的同步和client-to-client的同步特性允许应用程序在replication组内做负载均衡。例如,考虑到一个组内有5个站点, A, B, C, D 和 E,站点E刚刚倒掉了,站点A被选举为master。站点C 和 D 被配置成延迟同步的,当B注意到站点A是一个新master,它立即进行同步。当B完成和master的同步的时候,站点C 和 D 上的应用程序调用 DB_ENV->rep_sync 方法,使它们也进行同步。站点C 和 D (和 E, 当它完成了重新引导) 可以发送他们的请求到站点 B, 而B可以负担因为同步而产生的工作和网络流量的冲击, 使的master,站点A,有空去处理正常的应用程序工作量和由于选举而暂停的写请求。 阻塞client的操作: Clients在处理和master的同步的时候将阻塞bdb其它的操作。默认的,许多bdb方法将阻塞,直到client同步完成,然后,这些被阻塞的方法才继续调用。 那些不能等待的Client应用程序,将更愿意立即得到一个错误返回而不是阻塞,那么就应该用DB_REP_CONF_NOWAIT 标志调用DB_ENV->rep_set_config方法。如果这个clinet现在正在同步一个master,这个配置使得bdb方法调用立即返回一个DB_REP_LOCKOUT 错误,而不是阻塞。 Clients太落伍以至于不能同步: Clients试图去和master同步的时候可能会发现,同步是不可能的了,因为client和master 已经失去联系很久了。默认地,client和master自动的检测这个状态和执行clinet内部初始化。因为内部初始化需要传输整个数据库到clinet,这可能会发费一个相对较长的时间,可能需要clinet应用程序的数据库句柄重新被打开。 那些不能等待的应用程序,更愿意推后内部初始化直到一个更加便利的时间,或者更希望做一个热备份而不是执行内部初始化,应该用REP_CONF_NOAUTOINIT 标志来调用DB_ENV->rep_set_config方法。这个配置使得bdb返回DB_REP_JOIN_FAILURE到应用程序而不是执行内部初始化。 [...]

BDB Replication 一

Introduction bdb包括对构建基于复制(replication)的高可用性应用程序的支持。bdb replication组由一些独立配置的数据库环境组成。组里只有一个master数据库环境和一个或多个client环境。Master环境支持读和写,client环境支持只读。如果master环境倒掉 了,应用程序将可能提升一个client为新的master。数据库环境可能在单独的计算机上,在单独的硬件分区上(partitions)一个不统一的 内存访问系统上,或在一个单独的server的一个磁盘上。唯一的约束就是,replication组的所有的参与者必须在一个字节序 (endianness)相同的机器上(都是大数再前或都是小数在前的操作系统)。我们期望这个约束在以后的版本中会去掉。因为总是用bdb环境,任何数 量的并发进程或线程可能访问一个数据库环境。在master环境中,多个线程可能读写这个环境。在client环境中,多个线程可能要读这个环境。 应用程序可能被编写成在master和clients间提供不同程度的稳固性。系统能同步的运行以便复制品(replicas)能保证是最新的,对应于所 有已提交的事务。但是这样做可能回招致性能上的很大的下降。高性能解决方案有考虑全局的稳固性,允许clients的数据过时一个应用程序可控制的一段时 间。尽管bdb包括必要的构建高可用性数据库环境的底层基础,应用程序仍然必须提供一些鉴定的(critical)组成部分: 应用程序有责任提供通信下部构造。应用程序可能用任何适当的通信协议。例如RPC, TCP/IP, UDP, VI或底板(backplane)消息传递。 应用程序有责任命名。bdb涉及到一个replication组成员的时候是靠一个应用程序提供的id,应用程序必须映射那个id到一个特殊的数据库环境中或通信通道中。 应用程序有责任监视master和clients的状态,和识别任何不可用的(unavailable)数据库环境。应用程序必须提供所有的需要的安全策略。例如,应用程序可能选择去加密数据,用一个安全的套接层,或什么也不做。 最后,bdb replication实现还有一个附加的特性去增强可靠性。bdb中的replication实现成执行数据库更新用一个不同的编码路径而不是用标准的。这意味着,有bug的软件的操作可能会毁坏replication master,但不会把clients也毁坏。 Replication和相近的方法的描述: DB_ENV->rep_elect         举行一个replication竞选 DB_ENV->rep_process_message   处理一个replication消息 DB_ENV->rep_stat           Replication统计 DB_ENV->rep_sync           Replication同步 Replication 配置: DB_ENV->rep_set_config     配置replication系统 DB_ENV->rep_start         为replication配置一个环境 [...]