第三阶段应用层——1.7 数码相册—电子书(4)—select支持多输入

数码相册——电子书select支持多输入


一、前言

【1.7 数码相册—电子书(3)—轮询方式支持多输入】博文中,采用了轮询的方式,实现了电子书支持多输入模式。

  • 缺点:由于采用轮询的方式,程序一直在跑,CPU占用率高达90%,非常不友好
  • 改进:采用Linux中的select来函数来了优化标准输入的方式使得在没有标准输入的时候,程序处于休眠,当有标准输入的时候,程序立刻被唤醒,通过对于触摸屏的输入影响也不大。

二、select函数优化

1、简单介绍下select函数

  • 作用:
    使用select就可以完成非阻塞(所谓非阻塞方式non-block,就是进程或线程执行此函数时不必非要等待事件的发生,一旦执行才返回,以返回值的不同来反映函数的执行情况,如果事件发生则与阻塞方式相同,若事件没有发生返回一个代码来告知事件未发生,而进程或线程继续执行所以效率较高) 方式工作的程序,它能够监视我们需要监视的文件描述符的变化情况读写或是异常。
  • 函数原型:
#include <sys/select.h>   
    int select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset,struct timeval *timeout);
  • 参数说明:
    int maxfdp1:指集合中所有文件描述符的范围,即所有文件描述符的最大值加1
    fd_set *readset:fd_set结构体,一个文件描述符的集合,监视这些文件描述符的读变化
    fd_set *writeset:fd_set结构体,一个文件描述符的集合,监视这些文件描述符的写变化
    fd_set *exceptset:fd_set结构体,一个文件描述符的集合,监视这些文件描述符的异常情况
    struct timeval *timeout程序需等待的时间

  • struct timeval结构体:

struct timeval{      
        long tv_sec;   /*秒 */
        long tv_usec;  /*微秒 */   
    }
  • fd_set结构体:描述符集
    可以理解为是一个二进制数的数组如下图所示:当文件发生变化时,该文件的文件描述符对应为置为1
    在这里插入图片描述
    fd_set结构体,通过以下的宏来控制:
int FD_ZERO(int fd, fd_set *fdset);		//将一个fd_set描述符集的所有位都置0
int FD_CLR(int fd, fd_set *fdset);		//清除fd_set描述符集的某个位置0
int FD_SET(int fd, fd_set *fd_set);   	//将一个给定的文件描述符加入fd_set描述符集中
int FD_ISSET(int fd, fd_set *fdset);	//检查fd_set描述符集中指定的文件描述符是否可以读写

2、修改input管理者文件input_manager.c

通过修改此文件实现以下功能:

  1. main.c中调用AllInputDeviceInit()把定义的静态全局变量描述符集s_tRfds置为0
  2. 主函数的死循环中调用GetInputEvent(),在这个函数中:对多路输入设备的文件描述符进行读监视,此时程序处于休眠状态
  3. 当监视到其中一个文件描述符对应的输入设备有读信号唤醒程序,调用该输入设备的获取输入事件函数ptTmp->GetInputEvent(ptInputEvent)

#include <config.h>
#include <string.h>
#include <stdlib.h>
#include <sys/select.h>

#include "input_manager.h"

static PT_InputOpr s_ptInputOprHead;	//链表头
static fd_set s_tRfds;		//文件描述符集
static int s_MaxFd = -1;	//最大文件句柄

/* 函数名:	注册函数
 * 函数功能:构建一个链表:把多个拓展文件的结构体“串”起来
 * 函数实现:根据传入的结点,首先判断该链表头是否为空
 *				空则,头结点指向传入的节点,且把节点的ptNext域指向NULL
 *				不空则,尾插法插入链表
 */
int RegisterInputOpr(PT_InputOpr ptInputOpr)
{
	PT_InputOpr ptTmp;

	if (!s_ptInputOprHead)
	{
		s_ptInputOprHead   = ptInputOpr;
		ptInputOpr->ptNext = NULL;
	}
	else
	{
		ptTmp = s_ptInputOprHead;
		while (ptTmp->ptNext)
		{
			ptTmp = ptTmp->ptNext;
		}
		ptTmp->ptNext	  = ptInputOpr;
		ptInputOpr->ptNext = NULL;
	}

	return 0;
}

/* 显示支持拓展文件的名字 */
void ShowInputOpr(void)
{
	int i = 0;
	PT_InputOpr ptTmp = s_ptInputOprHead;

	while (ptTmp) {
		printf("%02d %s\n", i++, ptTmp->name);
		ptTmp = ptTmp->ptNext;
	}
}

/* 初始化函数 */
int InputInit(void)
{
	int error;
	
	error = StdinInit();
	error |= TouchScreenInit();

	return error;
}

/* 用select函数监测stdin,touchscreen
 * 有数据时在调用他们的GetInputEvent()获得具体事件
 */
int GetInputEvent(PT_InputEvent ptInputEvent)
{
	PT_InputOpr ptTmp;
	fd_set rfds;
	int ret;

	rfds = s_tRfds;
	ptTmp = s_ptInputOprHead;
	
	ret = select(s_MaxFd, &rfds, NULL, NULL, NULL);		//只对可读文件进行监视
	if (ret > 0) {
		/* 多路文件至少有一个可读 */
		while (ptTmp) {
			/* 查找该结构体的fds是否可读 */
			if (FD_ISSET(ptTmp->fds, &rfds)) {
				if (ptTmp->GetInputEvent(ptInputEvent) == 0)
					return 0;
			}
			ptTmp = ptTmp->ptNext;
		}
	}
	
	return 0;
}

/* 初始化所有支持的Input设备 */
int AllInputDeviceInit()
{
	int error;
	PT_InputOpr ptTmp;

	error = -1;
	ptTmp = s_ptInputOprHead;

	FD_ZERO(&s_tRfds);
	
	while (ptTmp) {
		if (ptTmp->DeviceInit() == 0) {
			FD_SET(ptTmp->fds, &s_tRfds);

			/* 更新文件句柄 */
			if (s_MaxFd < ptTmp->fds)
				s_MaxFd = ptTmp->fds;
			error = 0;
		}
		ptTmp = ptTmp->ptNext;
	}

	s_MaxFd++;
	return error;
}

三、编译与运行

1、编译

执行make,得到可执行文件show_file

2、运行

由于使用到触摸屏,需要调用tslib库来进行校准

  1. 执行./show_file -l,显示出当前支持的设备
    在这里插入图片描述
  2. 执行./shoe_file -s 16 -h HZK16 -f ./MSYH.TTF hz.txt
    执行top,可以看到此时应用程序:
    没有任何外部输入的时候,CPU占用率为0,处于休眠
    输入时才唤醒程序
    在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_42813232/article/details/107090460