当前位置: 首页> 房产> 政策 > 网络运营商远端无响应怎么解决_电商需要投入多少钱_网站排名优化公司哪家好_营销策略分析

网络运营商远端无响应怎么解决_电商需要投入多少钱_网站排名优化公司哪家好_营销策略分析

时间:2025/7/14 22:19:18来源:https://blog.csdn.net/m0_74375685/article/details/146475902 浏览次数:1次
网络运营商远端无响应怎么解决_电商需要投入多少钱_网站排名优化公司哪家好_营销策略分析

目录

1.输入系统

1.1 什么是输入系统

1.2 编写 APP 需要掌握的知识

1.内核使用 input_dev 结构体来表示输入设备(如图):

2.APP 可以得到一系列的输入事件,就是一个一个“struct input_event” (如图):

1.3 调试技巧

1.确定设备信息 

 2.使用命令读取数据

 2.不使用库的应用程序

2.1 APP 访问硬件的 4 种方式:妈妈怎么知道孩子醒了

2.2 获取设备信息

2.4 查询方式

2.4 休眠-唤醒方式

3.5 POLL/SELECT 方式

3.7 异步通知方式


 

1.输入系统

1.1 什么是输入系统

因为输入设备种类繁多,Linux 系统为了统一管理这些输入设备,实现了一套能兼容所有输入设备的框架:输入系统。驱动开发人员基于这套框架开发出程序,应用开发人员就可以 使用统一的 API 去使用设备。

输入系统框架:

 

假设用户程序直接访问 /dev/input/event0 设备节点,或者使用 tslib 访问设备节点,数据的流程如下:

  1. APP 发起读操作,若无数据则休眠
  2. 用户操作设备,硬件上产生中断
  3. 输入系统驱动层对应的驱动程序处理中断:
    读取到数据,转换为标准的输入事件,向核心层汇报。 所谓输入事件就是一个“struct input_event”结构体。
  4. 核心层可以决定把输入事件转发给上面哪个 handler 来处理:
    有多种 handler,比如:evdev_handler、kbd_handler、joydev_handler 等等。
    最常用的是 evdev_handler:它只是把 input_event 结构体保存在内核 buffer 等,APP 来读取时就原原本本地返回。
    它支持多个 APP 同时访问输入设备,每个 APP 都可以获得同一份输入事件。当 APP 正在等待数据时,evdev_handler 会把它唤醒,这样 APP 就可以返回数据。
  5. APP 对输入事件的处理:
    APP 获得数据的方法有 2 种 : 直接访问设备节点 ( 比如 /dev/input/event0,1,2,...),或者通过 tslib、libinput 这类库来间接访 问设备节点。这些库简化了对数据的处理。

 

1.2 编写 APP 需要掌握的知识

1.内核使用 input_dev 结构体来表示输入设备(如图):

 

2.APP 可以得到一系列的输入事件,就是一个一个“struct input_event” (如图):

每个输入事件 input_event 中都含有发生时间:
        timeval 表示的是“自系统启动以来过了多少时间”,它是一个结构体,含有 “tv_sec、tv_usec”两项 (即秒、微秒)。

input_event 中更重要的是:type(哪类事件)、code(哪个事件)、 value(事件值) 
 

(1) type:表示哪类事件(对应evbit)

如EV_KEY 表示按键类、EV_REL 表示相对位移(如鼠标),EV_ABS 表示 绝对位置(如触摸屏):


(2)code:表示该类事件下的哪一个事件

比如对于 EV_KEY(按键)类事件,键盘上有很多按键,所以可以有如图这些事件:


(3)value:表示事件值

value 可以是 0(表示按键被按下)、1(表示按键被松开)、2(表示长按)

对于触摸屏,value 就是坐标值、压力值
 

(4)事件间的界限

APP 读取数据时,可以得到一个或多个数据,如一个触摸屏的一个触点会 上报 X、Y 位置信息,也可能会上报压力值。

◼ APP 怎么知道它已经读到了完整的数据?

驱动程序上报完一系列的数据后,会上报一个“同步事件”,表示数据上报完 毕。APP 读到“同步事件”时,就知道已经读完了当前的数据。

同步事件也是一个 input_event 结构体,它的 type、code、value 三项都 是 0。

 

1.3 调试技巧

1.确定设备信息 

输入设备的设备节点名为/dev/input/eventX(也可能是/dev/eventX,X 表示 0、1、2 等数字)

查看设备节点,可以在板子上执行以下命令:

ls /dev/input/* -l

 得到如下图信息:


要知道这些设备节点对应什么硬件,在板子上执行以下命令,获取与 event 对应的相关设备信息

cat /proc/bus/input/devices 

得到下图信息: 

(1)I:设备 ID 

 由结构体 struct input_id 描述:

(2)N:设备名称

(3)P:系统层次结构中设备的物理路径

(4)S:位于 sys 文件系统的路径

(5)U:设备的唯一标识码

(6)H:与设备关联的输入句柄列表

(7)B:bitmaps(位图)

PROP:设备属性

EV:设备支持的事件类型

KEY:此设备具有的键/按钮

MSC:设备支持的其他事件

LED:设备上的指示灯


举一个例子,“B: ABS=2658000 3”如何理解?

它表示该设备支持 EV_ABS 这一类事件中的哪一些事件。

这是 2 个 32 位的数字:0x2658000、0x3,高位在前低位在后,组成一个 64 位的数字: “0x2658000,00000003”,数值为 1 的位有:0、1、47、48、50、53、54,即: 0、1、0x2f、0x30、0x32、0x35、0x36,对应以下这些宏:

 
2.使用命令读取数据

执行类似下面的命令,操作对应的输入设备就会读出并打印数据:

hexdump /dev/input/event0

 开发板上执行上述命令之后,点击按键或是点击触摸屏,就会打印如下图信息:

        上图中:type 为 3,对应 EV_ABS ;code 为 0x35 对应 ABS_MT_POSITION_X;code 为 0x36 对应 ABS_MT_POSITION_Y。
        上图中还发现有 2 个同步事件:它的 type、code、value 都为 0。表示电容屏上报了 2 次完整的数据。 

 

 2.不使用库的应用程序

2.1 APP 访问硬件的 4 种方式:妈妈怎么知道孩子醒了

 妈妈怎么知道卧室里小孩醒了?

时不时进房间看一下:查询方式
简单,但是累

进去房间陪小孩一起睡觉,小孩醒了会吵醒她:休眠-唤醒
不累,但是妈妈干不了活了

妈妈要干很多活,但是可以陪小孩睡一会,定个闹钟:poll 方式
要浪费点时间,但是可以继续干活。
妈妈要么是被小孩吵醒,要么是被闹钟吵醒。

妈妈在客厅干活,小孩醒了他会自己走出房门告诉妈妈:异步通知
妈妈、小孩互不耽误。

这 4 种方法没有优劣之分,在不同的场合使用不同的方法。

2.2 获取设备信息

通过 ioctl 获取设备信息(之前说过,这个函数功能很强大)

int ioctl(int fd, unsigned long request, ...); 

request参数可以理解为你想要 ioctl 做什么

有些驱动程序对 request 的格式有要求,它的格式如下:

dir:为_IOC_READ(即 2)时,表示 APP 要读数据;为_IOC_WRITE(即 4)时,表示 APP 要写数据
size:表示这个 ioctl 能传输数据的最大字节数
type、nr 的含义由具体的驱动程序决定。

一般都是已经有定义好的宏直接使用

 

2.4 查询方式

APP 调用 open 函数时,传入“O_NONBLOCK”表示“非阻塞” 

APP 调用 read 函数读取数据时,如果驱动程序中有数据,那么 APP 的 read 函数会返回数据,否则也会立刻返回错误。

2.4 休眠-唤醒方式

        APP 调用 open 函数时,不要传入“O_NONBLOCK”。
        APP 调用 read 函数读取数据时,如果驱动程序中有数据,那么 APP 的 read 函数会返回数据;否则 APP 就会在内核态休眠,有数据时才会唤醒。

查询+休眠唤醒方式源码(注释版):

/* 程序功能:读取输入设备信息及事件* 用法:./01_get_input_info /dev/input/event0 [noblock]* noblock: 非阻塞模式打开设备*/
int main(int argc, char **argv)
{int fd;                     // 设备文件描述符int err;                    // 错误码存储int len;                    // 读取长度/返回值int i;                      // 循环计数器unsigned char byte;         // 用于位操作的字节int bit;                    // 位计数器struct input_id id;         // 存储输入设备ID信息unsigned int evbit[2];      // 存储设备支持的事件类型位图(64位)struct input_event event;   // 输入事件结构体// 输入事件类型名称对照表(来自linux/input.h)char *ev_names[] = {"EV_SYN ",   // 0x00 同步事件"EV_KEY ",   // 0x01 按键事件"EV_REL ",   // 0x02 相对坐标事件(如鼠标)"EV_ABS ",   // 0x03 绝对坐标事件(如触摸屏)"EV_MSC ",   // 0x04 其他杂项事件"EV_SW  ",   // 0x05 开关事件"NULL ",     // 0x06 未使用/* ... 省略部分NULL ... */"EV_LED ",   // 0x11 LED控制事件"EV_SND ",   // 0x12 声音控制事件"NULL ",     // 0x13"EV_REP ",   // 0x14 重复事件(如按键重复)"EV_FF  ",   // 0x15 力反馈事件"EV_PWR ",   // 0x16 电源管理事件};// 参数检查:至少需要设备路径参数if (argc < 2) {printf("Usage: %s <dev> [noblock]\n", argv[0]);return -1;}// 打开设备文件(根据参数决定是否非阻塞)if (argc == 3 && !strcmp(argv[2], "noblock")) {fd = open(argv[1], O_RDWR | O_NONBLOCK);  // 非阻塞模式} else {fd = open(argv[1], O_RDWR);               // 默认阻塞模式}// 检查设备是否成功打开if (fd < 0) {printf("open %s err\n", argv[1]);return -1;}// 获取输入设备ID信息(通过ioctl)err = ioctl(fd, EVIOCGID, &id);if (err == 0) {  // 成功获取时打印信息printf("bustype = 0x%x\n", id.bustype);  // 总线类型printf("vendor  = 0x%x\n", id.vendor);   // 厂商IDprintf("product = 0x%x\n", id.product);  // 产品IDprintf("version = 0x%x\n", id.version);  // 设备版本}// 获取设备支持的事件类型位图len = ioctl(fd, EVIOCGBIT(0, sizeof(evbit)), &evbit);if (len > 0 && len <= sizeof(evbit)) {printf("support ev type: ");// 遍历位图中的每一位for (i = 0; i < len; i++) {byte = ((unsigned char *)evbit)[i];for (bit = 0; bit < 8; bit++) {// 如果某位被置1,表示支持对应事件类型if (byte & (1<<bit)) {printf("%s ", ev_names[i*8 + bit]);  // 打印事件类型名称}}}printf("\n");}// 主循环:持续读取输入事件(阻塞状态只会有数据时才会触发读取,其他时间都在休眠)while (1) {len = read(fd, &event, sizeof(event));if (len == sizeof(event)) {  // 成功读取事件printf("get event: type = 0x%x, code = 0x%x, value = 0x%x\n", event.type, event.code, event.value);} else {  // 读取失败(非阻塞状态未读取到时会打印错误)printf("read err %d\n", len);}}return 0;  // 实际上由于无限循环,不会执行到这里
}

 

3.5 POLL/SELECT 方式

POLL 机制、SELECT 机制是完全一样的,只是 APP 接口函数不一样。

简单地说,它们就是“定个闹钟”:在调用 poll、select 函数时可以传入 “超时时间”。在这段时间内,条件合适时(比如有数据可读、有空间可写)就会 立刻返回,否则等到“超时时间”结束时返回错误。

⚫ APP 先调用 open 函数时。

⚫ APP 不是直接调用 read 函数,而是先调用 poll 或 select 函数,这 2 个函 数中可以传入“超时时间”。
        它们的作用是:如果驱动程序中有数据,则立刻返回; 否则就休眠。
        在休眠期间,如果有人操作了硬件,驱动程序获得数据后就会把 APP 唤醒,导致 poll 或 select 立刻返回;如果在“超时时间”内无人操作硬件,则时间到后 poll 或 select 函数也会返回。

⚫ APP 根据 poll 或 select 的返回值判断有数据之后,就调用 read 函数读取 数据时,这时就会立刻获得数据。

⚫ poll/select 函数可以监测多个文件,可以监测多种事件:

在调用 poll 函数时,要指明:
        你要监测的文件:哪一个 fd  
        你想监测这个文件的哪种事件:是 POLLIN、还是 POLLOUT

/* 改进版输入设备事件监听程序(使用poll机制)* 用法:./program /dev/input/eventX* 特点:5秒超时检测,避免纯阻塞等待*/
int main(int argc, char **argv)
{int fd;                     // 设备文件描述符int err;                    // 错误码int len;                    // IO操作返回值int ret;                    // poll返回值(新增)int i;                      // 循环计数器unsigned char byte;         // 位操作字节int bit;                    // 位计数器struct input_id id;         // 设备ID信息unsigned int evbit[2];      // 事件类型位图struct input_event event;   // 输入事件结构体struct pollfd fds[1];       // poll结构体数组(关键改进)nfds_t nfds = 1;           // 监控的文件描述符数量...中间省略和上面一样的部分...// 主事件循环(使用poll改进)while (1) {// 初始化poll结构体(关键改进)fds[0].fd = fd;          // 监控的设备fdfds[0].events = POLLIN;  // 监控可读事件fds[0].revents = 0;       // 实际发生的事件// 调用poll(5秒超时)、nfds=1:监控的文件描述符数量ret = poll(fds, nfds, 5000);if (ret > 0) {     // 有事件发生if (fds[0].revents & POLLIN) { // 确认是可读事件// 非阻塞读取所有可用事件(改进点)while (read(fd, &event, sizeof(event)) == sizeof(event)) {printf("get event: type = 0x%x, code = 0x%x, value = 0x%x\n",event.type, event.code, event.value);}}} else if (ret == 0) {    printf("time out\n");  // 超时} else {                  printf("poll err\n");  // 错误}}return 0;  // 实际不会执行到此处
}

3.7 异步通知方式

所谓同步,就是“你慢我等你”。
那么异步就是:你慢那你就自己玩,我做自己的事去了,有情况再通知我。

异步通知,就是 APP 可以忙自己的事,当驱动程序用数据时它会主动给 APP 发信号,这会导致 APP 执行信号处理函数。

实际就是:驱动程序通知 APP 时,它会发出 “SIGIO” 信号,表示有“IO 事件”要 处理。

就 APP 而言,你想处理 SIGIO 信息,那么需要提供信号处理函数,并且要跟 SIGIO 挂钩。这可以通过一个 signal 函数来“给某个信号注册处理函数”(跟中断回调函数有点像),用法如图:

流程如下:
(1)编写信号处理函数: 

static void sig_func(int sig) 
{ int val; read(fd, &val, 4); printf("get button : 0x%x\n", val); 
} 

(2)注册信号处理函数: 

signal(SIGIO, sig_func); 

(3)打开驱动

fd = open(argv[1], O_RDWR);

(4)把进程 ID 告诉驱动:

fcntl(fd, F_SETOWN, getpid());
  • fd:要操作的文件描述符(如设备文件 /dev/input/eventX)。

  • cmd:控制命令,F_SETOWN 表示设置文件描述符的所有者。

  • arg:所有者进程 ID(getpid() 获取当前进程 PID)

 (5)使能驱动的 FASYNC 功能:

//获取文件描述符当前的状态标志
flags = fcntl(fd, F_GETFL); //修改文件描述符的状态标志,添加异步通知功能
fcntl(fd, F_SETFL, flags | FASYNC); 

源代码:

int fd;  // 全局文件描述符,用于信号处理函数访问/* SIGIO信号处理函数 - 当输入设备有数据时被调用 */
void my_sig_handler(int sig)
{struct input_event event;// 循环读取所有可用事件(非阻塞方式)while (read(fd, &event, sizeof(event)) == sizeof(event)){// 打印输入事件详细信息printf("get event: type = 0x%x, code = 0x%x, value = 0x%x\n", event.type, event.code, event.value);        }
}/* 主函数:输入设备异步读取示例 */
int main(int argc, char **argv)
{int err;int len;int i;unsigned char byte;int bit;struct input_id id;unsigned int evbit[2];  // 存储设备支持的事件类型位图unsigned int flags;int count = 0;// 输入事件类型名称映射表(对应linux/input.h中的定义)char *ev_names[] = {"EV_SYN ",  // 0x00 同步事件"EV_KEY ",  // 0x01 按键事件"EV_REL ",  // 0x02 相对坐标事件(如鼠标)"EV_ABS ",  // 0x03 绝对坐标事件(如触摸屏)/* ... 其他事件类型 ... */"EV_PWR ",  // 0x16 电源事件};// 参数检查:必须指定输入设备路径if (argc != 2){printf("Usage: %s <dev>\n", argv[0]);  // 如 ./05_input_read_fasync /dev/input/event0return -1;}/* 注册SIGIO信号处理函数 */signal(SIGIO, my_sig_handler);/* 以非阻塞方式打开输入设备 */fd = open(argv[1], O_RDWR | O_NONBLOCK);if (fd < 0){printf("open %s err\n", argv[1]);return -1;}...省略打印设备信息部分.../* 设置异步I/O的关键两步 */// 1. 将当前进程设置为文件描述符的所有者(接收SIGIO信号)fcntl(fd, F_SETOWN, getpid());// 2. 启用文件描述符的异步通知功能flags = fcntl(fd, F_GETFL);          // 获取当前文件状态标志fcntl(fd, F_SETFL, flags | FASYNC);  // 添加FASYNC标志/* 主循环(演示异步通知机制) *///有消息来就会触发信号函数处理,不影响主进程(像中断回调函数一样)while (1){printf("main loop count = %d\n", count++);  // 主程序可执行其他任务sleep(2);  // 模拟其他工作}return 0;
}

 

关键字:网络运营商远端无响应怎么解决_电商需要投入多少钱_网站排名优化公司哪家好_营销策略分析

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

责任编辑: