首页 > 其他分享 >POLL底层驱动机制

POLL底层驱动机制

时间:2023-05-17 17:23:22浏览次数:32  
标签:POLL 函数 APP drv fd 驱动 poll 唤醒 底层

1 前言

1.1 阻塞与非阻塞IO

APP 调用 open 函数时,不要传入“ O_NONBLOCK”。APP 调用 read 函数读取数据时,为阻塞io。
APP 调用 open 函数时,传入“ O_NONBLOCK”表示“非阻塞”。APP 调用 read 函数读取数据时,如果驱动程序中有数据,那么 APP 的 read函数会返回数据,否则也会立刻返回错误。这种需要APP反复主动去"轮询"设备,否则无法及时响应。

1.2 带超时的非阻塞IO(poll/select)

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

1.3 select

select会循环遍历它所监测的fd_set内的所有文件描述符对应的驱动程序的poll函数。select通过每个设备文件对应的poll函数提供的信息判断当前是否有资源可用(如可读或写),如果有的话则返回可用资源的文件描述符个数,没有的话则睡眠,等待有资源变为可用时再被唤醒继续执行。

那么会select会有2个结果:

1, 查询到资源,返回查询到的fd总数。
2,没查到,则睡眠
	①带timeout参数,timeout后,唤醒退出,此时fd总数为0
	②不带timeout, 阻塞且睡眠中,直到有资源可用才唤醒

fd_set结构体
fd_set结构体就是一个可用资源文件描述符的集合。

FD_SET(int fd, fd_set *fdset);       //将fd加入set集合
FD_CLR(int fd, fd_set *fdset);       //将fd从set集合中清除
FD_ISSET(int fd, fd_set *fdset);     //检测fd是否在set集合中,不在则返回0
FD_ZERO(fd_set *fdset);              //将set清零使集合中不含任何fd

image

点击查看代码
FD_ZERO(&wfds);
FD_SET(fd, &wfds);
tv.tv_sec = 0;
tv.tv_usec = 100 * 1000;

struct timespec start, end;
clock_gettime(CLOCK_MONOTONIC, &start);

ret = select(fd + 1, NULL, &wfds, NULL, &tv)) == -1);
if (ret == -1) {
	printf("select error(%s)\n", strerror(errno));
	return ret;
}

if (ret == 0) {
	printf("select timeout\n");
	ret = -1;
	return ret;
}

if (FD_ISSET(fd, &wfds)) {
	//start write data to drv
}

2 poll机制概述

使用休眠唤醒机制,实现简单,但是处于休眠等待中,如果要等很久,那么这种方式明显不好。比如前面所讲的一个按键字符设备驱动中,read函数中进行等待队列wait_event, 然后当按键按下,中断服务程序进行唤醒等待队列wake_up,read函数将会从休眠中唤醒返回数据给用户。 如果按键一直不去按下,read函数将会一直休眠,应用程序用户线程被一直阻塞。

1. 那么poll机制就是给它加一个超时机制,防止一直休眠和用户线程被阻塞。
2. APP 不知道驱动程序中是否有数据,可以先调用 poll 函数查询一下, poll 函数可以传入超时时间;
3. APP 进入内核态,调用到驱动程序的 poll 函数
	3.1 如果发现没有数据时就休眠一段时间;当超时时间到了之后,内核也会唤醒 APP;
	3.2 当有数据时,比如当按下按键时,驱动程序的中断服务程序被调用,它会记录数据、唤醒 APP;
4. APP 根据 poll 函数的返回值就可以知道是否有数据,如果有数据就调用read 得到数据

2.1 poll应用编程示例

poll/select 监测的事件
image

点击查看代码
struct pollfd fds[1];
nfds_t nfds = 1;
while (1) {
	fds[0].fd = fd;
	fds[0].events  = POLLIN;
	fds[0].revents = 0;
	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");
	}
}

打开设备文件。
设置 pollfd 结构体。
想查询哪个文件(fd)?
想查询什么事件(POLLIN)?
先清除“返回的事件” (revents)。
使用 poll 函数查询事件,指定超时时间为 5000(ms)。

3 poll机制底层原理

我们期望的大致流程如下:
image

1. app进行open, drv进行drv_open,注册好中断服务
2. app进行poll , drv进行drv_poll
3. 第一次如果没有数据到来,那么会执行else进行休眠,加入等待队列。
	要么被中断服务程序唤醒,进入for循环此时有数据到来返回;
	要么超时,也会从等待队列唤醒回来,进入for循环此时返回超时

可以看到会查询判断2次,但实际上内核做的更好,我们drv_poll中只需要:

1.把线程放入wq等待队列,并不会调用休眠
2.返回event状态

实际内核中poll函数流程如下:内核把poll抽出去了,调用sys_poll
image

1. app进行open, drv进行drv_open,注册好中断服务
2. app进行poll , 内核文件系统进行sys_poll
3. 调用驱动开发者实现的drv_poll
	调用poll_wait,把线程加入wq,但是不会进入休眠
	返回event状态
4. drv_poll返回后,sys_poll中进行数据判断(如果第一次进入没有数据到来,执行else, 将线程休眠,如果有数据则直接返回)
   sys_poll函数执行else休眠的过程中,会被event唤醒or被超时唤醒
   第2进入for循环执行drv_poll,如果被event唤醒了,则返回数据,否则说明是超时唤醒,返回超时
5.最终内核文件系统sys_poll返回,唤醒userspace线程

可以看到当用户调用poll函数,在底层drv下会调用2次drv_poll。用户线程不会一直阻塞休眠,要么有数据时v下的event唤醒,要么超时唤醒。

3.poll驱动编程实例(gpio key为例)

使用 poll 机制时,驱动程序的核心就是提供对应的 drv_poll 函数。在drv_poll 函数中要做 2 件事:

1. 把当前线程挂入队列 wq: poll_wait
	a) APP 调用一次 poll,可能会 drv_poll 被调用 2 次,但是我们并不需要把当前线程挂入队列 2 次。
	b) 可以使用内核的函数 poll_wait 把线程挂入队列,如果线程已经在队列里了,它就不会再次挂入。
2. 返回设备状态:
	APP 调用 poll 函数时,有可能是查询“有没有数据可以读”: POLLIN
	也有可能是查询“你有没有空间给我写数据”: POLLOUT。
	所以 drv_poll 要返回自己的当前状态: (POLLIN | POLLRDNORM) 或 (POLLOUT | POLLWRNORM)。
a) POLLRDNORM 等同于 POLLIN,为了兼容某些 APP 把它们一起返回。
b) POLLWRNORM 等同于 POLLOUT ,为了兼容某些 APP 把它们一起返回。

APP 调用 poll 后,很有可能会休眠。对应的,在中断服务程序中,也要有唤醒操作。

标签:POLL,函数,APP,drv,fd,驱动,poll,唤醒,底层
From: https://www.cnblogs.com/fuzidage/p/17389751.html

相关文章

  • nvidia显卡驱动和nvidia-cuda-toolkit组件的区别
    nvidia-cuda-toolkit和NVIDIA显卡驱动是两个不同的组件,它们在使用NVIDIAGPU进行计算和图形处理时发挥不同的作用。NVIDIA显卡驱动:NVIDIA显卡驱动是安装在计算机上的软件,它与NVIDIA的图形处理器(GPU)通信,控制GPU的功能和性能,并将计算机的图形输出显示在显示器上。驱动程序允许操......
  • mysql 底层数据存储结构
    内存和磁盘每次交互都是完整的页,数据页里面存放的是行(不仅仅是数据库的数据行,还有行格式等)页(16k,计算机与内存的最小单位)的上层单位还有区(一个区存放64个页,64*16k=1024k,刚好1M),区上面是段(一个或多个区组成),段上面是表空间(一个或多个段组成)行格式showtablestatuslike't_u......
  • epoll的运行原理
    概念初探epoll是一种I/O事件通知机制,是linux内核实现IO多路复用的一个实现。IO多路复用是指,在一个操作里同时监听多个输入输出源,在其中一个或多个输入输出源可用的时候返回,然后对其的进行读写操作。I/O输入输出(input/output)的对象可以是文件(file),网络(socket),进程之......
  • ubuntu20安装无线网卡AX1675i网卡驱动(AX210网卡也是这样安装)
    #非凡s3电脑上的网卡sudoaptupdatesudoapt-getinstall-ygitsudoapt-getinstallbuild-essential安装iwlwifi-*gitclonegit://git.kernel.org/pub/scm/linux/kernel/git/firmware/linux-firmware.git#或者去这个网址下载https://git.kernel.org/pub/scm/linux/kernel/gi......
  • DIY伺服驱动器方案,某成熟量产型号,基于TMS320F28 DIY伺服驱动器方案,某成熟量产型号,基于
    DIY伺服驱动器方案,某成熟量产型号,基于TMS320F28DIY伺服驱动器方案,某成熟量产型号,基于TMS320F28069设计开发。原理图和PCB源格式(AD打开)-控制板/驱动板/电源板/滤波板基于TMS320F28069的控制源代码产品资料,代码注释少,需要有一定基础,建议小白不要拿ID:27260669815636452......
  • SIEMENS/西门子TMS320F28335运动控制器 DSP28335驱动器 DSP28335运动控
    SIEMENS/西门子TMS320F28335运动控制器DSP28335驱动器DSP28335运动控制卡采用DSP28335作为主控支持有刷电机,无刷电机,伺服电机电机电机参数识别,运动控制支持速度环,位置环,编码器,模拟量接口支持CANOPEN,modbus总线包括原理图,源代码已移植量产使用,具有极高的参考价值ID:821896940360050......
  • 驱动器/控制器伺服驱动器资料 md500e代码 MD500E代码方案和
    驱动器/控制器伺服驱动器资料md500e代码MD500E代码方案和解析文档+原理图+送仿真资料。包含pmsm的foc控制算法,电阻、电感、磁链等参数的辩识算法,死区补偿算法过调制处理算法,弱磁控制算法,无感FOC控制算法,电流环自整定算法,磁链观测器算法。ID:9146682649041496......
  • 量产直流无刷电机驱动器资料 功能支持模式:有霍尔和无
    量产直流无刷电机驱动器资料功能支持模式:有霍尔和无霍尔两种;速度信号:0-5V的模拟量(0-100%的PWM)或modbus两种选择方式;使能和正反转信号:开关量或者modbus两种选择方式;速度反馈信号:可变频率的脉冲,每换相一次会出现一个跳变沿,或者使用modbus读取;电机供电电压和电流:通过modbus读取;该模......
  • 开发板三菱FX3U底层源码,总体功能和指令可能支持在RUN中下载程序,支持注释的写入和读取,
    开发板三菱FX3U底层源码,总体功能和指令可能支持在RUN中下载程序,支持注释的写入和读取,支持脉冲输出与定位指令(包括PLSY/PWM/PLSR/PLSV/DRVI/DRVA等指令)。对于FX3U,支持波特率9600/19200/38400/57600/115200自适应ID:58199670048570922......
  • 电机控制源码 电机控制源码,BLDC无刷直流电机基于stm3 2F1的有传感器和无传感驱动 直流
    电机控制源码电机控制源码,BLDC无刷直流电机基于stm32F1的有传感器和无传感驱动直流无刷电机有传感器和无传感驱动程序识货的赶紧上车。无传感的的实现是基于反电动势过零点实现的,无传感是霍尔实现,可供学习参考,程序有详细注释。实验学习内容1)直流无刷霍尔传感方波速度、电流、双......