阅读前言
本文以QNX系统官方的文档英文原版资料为参考,翻译和逐句校对后,对QNX操作系统的相关概念进行了深度整理,旨在帮助想要了解QNX的读者及开发者可以快速阅读,而不必查看晦涩难懂的英文原文,这些文章将会作为一个或多个系列进行发布,从遵从原文的翻译,到针对某些重要概念的穿插引入,以及再到各个重要专题的梳理,大致分为这三个层次部分,分不同的文章进行发布,依据这样的原则进行组织,读者可以更好的查找和理解。
1. 仪表化的微内核【Instrumented Microkernel】
QNX 微内核的仪表化版本(proconto -instr
)配备了复杂的跟踪和分析机制,可以实时监视系统的执行情况。procto -instr
模块可以在单cpu和SMP系统上工作。
procto -instr
模块使用很少的开销,并且提供了非常好的性能,它的速度通常是未仪表化内核的98%左右(当它没有进行日志记录时)。与这个有用的工具所增加的功能和灵活性相比,在仪表化的内核中的额外的代码量(在x86系统上大约为30 KB)是一个相对较小的代价。根据最终系统的内存占用需求,你可以选择使用这个特殊的内核作为开发/原型工具,或者也可以作为最终产品中的实际内核。
仪表化模块是非侵入性的,你可以监视该程序如何与内核交互而不必修改程序的源代码。你可以根据需要跟踪内核与系统中任何正在运行的线程或进程之间的交互,可以尽可能多,也可以尽可能少(例如,内核调用、状态更改和其他系统活动等相关信息)。你甚至可以监视中断。在此上下文中,所有此类活动都被称为事件【events】。
详细信息请参见《System Analysis Toolkit 用户指南》。(占个坑:暂时是英文原文,后续会修改为中文链接)
1.1. 仪表概览
下面是内核仪表化中涉及的基本任务:
- 被检测的微内核(proconto -instr)会发出跟踪事件,作为各种系统活动的结果。这些事件被自动复制到一组缓冲区中,这些缓冲区被分组成一个循环链表。
- 一旦缓冲区内的事件数量达到高水位标记,内核就会通知数据捕获实用程序。
- 然后,数据捕获实用程序把 “跟踪事件” 从缓冲区写入输出设备(例如,串行端口、事件文件等)。
- 然后,数据解释工具解释事件并将该数据呈现给用户。

1.2. 事件控制
考虑到活动系统中发生的大量活动,内核发出的事件数量可能是压倒性的(就数据量、处理需求和存储数据所需的资源而言)。但是你可以很容易地控制内核发出的数据量。
具体来说,你可以:
- 控制触发事件发出的初始条件;
- 应用预定义的内核过滤器来动态控制触发时间发出;
- 实现你自己的事件处理程序以进行更多筛选。
而且,一旦数据捕获实用程序(跟踪记录器)收集了数据,就可以对其进行分析。你可以对数据进行实时分析,也可以在收集完相关事件后进行离线分析。IDE中的系统分析工具以图形方式显示这些数据,以便你可以“看到”系统中正在发生的事情。
1.2.1. 发出模式
除了应用各种过滤器来控制事件流之外,你还可以指定内核可以使用的两种模式之一来发出事件:
- fast mode
只发出与事件最相关的信息(例如,只有两个内核调用参数)。
- wide mode
为同一事件生成更多信息(例如,所有内核调用参数)。
这里的权衡是速度与信息之间的权衡:fast 模式提供较少的数据,而 wide 模式为每个事件打包了更多的信息。无论采用哪种方式,你都可以轻松地调优系统,因为这些模式是基于每个事件的。
作为事件快速发出模式和事件宽发出模式之间差异的一个例子,让我们来看一下我们有可能会看到的MsgSendv() 调用项的信息类型:
Fast mode data | Number of bytes for the event |
Connection ID | 4 bytes |
Message data | 4 bytes (the first 4 bytes usually comprise the header) |
Total emitted: 8 bytes |
Wide mode data | Number of bytes for the event |
Connection ID | 4 bytes |
# of parts to send | 4 bytes |
# of parts to receive | 4 bytes |
Message data | 4 bytes (the first 4 bytes usually comprise the header) |
Message data | 4 bytes |
Message data | 4 bytes |
Total emitted: 24 bytes |
1.2.2. 环形缓冲区
内核可以将所有跟踪事件保存在内部环形缓冲区中,而不是总是向外部设备发出事件。
当满足某个触发条件时,可以根据需要以编程方式将该缓冲区转储到外部设备,使其成为一个非常强大的工具,识别在某些运行时条件下突然出现的难以捉摸的错误。
1.3. 数据解释
事件的数据包括高精度时间戳以及产生事件的CPU的ID号。此信息可以帮助你轻松地诊断计时问题(通常计时问题是困难的),这些问题更可能会在多处理器系统上发生。
事件格式还包括CPU平台(例如,x86, ARM等)和端序类型,这便于远程分析(无论是实时还是离线)。使用数据解释器,你可以以各种方式查看数据输出,例如:
- a timestamp-based linear presentation of the entire system
整个系统的基于时间戳的线性表示
- a “running” view of only the active threads/processes
只显示活动线程/进程的 “运行” 视图
- a state-based view of events per process/thread.
每个进程/线程的基于状态的事件视图。
数据解释器的线性输出可能看起来像这样:
TRACEPRINTER version 0.94
-- HEADER FILE INFORMATION --
TRACE_FILE_NAME:: /dev/shmem/tracebuffer
TRACE_DATE:: Fri Jun 8 13:14:40 2001
TRACE_VER_MAJOR:: 0
TRACE_VER_MINOR:: 96
TRACE_LITTLE_ENDIAN:: TRUE
TRACE_ENCODING:: 16 byte events
TRACE_BOOT_DATE:: Fri Jun 8 04:31:05 2001
TRACE_CYCLES_PER_SEC:: 400181900
TRACE_CPU_NUM:: 4
TRACE_SYSNAME:: QNX
TRACE_NODENAME:: x86quad.gp.qa
TRACE_SYS_RELEASE:: 6.1.0
TRACE_SYS_VERSION:: 2001/06/04-14:07:56
TRACE_MACHINE:: x86pc
TRACE_SYSPAGE_LEN:: 2440
-- KERNEL EVENTS --
t:0x1310da15 CPU:01 CONTROL :TIME msb:0x0000000f, lsb(offset):0x1310d81c
t:0x1310e89d CPU:01 PROCESS :PROCCREATE_NAME
ppid:0
pid:1
name:./procnto-smp-instr
t:0x1310eee4 CPU:00 THREAD :THCREATE pid:1 tid:1
t:0x1310f052 CPU:00 THREAD :THRUNNING pid:1 tid:1
t:0x1310f144 CPU:01 THREAD :THCREATE pid:1 tid:2
t:0x1310f201 CPU:01 THREAD :THREADY pid:1 tid:2
t:0x1310f32f CPU:02 THREAD :THCREATE pid:1 tid:3
t:0x1310f3ec CPU:02 THREAD :THREADY pid:1 tid:3
t:0x1310f52d CPU:03 THREAD :THCREATE pid:1 tid:4
t:0x1310f5ea CPU:03 THREAD :THRUNNING pid:1 tid:4
t:0x1310f731 CPU:02 THREAD :THCREATE pid:1 tid:5
为了帮助你微调对事件数据流的解释,我们提供了一个库(traceparser
),以便你可以编写自己的自定义事件解释器。
1.3.1. 使用IDE进行系统分析
系统分析工具包(SAT)的IDE模块可以作为一个全面的仪表化控制和后处理的可视化工具。
在IDE中,开发人员可以配置所有跟踪事件和模式,然后自动将日志文件传输到远程系统进行分析。作为一种可视化工具,IDE提供了一组丰富的事件和进程过滤器,旨在帮助开发人员快速精简大量事件集,以便只查看感兴趣的事件。
有关更多信息,请参阅《IDE用户指南》。(占个坑:暂时是原文,后续会修改为中文链接)
1.4. 前瞻性跟踪
虽然仪表化内核通常为仪表化及监视进程、线程和系统状态提供了一种优秀的不引人注目的方法,但你也可以让应用程序主动影响事件收集进程。
使用TraceEvent()
库函数调用,应用程序本身可以将自定义事件注入到跟踪流。在构建大型且耦合紧密的多组件系统时,此功能特别有用。
例如,下面的简单调用将会把名为eventcode
、first
和second
的整数值注入到事件流中:
TraceEvent(_NTO_TRACE_INSERTSUSEREVENT, eventcode, first, second);
你也可以在事件流中注入一个字符串(例如,“My Event”),如下面的代码所示:
#include <stdio.h>
#include <sys/trace.h>
/* Code to associate with emitted events */
#define MYEVENTCODE 12
int main(int argc, char **argv) {
printf("My pid is %d \n", getpid());
/* Inject two integer events (26, 1975) */
TraceEvent(_NTO_TRACE_INSERTSUSEREVENT, MYEVENTCODE,
26, 1975);
/* Inject a string event (My Event) */
TraceEvent(_NTO_TRACE_INSERTUSRSTREVENT, MYEVENTCODE,
"My Event");
return 0;
}
由traceprinter
数据解释器收集的输出看起来像这样:
.
.
.
t:0x38ea737e CPU:00 USREVENT:EVENT:12, d0:26 d1:1975
.
.
.
t:0x38ea7cb0 CPU:00 USREVENT:EVENT:12 STR:"My Event"
注意,编号 12
被指定为 trace user eventcode
。
这里主要是对微内核的仪表化方面进行一个概述,主要的内容还是要参考《2-2-17 System Analysis Toolkit 用户指南》中的内容(占个坑:暂时是原文,后续会修改为中文链接)。