注:本人已购买韦东山老师第三期项目视频,内容来源《数码相框项目视频》,只用于学习记录,如有侵权,请联系删除。
在上一节我们使用了轮询的方式获取标准输入和LCD触摸屏输入的输入事件,以控制电子书的上下翻页。轮序的方式存在着CPU占用率高的缺陷,使得CPU无法再干更多的事情,造成CPU资源的浪费。这一节使用 select 方式 获取输入事件。
1.在 ubuntu 终端输入 man select
命令,可以查看到 select 系统调用的相关解释,如下:
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
int select(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout);
参数:
- nfds:最大文件句柄 + 1 (打开文件会返回一个整数fd,而 nfds = fd + 1);
- readfds:被监测是否可读的文件;
- writefds:被监测是否可写的文件;
- exceptfds:被监测是否异常的文件;
- timeout:超时时间(监测多长时间(没有可读/可写/异常的话,timeout时间到就返回));
2.参考 man 手册中的 select 例子,进行源码修改,man 手册 select 例子的代码如下:(或者参考《UNIX 环境高级编程》中的 I/O 多路转接)
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
int main(void)
{
fd_set rfds;
struct timeval tv;
int retval;
/* Watch stdin (fd 0) to see when it has input. */
FD_ZERO(&rfds);
FD_SET(0, &rfds);
/* Wait up to five seconds. */
tv.tv_sec = 5;
tv.tv_usec = 0;
retval = select(1, &rfds, NULL, NULL, &tv);
/* Don't rely on the value of tv now! */
if (retval == -1)
perror("select()");
else if (retval)
printf("Data is available now.\n");
/* FD_ISSET(0, &rfds) will be true. */
else
printf("No data within five seconds.\n");
exit(EXIT_SUCCESS);
}
3.修改源码
(1) 修改 T_InputOpr 结构体,添加文件句柄 iFd,代码如下:
typedef struct InputOpr {
char *name;
int iFd; /* 文件句柄 */
int (*DeviceInit)(void);
int (*DeviceExit)(void);
int (*GetInputEvent)(PT_InputEvent ptInputEvent);
struct InputOpr *ptNext;
}T_InputOpr, *PT_InputOpr;
(2) 修改 input_manager.c:
① 添加全局变量:
static fd_set g_tRFds;
static int g_iMaxFd = -1;
② 修改 AllInputDevicesInit 函数:
int AllInputDevicesInit(void)
{
PT_InputOpr ptCur;
int iError = -1;
FD_ZERO(&g_tRFds); /* 清零 */
if (!g_ptInputOprHead)
{
DBG_PRINTF("don't have InputOpr\n");
return -1;
}
else
{
ptCur = g_ptInputOprHead;
do{
if (0 == ptCur->DeviceInit())
{
FD_SET(ptCur->iFd, &g_tRFds); /* 设置想要监测的文件 */
if (g_iMaxFd < ptCur->iFd)
g_iMaxFd = ptCur->iFd;
iError = 0;
}
ptCur = ptCur->ptNext;
}while(ptCur);
}
g_iMaxFd++; /* 最大文件句柄加1,因为select函数的参数nfds是最大文件句柄加1 */
return iError;
}
③ 修改 GetInputEvent 函数:
int GetInputEvent(PT_InputEvent ptInputEvent)
{
/* 使用select函数监测stdin、touchscreen,
* 有数据时再调用它的GetInputEvent函数来获得具体事件
*/
fd_set tRFds;
int iRet;
PT_InputOpr ptCur = g_ptInputOprHead;
tRFds = g_tRFds;
/* 参考:UNIX环境高级编程I/O多路转接
* 只检测可读的文件,其他情况不管,也不需要超时时间
* 休眠监测,不会占用 CPU 资源
*/
iRet = select(g_iMaxFd, &tRFds , NULL, NULL, NULL);
/* 返回值大于 0,表示检测的文件中至少有一个有数据可以读 */
if (iRet > 0)
{
while (ptCur)
{
/* 查看 ptTmp->iFd 是否被置位,如果被置位,表明有数据 */
if (FD_ISSET(ptCur->iFd, &tRFds))
{
if (0 == ptCur->GetInputEvent(ptInputEvent))
{
return 0;
}
}
/* 否则,进行下一个结点访问 */
ptCur = ptCur->ptNext;
}
}
return -1;
}
(2) 修改 stdin.c 的 StdinDeviceInit 函数:
/* stdin以非阻塞的方式获得数据 */
static int StdinDeviceInit(void)
{
struct termios tTTYState;
/* get the terminal state */
tcgetattr(STDIN_FILENO, &tTTYState);
/* turn off canonical mode */
tTTYState.c_lflag &= ~ICANON;
/* minimum of number input read */
tTTYState.c_cc[VMIN] = 1;
/* set the terminal attributes */
tcsetattr(STDIN_FILENO, TCSANOW, &tTTYState);
/* 添加记录标准输入设备的文件句柄, 设置 iFd 为 STDIN_FILENO(0)*/
g_tStdinInputOpr.iFd = STDIN_FILENO;
return 0;
}
(3) 修改 touchscreen.c 的 StdinDeviceInit 函数:
static int TouchScreenDeviceInit(void)
{
#if 0 /* tslib-1.4 版本 */
char *pcTSName = NULL;
/* 根据环境变量获取输入设备 */
if ((pcTSName = getenv("TSLIB_TSDEVICE")) != NULL)
{
/* 以非阻塞方式打开输入设备 */
g_ptTSDev = ts_open(pcTSName, 1);
}
else /* 如果环境变量没有设置输入设备 */
{
g_ptTSDev = ts_open("/dev/input/event0", 1);
}
if (!g_ptTSDev)
{
DBG_PRINTF("ts_open error!\n");
return -1;
}
if (ts_config(g_ptTSDev)) {
DBG_PRINTF("ts_config error\n");
return -1;
}
#else /* tslib-1.21 版本 */
g_ptTSDev = ts_setup(NULL, 1);
#endif
/* 添加:设置 iFd
* 我们用 ts_open 或者 ts_setup 得到的是 g_tTSDev,该结构体中利用 ts_fd 返回文件句柄
*/
g_tTouchScreenInputOpr.iFd = ts_fd(g_ptTSDev);
if (GetDispResolution(&g_iXres, &g_iYres))
{
return -1;
}
return 0;
}
4.测试:
-
① 安装触摸屏驱动;
-
② 在开发板启动telnet服务,为了登录进去观察CPU占用率:
telnetd -l /bin/sh
,然后使用 MobaXterm 通过 telnet 登录开发板,登录后如下图所示:
-
③ 运行电子书程序:
./show_file -s 24 -d fb -f MSYH.TTF -h HZK16 utf16le.txt
-
④ telnet 上开发板后,在 MobaXterm 输入 top 命令观察 CPU 占用率,如下图所示:
没有输入时:
当点击屏幕或者有标准输入时:
明显看到 CUP 占用率有变化,而且变化不是太大!这就是 select 的好处! select 是一种休眠监测,不会占用 CPU 的资源,适用于很多简单场合。