ARM主机远程控制系统的设计与实现
2010-03-23吴兆芝
吴兆芝,曲 波
(南京晓庄学院,江苏 南京 211171)
ARM处理器具有体积小、功耗少、成本低、性能高等特点,被越来越多地使用在工业控制领域.本文以ARM实验箱作为实验环境、ARM Linux作为操作系统平台、GNU工具链作为开发工具,使用嵌入式Web及嵌入式CGI编程技术,设计实现了一个ARM主机远程控制系统.本系统采用B/S方式,在客户端主机使用浏览器通过网络访问ARM主机的嵌入式Web服务程序,由Web服务程序调用其内部的嵌入式CGI子程序,由CGI子程序调用ARM主机的应用程序,实现对ARM主机的远程控制.
1 嵌入式Web编程技术
具备网络能力的嵌入式系统中最主要的趋势之一,就是包含Web(HTTP)服务器.对于ARM Linux操作系统而言,尽管存在几种较适合于嵌入式系统的开放源码Web服务器[1],如thttp和Boa等,但其体积较大(约50~70K左右),而且有些版本还不同程度地受到许可条款的限制.本文提出的嵌入式Web编程技术的目的是以最少的代码实现Web[2]服务器的基本功能:
(1)TCP/IP标准网络服务一般可有两种实现方式,一种是利用系统的超级服务器负责接收用户请求,然后调用相应的服务器程序;另一种是创建服务器守护进程,由服务器守护进程直接接收用户请求.前者适用于访问频度较低的网络服务.笔者采用守护进程方式[3,4]工作,利用Linux提供的daemon函数实现.
(2)从服务器响应用户请求的方式来看,有循环方式、多线程方式、多进程方式等多种模式.考虑到Linux操作系统的特点和Web服务的特点,采用多进程方式[3,4].
(3)只处理GET、POST请求.
(4)环境变量是Web服务器与CGI通信的重要手段,客户端的特征信息和查询信息都是通过环境变量提供给CGI的.为简化程序,本文实现的嵌入式Web服务只提供公共网关协议CGI所必需的环境变量.
在Web/Server系统中,由面向Web的服务器完成对用户客户端Web请求的服务,本质上就是对HTTP协议请求的响应过程.本系统的目的是实现对ARM主机的远程控制,无需使用过于复杂的Web页面,所以只要能响应客户端浏览器的GET和POST请求就足够了.主控程序如下:
static void get_http()
{
int x;
char *p,*q,req[1024];
if (getline(req,sizeof(req))<=0||
(p=strpbrk(req, “ ”)) == NULL)
return;
*p ++ = ' ';
if (strcmp(req, “GET”) != 0 && strcmp(req, “POST”) != 0)
return;
if ((q = strpbrk(p, “ ”)) != NULL)
*q = ' ';
unsetenv(“QUERY_STRING”);
if ((q = strchr(p, '?')) != 0) {
*q ++ = ' ';
setenv(“QUERY_STRING”, q, 1);
}
setenv(“SCRIPT_NAME”, p, 1);
strcpy(req, logfile);
req[strlen(req) - 4] = ' ';
setenv(“SCRIPT_FILENAME”, req, 1);
chk_head();
do_cgi();
}
其中,getline函数从连接套接字读入一行,返回值为该行长度,若长度不大于0,表示不是HTTP页面,则返回;主控程序对getline函数返回的首行字符串判断是否GET请求或POST请求,如果不是这二者,则返回.
chk_head函数解析头部信息,设置相应的环境变量.
do_cgi函数实现对CGI处理函数的调用.该函数首先创建双向管道,然后用fork函数创建一个子进程,在子进程中调用CGI处理函数my_cgi,并通过双向管道实现Web服务程序与CGI处理程序的双向信息传递.
2 嵌入式CGI编程
通常,CGI[2]程序都是存储在磁盘上,被Web服务器调用时从磁盘装入内存.装入操作明显会占用系统时间,降低CGI程序的响应速度.本系统采用嵌入式CGI技术,把CGI程序与嵌入式Web服务程序设计在一起,成为一个整体,既减小了体积,又明显提高了运行速度.
本系统实现的CGI处理是通过HTTP协议的POST请求实现的.POST请求页面首行若干行之后有一个空行,这是一个标志行,表示从下行开始为POST请求页面的参数,格式为:参数1=参数值1&参数2=参数值2&参数3=参数值3…参数n=参数值n,各参数之间用“&”分隔.
客户端Web程序对参数值中的某些字符作了变换,方法有两种:一种是将某字符用另一字符替换,例如将空格符用“+”替换;另一种是用“%XX”形式的字符串表示一个字符,其中XX为该字符的两位16进制ASCII码,例如“=”的16进制ASCII码为3D,则该串为“%3D”.
因此,CGI程序的一项重要工作就是处理POST请求,将各参数分解并将各参数值解码.关键代码如下:
voiddecode_info(char *ptr)
{
char *p0, *p1, *p2, c, is_eq = 0, s[3];
p0 = p1 = p2 = ptr;
while (*p0) {
c = *p0 ++;
switch(c) {
case '&':
if (!is_eq) {
p1 = p2;
} else {
*p1 ++ = ' '; p2 = p1; is_eq = 0;
}
continue;
case '+':
*p1 ++ = ' ';
continue;
case '%':
s[0] = *p0 ++; s[1] = *p0 ++; s[2] = ' '; *p1 ++ = x2c(s);
continue;
case '=':
*p1 ++ = ' '; is_eq = 1;
continue;
default:
*p1 ++ = c;
}
}
if (!is_eq)
p1 = p2;
else
*p1 ++ = ' ';
*p1 = ' ';
}
CGI程序的另一项重要工作是调用ARM主机的应用程序,实现对ARM主机的远程控制.在守护进程中调用系统命令或应用程序,要使用exec系统函数,本系统使用execlp函数[4]实现.以控制ARM实验箱直流电机为例,具体实现方法如下.
笔者使用的博创ARM2410S实验箱的ARM Linux系统提供了一个控制实验箱直流电机运转的演示程序,文件名为dcm_main,存放在/mnt/yaffs/motor/DC子目录下.该程序运行后,会启动实验箱直流电机转动,同时不断改变电机的转速及方向,并将其速度及方向值送标准输出设备输出.如果直接在CGI程序中调用该程序,则该程序运行过程中的输出信息会连续不断地通过CGI程序回送给客户机浏览器.为避免输出信息回送,专门设计了一个CGI调用脚本do_dcm,通过该脚本调用dcm_main,启动直流电机运转.脚本内容如下:
/mnt/yaffs/motor/DC/do_main > /dev/null
显然经过脚本调用后,do_main运行时的输出信息都被转向到了空文件/dev/null.
为简化程序,对于关闭直流电机(停止运转),采用中止do_main进程的方法实现.为此设计了CGI调用脚本un_dcm,脚本内容如下:
killall do_main > /dev/null
由于execlp函数调用系统命令或应用程序成功后,不再返回原调用程序,所以必须在CGI程序中先创建一个子进程,然后在子进程中调用execlp.由于篇幅所限,本文省略了一些技术细节.
3 结束语
本文提出的嵌入式Web及嵌入式CGI编程方法,集Web与CGI于一身,无需通用Web服务器的支持, 可节省ARM主机的系统资源,提高运行效率;全部用C语言设计而成[3,4],整个程序只有1200行,编译后的可执行程序不足32K,程序简洁易懂、短小精干,很有实用价值.
参考文献:
[1][加]Yaghmour.K.Building Embedded Linux System,2E[M].南京:东南大学出版社(英文影印版),2009.
[2][美]Andrew S.Tanenbaum.Computer Networks(Fourth Edition)[M].北京:清华大学出版社(英文影印版),2008.
[3][美]Marc J.Rochkind.Advanced UNIX Programming[M].北京:清华大学出版社(英文影印版),2006.
[4][美]Love,R.Linux System Programming[M].南京:东南大学出版社(英文影印版),2008.