当前位置: 首页 > news >正文

Linux 阻塞和非阻塞 IO 实验

目录

一、阻塞和非阻塞简介

1、IO 概念

2、阻塞与非阻塞

二、等待队列

1、等待队列头

2、等待队列项

3、将队列项添加/移除等待队列头

4、等待唤醒

5、等待事件

三、轮询

1、应用程序的非阻塞函数

2、Linux 驱动下的 poll 操作函数

四、阻塞IO之等待事件唤醒

添加两个头文件

1、makefile修改

 2、imx6uirq结构体

 2、驱动入口添加等待队列头

​编辑

3、imx6uirq_read添加等待

4、在定时器里面添加唤醒

验证 

代码

驱动入口

imx6uirq_read

定时器

五、阻塞IO之队列项

代码

六、非阻塞IO之select

 imx6uirq_read

构建驱动的poll操作函数

1、添加poll

 2、编写imx6uirq_poll函数 

编写APP

1、打开方式

 2、定义

3、循环读取

代码如下

 验证

七、非阻塞IO之poll

1、pollfd 结构体

 2、循环读取

代码如下

select函数和poll函数区别


一、阻塞和非阻塞简介

1、IO 概念

这里的“IO”并不是学习单片机的时候所说的“GPIO”(也就是引脚),这里的 IO 指的是 Input/Output也就是输入/输出,是应用程序对驱动设备的输入/输出操作。

2、阻塞与非阻塞

当应用程序对设备驱动进行操作的时候,如果不能获取到设备资源,那么阻塞式 IO 就会将应用程
序对应的线程挂起,直到设备资源可以获取为止;对于非阻塞 IO,应用程序对应的线程不会起,它要么一直轮询等待,直到设备资源可以使用,要么就直接放弃。

阻塞式 IO 如下图

阻塞 IO 访问图

 阻塞 IO 访问图中,read 函数从设备中读取数据,当设备不可用或数据未准备好的时候就会进入到休眠态。等设备可用的时候就会从休眠态唤醒,然后从设备中读取数据返回给应用程序。

非阻塞 IO 如下图

非阻塞 IO 访问图

 非阻塞 IO 访问图中,应用程序使用非阻塞访问方式从设备读取数据,当设备不可用或数据未准备好的时候会立即向内核返回一个错误码,表示数据读取失败。应用程序会再次重新读取数据,这样一直往复循环,直到数据读取成功。

对于设备驱动文件的默认读取方式就是阻塞式的;如果应用程序要采用非阻塞的方式来访问驱动设备文件,open 函数打开“/dev/xxx_dev”设备文件的时候添加了参数“O_NONBLOCK”,表示以非阻塞方式打开设备,这样从设备中读取数据的时候就是非阻塞方式的了

二、等待队列

涉及到的函数或宏定义会在代码中使用到的时候再作详细解析 

1、等待队列头

阻塞访问最大的好处就是当设备文件不可操作的时候进程可以进入休眠态,这样可以将CPU 资源让出来。但是,当设备文件可以操作的时候就必须唤醒进程,一般在中断函数里面完成唤醒工作。
Linux 内核提供了等待队列(wait queue)来实现阻塞进程的唤醒工作,如果要在驱动中使用等待队列,必须创建并初始化一个等待队列头,等待队列头使用结构体wait_queue_head_t 表示, wait_queue_head_t 结构体定义在文件 include/linux/wait.h 中

定义好等待队列头以后需要初始化, 使用 init_waitqueue_head 函数初始化等待队列头

也可以使用宏 DECLARE_WAIT_QUEUE_HEAD 来一次性完成等待队列头的定义的初始化

2、等待队列项

等待队列头就是一个等待队列的头部,每个访问设备的进程都是一个队列项,当设备不可用的时候就要将这些进程对应的等待队列添加到等待队列项里面。结构体 wait_queue_t 表示等待队列项

使用宏 DECLARE_WAITQUEUE 定义并初始化一个等待队列项

3、将队列项添加/移除等待队列头

当设备不可访问的时候就需要将进程对应的等待队列项添加到前面创建的等待队列头中,只有添加到等待队列头中以后进程才能进入休眠态。当设备可以访问以后再将进程对应的等待队列项从等待队列头中移除即可

等待队列项添加的 API 函数:add_wait_queue

等待队列项移除的 API 函数:remove_wait_queue

4、等待唤醒

当设备可以使用的时候就要唤醒进入休眠态的进程,唤醒可以使用两个函数:

wake_up和wake_up_interruptible

wake_up 函数可以唤醒处于 TASK_INTERRUPTIBLE 和 TASK_UNINTERRUPTIBLE 状态的进程,而 wake_up_interruptible 函数只能唤醒处于 TASK_INTERRUPTIBLE 状态的进程

5、等待事件

除了主动唤醒以外,也可以设置等待队列等待某个事件,当这个事件满足以后就自动唤醒等待队列中的进程,和等待事件有关的 API 函数如表

                        函数                                                描述
wait_event(wq, condition)等待以 wq 为等待队列头的等待队列被唤醒,前提是 condition 条件必须满足(为真),否则一直阻塞 。 此 函 数 会 将 进 程 设 置 为
TASK_UNINTERRUPTIBLE 状态
wait_event_timeout(wq, condition, timeout)功能和 wait_event 类似,但是此函数可以添加超时时间,以 jiffies 为单位。此函数有返回值,如果返回 0 的话表示超时时间到,而且 condition
为假。为 1 的话表示 condition 为真,也就是条件满足了。
wait_event_interruptible(wq, condition)与 wait_event 函数类似,但是此函数将进程设置为 TASK_INTERRUPTIBLE,就是可以被信号打断。
wait_event_interruptible_timeout(wq,
condition, timeout)
与 wait_event_timeout 函数类似,此函数也将进程设置为 TASK_INTERRUPTIBLE,可以被信号打断。

三、轮询

1、应用程序的非阻塞函数

如果用户应用程序以非阻塞的方式访问设备,设备驱动程序就要提供非阻塞的处理方式,也就是轮询。 poll、 epoll 和 select 可以用于处理轮询,应用程序通过 select、 epoll 或 poll 函数来查询设备是否可以操作,如果可以操作的话就从设备读取或者向设备写入数据。当应用程序调用 select、 epoll 或 poll 函数的时候设备驱动程序中的 poll 函数就会执行,因此需要在设备驱动程序中编写 poll 函数

通过 select、 epoll 或 poll 函数一切都设置好以后应用程序就可以通过 epoll_wait 函数来等待事件的发生

2、Linux 驱动下的 poll 操作函数

当应用程序调用 select 或 poll 函数来对驱动程序进行非阻塞访问的时候,驱动程序
file_operations 操作集中的 poll 函数就会执行。所以驱动程序的编写需要提供对应的 poll 函数

需要在驱动程序的 poll 函数中调用 poll_wait 函数, poll_wait 函数不会引起阻塞,只是将应用程序添加到 poll_table 中
 

四、阻塞IO之等待事件唤醒

在之前的“按键输入驱动”文章有留下的伏笔,使用“top”命令查看开发板运行情况,我们所编写的程序会占用很高的cpu,这显然是不合理的,下面开始解决它

添加两个头文件

#include <linux/wait.h>
#include <linux/ide.h>

1、makefile修改

利用上一个文章的工作区,删除情况如下图左边,makefile编译目标是imx6uirq.o即可

 2、imx6uirq结构体

添加等待队列头,要在驱动中使用等待队列,必须创建并初始化一个等待队列头,等待队列头使用结构体wait_queue_head_t 表示

 2、驱动入口添加等待队列头

在该函数内添加等待队列头

 这里用到init_waitqueue_head 函数初始化等待队列头,原型如下

void init_waitqueue_head(wait_queue_head_t *q)

参数 q 就是要初始化的等待队列头

3、imx6uirq_read添加等待

在该函数内添加等待,应用程序会调用该函数,该函数就会执行,所以就在这设置等待按键有效

 这里用函数,wait_event_interruptible原型如下

void wait_event_interruptible(wq, condition)
参数 wq 就是wq 为等待队列头的等待队列

参数 condition就是事件, condition 条件满足(为真),wq等待队列被唤醒,否则一直阻塞 。

 这里也可以用wait_event函数,但是wait_event函数会将进程设置为TASK_UNINTERRUPTIBLE 状态,就是不可以被信号打断,wait_event_interruptible函数将进程设置为TASK_INTERRUPTIBLE,就是可以被信号打断

简单来说,如果用wait_event_interruptible函数,运行应用程序,可以用“kill"命令”杀死应用程序,这个就是“kill”信号,而用wait_event函数,则使用“kill”也无法杀死,因为它不可以被信号打断

4、在定时器里面添加唤醒

 在定时器消抖之后,再确定按键有效就唤醒进程,这里用到wake_up函数,原型如下

void wake_up(wait_queue_head_t *q)
参数 q 就是要唤醒的等待队列头
wake_up 函数可以唤醒处于TASK_INTERRUPTIBLE和TASK_UNINTERRUPTIBLE状态的进程,而 wake_up_interruptible 函数只能唤醒处于 TASK_INTERRUPTIBLE 状态的进程

验证 

加载驱动之后,后台运行上章的app和本章的设备文件 ,再查看一下后台运行情况,就看到不会像之前那样占用过高,按下按键也会打印

 查看运行的应用程序的id是81,使用kill是可以将它“杀死”的

代码

驱动入口


static int __init imx6uirq_init(void){int ret = 0;imx6uirq.major = 0;if(imx6uirq.major){imx6uirq.devid =MKDEV(imx6uirq.major,0);ret = register_chrdev_region(imx6uirq.devid,IMX6UIRQ_CNT,IMX6UIRQ_NAME);if(ret < 0){goto fail_devid;}}else{ret = alloc_chrdev_region(&imx6uirq.devid,0,IMX6UIRQ_CNT,IMX6UIRQ_NAME);if(ret < 0){goto fail_devid;}imx6uirq.major = MAJOR(imx6uirq.devid);imx6uirq.minor = MINOR(imx6uirq.devid);printk("imx6uirq major = %d, minor = %d\r\n", imx6uirq.major, imx6uirq.minor);}imx6uirq.cdev.owner = THIS_MODULE;cdev_init(&imx6uirq.cdev, &imx6uirq_fops);ret = cdev_add(&imx6uirq.cdev,imx6uirq.devid,IMX6UIRQ_CNT);if(ret){goto fail_cdevadd;}imx6uirq.class = class_create(THIS_MODULE,IMX6UIRQ_NAME);if(IS_ERR(imx6uirq.class)){ret = PTR_ERR(imx6uirq.class);goto fail_class;}imx6uirq.device = device_create(imx6uirq.class,NULL,imx6uirq.devid,NULL,IMX6UIRQ_NAME);if(IS_ERR(imx6uirq.device)){ret = PTR_ERR(imx6uirq.device);goto fail_device;}/*初始化IO*/ret = keyio_init(&imx6uirq);if(ret < 0){goto fail_keyinit;}/*初始化原子变量*/atomic_set(&imx6uirq.keyvalue,INVAKEY);atomic_set(&imx6uirq.releasekey,0);/*等待队列头*/init_waitqueue_head(&imx6uirq.r_wait);return 0;
fail_keyinit:device_destroy(imx6uirq.class,IMX6UIRQ_CNT);
fail_device:class_destroy(imx6uirq.class);
fail_class:cdev_del(&imx6uirq.cdev);
fail_cdevadd:unregister_chrdev_region(imx6uirq.devid,IMX6UIRQ_CNT);
fail_devid:return ret;
}

imx6uirq_read

static ssize_t imx6uirq_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{int ret = 0;unsigned char keyvalue;unsigned char releasekey;struct imx6uirq_dev *dev = filp->private_data;/*等待事件*//*等待按键有效*/wait_event_interruptible(dev->r_wait,atomic_read(&dev->releasekey));keyvalue = atomic_read(&dev->keyvalue);releasekey = atomic_read(&dev->releasekey);if(releasekey){/*有效按键*/if(keyvalue & 0x80){keyvalue &= ~0x80;ret=__copy_to_user(buf,&keyvalue,sizeof(keyvalue));}else{goto data_error;}atomic_set(&dev->releasekey,0);/*按下标志清零*/}else{goto data_error;}return ret;
data_error:return -EINVAL;
}

定时器

static void timer_func(unsigned long arg){int value = 0;struct imx6uirq_dev *dev = (struct imx6uirq_dev *)arg;value = gpio_get_value(dev->irqkey[0].gpio);if(value == 0){/*按下*/atomic_set(&dev->keyvalue,dev->irqkey[0].value);}else if(value == 1){/*释放*/atomic_set(&dev->keyvalue,0x80 | (dev->irqkey[0].value));atomic_set(&dev->releasekey,1);}/*唤醒进程*/if(atomic_read(&dev->releasekey)){wake_up(&dev->r_wait);}
}

五、阻塞IO之队列项

这个和上面的等待唤醒效果是一样的,只需要修改imx6uirq_read函数即可

 66-68行,属于等待唤醒,将其屏蔽

71行,使用宏 DECLARE_WAITQUEUE 定义并初始化一个等待队列项,宏的内容如下:

DECLARE_WAITQUEUE(name, tsk)
name 就是等待队列项的名字, tsk 表示这个等待队列项属于哪个任务(进程),一般设置为
current , 在 Linux 内 核 中 current 相 当 于 一 个 全 局 变 量 , 表 示 当 前 进 程 。

DECLARE_WAITQUEUE 就是给当前正在运行的进程创建并初始化了一个等待队列项wait

72行,判断按键是否按下,没按下则执行以下操作

73行,当设备不可访问的时候就需要将进程对应的等待队列项添加到前面创建的等待队列头中,只有添加到等待队列头中以后进程才能进入休眠态。这里用到add_wait_queue函数,原型如下

void add_wait_queue(wait_queue_head_t *q, wait_queue_t *wait)

q: 等待队列项要加入的等待队列头。wait:要加入的等待队列项。返回值:无。

74行,使用 __set_current_state() 来设置进程的状态,原型如下

__set_current_state(state_value)

state_value为要设置进程的状态

75行,任务切换,使用schedule(),它没有任何参数和返回值,它的任务是从运行队列中找到一个进程,并随后将CPU分配给这个进程,这里就是当前进程,直接调用schedule()时,如果current进程因缺乏资源也就是没有按下按键,而要立刻被阻塞,就主动调用调度程序,会把current进程插入适当的等待队列,进入休眠。如果有按键按下,那么进入休眠态的进程就会唤醒,然后接着从休眠点开始运行。

78行,判断是否有信号唤醒(比如kill),用到signal_pending()函数,原型如下

int signal_pending(struct task_struct *p)

p一般为当前进程,也就是current

仅检查当前进程是否有信号处理(不会在这里处理信号),返回不为0表示有信号需要处理

如果是被信号唤醒,返回-ERESTARTSYS 这个错误码,goto到下图

 101-102行与82-83行一样,下面会解释

82行,经过78行判断,如果不是信号唤醒,那就是按键唤醒,设置为运行状态

83行,移除等待队列,这里用到remove_wait_queue()函数,原型如下

void remove_wait_queue(wait_queue_head_t *q,wait_queue_t *wait)

q: 要删除的等待队列项所处的等待队列头。wait:要删除的等待队列项。返回值:无

代码

static ssize_t imx6uirq_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{int ret = 0;unsigned char keyvalue;unsigned char releasekey;struct imx6uirq_dev *dev = filp->private_data;
#if 0/*等待事件*//*等待按键有效*/wait_event_interruptible(dev->r_wait,atomic_read(&dev->releasekey));
#endif/*定义一个等待队列项*/DECLARE_WAITQUEUE(wait, current);if(atomic_read(&dev->releasekey) == 0) { /* 没有按键按下 */      add_wait_queue(&dev->r_wait,&wait);/*将等待队列项添加到等待队列头*/__set_current_state(TASK_INTERRUPTIBLE);/*当前进行设置为可被打断的状态*/schedule();/*进行一次任务切换,切换进入睡眠*//* 判断是否为信号引起的唤醒 */if(signal_pending(current)){ret = -ERESTARTSYS;goto wait_error;}__set_current_state(TASK_RUNNING); /*设置为运行状态 */remove_wait_queue(&dev->r_wait, &wait); /*将等待队列移除 */}keyvalue = atomic_read(&dev->keyvalue);releasekey = atomic_read(&dev->releasekey);if(releasekey){/*有效按键*/if(keyvalue & 0x80){keyvalue &= ~0x80;ret=__copy_to_user(buf,&keyvalue,sizeof(keyvalue));}else{goto data_error;}atomic_set(&dev->releasekey,0);/*按下标志清零*/}else{goto data_error;}return 0;
wait_error:__set_current_state(TASK_RUNNING);/*将当前任务设置为运行状态*/remove_wait_queue(&dev->r_wait,&wait);/*将对应的队列项从等待队列头删除*/return ret;
data_error:return -EINVAL;
}

因为效果和上面等待唤醒一样的,不再截图

六、非阻塞IO之select

复制上面的内容,创建新的工作区

先添加头文件#include <linux/poll.h> 

 imx6uirq_read

 非阻塞IO也是在 imx6uirq_read函数里面添加,添加如图

 67行,判断是否为非阻塞,“f_flags"是文件标志,专门标识阻塞和非阻塞,检查用户请求是否是非堵塞式的操作,“O_NONBLOCK”表示采用非阻塞的文件IO方法

68-69行,如果是非阻塞操作,判断按键是否有效,无效直接返回错误值

71-74行,如果是阻塞操作,直接按上面的阻塞io的内容处理,这里用等待唤醒方式阻塞

构建驱动的poll操作函数

当应用程序调用 select 或 poll 函数来对驱动程序进行非阻塞访问的时候,驱动程序
file_operations 操作集中的 poll 函数就会执行,主要用来返回状态信息

1、添加poll

在操作集里面添加poll

 2、编写imx6uirq_poll函数 

128行, poll 函数原型如下

unsigned int (*poll) (struct file *filp, struct poll_table_struct *wait)
filp: 要打开的设备文件(文件描述符)。
wait: 结构体 poll_table_struct 类型指针, 由应用程序传递进来的。一般将此参数传递给
poll_wait 函数。
返回值:向应用程序返回设备或者资源状态

131行,设置私有数据

132行,需要在驱动程序的 poll 函数中调用 poll_wait 函数, poll_wait 函数不会引起阻塞,只是
将应用程序添加到 poll_table 中,原型如下:

void poll_wait(struct file * filp, wait_queue_head_t * wait_address, poll_table *p)
参数 wait_address 是要添加到 poll_table 中的等待队列头

参数 p 就是 poll_table,就是file_operations 中 poll 函数的 wait 参数

 134-137行,判断按键是否按下,如果按下就返回的资源状态,POLLIN 有数据可以读取,POLLRDNORM 等同于 POLLIN,普通数据可读

编写APP

先添加头文件#include <sys/select.h>

1、打开方式

打开文件用非阻塞方式打开

 2、定义

 24行,比如现在要从一个设备文件中读取数据,那么就可以定义一个 fd_set 变量,这个变量
要传递给readfds,readfds 用于监视指定描述符集的读变化,也就是监视这些文件是否可以读取,只要这些集合里面有一个文件可以读取那么 seclect 就会返回一个大于 0 的值表示文件可以读取。如果没有文件可以读取,那么就会根据 timeout 参数来判断是否超时

25行,timeout:超时时间,当我们调用 select 函数等待某些文件描述符可以设置超时时间,超时时
间使用结构体 timeval 表示,结构体定义如下

struct timeval {

                        long tv_sec; /* 秒*/
                        long tv_usec; /* 微妙*/

};

当 timeout 为 NULL 的时候就表示无限期的等待。

3、循环读取

 46-47行,在24行定义好一个 fd_set 变量以后可以使用如下一些宏进行操作

void FD_ZERO(fd_set *set)
void FD_SET(int fd, fd_set *set)
FD_ZERO 用于将 fd_set 变量的所有位都清零

 FD_SET 用于将 fd_set 变量的某个位置 1,也就是向 fd_set 添加一个文件描述符,参数 fd 就是要加入的文件描述符。

59-50行,构造超时时间为1秒

51,使用select函数,原型如下

int select (int             nfds
                fd_set        *readfds,
                fd_set        *writefds,
                fd_set        *exceptfds,
                struct timeval *timeout)                                                                                              返回值: 0,表示的话就表示超时发生,但是没有任何文件描述符可以进行操作;                                -1,发生错误;
               其他值,可以进行操作的文件描述符个数

 nfds: 所要监视的这三类文件描述集合中, 最大文件描述符加 1
上面24行定义的就是参数readfds,监视指定描述符集的读变化

writefds 和 readfs 类似,只是 writefs 用于监视这些文件是否可以进行写操作。 exceptfds 用于监视这些文件的异常,这里设置为NULL

上面25行定义的就是参数timeout,设置超时时间

52-68行,判断返回值,为0打印超时;为-1错误结束;其他值,使用fd_set 变量FD_ISSET
宏进行操作,原型如下

int FD_ISSET(int fd, fd_set *set)
FD_ISSET 用于测试一个文件是否属于某个集合,参数 fd 就是要判断的文件描述符

 这里用来判断文件变化是不是属于readfds集合引起的,如果是就执行60-65行操作,也就是按下按键会打印值

代码如下

imx6uirq_read

static ssize_t imx6uirq_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{int ret = 0;unsigned char keyvalue;unsigned char releasekey;struct imx6uirq_dev *dev = filp->private_data;if(filp->f_flags & O_NONBLOCK){/*非阻塞*/if(atomic_read(&dev->releasekey) == 0){return -EAGAIN;}}else {/*等待按键有效*/wait_event_interruptible(dev->r_wait,atomic_read(&dev->releasekey));}keyvalue = atomic_read(&dev->keyvalue);releasekey = atomic_read(&dev->releasekey);if(releasekey){/*有效按键*/if(keyvalue & 0x80){keyvalue &= ~0x80;ret=__copy_to_user(buf,&keyvalue,sizeof(keyvalue));}else{goto data_error;}atomic_set(&dev->releasekey,0);/*按下标志清零*/}else{goto data_error;}return 0;data_error:return -EINVAL;
}

 操作集

static const  struct file_operations imx6uirq_fops = {.owner	=   THIS_MODULE,.open   =   imx6uirq_open,.read   =   imx6uirq_read,.write  =   imx6uirq_write,.release =  imx6uirq_release,.poll   =   imx6uirq_poll,
};

 imx6uirq_poll函数

static unsigned int imx6uirq_poll(struct file *filp, struct poll_table_struct * wait)
{int mask = 0;struct imx6uirq_dev *dev = filp->private_data;poll_wait(filp, &dev->r_wait,wait);/*是否可读*/if(atomic_read(&dev->releasekey)){/*按键按下,可读*/mask = POLLIN | POLLRDNORM ; /*返回pollin*/}return mask;
}

 imx6uirqAPP

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/select.h>#define CLOSE_CMD _IO(0XEF ,1)  /*关闭命令*/
#define OPEN_CMD _IO(0XEF,2)    /*打开命令*/
#define SETPERIOD_CMD _IO(0XEF,3) /*设置周期*//*argc:应用程序参数个数(argv数组元素个数)argv:具体参数,也可以写作char **argv./imx6uirqAPP <filename>    ./imx6uirqAPP  /dev/imx6uirq
*/
int main(int argc, char *argv[])
{fd_set readfds;/* 读操作文件描述符集 */struct timeval timeout;/* 超时结构体 */int fd,ret;char *filename;unsigned char data;/*判断命令行输入参数是否正确*/if(argc != 2){printf("error usage!\r\n");return -1;}/*用指针指向文件*/filename = argv[1];/*打开文件,非阻塞打开*/fd = open(filename , O_RDWR | O_NONBLOCK);if(fd < 0){printf("file open failed\r\n",filename);return -1;}/*循环读取*/while(1){FD_ZERO(&readfds);/* 清除 readfds */FD_SET(fd, &readfds);/* 将 fd 添加到 readfds 里面 *//* 构造超时时间 */timeout.tv_sec = 1;timeout.tv_usec = 0;/*1s*/ret  = select(fd + 1 ,&readfds , NULL,NULL,&timeout );switch(ret){case 0:/*超时*/printf("select timeout!\r\n");break;case -1:/*错误*/break;default:/*可读取数据*/if(FD_ISSET(fd,&readfds)){/* 判断是否为 fd 文件描述符 */ret = read(fd,&data,sizeof(data));/* 使用 read 函数读取数据 */if(ret<0){}else{if(data)printf(" key vaule = %d\r\n",data);}}break;}}/*关闭文件*/close(fd);return 0;
}

 验证

可以看到,后台运行下,如果没按下按键一秒就打印一次超时,按下就会打印值,而且cpu占用也不多

七、非阻塞IO之poll

这里只需要改APP即可,添加头文件#include <poll.h>

1、pollfd 结构体

 结构体原型如下

struct pollfd {        int fd;/* 文件描述符*/
                                short events;/* 请求的事件*/
                                short revents;/* 返回的事件*/

}

fd 是要监视的文件描述符,如果 fd 无效的话那么 events 监视事件也就无效,并且 revents
返回 0。

events 是要监视的事件,可监视的事件类型如; POLLIN(有数据可读) 和 POLLRDNORM(等同POLLIN)等等

revents 是返回参数,也就是返回的事件, 由 Linux 内核设置具体的返回事

 2、循环读取

 80-81行,设置文件描述符和请求的事件,也就是监视的事件POLLIN

83行,用到poll 函数,用来监控事件的发生,原型如下

int poll(struct pollfd *fds,
                nfds_t nfds,
                int timeout)

可以看到,这里的fds就是上面30行定义的

 nfds: poll 函数要监视的文件描述符数量,这里就监控事件POLLIN,所以为1

timeout: 超时时间,单位为 ms,这里设置500ms

返回值:返回 revents 域中不为 0 的 pollfd 结构体个数,也就是发生事件或错误的文件描述符数量; 0,超时; -1,发生错误,并且设置 errno 为错误类型。

84-100行,根据返回值进行具体操作,和上面的select函数的差不多一样

验证

 效果也是和上面select函数的一样

代码如下

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/select.h>
#include <poll.h>#define CLOSE_CMD _IO(0XEF ,1)  /*关闭命令*/
#define OPEN_CMD _IO(0XEF,2)    /*打开命令*/
#define SETPERIOD_CMD _IO(0XEF,3) /*设置周期*//*argc:应用程序参数个数(argv数组元素个数)argv:具体参数,也可以写作char **argv./imx6uirqAPP <filename>    ./imx6uirqAPP  /dev/imx6uirq
*/
int main(int argc, char *argv[])
{
#if 0fd_set readfds;/* 读操作文件描述符集 */struct timeval timeout;/* 超时结构体 */#endifstruct pollfd fds;/*要监视的文件描述符集合以及要监视的事件*/int fd,ret;char *filename;unsigned char data;/*判断命令行输入参数是否正确*/if(argc != 2){printf("error usage!\r\n");return -1;}/*用指针指向文件*/filename = argv[1];/*打开文件,非阻塞打开*/fd = open(filename , O_RDWR | O_NONBLOCK);if(fd < 0){printf("file open failed\r\n",filename);return -1;}/*循环读取*/while(1){fds.fd = fd;fds.events = POLLIN;ret  = poll(&fds ,1, 500);/*超时时间500ms*/if(ret == 0){/*超时*/printf("select timeout!\r\n");}else if (ret < 0)/*错误*/{}else{if(fds.revents & POLLIN){/*可读取*/ret = read(fd,&data,sizeof(data));/* 使用 read 函数读取数据 */if(ret<0){}else{if(data)printf(" key vaule = %d\r\n",data);}}}}/*关闭文件*/close(fd);return 0;
}

select函数和poll函数区别

在单个线程中, select 函数能够监视的文件描述符数量有最大的限制,一般为 1024,可以
修改内核将监视的文件描述符数量改大,但是这样会降低效率!这个时候就可以使用 poll 函数,
poll 函数本质上和 select 没有太大的差别,但是 poll 函数没有最大文件描述符限制,
 

相关文章:

Linux 阻塞和非阻塞 IO 实验

目录 一、阻塞和非阻塞简介 1、IO 概念 2、阻塞与非阻塞 二、等待队列 1、等待队列头 2、等待队列项 3、将队列项添加/移除等待队列头 4、等待唤醒 5、等待事件 三、轮询 1、应用程序的非阻塞函数 2、Linux 驱动下的 poll 操作函数 四、阻塞IO之等待事件唤醒 添加…...

你要的react+ts最佳实践指南

本文根据日常开发实践&#xff0c;参考优秀文章、文档&#xff0c;来说说 TypeScript 是如何较优雅的融入 React 项目的。 温馨提示&#xff1a;日常开发中已全面拥抱函数式组件和 React Hooks&#xff0c;class 类组件的写法这里不提及。 前沿 以前有 JSX 语法&#xff0c;…...

软件测试人员会被替代吗?IT行业哪个方向的前景最好?字节12年测开是这样说的

互联网测试从业12年&#xff0c;前来作答。 逻辑上来说&#xff0c;软件工程最初始只需要两个岗位&#xff0c;一个是产品经理。&#xff0c;一个是研发&#xff08;开发&#xff09;&#xff0c;剩余的 所有岗位都是由他们衍生而来的。 第三个岗位大概率就是测试&#xff0c…...

十六、vue3.0之富文本编辑器的选择

在工作过程中我们会遇到很多的时候会使用到富文本编辑器,市场上流行的也是各种各样的,那么究竟如何选择呢,今天就给大家讲讲有哪一些,方便大家的选择。 一、TinyMCE TinyMCE 是富文本编辑器领域的头部玩家之一,主流富文本编辑器,功能非常全,你需要的大多数功能它都支持…...

kafka(一) 的架构,各概念

Kafka架构 Kafak 总体架构图中包含多个概念&#xff1a; &#xff08;1&#xff09;ZooKeeper&#xff1a;Zookeeper负责保存broker集群元数据&#xff0c;并对控制器进行选举等操作。 &#xff08;2&#xff09;Producer&#xff1a; 生产者负责创建消息&#xff0c;将消息发…...

【ts的常用类型】

ts的常用类型前言安装ts常见类型原始类型 、数组、 any变量上的类型注解函数对象类型联合类型类型别名接口接口和类型别名的对比前言 typescript中为了使编写的代码更规范&#xff0c;更有利于维护&#xff0c;增加了类型校验&#xff0c;安装 安装 typescript npm i typescr…...

Hyper-V与安卓模拟器不共存

一是某些新的模拟器已经开始使用新接口开发&#xff0c;支持了共存&#xff0c;安装这种新的安卓模拟器即可。 对于不支持共存的模拟器&#xff0c;只得增加一个windows开机后的系统选项&#xff0c;如果需要切换这两种不同选项使用系统&#xff0c;每次切换都需要重启windows系…...

【图像分类】卷积神经网络之ZFNet网络模型结构详解

写在前面: 首先感谢兄弟们的关注和订阅,让我有创作的动力,在创作过程我会尽最大能力,保证作品的质量,如果有问题,可以私信我,让我们携手共进,共创辉煌。 1. 前言 由于AlexNet的提出,大型卷积网络开始变得流行起来,但是人们对于网络究竟为什么能表现的这么好,以及怎…...

亿级高并发电商项目-- 实战篇 --万达商城项目 十三(编写购物车、优化修改商品、下架商品方法、购物车模块监听修改商品、删除商品消息)

&#x1f44f;作者简介&#xff1a;大家好&#xff0c;我是小童&#xff0c;Java开发工程师&#xff0c;CSDN博客博主&#xff0c;Java领域新星创作者 &#x1f4d5;系列专栏&#xff1a;前端、Java、Java中间件大全、微信小程序、微信支付、若依框架、Spring全家桶 &#x1f4…...

springboot 虚拟线程demo

jd19支持虚拟线程&#xff0c;虚拟线程是轻量级的线程&#xff0c;它们不与操作系统线程绑定&#xff0c;而是由 JVM 来管理。它们适用于“每个请求一个线程”的编程风格&#xff0c;同时没有操作系统线程的限制。我们能够创建数以百万计的虚拟线程而不会影响吞吐。 做个 spri…...

CTFer成长之路之逻辑漏洞

逻辑漏洞CTF 访问url: http://1b43ac78-61f7-4b3c-9ab7-d7e131e7da80.node3.buuoj.cn/ 登录页面用随意用户名密码登录 访问url&#xff1a; http://1b43ac78-61f7-4b3c-9ab7-d7e131e7da80.node3.buuoj.cn/user.php 登陆后有商品列表&#xff0c;共三个商品,点击购买flag 钱…...

入门力扣自学笔记238 C++ (题目编号:1144)

1144. 递减元素使数组呈锯齿状 题目&#xff1a; 给你一个整数数组 nums&#xff0c;每次 操作 会从中选择一个元素并 将该元素的值减少 1。 如果符合下列情况之一&#xff0c;则数组 A 就是 锯齿数组&#xff1a; 每个偶数索引对应的元素都大于相邻的元素&#xff0c;即 A…...

蓝桥杯-寒假作业

没有白走的路&#xff0c;每一步都算数&#x1f388;&#x1f388;&#x1f388; 题目描述&#xff1a; 有四个等式&#xff0c;每个等式的运算规则已经定好了&#xff0c;也就是我们常见的小学的四则运算&#xff0c;但是能够用来四则运算的数字非常有限&#xff0c;包括1~13…...

测试用例篇

1.测试用例的意义 测试用例&#xff08;Test Case&#xff09;是为了实施测试而向被测试的系统提供的一组集合&#xff0c;这组集合包含&#xff1a;测试环境、操作步骤、测试数据、预期结果等要素。 测试用例的意义是为了帮助测试人员了解测什么&#xff0c;怎么测 eg&#x…...

自动驾驶自主避障概况

文章目录前言1. 自主避障在自动驾驶系统架构中的位置2. 自主避障算法分类2.1 人工势场法&#xff08;APF&#xff09;2.1.1引力势场的构建2.1.2斥力势场的构建2.1.3人工势场法的改进2.2 TEB&#xff08;Timed-Eastic-Band, 定时弹性带&#xff09;2.3 栅格法2.4 向量场直方图(V…...

Python实用的库排名…

Python 是一个功能强大的编程语言&#xff0c;有着丰富的第三方库和模块&#xff0c;可以帮助你解决各种各样的问题。以下是一些比较厉害的 Python 库&#xff1a; NumPy&#xff1a;一个强大的数值计算库&#xff0c;提供了高效的数组和矩阵操作功能。 Pandas&#xff1a;提供…...

【YOLO系列】YOLOv4论文超详细解读1(翻译 +学习笔记)

前言 经过上一期的开篇介绍&#xff0c;我们知道YOLO之父Redmon在twitter正式宣布退出cv界&#xff0c;大家都以为YOLO系列就此终结的时候&#xff0c;天空一声巨响&#xff0c;YOLOv4闪亮登场&#xff01;v4作者是AlexeyAB大神&#xff0c;虽然换人了&#xff0c;但论文中给出…...

【神经网络】Transformer基础问答

1.Transforme与LSTM的区别 transformer和LSTM最大的区别就是LSTM的训练是迭代的&#xff0c;无法并行训练&#xff0c;LSTM单元计算完T时刻信息后&#xff0c;才会处理T1时刻的信息&#xff0c;T 1时刻的计算依赖 T-时刻的隐层计算结果。而transformer的训练是并行了&#xff0…...

制定防火墙策略的步骤和建议

制定防火墙策略是保护企业网络环境安全的关键一步。下面是一些制定防火墙策略的步骤和建议&#xff0c;供参考&#xff1a; 识别网络资产&#xff1a;确定企业网络环境中所有的网络资产&#xff0c;包括服务器、应用程序、数据库、移动设备和终端用户设备等&#xff0c;并进行…...

新必应(New Bing)国内申请与使用教程

微软的新必应&#xff08;New Bing&#xff09;基于GPT4模型&#xff0c;比ChatGPT的GPT3.5模型领先半个世代。并且集成了Edge浏览器的数据资源&#xff0c;功能更加强大。经过不断的踩坑&#xff0c;终于申请到了New Bing的使用权限&#xff0c;且国内网络也能够正常使用&…...

博客系统——项目测试报告

目录 前言 博客系统——项目介绍 1、测试计划 1.1、功能测试 1.1.1、编写测试用例 1.1.2、实际执行步骤 1.2、使用Selenium进行Web自动化测试 1.2.1、引入依赖 1.2.2、提取共性&#xff0c;实现代码复用 1.2.3、创建测试套件类 1.2.4、博客登录页自动化测试 1.2.5、…...

Macbook M1 安装PDI(Kettle) 9.3

Macbook M1 安装PDI(Kettle) 9.3 当前 PDI&#xff08;Kettle&#xff09;最新版为9.3&#xff0c;依赖Java JDK 11。因为没有专门用于 M1的程序&#xff0c;需要下载并安装x86_64架构的JDK及依赖软件&#xff0c;并 “强制在Intel模式下运行shell” 的方式来实现 Kettle 的正…...

机器学习——模型评估

在学习得到的模型投放使用之前&#xff0c;通常需要对其进行性能评估。为此&#xff0c;需使用一个“测试集”(testing set&#xff09;来测试模型对新样本的泛化能力&#xff0c;然后以测试集上的“测试误差( tootino error)作为泛化误差的近似。我们假设测试集是从样本真实分…...

react react-redux学习记录

react react-redux学习记录1.原理2.怎么用呢2.1 容器组件2.2UI组件2.3 App.jsx3.简化3.1简写mapDispatch3.2 Provider组件的使用3.3整合UI组件和容器组件1.原理 UI组件:不能使用任何redux的api&#xff0c;只负责页面的呈现、交互等。 容器组件&#xff1a;负责和redux通信&…...

nodejs环境配置

啥是node.js 简单理解就是js运行环境 啥是npm 简单理解就是nodejs包管理工具&#xff0c;全称Node Package Manager 啥是cnpm npm的开源镜像&#xff0c;在国内使用cnpm替代npm可以起到加速的效果 https://npmmirror.com/ ①安装node.js https://nodejs.org/en/download/ 下载…...

数据治理之元数据管理Atlas

数据治理之元数据管理的利器——Atlas 一、数据治理与元数据管理 1.1 背景 为什么要做数据治理&#xff1f; 业务繁多&#xff0c;数据繁多&#xff0c;业务数据不断迭代。人员流动&#xff0c;文档不全&#xff0c;逻辑不清楚&#xff0c;对于数据很难直观理解&#xff0c;…...

15 Nacos客户端实例注册源码分析

Nacos客户端实例注册源码分析 实例客户端注册入口 流程图&#xff1a; 实际上我们在真实的生产环境中&#xff0c;我们要让某一个服务注册到Nacos中&#xff0c;我们首先要引入一个依赖&#xff1a; <dependency><groupId>com.alibaba.cloud</groupId>&l…...

C++将派生类赋值给基类(向上转型)

1.将派生类对象赋值给基类对象 #include <iostream> using namespace std;//基类 class A{ public:A(int a); public:void display(); public:int m_a; }; A::A(int a): m_a(a){ } void A::display(){cout<<"Class A: m_a"<<m_a<<endl; }//…...

使用Platform Designer创建Nios II 最小系统

Nios II简介 ​ Nios II 软核处理器十多年前就有了&#xff0c;它和xilinx的MicroBlaze类似&#xff0c;性能相比硬核处理器要差得多&#xff0c;工程应用也不是很多&#xff0c;那还有必须学习一下吗&#xff1f;我个人认为了解一下Nios II开发流程&#xff0c;对intel FPGA开…...

CD销售管理系统

技术&#xff1a;Java、JSP等摘要&#xff1a;二十一世纪是一个集数字化&#xff0c;网络化&#xff0c;信息化的&#xff0c;以网络为核心的社会。中国的网民充分领略到“畅游天地间&#xff0c;网络无极限” 所带来的畅快。随着Internet的飞速发展&#xff0c;使得网络的应用…...

网站运营暂停/seo技术建站

转载于:https://www.cnblogs.com/pubgoso/p/10759712.html...

宝塔面板wordpress静态化/百度热搜排名

通常写启动屏&#xff0c;都有个很不喜欢的问题&#xff0c;就是会空白几秒才显示界面&#xff0c;而且界面还是很简单的&#xff01; 解决办法 1 写一个透明的主题&#xff0c;一般启动屏都是不要bar的所以继承AppTheme.NoActionBar 1 <style name"Theme.AppStartLoa…...

wordpress个人博客主题/哈尔滨关键词优化方式

教材&#xff1a;《数据库系统概论&#xff08;第五版&#xff09;》王珊 高教出版社 目录数据、数据库数据管理技术经历的阶段DBMS 必需功能数据库的核心和基础数据模型组成三要素关系的两个不变性数据模型分为两类概念模型(信息模型)逻辑模型和物理模型关系模型优点缺点DBS 三…...

网站设计大概多少钱/百度学术官网论文查重免费

2019独角兽企业重金招聘Python工程师标准>>> 大数据时代来临&#xff0c;如果你还不知道Kafka那你就真的out了&#xff01;据统计&#xff0c;有三分之一的世界财富500强企业正在使用Kafka&#xff0c;包括所有TOP10旅游公司&#xff0c;7家TOP10银行&#xff0c;8家…...

江苏省义务教育标准化建设网站/360指数查询工具

0.Manjaro启动U盘的制作 推荐使用4-16G容量的U盘&#xff0c;避免兼容性问题&#xff08;U盘太大可能会无法启动&#xff09;。 用rufus就可以&#xff0c;注意选用DD模式才能成功制作&#xff08;默认是hyperiso&#xff09;。 如果在linux环境里&#xff0c;先用sudo fdisk -…...

长宁区小学网站建设/农产品网络营销

无重复字符的最长子串:Python实现滑动窗口算法 在日常编程中,我们经常需要解决字符串相关问题。例如,如何在一个字符串中寻找最长的无重复字符的子串?这个问题看似简单,但是其实需要运用到高级数据结构和算法。在本文中,我们将介绍如何用Python实现滑动窗口算法来解决这个…...