用非阻塞式I/O模型降低CPU使用率

若应用程序以非阻塞的方式读取,则驱动程序也要以非阻塞的方式返回,也就是轮询的方式。poll、epoll和select函数可以用于处理轮询,应用程序可通过这些函数来查询设备是否可以操作,如果可以操作的话就从设备读取或者向设备写入数据

1. 驱动中的poll操作函数

应用程序调用select、poll或epoll函数来对驱动程序进行非阻塞访问时,驱动程序中file_operations操作集中的poll函数就会执行。所以驱动程序中需要提供对应的poll函数,其原型如下所示:

unsigned int (*poll) (struct file *filp, struct poll_table_struct *wait)
//filp:要打开的设备文件(文件描述符)
//wait:结构体 poll_table_struct 类型指针,由应用程序传递进来的,
//  一般将此参数传递给poll_wait 函数
//返回值:向应用程序返回设备或者资源状态,可以返回的资源状态如下:
//   POLLIN 有数据可以读取
//   POLLPRI 有紧急的数据需要读取
//   POLLOUT 可以写数据
//   POLLERR 指定的文件描述符发生错误
//   POLLHUP 指定的文件描述符挂起
//   POLLNVAL 无效的请求
//   POLLRDNORM 等同于 POLLIN,普通数据可读

需要在驱动程序的poll函数中调用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参数

2. 非阻塞式I/O程序编写

用阻塞式I/O模型降低CPU使用率一文的代码基础上进行改编,设备树文件无需修改,只需修改应用程序和驱动程序里的部分代码即可

⏩ 驱动程序poll函数处理部分

......
#define IMX6UIRQ_NAME "noblockio" 
......
unsigned int imx6uirq_poll(struct file *filp, struct poll_table_struct *wait) {
 unsigned int mask = 0;
 struct imx6uirq_dev *dev = (struct imx6uirq_dev *)filp->private_data;
 /* 将等待队列头添加到poll_table中 */
 poll_wait(filp, &dev->r_wait, wait);    
 /* 按键按下 */
 if(atomic_read(&dev->releasekey)) {
  mask = POLLIN | POLLRDNORM;  /* 返回PLLIN */
 }
 return mask;
}
/* 设备操作函数 */
static struct file_operations imx6uirq_fops = {
 .owner = THIS_MODULE,
 .open = imx6uirq_open,
 .read = imx6uirq_read,
 .poll = imx6uirq_poll,
};

⏩ 驱动程序read函数处理部分

static ssize_t imx6uirq_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt) {
 ......
 ......
 /* 非阻塞访问 */
 if (filp->f_flags & O_NONBLOCK) {
  /* 没有按键按下,返回-EAGAIN */
  if(atomic_read(&dev->releasekey) == 0) {
   return -EAGAIN;
  }
 } else { /* 阻塞访问 */    
  /* 加入等待队列,等待被唤醒,也就是有按键按下 */
  ret = wait_event_interruptible(dev->r_wait, atomic_read(&dev->releasekey)); 
  if (ret) {
   goto wait_error;
  }
 }
 ......
 ...... 
wait_error:
    return ret;
data_error:
    return -EINVAL;
}

⏩ 应用程序中,使用select函数来实现非阻塞访问

int main(int argc, char *argv[])
 int fd;
 int ret = 0;
 char *filename;
 struct pollfd fds;
 fd_set readfds;
 struct timeval timeout;
 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("Can't open file %s\r\n", filename);
  return -1;
 }

 /***** 以下代码使用 select 函数来实现非阻塞访问 *****/
 while(1) {
  FD_ZERO(&readfds);
  FD_SET(fd, &readfds);
  /* 构造超时时间 */
  timeout.tv_sec = 0;
  timeout.tv_usec = 500000; /* 500ms */
  ret = select(fd + 1, &readfds, NULL, NULL, &timeout);
  switch (ret){            
   case 0:  /* 超时 */
    /* 用户自定义超时处理 */
    break;            
   case -1: /* 错误 */
    /* 用户自定义错误处理 */
    break;           
   default: /* 可以读取数据 */
    if(FD_ISSET(fd, &readfds)){
     ret = read(fd, &data, sizeof(data));
     if (ret < 0) {
      /* 读取错误 */
     } else {
      if (data)
       printf("key value=%d\r\n", data);
     }
    }
    break;
  }
 }
 close(fd);
 return ret;
}

3. 编译测试

⏩ 编译驱动程序:当前目录下创建Makefile文件,并make编译

KERNELDIR := /home/andyxi/linux/kernel/linux-imx-rel_imx_4.1.15_2.1.0_ga_andyxi
CURRENT_PATH := $(shell pwd)
obj-m := noblockio.o

build: kernel_modules

kernel_modules:
$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules
clean:
$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean

⏩ 编译测试程序:无需内核参与,直接编译即可

arm-linux-gnueabihf-gcc noblockioApp.c -o noblockioApp

⏩ 将驱动文件和测试文件拷贝至rootfs/lib/modules/4.1.15后加载驱动

depmod  #第一次加载驱动时,需使用“depmod”命令
modprobe noblockio.ko

⏩使用./noblockioApp /dev/noblockio &命令,以后台模式运行应用程序,此时按下按键,应用程序会打印出按键值
在这里插入图片描述

⏩ 使用top命令查看noblockioApp的CPU使用率:可见使用非阻塞方式读处理后,CPU的使用率也非常低
在这里插入图片描述

⏩ 使用kill命令关闭后台运行的应用程序后,卸载驱动
在这里插入图片描述

在这里插入图片描述

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

安迪西嵌入式

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值