1.1 QNX目标系统嵌入
利用QNX的模块性和和可裁剪性,其嵌入过程一般是:
构建Buildfile -> 编译buildfile生成系统映象文件 -> 启动目标系统 ->嵌入式系统软件设计。
其中的关键是构建Buildfile[19]。通常一个嵌入式系统需要一个可启动的操作系统映象文件(OS Image)。对于基于QNX的嵌入式一个应用系统,就是根据所选择的CPU类型以及应用程序所需要的操作系统模块来定制嵌入式系统。构建Buildfile的过程就是配置操作系统映象的过程。简单来说Buildfile是一个产生映象文件所需遵守的一组规则、准则。Buildfile由三部分组成,其结构如下:
l bootstrap script(启动引导脚本)
l startup script(启动脚本)
l file list(文件列表)
1.1.1 启动引导脚本(bootstrap script)
不同的CPU其启动的方式不同,相应的启动引导程序也就不同。启动引导脚本bootstrap script的作用就是配置和CPU相适应的微内核。一个典型的启动引导脚本如下:
[virtual=x86,bios +compress] .bootstrap = {
startup-bios -s 64k -D 8250.3f8.57600 -A
PATH=/proc/boot procnto
}
在这个例子中,第一行说明它是一个启动引导文件。其中“virtual” 表明该Buildfile将构造一个与启动时产生的虚拟地址空间相对应的启动映象。关键词“x86,bios” 则分别指处理器(x86)和机器类型(通过bios启动)。而“+compress”使得映象文件被压缩,以产生更小的映象文件。startup-bios是运行于具有BIOS的PC兼容系统的可执行程序,主要负责利用BIOS检测PC硬件资源,在这里主要是提取映象,将其置于RAM的相应位置,做基本的配置并运行内核。该例中“-s 64k”表明复制ROM中第一段64k视频BIOS到RAM使其更快的执行,即所谓的ROM BIOS映射。第二项参数表明以57600波特率打开第一个串口作为一个调试通道,其调试输出可以在另一台通过计算机数据传输线(null modem cable)连接的PC上捕捉到。最后一项参数则是实时系统中比较典型的情形,其作用是在任何异常内核终止后系统均立即重启动。“PATH=/proc/boot” 确定了PATH环境变量。“procnto” 是微内核和进程管理器,包括QNX 6 微内核,进程管理,内存管理以及路径名管理,每个利用mkifs工具的可启动映象都必须具有procnto。需要注意的是在bootstrap中,第一个可执行程序必须是startup-*,而最后一个必须是procnto!
1.1.2 启动脚本(startup script)
启动脚本是在进程管理启动后的一系列将要被执行的命令列表。一个简单的script如下:
[+script] .script = {
devc-con &
reopen /dev/con
+session] sh
}
可以在这里进行配置网络,设置显示卡等操作。通常我们自己编写的需要开机自动执行的程序也放在这里,详细例子见开发案例。
1.1.3 文件列表(file list)
文件列表是上面的系统程序和用户程序执行所必须的一些文件列表,尤其是一些共享库。由于用户程序是用C语言开发的,所以必须至少包括一个标准C共享库。例如(“#”后的内容为注释):
#include the C shared library
libc.so
#create a symlink called ldqnx.so.2 to it
[type=link] /usr/lib/ldqnx.so.2=/proc/boot/libc.so
其中[type=link]行是用一个ldqnx.so.2重新定位libc.so。如果不知道在映象文件中都是需要哪些共享库,可以利用“objdump” utility(实用程序)来显示相关的需求信息。假如我们用到了ping命令,可以在终端执行:
# objdump -x ‘which ping’ | grep NEEDED
回车,将显示信息:
objdump: /usr/bin/ping: no symbols
NEEDED libsocket.so.2
NEEDED libc.so.2
表明ping命令需要libsocket.so.2和libc.so.2,再利用objdump
# objdump -x /lib/libsocket.so.2 | grep NEEDED
NEEDED libc.so.2
# objdump -x /lib/libc.so.2 | grep NEEDED
即是说libsocket.so.2需要libc.so.2,而libc.so.2不需要任何其他的库。那么如果包括ping在映象中,就必须在文件列表中列出libsocket.so.2和libc.so.2。
有了Buildfile,我们就可以通过“mkifs”utility生成启动映象文件,其语法格式是:
#mkifs [可选参数] 源文件 目标文件
比如:
#mkifs -v mydoc.build mydoc.ifs
就得到了映象文件mybuild.ifs
启动映象文件生成后就可以嵌入到目标系统,启动目标系统,为进一步的系统开发做好了准备。详细的例子见如下开发案例一。
1.1.4 QNX嵌入案例分析
目标系统:pc104的板子,DOC(Diskonchip)存储器。
设计目标:启动图形界面程序,通过网络实现资源互访。
1、构建Buildfile,我们的mydoc.build如下:
[virtual=x86,bios +compress] .bootstrap = {
startup-bios –NTarget # 使目标机的名字为Target
PATH=/proc/boot:/bin:/sbin:/usr/bin:/usr/sbin:/usr/photon/bin LD_LIBRARY_PATH=/proc/boot:/dev/shmem:/lib:/lib/dll:/usr/lib:/usr/lib/dll procnto
}
[+script] .script = {
devc-con & # 在后台打开一个控制台。
reopen /dev/con1
display_msg Welcome to QNX-DOC world !
pci-bios &
waitfor /dev/pci
devb-doc blk automount=hd0t77:/ & # 后台运行DOC驱动程序
#以下设置网络
io-net -d rtl -ptcpip -pqnet &
ifconfig en0 192.168.1.1 # IP地址
route add default 192.168.1.2 #网关IP
waitfor /dev/socket # 等待设置
waitfor /bin 15 # 等待调入其他命令
# 后台运行其他程序
pipe &
mqueue &
devc-pty -n 32 &
waitfor /sbin
waitfor /usr/sbin
# 设置环境变量
SYSNAME=nto
TERM=vt100
# 启动sh
reopen /dev/con1
[+session] sh
}
[type=link] /tmp=/dev/shmem # 设置/tmp为共享内存区
[type=link] /usr/lib/ldqnx.so.2=/proc/boot/libc.so # 加入运行时库
libc.so #标准C库
libsocket.so # socket 库
devn-rtl.so # 网卡驱动
npm-tcpip.so # TCP/IP协议
npm-qnet.so # Qnet协议
libcam.so #包含磁盘文件,使我们能够访问DOC上的文件
io-blk.so
cam-disk.so # 文件系统所需共享库
fs-qnx4.so
devc-con # 控制台驱动
devb-doc # DOC驱动
io-net # 可执行文件文件列表
ifconfig
pipe
mqueue
devc-pty
pci-bios
2、编译生成mydoc.ifs
3、做启动软盘
把软盘放进开发机软驱,在终端运行:
#dinit /dev/fd0
#mount /dev/fd0 /mnt/fd0
#cp /…/mydoc.ifs /mnt/fd0/.boot
这里假设mydoc.ifs在目录/…/下
4、启动目标系统,格式化DOC
#devb-doc &
# fdisk /dev/hd0 delete -a
# fdisk /dev/hd0 add -s 1 qnx all
# fdisk /dev/hd0 boot -s 1
# fdisk /dev/hd0 loaderfds
注意,DOC上所有的原有数据都不复存在,需要慎重。
5、重新软盘启动目标机,初始化QNX文件系统
# dinit -h /dev/hd0t77
# dinit -hb /dev/hd0t77
# mount /dev/hd0t77 /
此时在开发机上运行“# ls /net”应该可以看到Target和Host(假设开发机的名字为Host)。
# cp /…/mydoc.ifs /net/Target/.boot
至此,DOC目标机就可以自启动,而不再依靠软盘。
1.2 Photon的嵌入过程
在桌面环境中Photon的运行是通过脚本“ph”实现的[35]。ph脚本主要做如下一些工作:
l 启动Photon服务
l 检测输入硬件设备
l 启动输入硬件设备驱动
l 启动显卡驱动,初始化视频硬件为合适的模式
l 启动字体管理器
l 启动windows管理器
l 启动各个桌面服务程序,比如shelf、桌面背景管理器等。
在嵌入式环境中,需要手工启动上述各个服务。这样做有很多好处,比如可以自己决定系统需要的文件,合理配置自己的嵌入式运行环境。下面详细阐述Photon的嵌入过程:
1、设置PHOTON_PATH环境变量
PHOTON_PATH环境变量是为了保存Photon安装的基本目录。缺省情况下是目录/usr/photon。该目录下一般至少存在一下几个子目录:
l bin:存放Photon的可执行程序
l font_repository :Photon的字体以及字体配置文件(OS independent)
l palette:图形调色板 (OS independent)
l translations:Photon 语言翻译支持 (OS independent)
PHOTON_PATH环境变量的设置可以通过export进行,如下所示:
export PHOTON_PATH=/usr/photon
2、启动Photon服务器
如果不需要给Photon服务进程传递任何参数,可以简单的启动如下:
Photon &
但是,如果嵌入式环境中有触摸屏,需要增加Photon的参数-D,-R,-U等。因为人的手指比一个象素大的多,为阻止触摸位置的任意变化就需要指定-U选项。需要注意的是Photon必须包含在PATH环境变量下。在QNX Neutrino中,该位置是/usr/photon/bin。可以通过
export PATH=:/bin:/usr/bin:/usr/photon/bin
来设置PATH环境变量。
3、启动输入设备驱动
在桌面环境中,一般通过inputtrap工具来自动搜索输入设备(鼠标、键盘、触摸屏等)并为其启动合适的驱动程序。在嵌入式环境中,由于存储空间的限制一般不用inputtrap,因为inputtrap比较大而且嵌入式环境中输入设备可能在一个特殊的位置或者不被已存在的devi-*驱动支持。当然,如果情况允许也可以利用inputtrap,毕竟方便省事的多。手工加载驱动需要用devi-*系列驱动,具体见后面的实例。devi-*系列驱动的位置是/usr/photon/bin。
4、启动字体管理器
在建立嵌入式系统时要根据自己的情况决定需要支持什么样的字体以及是否需要矢量字体等。字体的确定主要有以下几个方面:
l 一般都需要cursor font(phcursor.phf)。
l 如果嵌入式系统中包括了pterm(Photon的一个终端程序),则需PC Terminal(pcterm*.phf),PC Serif(pcs*.phf),或者PC Sanserif (pcss*.phf)字体族。而且需要建立一个$HOME/.photon/pterm.rc文件或$PHOTON_PATH/config/pterm.rc文件来配置终端字体。
l 大多数基于widget应用程序需要如下几个定义在fontmap文件里的别名字体:
n TextFont
n MenuFont
n FixedFont
n BalloonFont
n TitleFont
l Web浏览器需要如下几种字体:
n Body字体(例如PrimaSans BT、Dutch 801 Rm BT等)
n Heading字体(例如Swis721 BT等)
n Nonproportional字体(例如Courier10 BT、PrimaSansMono BT等)
QNX Photon的字体管理器主要有以下几种:
l phfontphf:只支持Bitmap字体。
l phfontpfr:支持Scalable Bitstream PFR,和TrueType Collection字体。
l phfontFF:支持Scalable TrueType, Type 1, Type 2, Bitstream Speedo (retail encryption),以及Bitstream Stroke等字体。
l phfontFA:支持以上所有字体。
我们要根据自己所使用字体的实际情况选择合适的字体管理器。字体管理器的启动很简单,如:
/usr/photon/bin/phfontphf &
如果启动映象里没有包括使用的字体文件,在启动字体管理器时需要指定包括这些字体文件的路径,比如:
/usr/photon/bin/phfontphf -d /my_dir/font_repository
必须注意的是my_dir必须与$PHOTON_PATH一致。
5、启动显卡驱动
Photon图形子系统的启动通过io-graphics实现。例如:
io-graphics -g640x480x8 -dldevg-vga.so -P/usr/photon/palette/vga4.pal
io-graphics -g1024x768x16 -dldevg-vesabios.so
io-graphics -g1024x768x16 -dldevg-rage.so -d0x1002,0x4755 -I0
其中,-g指定图形的显示分辨率和颜色深度,-dl指明硬件的驱动程序,-d指明驱动唯一确定硬件所需要的PCI制造商以及设备ID,如果有两个具有相同的制造商和ID的图形卡的话需要用-I指明是哪块卡,-P指定使用的palette文件。
这一部分需要的文件主要有:
l /usr/photon/bin/io-graphics:启动图形子系统
l /lib/dll/devg-*:设备驱动
l /usr/lib/libdisputil.so.2:devg*驱动使用的库
l /usr/lib/libffb.so.2:devg*驱动使用的库
l /usr/lib/libgui.so:io-graphics使用的库
l /lib/dll/gri-photon.so:Photon DLL
l /usr/lib/libphrender.so:The software rendering routines。
6、启动windows管理器(可选)
QNX Photon Window管理器是pwm(the Photon Window Manager),它提供了标准的窗口管理功能,包括move、resize、minimize、maximize、raise、lower和close。应用程序可以根据需要定制由pwm提供的标准windows框架。在QNX中,可以通过Ctrl-Alt-Backspace结束图形模式回到终端模式。但在嵌入式运行环境中往往不提倡最终用户这样做,我们可以通过pwm来阻止该情况的发生。例如:
/usr/photon/bin/pwm –k
7、启动自己的GUI程序
如果自己设计的图形界面程序是一个单个的可执行程序且不需要window管理器的话,可以静态编译。如果需要窗口管理器或者有不止一个Photon程序在运行的话最好连接动态链接库编译。
只要好好把握以上这七步,很容易就可以定制出优异的QNX图形运行系统。下面修改开发案例一中的Buildfile,使图形界面程序可以在目标系统上执行。在Buildfile中增加如下部分,重新编译Buildfile,并把mydoc.ifs复制到目标系统的/ .altboot,重新启动目标系统。注意启动时按Esc使系统以altboot启动。
waitfor /usr/photon
Photon &
waitfor /dev/photon
phfontFA &
io-graphics -g800x600x15 -dldevg-geode.so -I0 -d0x100b,0x504 &
devi-hirun kbd kbddev ps2 mousedev & #PS/2 键盘和鼠标驱动
myph-program # 执行自己的图形界面程序程序
libm.so.2 # 包含图形显示文件
devg-geode.so # 显卡驱动