linux

位置:IT落伍者 >> linux >> 浏览文章

基于 Linux 和 MiniGUI 的嵌入式系统软件开发指南(六)


发布日期:2020年11月14日
 
基于 Linux 和 MiniGUI 的嵌入式系统软件开发指南(六)

作者魏永明

MiniGUI 提供的非 GUI/GDI 接口

本文讲述了 MiniGUI 为应用程序提供的非 GUI/GDI 接口这些接口能够帮助应用程序更好地和操作系统交互扩展应用程序功能并提高应用程序的可移植性内容主要涉及到如下几个方面MiniGUILite 和 select 系统调用基于 UNIX Domain Socket 的进程间通讯编写可移植性代码等

引言

一般而言GUI 系统的应用程序编程接口主要集中于窗口消息队列图形设备等相关方面但因为 GUI 系统在处理系统事件时通常会提供自己的机制而这些机制往往会和操作系统本身提供的机制不相兼容比如MiniGUI 提供了消息循环机制而应用程序的结构一般是消息驱动的也就是说应用程序通过被动接收消息来工作但很多情况下应用程序需要主动监视某个系统事件比如在 UNIX 操作系统中可以通过 select 系统调用监听某个文件描述符上是否有可读数据这样如何将 MiniGUI 的消息队列机制和现有操作系统的其他机制融合在一起就成了一个较为困难的问题本文将讲述几种解决这一问题的方法

我们知道MiniGUILite 采用 UNIX Domain Socket 实现客户程序和服务器程序之间的交互应用程序也可以利用这一机制完成自己的通讯任务――客户向服务器提交请求而服务器完成对客户的请求处理并应答一方面在 MiniGUILite 的服务器程序中你可以扩展这一机制注册自己的请求处理函数完成定制的请求/响应通讯任务另一方面MiniGUILite 当中也提供了若干用来创建和操作 UNIX Domain Socket 的函数任何 MiniGUILite 的应用程序都可以建立 UNIX Domain Socket并完成和其他 MiniGUILite 应用程序之间的数据交换本文将举例讲述如何利用 MiniGUILite 提供的函数完成此类通讯任务

嵌入式 Linux 系统现在能够在许多不同架构的硬件平台上运行MiniGUI 也能够在这些硬件平台上运行但由于许多硬件平台具有和其他硬件平台不同的特性比如说常见的 CPU 是 Little Endian 的而某些 CPU 则是 Big Endian 的这要求我们在编写代码尤其是文件 I/O 相关代码时必须编写可移植代码以便适合具有不同架构的平台本文将描述 MiniGUI 为应用程序提供的可移植性函数及其用法

除了与上述内容相关的函数之外MiniGUI 还提供了其他一些函数本文最后部分将描述这些函数的用途和用法包括配置文件读写以及定点数运算

MiniGUILite和 select 系统调用

我们知道在 MiniGUILite 之上运行的应用程序只有一个消息队列应用程序在初始化之后会建立一个消息循环然后不停地从这个消息队列当中获得消息并处理直到接收到 MSG_QUIT 消息为止应用程序的窗口过程在处理消息时要在处理完消息之后立即返回以便有机会获得其他的消息并处理现在如果应用程序在处理某个消息时监听某个文件描述符而调用 select 系统调用就有可能会出现问题――因为 select 系统调用可能会长时间阻塞而由 MiniGUILite 服务器发送给客户的事件得不到及时处理这样消息驱动的方式和 select 系统调用就难于很好地融合在 MiniGUIThreads 中因为每个线程都有自己相应的消息队列而系统消息队列是由单独运行的 desktop 线程管理的所以任何一个应用程序建立的线程都可以长时间阻塞从而可以调用类似 select 的系统调用但在 MiniGUILite 当中如果要监听某个应用程序自己的文件描述符事件必须进行恰当的处理以避免长时间阻塞

在 MiniGUILite 当中有几种解决这一问题的办法

在调用 select 系统调用时传递超时值保证 select 系统调用不会长时间阻塞

设置定时器定时器到期时利用 select 系统调用查看被监听的文件描述符如果没有相应的事件发生则立即返回否则进行读写操作

利用 MiniGUILite 提供的 RegisterListenFD 函数在系统中注册监听文件描述符并在被监听的文件描述符上发生指定的事件时向某个窗口发送 MSG_FDEVENT 消息

由于前两种解决方法比较简单这里我们重点讲述的第三种解决办法MiniGUILite 为应用程序提供了如下两个函数及一个宏

#define MAX_NR_LISTEN_FD

/* Return TRUE if all OK and FALSE on error */

BOOL GUIAPI RegisterListenFD (int fd int type HWND hwnd void* context);

/* Return TRUE if all OK and FALSE on error */

BOOL GUIAPI UnregisterListenFD (int fd);

MAX_NR_LISTEN_FD 宏定义了系统能够监听的最多文件描述符数默认定义为

RegisterListenFD 函数在系统当中注册一个需要监听的文件描述符并指定监听的事件类型(type 参数可取 POLLINPOLLOUT 或者 POLLERR)接收 MSG_FDEVENT 消息的窗口句柄以及一个上下文信息

UnregisterListenFD 函数注销一个被注册的监听文件描述符

在应用程序使用RegisterListenFD 函数注册了被监听的文件描述符之后当指定的事件发生在该文件描述符上时系统会将 MSG_FDEVENT 消息发送到指定的窗口应用程序可在窗口过程中接收该消息并处理MiniGUI 中的 libvcongui 就利用了上述函数监听来自主控伪终端上的可读事件如下面的程序段所示(vcongui/vconguic)

/* 注册主控伪终端伪监听文件描述符 */

RegisterListenFD (pConInfo>masterPty POLLIN hMainWnd );

/* 进入消息循环 */

while (!pConInfo>terminate && GetMessage (&Msg hMainWnd)) {

DispatchMessage (&Msg);

}

/* 注销监听文件描述符 */

UnregisterListenFD (pConInfo>masterPty);

/* 虚拟控制台的窗口过程 */

static int VCOnGUIMainWinProc (HWND hWnd int message WPARAM wParam LPARAM lParam)

{

PCONINFO pConInfo;

pConInfo = (PCONINFO)GetWindowAdditionalData (hWnd);

switch (message) {

/* 接收到 MSG_FDEVENT 消息则处理主控伪终端上的输入数据 */

case MSG_FDEVENT:

ReadMasterPty (pConInfo);

break;

}

/* 调用默认窗口过程 */

if (pConInfo>DefWinProc)

return (*pConInfo>DefWinProc)(hWnd message wParam lParam);

else

return DefaultMainWinProc (hWnd message wParam lParam);

}

节当中我们还可以看到RegisterListenFD 函数的使用显然通过这种简单的注册监听文件描述符的接口MiniGUILite 程序能够方便地利用底层的消息机制完成对异步事件的处理

MiniGUILite 与进程间通讯

简单请求/应答处理

我们知道MiniGUILite 利用了 UNIX Domain Socket 实现服务器和客户程序之间的通讯为了实现客户和服务器之间的简单方便的通讯MiniGUILite 中定义了一种简单的请求/响应结构客户程序通过指定的结构将请求发送到服务器服务器处理请求并应答在客户端一个请求定义如下(include/gdih)

typedef struct tagREQUEST {

int id;

const void* data;

size_t len_data;

} REQUEST;

typedef REQUEST* PREQUEST;

其中id 是用来标识请求类型的整型数data 是发送给该请求的关联数据len_data 则是数据的长度客户在初始化 REQUEST 结构之后就可以调用 cli_request 向服务器发送请求并等待服务器的应答该函数的原型如下

/* send a request to server and wait reply */

int cli_request (PREQUEST request void* result int len_rslt);

服务器程序(即 mginit)会在自己的消息循环当中获得来自客户的请求并进行处理最终会将处理结果发送给客户

在上述这种简单的客户/服务器通讯中客户和服务器必须就每个请求类型达成一致也就是说客户和服务器必须了解每种类型请求的数据含义并进行恰当的处理

MiniGUILite 利用上述这种简单的通讯方法实现了若干系统级的通讯任务

鼠标光标的管理鼠标光标是一个全局资源当客户需要创建或者销毁鼠标光标改变鼠标光标的形状位置显示或者隐藏鼠标时就发送请求到服务器服务器程序完成相应任务并将结果发送给客户

层及活动客户管理当客户查询层的信息新建层加入某个已有层或者设置层中的活动客户时通过该接口发送请求到服务器

其他一些系统级的任务比如在新的 GDI 接口中服务器程序统一管理显示卡中可能用来建立内存 DC 的显示内存当客户要申请建立在显示内存中的内存 DC 时就会发送请求到服务器

为了让应用程序也能够通过这种简单的方式实现客户和服务器之间的通讯服务器程序可以注册一些定制的请求处理函数然后客户就可以向服务器发送这些请求为此MiniGUILite 提供了如下接口

#define MAX_SYS_REQID x

#define MAX_REQID x

/*

* Register user defined request handlers for server

* Note that user defined request id should larger

上一篇:Turbo Linux下IP的伪装

下一篇:一张软盘上的Linux或小Linux的文件组成