第三階段應用層——1.7 數碼相冊—電子書(4)—select支持多輸入

數碼相冊——電子書select支持多輸入

  • 硬件平臺:韋東山嵌入式Linxu開發板(S3C2440.v3)
  • 軟件平臺:運行於VMware Workstation 12 Player下UbuntuLTS16.04_x64 系統
  • 參考資料:《嵌入式Linux應用開發手冊》、《嵌入式Linux應用開發手冊第2版》、【linux select函數詳解】
  • 開發環境:Linux 3.4.2內核、arm-linux-gcc 4.3.2工具鏈


一、前言

【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,處於休眠
    輸入時才喚醒程序
    在這裏插入圖片描述
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章