6月19日考过了RHCE。RHCE在国际上的认可度很高,因为这是一个基于实际操作的考试项目。不管你理论再好,如果在规定的时间内不能完成任务,那也会失败。这点其实对现场工程师很重要,在现场处理具体问题的时候,客户才不会听你讲长篇理论,只要你把问题解决了,才能让客户信服。为什么SUN的认证考试不这么搞呢?
星期三 六月 06, 2007
以前就听美金提起过Unix体验中心(http://www.unix-center.net/),但一直没用上去看看。今天逛了一下,发现已经投入运行的系统还不少呢。
------------------------------------------------------
T1000/Solaris系统:
硬件环境:1 颗UltraSPARC T1芯片,CPU 主频为1.0 GHz,八核四线程配置8 GB内存
软件环境:Solaris 10 Update 3 for SPARC
机器域名:t1000.unix-center.net(公网),t1000-edu.unix-center.net(教育网)
X4100/Solaris系统:
硬件环境:2 颗双核单线程的AMD Opteron 280芯片,CPU 主频为2.4 GHz,配置4 GB内存
软件环境:Solaris 10 Update 3 for x86/x64
机器域名:x4100.unix-center.net(公网),x4100-edu.unix-center.net(教育网)
PE860/Solaris系统:
硬件环境:1 颗双核单线程的Intel Xeon 3050芯片,CPU 主频为2.13 GHz,配置1 GB内存
软件环境:Solaris 10 Update 3 for x86/x64
机器域名:solaris.unix-center.net(公网),solaris-edu.unix-center.net(教育网)
PE860/Fedora系统:
硬件环境:1 颗双核单线程的Intel Xeon 3050芯片,CPU 主频为2.13 GHz,配置1 GB内存
软件环境:Fedora Core 6
机器域名:fedora.unix-center.net(公网),fedora-edu.unix-center.net(教育网)
PE860/Ubuntu系统:
硬件环境:1 颗双核单线程的Intel Xeon 3050芯片,CPU 主频为2.13 GHz,配置1 GB内存
软件环境:Ubuntu 6.10
机器域名:ubuntu.unix-center.net(公网),ubuntu-edu.unix-center.net(教育网)
PE860/FreeBSD系统:
硬件环境:1 颗双核单线程的Intel Xeon 3050芯片,CPU 主频为2.13 GHz,配置1 GB内存
软件环境:FreeBSD 6.2
机器域名:freebsd.unix-center.net(公网),freebsd-edu.unix-center.net(教育网)
龙芯福珑系统:
硬件环境: 3 台配置龙芯2E处理器的龙芯福珑计算机,CPU 主频为666 MHz,配置256 MB内存
软件环境:Debian Linux for MIPS
机器域名:仅限内网连接
------------------------------------------------
值得自豪的是这里有两台SUN的服务器,有三台运行的是Solaris 10操作系统。当然,我也为有使用我国自主研发的龙芯处理器的龙芯福珑电脑感到骄傲和自豪。
想找个UNIX开发测试环境的朋友不妨试试。
星期五 五月 11, 2007
星期三 五月 09, 2007
[ English version ]
一直说要写一篇文章来介绍我们公司的Perl牛人 Bill Gou写的perfmon软件却一直没有动,今天不能再拖了。
perfmon是一套用Perl编写的软件包,收集系统性能数据(sar/netstat/ps),并绘制出直观的性能曲线图(日报/周报/月报或者用户定制的周期)。自从我加入到SUN公司,我就没有看到过类似的软件(不管是公司的,还是外部的),B哥的这套工具,可以说是填补了这方面的空白。据我所知,对于IBM的AIX操作系统,有一个这样的工具,叫做nmon(Nigel's Monitor),因此,我们都开玩笑的对B哥说,应该把perfmon改个名字,叫bmon 
perfmon使用GNUplot软件来绘制图形,根据你制定的数据集,perfmon可以绘制以下图形(点击连接查看图形,有的图形暂缺):
所有图形都以PNG(Portable Network Graphics)格式保存。
要使用perfmon非常简单,只需按照下面的步骤执行:
一、安装必须的软件
由于要使用GNUplot绘图,因此必须先安装gnuplot软件。对于Solaris 10的用户,可以跳过这一步,因为Solaris 10已经预装了gnuplot程序(/opt/sfw/bin/gnuplot)。对于Solaris 8/Solaris 9的用户,可以从http://www.sunfreeware.com下载安装软件包。同时要下载的还有libpng软件包,否则gnuplot运行时可能会出现找不到动态连接库libpng.so.3的错误。
Solaris 8的用户,还需要安装sar和sadc的补丁110941-03或更高版本(对于其他OS版本,也建议升级到最新的补丁)。
二、安装并配置perfmon
1.创建perfmon用户(如果你的系统上已经有此用户名,你可以选择用其它名字。建议此用户只用户perfmon使用)
2.下载最新的perfmon到你的perfmon用户目录下。
3. 将最新的perfmon-x.x.x.tar.gz解压解包(升级也使用此方式,配置文件不会被覆盖):gzip -dc perfmon-x.x.x.tar.gz|tar xvf -
4. 编辑配置文件 perfmon.conf,所有配置项都是以'item=value'的格式定义。配置项见下表:
| 配置项 | 说明 |
| datadir | 存放数据的目录,缺省是perfmon用户目录下的data目录 |
| sadc | /usr/lib/sa/sadc |
| sar | /usr/bin/sar |
| gnuplot | gnuplot程序的全路径名,如:/usr/local/bin/gnuplot,或者/opt/sfw/bin/gnuplot(具体情况可能有所不同) |
| uname | /usr/bin/uname |
| netstat | /usr/bin/netstat |
| ps | /usr/bin/ps |
| plotset | 需要绘制的图形集。有效的图形集为:cpu,mem,paging,swap,io,ps,net,net2.pkt,net2.kbytes,net2.size。多个图形集以逗号(,)分隔。 |
| netif | 包含在图形中的网络接口。多个网络接口以逗号(,)分隔 |
| psuser | 需要绘制进程数量图的用户。多个用户以逗号(,)分隔,total代表所有用户。 |
| retention | 数据保留的时间。具体视空间增长情况而定,缺省是40天。 |
| compress | 缺省bzip2 |
| daily_interval | 绘制日报图形的时间间隔。缺省为default,即采用与采样相同的时间间隔。有效的时间间隔为数字加上h(表示小时,比如1h),或者加上m(表示分钟,比如10m),或者加上s(表示秒,比如30s)。下同 |
| weekly_interval | 绘制周报图形的时间间隔。缺省为30m |
| monthly_interval | 绘制月报图形的时间间隔。缺省为2h |
设置一定要正确,否则可能无法绘制图形。
5. 修改perfmon.rc中的下面一行:
/usr/bin/su perfmon -c "/export/share/perfmon/perfreset"
将perfreset设置为实际的碌径。
6. 拷贝perfmon.rc到/etc/init.d,并在/etc/rc2.d下面建立一个符号联接:
#cp perfmon.rc /etc/init.d/perfmon
# ln -s /etc/init.d/perfmon /etc/rc2.d/S21perfmon
启动perfmon: #/etc/init.d/perfmon start
7. 编辑perfmon的crontab,指定其绘制日报/周报/月报的具体时间。例子见perfmon随带的README文件。
配置完perfmon之后,就可以在相应的<datadir>/plot下面找到日报(daily)/周报(weekly)/月报(monthly)的图形。
三、离线使用(手动绘制方式)
perfmon还提供了手动绘制方式,你只需要使用相应的命令即可:
perfplot - 绘制日报
perfplot_weekly - 绘制周报
perfplot_monthly - 绘制月报
perfplot_range - 绘制指定的起止区间图形。
上述命令都可以使用 -h 开关获得使用说明。注意,仅当data目录下有相应的数据文件时,才能绘制出图形。
四、Troubleshooting
如果你的perfmon没有图形产生,或者产生的图形有问题。你应该首先查看perfmon用户的mail是否有错误信息,是否缺少程序或者动态连接库。perfplot程序在绘制完图形后,会删除相应的临时文件,为了保留临时文件,可以通过设置环境变量PERFMON_DEBUG=1,再运行相应的perfplot程序,然后把错误信息和临时文件收集起来以被诊断。
五、有更好的建议或者其他问题
perfmon以GPL(GNU Generic Public License)发布,如果你希望对该软件进行改善,或者合作,定制,请联系软件作者:Bill.Gou-AT-Sun-DOT-COM
听B哥说正在准备2.0,新版将采用更好的kstat技术(netstat可能导致TCP/IP栈进入串行操作方式,在网络高负荷的情况下,可能导致性能问题)及其他新的features,热切期待中。
星期三 五月 02, 2007
LDoms 1.0正式版终于发布了。大家可以通过访问http://www.sun.com/ldoms了解更多信息。
星期二 四月 24, 2007
今天向大家介绍一款超级快的源代码检索和交叉引用工具-OpenGrok

OpenGrok全部用Java编写的,完全与平台无关。要使用OpenGrok,需要先安装以下软件:
- Latest Java
- A servlet container like
GlassFish or
Tomcat - Exuberant Ctags
- Subversion 1.3.0 if subversion support is needed
- Mercurial if Mercurial support is needed
如果只是用于浏览源代码,则不需要最后两项。
目前最新的opengrok是opengrok-0.4.tar.gz (4M) 。
到网上找了半天,居然没有找到可以下载Opensolaris source code的地方,http://opensolaris.org/os/downloads/on/ 上的option 1和option 4给出的http连接里面就没有on-src文件,Option 2给的一个bittorrent文件又是"NOT FOUND",option 3: Mercurial简直太慢,另外一个subversion我就没有再试了。还好昨天收到了Starter kit,光盘上有,不用花精力去找了。
在这里,我以在Opensuse 10.2上安装及配置 GlassFish + OpenGrok + OpenSolaris O/N source为例。(对于其它平台也类似,因为相关程序都是java编写的)。
首先将源代码解压到一个目录,比如:/export/home/solsource/usr/src
[安装及配置opengrok]
安装:tar zxvf opengrok-0.4.tar.gz
run.bat/run-quiet.sh/run.sh可用于定时运行以更新源代码的索引文件(index database)。需要修改这些文件里面的路径(比如SRC_ROOT,DATA_ROOT等)以反映你的实际情况。
由于我不会更新源代码,因此我只用调用一次opengrok.jar创建索引文件即可。
opengrok.jar有两种调用模式:
- 第一种,启用图形界面: java -jar opengrok.jar
程序启动后,会显示一个主窗口

点击'Search'文本框右面的文件夹图标打开一个新窗口,

选择相应的'Search index data directory'和'Source tree directory',点击'update'按钮,此时会出现正在更新的信息和进度条,

索引数据库更新完成后,会显示下面的信息。

点击'Close'以返回主窗口。
此时你就可以在相应文本框中输入关键字进行查找。双击查找结果就会打开一个文本编辑器(但是注意,在文本编辑器中就没有交叉引用及查找功能,如果你需要打开的每个文件都具有交叉引用的信息,那么你就需要继续下面的glassfish配置)
- 第二种,命令行: java -jar opengrok.jar -s SRC_ROOT DATA_ROOT
SRC_ROOT和DATA_ROOT与第一种方式一样。
为了配合glassfish的使用,需要修改source.war文件:
- 从source.war文件中解压出web.xml文件:unzip source.war WEB-INF/web.xml
- 编辑web.xml,修改其中对SRC_ROOT和DATA_ROOT的设定
- 重新打包source.war:zip -u source.war WEB-INF/web.xml
下面是我的设置:
<context-param>
<param-name>DATA_ROOT</param-name>
<param-value>/export/home/solsource/opengrok_data</param-value>
<description>REQUIRED: Full path of the directory where data files generated by OpenGrok are stored</description>
</context-param>
<context-param>
<param-name>SRC_ROOT</param-name>
<param-value>/export/home/solsource/usr/src</param-value>
<description>REQUIRED: Full path to source tree</description>
</context-param>
[安装及配置glassfish]

从https://glassfish.dev.java.net/网站下载最新的glassfish,是一个后缀名为jar的软件包。用java程序安装此软件包:
java -jar glassfish-installer-v2-b33e.jar
会显示CDDL 1.0 License信息,回答'A',就会自动解包,当显示"installation complete",表示安装完成,此时在当前目录下会产生一个glassfish的目录。
在设置glassfish之前,你需要设置以下环境变量:
- JDK_HOME
- JRE_HOME
- JAVA_BINDIR
- JAVA_HOME
- JAVA_ROOT
这些变量应该与你安装的最新的java的路径相对应。
为了设置glassfish,我们还需要用ant工具。glassfish软件包自带了一个ant,在glassfish/lib/ant目录下面,因此我们要设置ANT_HOME指向这个glassfish/lib/ant(注意,这里只是相对路径,你要根据你的情况更改);另外我们还需要给$ANT_HOME/bin/ant加上执行权限。
然后运行命令: $ANT_HOME/bin/ant -f setup.xml
ant就会根据setup.xml进行编译。编译如果成功,会显示如下信息:
BUILD SUCCESSFUL
Total time: 35 seconds
注意:你的编译时间可能有所不同。
你可能已经注意到了编译过程中输出的缺省的端口号设置:
create.domain:
[exec] Option adminuser deprecated, use --user instead.
[exec] Using port 4848 for Admin.
[exec] Using port 8080 for HTTP Instance.
[exec] Using port 7676 for JMS.
[exec] Using port 3700 for IIOP.
[exec] Using port 8181 for HTTP_SSL.
[exec] Using default port 3820 for IIOP_SSL.
[exec] Using default port 3920 for IIOP_MUTUALAUTH.
[exec] Using default port 8686 for JMX_ADMIN.
现在我们就可以启动web server。进入glassfish/bin目录,执行:./asadmin,
Use "exit" to exit and "help" for online help.
asadmin> 输入start-appserv
当你看到如下信息时,表示appserver启动成功:
Domain listens on at least following ports for connections:
[8080 8181 4848 3700 3820 3920 8686 ].
启动浏览器访问http://localhost:8080/看是否有"Your server is up and running!"的信息。
访问http://localhost:4848/,输入用户名: admin 密码: adminadmin (缺省)
在右面的"Common Task"页面上点击"Deploy Web Application(.war),然后在"Deploy Enterprise Applications/Modules"页面"Location"处选择"Packaged file to be uploaded to the server",点击"Browse",选中在“配置opengrok”任务中编辑好的source.war文件,点击"open",然后点击右上角的"OK"按钮,你就完成了opengrok的布局。
现在你就可以在你的浏览器中输入http://localhost:8080/source/开始你的源代码之旅了。
现在就下载glassfish并填写调查表,说不定你还有机会中一个iPod Nano呢。心动不如行动!
星期一 四月 23, 2007
今天收到了我订购的OpenSolaris starter kit,迫不及待地打开,光盘做得相当漂亮。一共有两张DVD:

< disc 1 >包括:
- Learning Materials
- Belenix LiveCD
- Schilix LiveCD
- Nexenta LiveCD
- Nexenta Install
- OpenSolaris Source
< disk 2 > 是: Solaris Express Community Edition b57
我觉得disk 1做得真是太好了,简直就是All-in-ONE。而且很好用,你只用从这张光盘启动,就可以选择任何一个Opensolaris的分发版来使用:

世界上最好的操作系统Opensolaris的精华都在这两张DVD上面了。还没有订购的朋友们,赶紧到这里http://www.opensolaris.org/kits/订购,完全FREE。
星期日 四月 22, 2007
今天是世界地球日(Earth Day),全世界的人类都应该善待我们的地球,因为我们的自子孙孙还要在地球上生存。

星期五 四月 13, 2007
星期四 三月 22, 2007
在上一篇blog中,我们了解了DTrace的内置变量、函数、操作等。DTrace还内建了一些宏变量(Macro Variable),在D程序中可以直接使用这些宏变量,以增强D程序的可移植性。
表1 - D宏变量
| 名称 | 说明 | 参考相关系统调用 | |
| $[0-9]+ | 宏参数 | ||
| $egid | 有效组ID | getegid(2) | |
| $euid | 有效用户ID | geteuid(2) | |
| $gid | 实际组ID | getgid(2) | |
| $pid | 进程ID | getpid(2) | |
| $pgid | 进程组ID | getpgid(2) | |
| $ppid | 父进程ID | getppid(2) | |
| $projid | 项目ID | getprojid(2) | |
| $sid | 会话ID | getsid(2) | |
| $target | 目标进程ID | ||
| $taskid | 任务ID | gettaskid(2) | |
| $uid | 实际用户ID | getuid(2) |
在上表中,除宏参数和$target外,其它宏变量都与当时触发探测器的进程相关联。
宏参数表示传递给D程序的参数,如果D程序 macro.d接受3个参数,那么$0对应macro.d即D程序名,$1对应于第1个参数,$2对应于第2个参数,以此类推。如果要传递字符串给D程序,则相应的宏参数前面要再加上一个美元$符号。比如macro.d中,如果第3个参数是字符串,那么在D程序中应该使用$$3来引用。
$target被Dtrace替换为目标的进程号,如果是使用-p参数指定,则$target就是该进程号,如果是-c,则target对应-c后面的命令运行时的进程号。为便于大家理解,我们编写一个简单的D程序,只打印target的信息。
target.d
再编写一个测试用的shell脚本,此脚本只打印自己的进程号
pid.sh
然后我们执行以下操作
# echo $$
710
# ./target.d -p $$
target=710
# ./target.d -c ./pid.sh
mypid=766
target=766
怎么样,明白$target的含义了吧。
DTrace提供了可调整的选项,选项通过#pragma D option指定,有的选项也可以在命令行指定。
表2 - DTrace选项
| 选项名 | 命令行开关 | 值 | 描述 |
| aggrate | 时间或者频率(无后缀) | 聚合读取的频率 | |
| aggsize | 大小 | 聚合缓冲区的大小 | |
| bufresize | auto或者manual | 缓冲区调整大小的策略 | |
| bufsize | -b | 大小 | 主缓冲区大小 |
| cleanrate | 时间 | 清除的频率 | |
| cpu | -c | CPU标号 | 指定在该CPU上启用探测器跟踪 |
| defaultargs | 允许引用未指定的宏参数 | ||
| destructive | -w | 允许破坏性操作 | |
| dynvarsize | 动态变量空间大小 | ||
| flowindent | -F | 在进入函数时缩进显示,并加前缀->,退出函数时取消缩进,并加前缀<- | |
| grabanon | -a | 声明匿名跟踪状态 | |
| jstackframe | 数字 | 缺省的jstack()栈帧的数量 | |
| jstackstrsize | 数字 | jstack()缺省字符串大小 | |
| nspec | 数字 | 推理缓冲区的个数 | |
| quiet | -q | 仅输出显示跟踪的数据(比如printf) | |
| specsize | 大小 | 推理缓冲区的大小 | |
| strsize | 大小 | 字符串大小 | |
| stackframes | 数字 | 栈帧数 | |
| stackindent | 数字 | 当缩进stack()和ustack()是的空 | |
| statusrate | 时间 | 状态检查的频率 | |
| switchrate | 时间 | 缓冲区切换的频率 | |
| ustackframes | 数字 | 用户栈帧数 |
下面我们来了解一下DTrace中缓冲区及其管理(Data buffering and management)。
缓冲区及其管理是DTrace架构为其消费者提供的重要服务。DTrace操作有很多都是与数据记录相关的,这些数据是记录在DTrace的缓冲区中的。每次DTrace调用都会使用到“主缓冲区(Principal Buffer)”,主缓冲区是基于每个CPU分配的。对于缓冲区的管理有以下策略。
switch策略
缺省情况下,主缓冲区采用switch策略。在此策略下,每个CPU的缓冲区成对分配:一个处于活动状态,另一个处于非活动状态。当DTrace使用者试图访问缓冲区时,内核会切换(switch)活动缓冲区和非活动缓冲区,切换方式会保证跟踪的数据不会丢失。切换完成后,新的非活动缓冲区将复制给DTrace使用者。切换的速率可以通过switchrate选项控制,如果不带时间后缀,则缺省是每秒的次数。可以使用bufsize来调整主缓冲区的大小。
fill策略
在此策略下,当任何一个CPU的缓冲区已经填充满时,Dtrace将停止跟踪,并处理所有缓冲区。 要使用此策略,需要将bufpolicy设置为fill,可以使用命令行-x bufpolicy=fill或者编译指令#pragma D option bufpolicy=fill
ring策略
在此策略下,DTrace将主缓冲区作为一个环形缓冲区对待,即当缓冲区填满时,数据会重新从缓冲区开始记录。Dtrace只会在程序终止时才会输出信息。此策略指定方式,命令行-x bufpolicy=ring,编译指令#pragma D option bufpolicy=ring
其它缓冲区
除了上面的缓冲区外,还有:聚合缓冲区(aggregate buffer)以及一个或者多个推理缓冲区(Speculative buffer)。聚合缓冲区是聚合函数会用到的缓冲区,而推理缓冲区则是推理跟踪会用到的缓冲区。
聚合
如果需要调查与性能相关的系统问题,就可以用到Dtrace提供的聚合操作。聚合操作是针对聚合函数(Aggregating Functions)而言的。聚合函数具有以下属性:
f( f(X0) U f(X1) U ... U f(Xn) ) = f ( X0 U X1 U ... U Xn )
换句话讲,就是对整个数据集合的子集应用聚合函数,然后再对结果应用该聚合函数,得到的最终结果与对整个数据集合本身应用该聚合函数相同。比如求给定数据集合之和的SUM函数,就是一个聚合函数。
DTrace中的聚合函数见下表:
表3 - DTrace聚合函数
| 函数名 | 参数 | 结果 |
| count | 无 | 调用次数 |
| sum | 标量表达式 | 所指定表达式的总和 |
| avg | 标量表达式 | 所指定表达式的算术平均值 |
| min | 标量表达式 | 所指定表达式的最小值 |
| max | 标量表达式 | 所指定表达式的最大值 |
| lquantize | 标量表达式,下限,上限,步长值 | 所指定表达式的值的线性频率分布 |
| quantize | 标量表达式 | 所指定表达式的值的二次方幂频率分布 |
DTrace将聚合函数的结果存储在称为聚合(Aggregation)的特殊对象中。其语法为:
@name[keys]=aggfunc(args);
name是聚合的名称,可以省略,keys是索引,可以是有逗号分隔的表达式,aggfunc是上表提到的函数,args是聚合函数的参数。聚合与关联数组的区别是其名字是以@作为前缀的,@name与name在不同的名称空间。
比如我们想查看5秒钟内系统调用的次数
# dtrace -n 'syscall:::entry{@counts["syscall numbers"]=count();}tick-5sec{exit(0);}'
dtrace: description 'syscall:::entry' matched 232 probes
CPU ID FUNCTION:NAME
0 49049 :tick-5sec
syscall numbers 241
此例中聚合@count的key是字符串"syscall numbers"。
我们还想再进一步了解到底是什么程序调用的系统调用最多,可能这个程序就是导致系统性能下降的主谋
# dtrace -n 'syscall:::entry{@counts[execname]=count();}tick-5sec{exit(0);}'
dtrace: description 'syscall:::entry' matched 232 probes
CPU ID FUNCTION:NAME
0 49049 :tick-5sec
svc.configd 1
svc.startd 1
Xorg 4
nmbd 4
sendmail 10
dtrace 229
在此例中@counts的key是D内置变量execname。
在进一步细化,看看什么系统调用最多
# dtrace -n 'syscall:::entry{@counts[execname,probefunc]=count();}tick-5sec{exit(0);}'
dtrace: description 'syscall:::entry' matched 232 probes
CPU ID FUNCTION:NAME
0 49049 :tick-5sec
automountd gtime 1
dtrace mmap 1
dtrace schedctl 1
fmd lwp_park 1
in.routed pollsys 1
inetd lwp_park 1
sendmail pollsys 1
automountd doorfs 2
sendmail lwp_sigmask 2
dtrace sysconfig 3
sendmail pset 3
dtrace sigaction 4
sendmail gtime 4
dtrace lwp_park 5
dtrace brk 8
dtrace p_online 32
dtrace ioctl 177
在此例中@counts的key是execname,probefunc。
使用lquantize,我们了解需要调查的表达式的分布情况。比如,我们想知道系统调用write打开的文件描述符(file descriptor)的线性分布情况。
# dtrace -n 'syscall::write:entry{@fds[execname]=lquantize(arg0,0,100,1)}'
dtrace: description 'syscall::write:entry' matched 1 probe
^C
dtrace
value ------------- Distribution ------------- count
0 | 0
1 |@@@@@@@@@@@@@@@@@@@@ 1
2 | 0
sshd
value ------------- Distribution ------------- count
3 | 0
4 |@@@@@@@@@@@@@@@@@@@@ 1
5 | 0
6 | 0
7 | 0
8 |@@@@@@@@@@@@@@@@@@@@ 1
9 | 0
在上例中,我们可以看到,在该时间内,sshd进程对文件描述符4操作了1次,对文件描述符8操作了1次。虽然不具有实际意义,但可以帮助我们理解lquantize的作用。
如果要聚合的表达式的值非常大,使用lquantize可能会输出太多信息,这种情况下可以使用quantize来聚合。
下面是一个统计执行程序系统调用的时间分布的D脚本: time.d
#!/usr/sbin/dtrace -s
syscall:::entry
{
self->ts=timestamp;
}
syscall:::return
/self->ts/
{
@time[execname]=quantize(timestamp-self->ts);
}
执行一段时间,按Ctrl+C中断。限于篇幅,下面只列出部分信息。
# ./time.d
dtrace: script './time.d' matched 462 probes
^C
sendmail
value ------------- Distribution ------------- count
1024 | 0
2048 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 7
4096 |@@@@ 1
8192 |@@@@ 1
16384 | 0
sshd
value ------------- Distribution ------------- count
1024 | 0
2048 |@@@@@@@@@@@@@@@@@@@ 7
4096 |@@@@@ 2
8192 |@@@@@ 2
16384 |@@@@@ 2
32768 | 0
65536 |@@@@@ 2
131072 | 0
以sendmail程序为例:
系统调用执行时间(从entry到return)在大于等于2048纳秒并小于4096纳秒区间共有7次,在大于等于4096纳秒小于8192纳秒区间共有1次,在大于等于8192纳秒小于16384纳秒区间共有1次。
在聚合一段时间后,可能需要对某个常数因子进行标准化(normalize),以更好的分析数据。如下例,我们按照执行的时间来标准化聚合数据,以得到每秒钟的系统调用数。
}
标准化不会修改原始数据。与标准化相对应的是“取消标准化(denormalize)"函数,此函数可以将聚合恢复到标准化之前的状态。
聚合的数据会随着时间的增加而增加,你可以定期使用clear()和trunc()函数进行清除。clear()与trunc()的区别是clear()只会清除聚合的值,而trunc()则会同时清除聚合的值和键(key)。
下面的例子每秒钟打印上一秒程序对系统调用的使用情况
# dtrace -n 'syscall:::entry{@counts[execname]=count();}tick-1sec{printa(@counts);trunc(@counts);}'
dtrace: description 'syscall:::entry' matched 232 probes
CPU ID FUNCTION:NAME
0 49050 :tick-1sec
syslogd 16
dtrace 91
0 49050 :tick-1sec
fmd 1
inetd 1
sendmail 11
sshd 40
dtrace 45
0 49050 :tick-1sec
sshd 8
dtrace 42
0 49050 :tick-1sec
sshd 8
dtrace 39
0 49050 :tick-1sec
nmbd 4
sshd 8
dtrace 39
0 49050 :tick-1sec
svc.configd 1
svc.startd 1
sshd 8
dtrace 40
0 49050 :tick-1sec
sshd 8
sendmail 10
dtrace 41
^C
在分析实际的性能问题时,建议使用聚合作为你的出发点。
推理跟踪(Speculative Tracing)
推理跟踪是DTrace提供的用于试探性地跟踪数据的工具,它可以在事后才来决定是将这些数据提交(commit())到跟踪缓冲区,还是放弃(discard())。
DTrace为推理跟踪提供了以下函数:
表4 - DTrace推理函数
| 函数名 | 参数 | 说明 |
| speculation | 无 | 返回一个新的推理缓冲区的标志符 |
| speculate | 推理缓冲区的标志符ID | 子句的其余部分会把数据存放到由ID指定的推理缓冲区里 |
| commit | 推理缓冲区的标志符ID | 提交与ID相关的推理缓冲区 |
| discard | 推理缓冲区的标志符ID | 放弃与ID |
推理缓冲区是一种有限的资源,如果无推理缓冲区可用,则speculation()返回0,表示无效的ID。
speculate()操作要放在所有需要跟踪的数据记录操作之前。speculate()不能放在数据记录之后,否则DTrace会编译出错。不能对聚合操作,破坏性操作和exit()进行推理跟踪。通常的做法是将speculation()的结果赋给线程局部变量,然后使用该变量作为其它探测器的谓词以及speculate()的参数。
当推理缓冲区被提交时,其数据将被复制到主缓冲区中。如果放弃推理缓冲区,其数据将被丢弃。
下面的示例einvalspec.d展示了推理跟踪的一种应用方式,用来显示特定的代码路径。当系统调用返回错误代码EINVAL时,我们就打印出其代码路径。
#pragma D option flowindet 表示当进入函数时,缩进显示,并加上前缀 ->,当退出函数时,取消缩进,并加上前缀<-
#pragma D option nspec=200 表示推理缓冲区的个数(如果不指定,缺省只有一个)
/* */ 之间的内容是注释,
discard(self->spec); /*不是我们关心的情况,丢弃推理缓冲区数据*/
你还可以将上面程序中的EINVAL改为你关心的其它错误代码(具体错误代码信息,请查阅intro(2)手册页)。
星期三 三月 21, 2007
通过上一次的介绍,相信大家对DTRACE已经有了一个初步的认识。上一次结束时专门留了一个例子,可能大家第一次看有很多不明白的地方,没有关系,随着我们对DTRACE更多的介绍,很快就会”云开雾散“了。
D语言作为一种编程语言,自然就有其语法、关键字、数据结构、运算符、函数等,我将一一介绍。
D语言中标志符名称与C语言类似,由字母、数字和下划线组成,其中第一个字符必须是字母或者下划线。D语言预留了一些关键字供DTRACE本身使用,关键字不能用做D变量的名称。D关键字列表参阅《Solaris动态跟踪指南》,这里只列出一些常用的。
表1 - 常用DTRACE关键字
| 关键字 | 描述 |
| inline | 编译期间将指定的D变量替换为预定义的值或者表达式,inline可以申明类型 |
| sizeof | 计算对象的大小 |
| self | 表示将D变量存放在线程(thread)的私有空间里 |
| this | 表示D变量的有效范围在this所在的子句内 |
D语言中定义了整数类型和浮点类型,以及用于表示ASCII字符串的string类型。整数类型随机器字长的不同而不同。机器字长可以用命令isainfo -b来查看。
表2 - D整数类型
| 类型名称 | 32位机器字长 | 64位机器字长 |
| char | 1个字节 | 1个字节 |
| short | 2个字节 | 2个字节 |
| int | 4个字节 | 4个字节 |
| long | 4个字节 | 8个字节 |
| longlong | 8个字节 | 8个字节 |
一点小知识,C语言中有ILP32和LP64两种数据模型,ILP32指的就是int/long/pointer(指针)是32位,LP64指的是long/pointer是64位。
对于整数类型,又分为带符号(signed)和无符号(unsigned)两种,因为是否带符号决定了对其最高位(most-significant)的解释。无符号整型通常是在相应的整型前面添加unsigned或者u限定符。
表3 - D整数类型别名
| 类型名称 | 说明 |
| int8_t / uint8_t | 1字节带符号整数 / 1字节无符号整数 |
| int16_t / uint16_t | 2字节带符号整数 / 2字节无符号整数 |
| int32_t / uint32_t | 4字节带符号整数 / 4字节无符号整数 |
| int64_t / uint64_t | 8字节带符号整数 / 8字节无符号整数 |
| intptr_t / uintptr_t | 大小等于指针的带符号整数 / 大小等于指针的无符号整数 |
D语言中也定义了转义序列如'\n'表示回车。
D语言中定义了算术运算符、关系运算符、逻辑运算符、按位运算符、赋值运算符、递增和递减运算符、条件表达式(即 ? : 运算符),由于这些运算符及其优先级与C语言基本相同,就不在这里占用篇幅了。D语言也支持”强制类型转换“,即把一种类型转换为另一种兼容类型,比如将指针转换为整数。
除了上面的数据类型,D语言还提供有数组(array)和关联数组(associative array),关联数组中有一种特殊类型叫做聚合(aggregation),在后面会看到。关联数组通过一个称为键(key)的名称来检索数据,用过Perl的朋友相信不会陌生。定义关联数组,只需作以下赋值操作即可:
name[key]=expression;
例如: people["sam.wan",30]=100
D语言中的变量是不需要预定义就可以直接使用的。但是在没有赋值之前,是不能出现在谓词中和赋值运算等号右侧。请看下面的3个例子:
例子1
# dtrace -n 'BEGIN{a=1;exit(0);}END{printf("a=%d\n",a);}'
dtrace: description 'BEGIN' matched 2 probes
CPU ID FUNCTION:NAME
0 1 :BEGIN
0 2 :END a=1
例子2
# dtrace -n 'BEGIN/a==0/{exit(0);}END{printf("a=%d\n",a);}'
dtrace: invalid probe specifier BEGIN/a==0/{exit(0);}END{printf("a=%d\n",a);}: in predicate: failed to resolve a: Unknown variable name
例子3
# dtrace -n 'BEGIN{a=a+1;exit(0);}END{printf("a=%d\n",a);}'
dtrace: invalid probe specifier BEGIN{a=a+1;exit(0);}END{printf("a=%d\n",a);}: in action list: a has not yet been declared or assigned
缺省情况下,D语言中定义的变量是全局范围的。在多线程环境中,全局变量是不安全的,因为可能多个线程都会进行访问,因此,D语言引入了线程局部变量标志符self。通过在一个变量前面添加self->修饰,可以将该变量存放在线程自己的局部空间中,这样不会受到其它线程的影响,这种方法对于现在越来越多的并发操作环境十分有利。线程局部变量与全局变量在不同的名称空间(name space)中,因此即使名字相同也不会冲突,比如self->aaa和aaa是两个不同的变量。
特别需要提醒注意的是,在使用完一个变量之后,要将该标量赋值为'0',这样DTRACE就会回收释放其所占用的内存空间。对用一个好的程序员来说,释放空间和分配空间同样重要。
D语言中还有一种特殊的变量叫“子句局部变量(Clause Local)”,通过在变量名前添加this->修饰符完成。子句局部变量的作用域只在其定义的子句内有效。D语言中的变量缺省情况下会被赋值为0,但是子句局部变量除外。
除用户定义的变量外,D语言本身提供了一些非常有用的内置变量,所有这些内置变量都是全局变量。
表4 - DTrace内置变量
| 类型和名称 | 说明 |
| int64_t arg0,...,arg9 | 探测器的前10个输入参数(64位整数)。如果当前探测器参数个数少于10,则未定义的参数值不确定 |
| args[] | 与arg0...arg9不同,args[]是有类型的,其类型对应与当前探测器的参数类型。 |
| uintptr_t caller | 进入当前探测器之前的当前线程的程序计数器(PC)位置 |
| chipid_t chip | 当前物理芯片的CPU芯片标志符 |
| processorid_t cpu | 当前CPU的编号 |
| cpuinfo_t *curcpu | 当前CPU的信息(具体结构后面会讲到) |
| lwpsinfo_t *curlwpsinfo | 与当前线程关联的轻量进程(LightWeight Process,LWP)的信息(具体结构见后) |
| psinfo_t *curpsinfo | 与当前线程关联的进程的信息 |
| kthread_t *curthread | 当前线程在内核中的数据结构(kthread_t)的地址,kthread_t的定义在<sys/thread.h>中。 |
| string cwd | 当前进程的工作路径(Current Working Directory) |
| uint_t epid | 当前探测器的已启用的探测器ID号。 |
| int errno | 当前线程最后一次执行的系统调用的返回错误值 |
| string execname | 当前进程的名称 |
| gid_t gid | 组ID |
| uint_t id | 当前探测器的唯一ID号,dtrace -l的第一列 |
| uint_t ipl | 触发探测器时当前CPU的中断优先级(Interrupt Priority Level,IPL)。 |
| lgrp_id_t lgrp | 当前CPU所属的延迟组(Latency Group)的ID |
| pid_t pid | 当前进程号 |
| pid_t ppid | 当前进程的父进程 |
| string probefunc | 当前探测器的函数名 |
| string probemod | 当前探测器的模块名 |
| string probename | 当前探测器的名字 |
| string probeprov | 当前探测器的提供器名 |
| psetid_t pset | 当前CPU所属的处理器集(Processor Set)的ID |
| string root | 当前进程的根目录名 |
| uint_t stackdepth | 当前线程的栈帧(Stack Frame)的深度。即其调用的函数的层次数。 |
| id_t tid | 当前线程的线程ID |
| uint64_t timestamp | 以纳秒(ns)为单位的时间计数器。此计数器从过去的任意点递增,仅用于相对计算中。 |
| uid_t uid | 当前进程的实际用户ID |
| uint64_t uregs[] | 当前线程的用户寄存器值 |
| uint64_t vtimestamp | 以纳秒(ns)为单位的时间计数器,实际是当前线程在CPU中已运行的时间减去DTrace谓词和操作所花费的时间。同timestamp一样,仅用于相对计算。 |
| uint64_t walltimestamp | 自1970年1月1日00:00世界标准时间以来的纳秒数。 |
Dtrace还可以使用反引号(backquote `)访问操作系统内核中定义的变量,但不能进行修改。
内核中定义的变量可以通过查看内核的变量符号表(Name Symbol)来找到。
#echo "::nm"|mdb -k|more
比如我们想通过DTrace来查看每秒钟freemem的值。freemem表示当前系统中可用的内存页数
#dtrace -qn 'tick-1sec{printf("%d pages of freemem\n",`freemem)}'
由于Solaris可用动态加载模块,各个模块可能有相同的变量名,为了避免冲突,可用使用模块名来区分,比如访问a模块的x变量:a`x
DTrace还提供操作和子例程。
如果DTrace子句为空,则会采用缺省操作。缺省操作即显示已启用的探测器的标志符。
数据记录操作
数据记录操作总会往指定的缓冲区中放入数据。
void trace(expression) 将expression的结果放到指定的缓冲区(在后面会提到)。
void tracemem(address,size_t nbytes),从address地址复制nbytes的内容到指定缓冲区。
void printf(string format,...) 格式化输出。具体格式可用参见printf(3C)手册页。
void printa(aggregation)/void printa(string format,aggregation) 显示及格式化聚合(在后面会提到)
void stack(void)/void stack(int nframes) 将指定长度的栈帧记录拷贝到指定的缓冲区。
void ustack(int nframes,int size)/void ustack(int nframes)/void ustack(void),同上,只是操作的是用户栈
破坏性操作
void stop(void) 停止触发当前探测器的进程
void raise(int signal) 将指定的信号signal发送至触发当前探测器的进程
void copyout(void *buf,uintptr_t addr,size_t nbytes) 从buf地址拷贝nbytes字节到当前进程的addr地址处。
void copyoutstr(string str,uintptr_t addr,size_t maxlen) 将字符串string拷贝到当前进程的addr地址处
void system(string program,..) 执行程序
内核破坏性操作(下面的操作将会影响整个系统的运行)
void breakpoint(void) 发生一个内核断点
void panic(void) 触发panic()操作(这个相信大家都再熟悉不过了)
void chill(int nanoseconds) DTrace执行nanoseconds时间的spin操作(循环),如果nanoseconds> 500milliseconds,则会失败。
特殊操作
推测性操作(Speculative Actions),有speculate(),commit(),discard(),在后面会提到。
void exit(int status) 立即停止DTrace跟踪。
子例程
与操作不同,子例程只会影响DTrace的内部状态。
void *alloca(size_t size) 分配size字节的临时空间,返回一个8字节对齐的指针。
string basename(char *str) 从str中去除前缀和目录名
void bcopy(void *src,void *dest,sizt_t size) 从src拷贝size字节到dest。
string cleanpath(char *str) 去除str中的/./和/../等
void *copyin(uintptr_t addr,size_t size) 从用户地址空间addr处拷贝size字节到Dtrace临时缓冲区中,并返回缓冲区地址。
string *copyinstr(uintptr_t addr) 从用户地址空间addr除拷贝已null结尾的ASCII字符串到Dtrace临时缓冲区,并返回缓冲区地址。
void copyinto(uintptr_t addr,size_t size,void *dest) 从用户地址空间addr处拷贝size字节到Dtrace临时缓冲区的dest处。
string dirname(char *str) 返回str的目录名
size_t msgdsize(mblk_t *mp) 返回mp指向的数据消息的字节数
size_t msgsize(mblk_t *mp) 返回mp消息字节数
int mutex_owned(kmutex_t *mutex) 如果当前线程拥有互斥锁mutex,则返回非零;否则返回0
kthread_t *mutex_owner(kmutex_t *mutex) 返回mutex互斥锁的属主的线程数据结构kthread_t的指针。如果没有属主或者该互斥锁是自旋锁(Spin Mutex),则返回null。
int mutex_type_adaptive(kmutex_t *mutex) 如果mutex是自适应互斥锁(MUTEX_ADAPTIVE类型),则返回非0,否则返回0。
int progenyof(pid_t pid) 如果触发当前探测器的进程是指定进程的子孙,则返回非0
int rand(void) 返回一个伪随机整数
int rw_iswriter(krwlock_t *rwlock) 如果指定的读写锁rwlock被一个写入者占有或者要求获得,则返回非0,否则返回0
int rw_write_held(krwlock_t *rwlock) 如果指定的读写锁当前被一个写入者占有,则返回非0,否则返回0
int speculation(void) 为speculate()操作预留一个推测性跟踪缓冲区,并返回这个缓冲区的标志符。
string strjoin(char *str1,char *str2) 串联str1和str2到临时空间,并返回其地址。
size_t strlen(string str) 返回指定字串的长度(不包括结尾的空字节null)
看了这么多操作和子例程,是不是有点受打击了?没有关系,慢慢来,先熟悉一下,在今后具体使用时,就会印象深刻。
关于前面的copyin/copyinstr/copyinto子例程再多作一点说明:
在Solaris(UNIX)系统中,用户程序是运行在用户地址空间里面,当用户程序执行系统调用比如open(2)时,才会进入到内核空间执行(我们通常称之为"陷入trap"
,为了访问用户地址空间的字符串,就必须将其拷贝到内核空间里面来,否则内核找不到相应的地址,就会报错。看下面的一个例子。
我们想知道是什么程序在调用open(2),以及打开什么文件。这里很自然我们会用到syscall提供器的open:entry探测器。此探测器的参数可用从open(2)的手册页查到(所有的syscall提供器提供的探测器都可用在相对应的系统调用手册中查到)
int open(const char *path, int oflag, /* mode_t mode */);
我们只关心第一个参数arg0,这是一个字符串指针(即将要打开的文件名)。对于字符串指针,可以使用"%s"进行格式化输出。
运行一下看看
# ./who_open_what.d
dtrace: failed to compile script ./who_open_what.d: line 5: printf( ) argument #4 is incompatible with conversion #3 prototype:
conversion: %s
prototype: char [] or string (or use stringof)
argument: int64_t
错误,为什么,因为传递给内核的是一个用户地址空间的指针,内核无法访问该地址,内核只看到一个指针,因此Dtrace认为格式化用的是"%s",但是传递的却是一个int64_t类型,不匹配。
正确的程序应该是:
再来看看
# ./who_open_what.d
nfsmapid[272] opened /etc/default/nfs
nfsmapid[272] opened /etc/resolv.conf
init[1] opened /etc/inittab
init[1] opened /etc/svc/volatile/init-next.state
init[1] opened /etc/svc/volatile/init-next.state
init[1] opened /etc/inittab
...
是不是很有趣。
更多有趣的还在后头,别走开哦 
星期二 三月 20, 2007
记得几年前看过一部美国大片叫《全民公敌(Enemy of the State)》,在里面,谋杀国会议员的主谋强沃特和他的属下,为了取回记录着其犯罪事实的磁碟片,用高科技的卫星监视,使主人公史密斯的行踪处于严密的监控中。当时就对美国高科技跟踪系统惊叹不已。当然作为一个普通公民,是不希望自己受到监视的。但是对于计算机系统,如果能够对系统的运行情况进行监视并了如指掌,进而发现其中的臭虫(bug),那将是一件令IT管理者和开发者兴奋的事。今天我要介绍的SolarisTM Dtrace就是这样一个好帮手!
我的第一篇Blog就提到了Dtrace,但是没有作更多的说明。今天我将对Dtrace作比较详细的介绍,一是作为自己学习Dtrace的一点心得,二是希望对还没有使用Dtrace的朋友们提供一点入门知识,更详细的信息请参阅第一篇Blog中提到的资源。为了与中文版的《Solaris动态跟踪指南》保持一致,下面的术语都采用书中的翻译。
DTRACE(全称Dynamic Tracing)是SolarisTM 10中引入的一种可以对核心(kernel)和应用程序(user application)进行动态跟踪并且对系统运行不构成任何危险的技术。下面是理解Dtrace的几个要点:
1. Dtrace的实现是紧密地结合到核心里的(intimately integrated),即Dtrace的源代码是分布到了Kernel的各个部分中。除了Dtrace的执行程序dtrace.c和头文件<sys/dtrace.h>,<sys/dtrace_impl.h>外,其它实现dtrace的代码遍布到Solaris Source tree的各个文件。具体请参见 Bryan Cantrill的Blog - "The Observation Deck"
2. Dtrace架构中一个很重要的组件是"探测器(Probe)",简单讲,探测器就是核心源代码中某一个特点的”点“。在普通的Solaris 10内核中,这样的”点“有4万多个,而且还可以随着模块的加载而增加。探测器在没有被”启用(enable)“时,对核心是没有任何影响的,这时的核心与没有dtrace功能的核心如Solaris 8/9是没有任何区别的。当探测器被启用后,Solaris会动态地往核心中为启用的探测器加入相应的指令来实现探测器被"触发(fire)"时的“操作(action)"。
3. Dtrace架构可以简单的理解为”Dtrace提供器(Provider)和Dtrace使用者(Consumer)”模式。如下图所示:

”提供器“提供了”探测器“,而”使用者“通过libdtrace(3LIB)库和相应的设备文件或者其它方式来使用”提供器“提供的”探测器"。如上图所示,除了我们下面将会介绍的/usr/sbin/dtrace命令外,Solaris 10系统中还有很多收集统计信息的工具比如intrstat(1M),plockstat(1M),lockstat(1M)等都是Dtrace使用者。使用plockstat -V -p <pid>,你就可以看到plockstat使用的dtrace命令。
4. Dtrace本身是安全的,即不会对内核的运行造成影响。Dtrace可以读取内核变量,却不能修改内核变量。但是Dtrace提供了”破坏性(destructive)"的操作比如panic(),如果你使用了这些动作,是会中断系统运行的。
在学习Dtrace的过程中,要切记上面的几点。
下面就重点介绍一下Dtrace中日常使用最频繁的一个Dtrace使用者/usr/sbin/dtrace命令。dtrace(1M)可以以命令行形式调用,也可以通过D-script调用。D-script是用Dtrace提供的D语言来编写的脚本程序。D语言类似于C和awk,但是没有程序控制如for,if等机制,也许是为了更好的控制系统的稳定性。
命令行调用的例子: dtrace -n 'syscall::open*:entry{trace(execname)}'
D-script例子:
#!/usr/sbin/dtrace -s syscall::open:entry, syscall::open64:entry { trace(execname); } |
不管是命令行方式还是脚本方式,都要指定至少一个探测器。每个探测器都是一个“四元组(4-tuple)",但是有的部分可以省略。探测器的具体格式如下:
Provider:Module:Function:Name
各部分的含义如下:
- Provider即提供器,发布此探测器的Dtrace提供器的名称。比如:syscall是所有系统调用的提供器,sysinfo是系统统计信息的提供器,proc是进程信息的提供器。不同系统不同版本的Solaris的提供器的数量不同。使用下面的命令可以查看系统中有多少个提供器.
#dtrace -l|grep -v "PROVIDER"|awk '{print $2}'|sort -u
- Module即模块,是此探测器对应于特定的程序位置时,其所在模块的名称。对于应用程序,模块名可以是动态链接库的名字,比如:libc,或者主程序a.out。有的探测器没有模块名。
- Function即函数,探测器所在函数的名称
- Name即名字,最后一个组成部分。
探测器的四元组名字如果某个部分为空,则表示匹配该字段的所有可能性,星号(*)也是通配符,表示匹配任意字符串。现在我们再来看上面的两个例子。第一个命令行例子表示启用syscall提供器中所有模块里面名字以open开头的函数的entry探测器;而第二个脚本例子表示匹配syscall提供器中所有模块里面名字是open或者open64函数的entry探测器,其中的逗号表示或者的关系。命令行方式调用时,如果不使用-l开关,则指定的探测器将被启用,对于脚本方式,-s后面即D-script程序的正文部分。
一个D程序的结构如下:
上面的伪代码(pseudo-code)描述了一个D程序的大致结构,其中除了探测器描述部分,其它的部分如谓词、操作都不是必须的。第0行指明D程序的解释器(interpreter),就是/usr/sbin/dtrace;第1行使用pragma关键字指定特定的D程序编译指令;从第2行起就是对相应的探测器的启用,并定义在指定的探测器被触发时应该执行的操作,操作以分号结尾。其中,在探测器描述和操作之间用 / / 符号隔开的部分称为"谓词(Predicate)"。前面已经提到,在D语言中,没有if语句和循环,只有通过谓词来进行判断,谓词是一系列的逻辑运算,如果计算结果是false(0),则忽略探测器的触发,当然更不会执行该探测器定义的任何操作;只有当谓词计算为true(非0)时,相应的操作才会被执行。D程序的执行是从上至下顺序执行的,花括号{}包围的部分是对应探测器被触发且谓词为真时的执行子句块,对于同一个探测器描述,可以指定多个执行子句块。
当你编辑完成一个D程序,并且使用dtrace -s或者通过直接添加执行权限来执行时,Dtrace首先会将你的脚本程序编译成一个安全的中间格式(有点类似于Java程序的运行机制),然后才会被加载到内核中执行。Dtrace的执行环境还会检查并处理运行时错误(run-time errors)比如被零除(dividing by zero),访问无效地址等。因此Dtrace是相当安全的。
当Dtrace程序被加载到内核执行时,相应的探测点被启用,如果有涉及探测点的事件发生,我们就把它称之为“触发”,如果此时谓词计算为true,则相应的操作就会被执行。为便于大家理解“启用”和“触发”两个概念,我们举一个日常生活中的实际例子。
现在全国各个城市为了更好地规范交通秩序,都安装了很多“电子警察”(就是“探测器”),安装完成就打开(即“启用”),如果有车闯红灯,就会激活安装在地上的感应线(”触发“),那么”电子警察“就会拍照,很快罚单就会送到你家里(这就是”操作“)。
通过上面这个例子,大家应该有个更加形象的认识了吧。
作为今天的结束,下面是一个监视谁(用户ID)使用什么命令访问一个文件(文件以参数形式传递)的例子。
who_access_thisfile.d
#!/usr/sbin/dtrace -qs
syscall::creat*:return,
syscall::open*:return
/arg0 != -1 && fds[arg0].fi_pathname == $1 /
{
printf("uid#%d %s %s\n",uid,execname,$1);
}
chmod +x who_access_thisfile.d,然后执行./who_access_thisfile.d /etc/passwd,在另一个终端上试试cat /etc/passwd, vi /etc/passwd,看看你都看到了什么信息,你原来能做到吗?
更多的信息,将在下一次中介绍。
星期四 三月 15, 2007
今天在我的LAPTOP上玩了一下VirtualBox上跑Belenix,感觉还不错。
VirtualBox是InnoTek公司开发的一款虚拟机软件,它是按照GNU Public License (GPL)进行分发的。VirtualBox比Xen功能强大并且具有更为友好的用户界面,同时它本身又比VMWare少用系统资源。是一款不可多得的开源虚拟机软件。
Belenix是基于OpenSolaris的Live CD(可直接从光盘运行),非常适合用来测试OpenSolaris。
我的LAPTOP配置了1G内存,安装的是Slackware Linux(current),内核是2.6.15.2,我分配了512M内存给VirtualBox跑Belenix。我是从VirtualBox网站下载的针对Linux所有发行版的最新1.3.8版的安装文件。
安装非常简单:
1. chmod +x VirtualBox_1.3.8_Linux_x86.run
2. ./VirtualBox_1.3.8_Linux_x86.run install (卸载使用uninstall参数)
安装过程中会根据你当前的内核编译模块vboxdrv.ko,模块会放在 /lib/modules/`uname -r`/misc/下面,另外会生成一个启动脚本,在Slackware下是/etc/rc.d/rc.vboxdrv,同时你需要把你的用户加入到vboxusers组中。
启动时,首先运行启动脚本 /etc/rc.d/rc.vboxdrv start,启动脚本会产生相应的设备文件/dev/vboxdrv,并加载模块vboxdrv.ko到核心里,然后你就可以使用VirtualBox命令,之后的操作就像使用VMWare一样简单了。
下面是一些屏幕截图的链接
大家感兴趣可以自己试试! 
Snap Preview Anywhere[tm] 是
Snap公司提供的一个免费的显示网页超链接缩略图的软件。你只要在其主页http://www.snap.com/登记,你就可以向你的网页中添加script来获得缩略图功能。这样当别人访问你的网页时,只要他或她把鼠标放在一个链接上,一会儿就能看到这个链接网页的缩略图,就像你刚才把鼠标放在snap的主页上一样。是不是很酷?
This blog copyright 2009 by samwan

