现在的位置: 首页 > 综合 > 正文

QNX 系统 HAM 应用分析及实现

2014年08月29日 ⁄ 综合 ⁄ 共 14166字 ⁄ 字号 评论关闭

1        引言

1.1文档内容

    系统的高可靠性设计需要在模块功能,进程,或线程部分功能失效时能进行相应的处理,进行故障恢复等处理。QNX提供了一套完整的可靠性处理方案,就是HAM模块。本文分析了QNX HAM模块的功能。以便溶合进实际项目。

1.2适用平台

软件环境:QNX CAR2

1.3 述语

述语

描述

entry

实体(指进程)

condition

条件(指异常退出,心跳异常等事件)

action

动作(条件的具体处理办法)

HAM

High Availability Manager.

 

2        
Ham
功能介绍

2.1.   进程自主监测

进程自已监测自已,请求HAM服务器,当我挂死了,做什么事情

进程可以自已选择监测的开始和结束时间,选择相关的触发条件和执行动作。比如进程可以监控内部某个子线程或子代码段的异常,也可以自已触发心跳到服务器,监测心跳异常情况。

使用场景如下:

1.由于APP不是常驻内存的,但是如果APP需要执行一段有可能BLOCK或者出现异常的代码或线程时,可以请求HAM监控,当我出现异常时,请重新启动我。

2.2.   监测外部进程

   进程可以监控外部的进程(守护进程或服务进程等),“请求HAM服务器,当B挂死了,请告诉我,但是B对此毫不知情”。

使用场景如下:

1.  如果多个APP同时会请求使用某个中间件的服务,可以由中间件在单个APP的请求处期间,启动对APP的监测,请求当正在处理的APP异常退出时,通知我进行相关的清理。

如USB请求播放列表,但是中途APP挂掉了。这样中间件可以同步的停止播放。

2.  安装管理器可以对常驻内存的进程进行监控,发现其异常退出时,立即重启,同时也可以在需要其长久退出时,停止监控,使其退出。

2.3.   批量处理异常

   系统中存在这样一种虚拟进程,可以监控所有被监控的进程,“当任意被监控进程退出或无心跳时,请做以下的工作”。对所有进程的任一种异常注册相同的处理。

   这种进程不可添加删除,只能向其注册触发条件和执行动作,你可以将多个条件和动作注册上去。

使用场景如下:

1.  对系统中所有的被监控进程注册统一的处理。当有进程异常退出时,进行系统重要日志的记录。

2.4.   进程状态变迁

进程可以内部状态变迁时,将这种变迁状态通知外部进程,外部进程感知到这种状态后,可以进行相当的应对措施或紧急修补。比如:服务进程在其检测到硬件故障,暂时无法提供某种服务时,通知所有使用该服务的客户端,服务状态不可用,服务端收到这状态变迁时,进行相关的处理(如暂时停止相关功能);当服务器的故障恢复时,再通知客户端进行相关的恢复。

实例分析:

中间件mediaware提供音频播放服务,当其检测到音频芯片坏掉时,将停止音频播放服务。状态由OK转变为POK。Usbapp获取这种状态并将此功能禁止和提示用户。

l       
Mediaware代码片段:

ham_connect(0);

ehdl = ham_entity_handle(ND_LOCAL_NODE, "mediaware", 0);

 

ham_entity_condition_state(ehdl,STA_OK,0);

……………………

ham_entity_condition_state(ehdl, STA_POK,0);

 

      
ham_entity_handle_free(ehdl);

l       
usbapp代码片段:

ham_connect(0);

ehdl = ham_entity_handle(ND_LOCAL_NODE, " mediaware", 0);

chd1 = ham_condition_state(ehdl,processpok,
STA_OK, STA_POK,0);

ahdl = ham_action_notify_pulse(chdl, "notify pok ", ND_LOCAL_NODE,pid, chid, PCODEINETDRESTART1, value, HREARMAFTERRESTART);

ham_disconnect(0);

2.5.   进程事件通知

进程内部发生变化或异常时,可以将这种局部异常以事件的方式通知外部进程,这种通知方式相对于状态变迁通知来说,能提供更多的参数,粒度更细。

实例分析:

比如一个中间件同时提供两种声音通道的输出服务,但是当其中一种通道出现异常时,可以通知相关的客户端,具体是哪个通道异常;在恢复时再通知哪一个通道其正常。

l       
Mediaware代码发送片段:

ham_connect(0);

ehdl = ham_entity_handle(ND_LOCAL_NODE, "mediaware", 0);

//rtype=1表示通道异常,rclass=2表示通道2异常

ham_entity_condition_raise(ehdl, rtype=1, rclass=2, rseverity, flags );

 

      
ham_entity_handle_free(ehdl);

l       
usbapp代码接收片段:

ham_connect(0);

ehdl = ham_entity_handle(ND_LOCAL_NODE, " mediaware", 0);

chd1 = ham_condition_raise(ehdl,processpok,1,2,0,0);

ahdl = ham_action_notify_pulse(chdl, "notify pok ", ND_LOCAL_NODE,pid, chid, PCODEINETDRESTART1, value, HREARMAFTERRESTART);

ham_disconnect(0);

2.6.   客户端恢复功能

征对一些I/0操作的场景,HAM提供了一种连接/文件读写等的异常自主恢复功能,提供一种高可告靠性的I/0操作功能。比如客户端和服务器之间存在消息连接,在客户端获取数据期间,服务器重启了,那么客户端保存的连接句柄有可能失效。这种情况下,可以让客户端进行HAM监控,HAM会自动重连接,返回相同的句柄。

3        
condition
执行方式

每种条件都有三种执行方式:

HCONDNOWAIT

一个单独的线程来汇总处理所有此类型的条件。

HCONDINDEPENDENT

每个条件都会实时创建一个线程去处理。

OTHER:(NOHCONDNOWAIT AND NO HCONDINDEPENDENT

一个单独的线程为汇总处理所有此类型的的条件。

注意:同种方式内所有条件都是FIFO的方式执行。HCONDNOWAIT优先级高于HCONDINDEPENDENT

4        
condition
的类型

CONDDEATH  进程中止

CONDABNORMALDEATH
进程异常中止

CONDDETACH  进程连接

CONDATTACH  进程去连接

CONDHBEATMISSEDHIGH
进程失去心跳达最大次数

CONDHBEATMISSEDLOW
进程失去心跳达最小次数

CONDRESTART 进程重新启动

CONDANY  匹配任意类型

5        
action
方式

每个条件可以顺序注册多个action,所有action都是以FIFO的方式执行。

动作类型如下:

重启

延时

执行脚本

发送PULSE事件

发送系统信号事件

执行失败后的执行脚本。

6        
/entry/condition/action
控制

对于进程,条件和动作都可以通过API函数进行有效,无效的控制,提供临时的开启或关闭某种条件的功能。

ham_entity_control()

ham_condition_control()

ham_action_control()

7        
ham
开启和关闭

命令行打开HAM,通过命令行或API函数都可以关闭HAM,方式如下:

Ham

hamctrl -stop

int ham_stop(void);

 

8         范例

ham_entity_t *ehdl;

ham_condition_t * chdl;

ham_action_t *ahdl;

int status;

 

ehdl = ham_attach("usb1.app", 0, -1, NULL, 0);//利用进程名启动监控

/* usb1.app  running and monitored now */

/*对这个监控注册一个进程异常死掉的条件*/

chdl = ham_condition(ehdl, CONDABNORMALDEATH, "death", HREARMAFTERRESTART);

/*对这个监控注册一个进程异常死掉的后重启的动作*/

ahdl =

ham_action_restart(chdl, "restart", “/opt/apps/usb1.app”,HREARMAFTERRESTART);

/*对这个监控注册一个进程异常死掉的执行的脚本*/

ahdl =

ham_action_execute(chdl,"MountDir1","/bin/xxx.sh",HREARMAFTERRESTART|HACTIONDONOW));

/*对这个监控注册一个进程死掉后,给本进程发送一个pulse事件,具体的事件接受有另外的代码,稍后详细提供*/

ahdl = ham_action_notify_pulse(chdl, "notifypulsedeath",ND_LOCAL_NODE, pid,

chid, PCODEINETDDEATH, value, HREARMAFTERRESTART);

 

 

status = ham_detach(ehdl,0);//利用句柄进行去监控

/* status = ham_detach_name(0, "inetd",0);  利用进程名称进行去监控*/

 

...

ham_entity_t *ehdl; /* The entity Handle */

int status;

/*

connects to a HAM with a heartbeat of 5 seconds

and an entity name of "client1", and no flags

it also specifies hpdh = 4, and hpdh = 8

*/

ehdl = ham_attach_self("client1", 5000000000, 4, 8, 0);

if (ehdl == NULL) {

printf("Could not attach to Ham\n");

exit(-1);

}

/*心跳失去次数达到最大值*/

chdl = ham_condition(ehdl, CONDHBEATMISSEDHIGH, "hith", HREARMAFTERRESTART);

 

/*心跳失去次数达到最小值*/

chdl = ham_condition(ehdl, CONDHBEATMISSEDLOW, "low", HREARMAFTERRESTART);

 

 

/* Detach from a HAM using the original handle */

status = ham_detach_self(ehdl,0);

 

 

8.1       
Example to monitor inetd

The following code snippet shows how to begin monitoring theinetd
process:

#include <stdio.h>

#include <string.h>

#include <stdlib.h>

#include <unistd.h>

#include <sys/stat.h>

May 10, 2010 Chapter 4 Using the High Availability Manager
35

HAM API .2010, QNX Software Systems GmbH & Co. KG.

#include <sys/netmgr.h>

#include <fcntl.h>

#include <ha/ham.h>

int main(int argc, char *argv[])

{

int status;

char *inetdpath;

ham_entity_t *ehdl;

ham_condition_t *chdl;

ham_action_t *ahdl;

int inetdpid;

inetdpath = strdup("/usr/sbin/inetd -D");

inetdpid = -1;

ham_connect(0);

ehdl = ham_attach("inetd", ND_LOCAL_NODE, inetdpid, inetdpath, 0);

if (ehdl != NULL)

{

chdl = ham_condition(ehdl,CONDDEATH, "death", HREARMAFTERRESTART);

if (chdl != NULL) {

ahdl = ham_action_restart(chdl, "restart", inetdpath,

HREARMAFTERRESTART);

if (ahdl == NULL)

printf("add action failed\n");

}

else

printf("add condition failed\n");

}

else

printf("add entity failed\n");

ham_disconnect(0);

exit(0);

}

8.2     
Example to monitor fs-nfs2

The following code snippet shows how to begin monitoring thefs-nfs2
process:

#include <stdio.h>

#include <string.h>

#include <stdlib.h>

#include <unistd.h>

#include <sys/stat.h>

#include <sys/netmgr.h>

#include <fcntl.h>

#include <ha/ham.h>

int main(int argc, char *argv[])

{

int status;

ham_entity_t *ehdl;

ham_condition_t *chdl;

ham_action_t *ahdl;

char *fsnfspath;

int fsnfs2pid;

fsnfspath = strdup("/usr/sbin/fs-nfs2");

fsnfs2pid = -1;

ham_connect(0);

ehdl = ham_attach("Fs-nfs2", ND_LOCAL_NODE, fsnfs2pid, fsnfspath, 0);

if (ehdl != NULL)

{

chdl = ham_condition(ehdl,CONDDEATH, "Death", HREARMAFTERRESTART);

if (chdl != NULL) {

ahdl = ham_action_restart(chdl, "Restart", fsnfspath,

HREARMAFTERRESTART);

if (ahdl == NULL)

printf("add action failed\n");

else {

36 Chapter 4
Using the High Availability Manager May 10, 2010

. 2010, QNX Software Systems GmbH & Co. KG. A client example

ahdl = ham_action_waitfor(chdl, "Delay1", NULL, 2000,

HREARMAFTERRESTART);

if (ahdl == NULL)

printf("add action failed\n");

ahdl = ham_action_execute(chdl, "MountDir1",

"/bin/mount -t nfs a.b.c.d:/dir1 /dir1",

HREARMAFTERRESTART|HACTIONDONOW));

if (ahdl == NULL)

printf("add action failed\n");

ahdl = ham_action_waitfor(chdl, "Delay2", NULL, 2000,

HREARMAFTERRESTART);

if (ahdl == NULL)

printf("add action failed\n");

ahdl = ham_action_execute(chdl, "Mountdir2",

"/bin/mount -t nfs a.b.c.d:/dir2 /dir2",

HREARMAFTERRESTART|HACTIONDONOW);

if (ahdl == NULL)

printf("add action failed\n");

}

}

else

printf("add condition failed\n");

}

else

printf("add entity failed\n");

ham_disconnect(0);

exit(0);

}

8.3     
A client example

Here’s an example of a client that obtains notifications via pulses and signals about

significant events from a HAM. It registers a pulse-notification scheme in the event

that inetd dies or detaches. It also registers a signal-notification mechanism for the

death of fs-nfs2.

This example also demonstrates how the delayed notification occurs, and shows how

to overcome this using an HCONDINDEPENDENT condition.

#include <stdio.h>

#include <string.h>

May 10, 2010 Chapter 4 • Using the High Availability Manager 37

A client example 2010, QNX Software Systems GmbH & Co. KG.

#include <stdlib.h>

#include <unistd.h>

#include <sys/stat.h>

#include <fcntl.h>

#include <errno.h>

#include <sys/neutrino.h>

#include <sys/iomsg.h>

#include <sys/netmgr.h>

#include <signal.h>

#include <ha/ham.h>

#define PCODEINETDDEATH _PULSE_CODE_MINAVAIL+1

#define PCODEINETDDETACH _PULSE_CODE_MINAVAIL+2

#define PCODENFSDELAYED _PULSE_CODE_MINAVAIL+3

#define PCODEINETDRESTART1 _PULSE_CODE_MINAVAIL+4

#define PCODEINETDRESTART2 _PULSE_CODE_MINAVAIL+5

#define MYSIG SIGRTMIN+1

int fsnfs_value;

/* Signal handler to handle the death notify of fs-nfs2 */

void MySigHandler(int signo, siginfo_t *info, void *extra)

{

printf("Received signal %d, with code = %d, value %d\n",

signo, info->si_code, info->si_value.sival_int);

if (info->si_value.sival_int == fsnfs_value)

printf("FS-nfs2 died, this is the notify signal\n");

return;

}

int main(int argc, char *argv[])

{

int chid, coid, rcvid;

struct _pulse pulse;

pid_t pid;

int status;

int value;

ham_entity_t *ehdl;

ham_condition_t *chdl;

ham_action_t *ahdl;

struct sigaction sa;

int scode;

int svalue;

/* we need a channel to receive the pulse notification on */

chid =ChannelCreate(
0 );

/* and we need a connection to that channel for the pulse to be

delivered on */

coid = ConnectAttach( 0, 0, chid, _NTO_SIDE_CHANNEL, 0 );

/* fill in the event structure for a pulse */

pid = getpid();

value = 13;

ham_connect(0);

/* Assumes there is already an entity by the name "inetd" */

chdl = ham_condition_handle(ND_LOCAL_NODE, "inetd","death",0);

ahdl = ham_action_notify_pulse(chdl, "notifypulsedeath",ND_LOCAL_NODE, pid,

chid, PCODEINETDDEATH, value, HREARMAFTERRESTART);

ham_action_handle_free(ahdl);

ham_condition_handle_free(chdl);

ehdl = ham_entity_handle(ND_LOCAL_NODE, "inetd", 0);

chdl = ham_condition(ehdl, CONDDETACH, "detach", HREARMAFTERRESTART);

ahdl = ham_action_notify_pulse(chdl, "notifypulsedetach",ND_LOCAL_NODE, pid,

chid, PCODEINETDDETACH, value, HREARMAFTERRESTART);

ham_action_handle_free(ahdl);

ham_condition_handle_free(chdl);

ham_entity_handle_free(ehdl);

fsnfs_value = 18; /* value we expect when fs-nfs dies */

scode = 0;

svalue = fsnfs_value;

38 Chapter 4 • Using the High Availability Manager May 10, 2010

2010, QNX Software Systems GmbH & Co. KG. A client example

sa.sa_sigaction = MySigHandler;

sigemptyset(&sa.sa_mask);

sa.sa_flags = SA_SIGINFO;

sigaction(MYSIG, &sa, NULL);

/*

Assumes there is an entity by the name "Fs-nfs2".

We use "Fs-nfs2" to symbolically represent the entity

fs-nfs2. Any name can be used to represent the

entity, but it’s best to use a readable and meaningful name.

*/

ehdl = ham_entity_handle(ND_LOCAL_NODE, "Fs-nfs2", 0);

/*

Add a new condition, which will be an "independent" condition.

This means that notifications/actions inside this condition

are not affected by "waitfor" delays in other action

sequence threads

*/

chdl = ham_condition(ehdl,CONDDEATH, "DeathSep",

HCONDINDEPENDENT|HREARMAFTERRESTART);

ahdl = ham_action_notify_signal(chdl, "notifysignaldeath",ND_LOCAL_NODE,

pid, MYSIG, scode, svalue, HREARMAFTERRESTART);

ham_action_handle_free(ahdl);

ham_condition_handle_free(chdl);

ham_entity_handle_free(ehdl);

chdl = ham_condition_handle(ND_LOCAL_NODE, "Fs-nfs2","Death",0);

/*

This action is added to a condition that does not

have an HCONDNOWAIT. Since we are unaware what the condition

already contains, we might end up getting a delayed notification

since the action sequence might have "arbitrary" delays and

"waits" in it.

*/

ahdl = ham_action_notify_pulse(chdl, "delayednfsdeathpulse", ND_LOCAL_NODE,

pid, chid, PCODENFSDELAYED, value, HREARMAFTERRESTART);

ham_action_handle_free(ahdl);

ham_condition_handle_free(chdl);

ehdl = ham_entity_handle(ND_LOCAL_NODE, "inetd", 0);

/* We force this condition to be independent of all others. */

chdl = ham_condition(ehdl, CONDRESTART, "restart",

HREARMAFTERRESTART|HCONDINDEPENDENT);

ahdl =
ham_action_notify_pulse
(chdl, "notifyrestart_imm", ND_LOCAL_NODE,

pid, chid, PCODEINETDRESTART1, value, HREARMAFTERRESTART);

ham_action_handle_free(ahdl);

ahdl = ham_action_waitfor(chdl, "delay",NULL,6532, HREARMAFTERRESTART);

ham_action_handle_free(ahdl);

ahdl = ham_action_notify_pulse(chdl, "notifyrestart_delayed", ND_LOCAL_NODE,

pid, chid, PCODEINETDRESTART2, value, HREARMAFTERRESTART);

ham_action_handle_free(ahdl);

ham_condition_handle_free(chdl);

ham_entity_handle_free(ehdl);

while (1) {

rcvid = MsgReceivePulse( chid, &pulse, sizeof( pulse ), NULL );

if (rcvid < 0) {

if (errno != EINTR) {

exit(-1);

}

}

else {

switch (pulse.code) {

case PCODEINETDDEATH:

printf("Inetd Death Pulse\n");

break;

case PCODENFSDELAYED:

printf("Fs-nfs2 died: this is the possibly delayed pulse\n");

break;

case PCODEINETDDETACH:

printf("Inetd detached, so quitting\n");

May 10, 2010 Chapter 4 • Using the High Availability Manager 39

Starting and stopping a HAM 2010, QNX Software Systems GmbH & Co. KG.

goto the_end;

case PCODEINETDRESTART1:

printf("Inetd Restart Pulse: Immediate\n");

break;

case PCODEINETDRESTART2:

printf("Inetd Restart Pulse: Delayed\n");

break;

}

}

}

/*

At this point we are no longer waiting for the

information about inetd, since we know that it

has exited.

We will still continue to obtain information about the

death of fs-nfs2, since we did not remove those actions.

If we exit now, the next time those actions are executed

they will fail (notifications fail if the receiver does not

exist anymore), and they will automatically get removed and

cleaned up.

*/

the_end:

ham_disconnect(0);

exit(0);

}

9          参考文档

ham.pdf

10      QNX项目HAM设计实例

1.        
软件分类为以下几种:

分类

举例

监控类型

监控方案

常驻进程

中间件或常驻app

A

 

非常驻进程

Usb.app

B

 

系统功能进程

如挂载数据分区失败

Mount /dev/sda1 /opt

C

 

 

 

 

 

 

 

 

 

以上属性在安装时通过配置文件指定,安装后在系统的PPS中读取属性。

2.        软件异常DFAMA分析:

分类

故障类型

动作

AB

运行中,子功能体block

l         杀掉进程

l         执行崩溃脚本(通知相关的中间件停止相关的动作)

l         重启进程

l        如果重启失败则执行清场脚本(通知navi/middleware)

l         日志记录:时间,模块,异常类型,总累计次数,现场信息

AB

运行中崩溃。

l         重启进程

l         执行崩溃脚本(通知相关的中间件停止相关的动作)

l         如果重启失败则执行清场脚本(通知navi/middleware)

l         日志记录:时间,模块,异常类型,总累计次数,现场信息

C

数据分区挂载失败

1. 
再尝试挂载

2. 
此次尝试挂载失败时:

l         提示用户分区异常,修复中。

l         进行分区格式化。

l         提示用户重启倒计时,用户可中断重启。

3.日志记录:时间,模块,异常类型,总累计次数,现场信息

 

 

 

3.        
HAM
设计方案

设计原则:

l        
HAM
的整个功能设计中并未考虑到启动后又失败,不断反复重启的情况。因为HAM的机制的是监控你,当你处于一种异常条件时,让我做什么事情,以及当我做这件事情失败了后怎么处理。征对软件高频度的挂机,高频度的启动不了,高频度的异常等情况,任何可靠性设计都无法完美的解决,只能通过修正BUG解决。

l         简单实用化的对HAM进行溶合:软件崩溃了,我们需要做的有三件事:通知相关其它模块停止相关服务;杀掉进程(block),重新启动;日志记录,方便追踪定位,如果重启失败,则通知相应模块进行清场回退。

l         由于每个模块的退出可能执行的通知和清场操作都不一样,无法集后统一处理;另外还要考虑到第三方软件的HAM方案,所以需要一种灵活的方案。由于HAM提供最主要的注册行为是:注册重启和注册执行脚本,所以对于模块的清场处理,我们通过由运行由模块提供脚本实现。模块需要执行什么清场及自救措施,就提供什么样的脚本。比如可以在脚本中写navigator或中间件的pps来通知其退出。

l         对于非常驻APP的监测方式。非常驻模块的监测有两种方式:1)模块自主监测。模块在启动时注册HAM监测,在退出时去注册HAM监测。2)外部监测。模块启动时通知外部进程启动监测,模块退出时通知外部进程退出监测。由于非常驻内存的APP都是由NAVIGATOR统一启动和退出,所以对于此类APP,统一由NAVIGATOR来进行HAM相关的注册和去注册工作。

l        因为我需要知道哪些程序需要监控,哪些不需要,如何监控,还要支持动态扩展。所有系统的功能程序(驱动/app/中间件等)都需要安装或者注册,并通过属性参数指定是否为常驻程序,是否需要HAM监测(默认为是),HAM监测类型(心跳监控/其它监控),崩溃脚本:(安装目录下的crash.sh),清场脚本(安装目录下的clear.sh)。如果无,不放置即可。

典型应用场景:

1.        
新闻查看
APP,申请进行新闻的TTS播放。

²        在播报的过程中APP崩溃了,这样后台一直在播报,并无法控制其停止。

²        在播放过程中APP崩溃了,并且在这种状态下重启失败,导致UI黑屏。正常情况下是重启成功,恢复UI显示。

2.        
USB
APP,正在选择循环播放后台歌曲列表。

²        在播放过程中APP崩溃了,这样后台将一直播放,且无法控制其停止。

²        在播放过程中APP崩溃了,并且重启失败,导致UI黑屏。

征对以上两种异常场景,设计机制如下:

             
APP进行异常监测,注册崩溃执行脚本(crash.sh)和重启失败执行脚本(clear.sh),这两个脚本在模块安装时提供,在其中可以分别执行不同类型的恢复动作。

如:crash.sh中通知中间件停止播放;clear.sh中通知navigator切到上一源和上一UI

设计方案:

HAM监控对象分两种:

常驻内存进程

需要一直处于运行状态,如发现未处于运行状态则立即重启。

非常驻内存进程

只有在使用时处于运行状态,使用完毕后退出。

HAM监控方式分两种:

心跳监控方式

征对于存在局部易BLOCK的进程,需要进程内置HAM代码,进行心跳发送。

外部监控方式

被监测进程无需干与,纯由外部进行监测,在发现其异常退出后重新启动,并使其一直处于运行状态。

以上两两可以任意组合。

抱歉!评论已关闭.