ICode9

精准搜索请尝试: 精确搜索
首页 > 系统相关> 文章详细

linux驱动 阻塞和非阻塞IO 篇二

2021-09-13 23:57:50  阅读:134  来源: 互联网

标签:usb ...... struct 阻塞 usblp IO linux poll wait


@上一篇介绍了linux阻塞与非阻塞的基本概念,以及应用程序的小demo和kernel层对应的api函数。那接下来就以实例来分析,如何在linux驱动层添加等待队列和轮询的方法,以及区别。
**

一:简介

**
在linux驱动中,存在很常见的两种设备访问模式,所以在编写驱动的时候,一定要考虑到阻塞和非阻塞。这样做有以下好处
1. linux驱动标准的的写法,让你写的驱动正式,拿的出手,也能锻炼个人的规划能力、编程能力、思维能力;
2. 提高个人的审美能力,当你去看一个驱动,里面的各种框架,组件的交叉的逻辑之美,你会不禁赞叹,linux内核原来是这样,会有一种恍然大悟的感觉;
3.以上两点均是个人见解,阻塞和非阻塞有它存在的意义:针对不同的驱动设备,应运而生,提高驱动对设备资源访问的及时性,有效性,提高cpu的使用效率;
4.在应用程序对具体设备进行编程时,加阻塞和非阻塞对cpu的使用率,占有率是有巨大的影响。
举个例子:我们编写一个应用程序,读写按键的状态。在应用程序中,在while中轮询访问设备节点。如果应用中不加入阻塞方式,驱动中不加载阻塞方式,你应用程序在执行时,可以top看一下,执行文件在系统中占用cpu的使用率,接近100%,这样极大影响系统的整体性能,也非常不合理。加入阻塞方式,使用率会几乎为0%,这样才是我们寻求的正确方式。

**

二:等待队列

**
1. 实例分析:
以usb/class/usblp.c 为例,在probe中init rwait wwrite 等待队列头,注册设备类—>设备类里加入设备文件操作接口—>在接口里添加文件poll属性—>编写对应的poll函数,当应用程序读访问时,宏定义一个read等待队列,如果没有数据可读,切换状态进入休眠状态,等待唤醒;如果可读,设置状态为runing,删除read等待队列。写同样,如下:

#include <linux/poll.h>

struct usblp {
......
	wait_queue_head_t	rwait, wwait;
......
};

static void usblp_bulk_read(struct urb *urb)
{
......
	//如果有数据可读,就唤醒read队列
	wake_up(&usblp->rwait);
......
}

static void usblp_bulk_write(struct urb *urb)
{
......
	//如果有数据可写,就唤醒write队列
	wake_up(&usblp->wwait);
......
}

/* No kernel lock - fine */
static __poll_t usblp_poll(struct file *file, struct poll_table_struct *wait)
{
	__poll_t ret;
	unsigned long flags;

	struct usblp *usblp = file->private_data;
	/* Should we check file->f_mode & FMODE_WRITE before poll_wait()? */
	poll_wait(file, &usblp->rwait, wait);
	poll_wait(file, &usblp->wwait, wait);
	spin_lock_irqsave(&usblp->lock, flags);
	ret = ((usblp->bidir && usblp->rcomplete) ? EPOLLIN  | EPOLLRDNORM : 0) |
	   ((usblp->no_paper || usblp->wcomplete) ? EPOLLOUT | EPOLLWRNORM : 0);
	spin_unlock_irqrestore(&usblp->lock, flags);
	return ret;
}


static ssize_t usblp_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos)
{
......
	if ((rv = usblp_wwait(usblp, !!(file->f_flags & O_NONBLOCK))) < 0)
		goto raise_wait;

......
			wake_up(&usblp->wwait);

		/*
		 * Step 2: Wait for transfer to end, collect results.
		 */
		rv = usblp_wwait(usblp, !!(file->f_flags&O_NONBLOCK));
......	
}



static int usblp_wwait(struct usblp *usblp, int nonblock)
{
	DECLARE_WAITQUEUE(waita, current);//创建一个等待队列
	int rc;
	int err = 0;
//如果不可write,就将当前任务添加到wwait中宝股份,
	add_wait_queue(&usblp->wwait, &waita);
	for (;;) {
		if (mutex_lock_interruptible(&usblp->mut)) {
			rc = -EINTR;
			break;
		}
		//设置可以被TASK_INTERRUPTIBLE 信号打断。
		set_current_state(TASK_INTERRUPTIBLE);
		rc = usblp_wtest(usblp, nonblock);
		mutex_unlock(&usblp->mut);
		if (rc <= 0)
			break;

		//进行任务切换,当前进程进入睡眠状态
		if (schedule_timeout(msecs_to_jiffies(1500)) == 0) {
			if (usblp->flags & LP_ABORT) {
				err = usblp_check_status(usblp, err);
				if (err == 1) {	/* Paper out */
					rc = -ENOSPC;
					break;
				}
			} else {
				/* Prod the printer, Gentoo#251237. */
				mutex_lock(&usblp->mut);
				usblp_read_status(usblp, usblp->statusbuf);
				mutex_unlock(&usblp->mut);
			}
		}
	}
	//如果可write,将当前任务状态设置为running,调用remove_wait_queue将进程从等待队列中删除
	set_current_state(TASK_RUNNING);
	remove_wait_queue(&usblp->wwait, &waita);
	return rc;
}


static int usblp_rwait_and_lock(struct usblp *usblp, int nonblock)
{
	DECLARE_WAITQUEUE(waita, current);
	int rc;

	add_wait_queue(&usblp->rwait, &waita);
	for (;;) {
		if (mutex_lock_interruptible(&usblp->mut)) {
			rc = -EINTR;
			break;
		}
		set_current_state(TASK_INTERRUPTIBLE);
		if ((rc = usblp_rtest(usblp, nonblock)) < 0) {
			mutex_unlock(&usblp->mut);
			break;
		}
		if (rc == 0)	/* Keep it locked */
			break;
		mutex_unlock(&usblp->mut);
		schedule();
	}
	set_current_state(TASK_RUNNING);
	remove_wait_queue(&usblp->rwait, &waita);
	return rc;
}


static const struct file_operations usblp_fops = {
	.owner =	THIS_MODULE,
	.read =		usblp_read,
	.write =	usblp_write,
	.poll =		usblp_poll,
	.unlocked_ioctl =	usblp_ioctl,
	.compat_ioctl =		usblp_ioctl,
	.open =		usblp_open,
	.release =	usblp_release,
	.llseek =	noop_llseek,
};

static struct usb_class_driver usblp_class = {
	.name =		"lp%d",
	.devnode =	usblp_devnode,
	.fops =		&usblp_fops,
	.minor_base =	USBLP_MINOR_BASE,
};

static int usblp_probe(struct usb_interface *intf,
		       const struct usb_device_id *id)
{
......
	init_waitqueue_head(&usblp->rwait);
	init_waitqueue_head(&usblp->wwait);
......
	retval = usb_register_dev(intf, &usblp_class);
......
	return 0;
}

static void usblp_disconnect(struct usb_interface *intf)
{
......
	wake_up(&usblp->wwait);
	wake_up(&usblp->rwait);
......
}


static struct usb_driver usblp_driver = {
	.name =		"usblp",
	.probe =	usblp_probe,
	.disconnect =	usblp_disconnect,
	.suspend =	usblp_suspend,
	.resume =	usblp_resume,
	.id_table =	usblp_ids,
	.supports_autosuspend =	1,
};
......

2.注意事项
a.将任务或者进程加入到等待队列头;
b.在合适的点唤醒等待队列,一般在中断中。

三:轮询

**
实例分析:
以kernel/usb/misc/ldusb.c 为例,在probe中init read write 等带队列,注册设备类—>设备类里加入设备文件操作接口—>在接口里添加文件poll属性—>编写对应的poll函数,当应用程序读写访问时,判断是否为非阻塞访问,根据poll函数pollin状态,就会调用相关的wake up函数,实现数据访问,实现非阻塞式方式。

#include <linux/poll.h>//kernel轮询头文件

/* Structure to hold all of our device specific stuff */
struct ld_usb {
......
	wait_queue_head_t	read_wait; //读队列头
	wait_queue_head_t	write_wait; //写队列头
......
};



/**
 *	ld_usb_write
 */
static ssize_t ld_usb_write(struct file *file, const char __user *buffer,
			    size_t count, loff_t *ppos)
{
......

	/* wait until previous transfer is finished */
	//判断是否为非阻塞式访问,如果是write有效,没有返回-EAGAIN。
	if (dev->interrupt_out_busy) {
		if (file->f_flags & O_NONBLOCK) {
			retval = -EAGAIN;
			goto unlock_exit;
		}
		//同read
		retval = wait_event_interruptible(dev->write_wait, !dev->interrupt_out_busy);
		if (retval < 0) {
			goto unlock_exit;
		}
	}
......
}
	
/**
 *	ld_usb_read
 */
static ssize_t ld_usb_read(struct file *file, char __user *buffer, size_t count,
			   loff_t *ppos)
{
......
		//判断是否为非阻塞式访问,如果是read有效,没有返回-EAGAIN。
		if (file->f_flags & O_NONBLOCK) {
			retval = -EAGAIN;
			goto unlock_exit;
		}
		//加入等待队列,等待唤醒,也就是有数据读,实现非阻塞read
		retval = wait_event_interruptible(dev->read_wait, dev->interrupt_in_done);

......	
};

/**
 *	ld_usb_poll 
 *	用于处理非阻塞访问函数
 *	参数file:要打开的设备文件
 *	参数wait:等待列表(poll_table)
 */
static __poll_t ld_usb_poll(struct file *file, poll_table *wait)
{
	struct ld_usb *dev;
	__poll_t mask = 0;

	dev = file->private_data;

	if (!dev->intf)
		return EPOLLERR | EPOLLHUP;
//将等待队列头加到poll_table中
	poll_wait(file, &dev->read_wait, wait);
	poll_wait(file, &dev->write_wait, wait);

	if (dev->ring_head != dev->ring_tail) //如果有数据,就返回EPOLLIN,表示有数据
		mask |= EPOLLIN | EPOLLRDNORM;
	if (!dev->interrupt_out_busy)
		mask |= EPOLLOUT | EPOLLWRNORM;

}


/* file operations needed when we register this driver */
static const struct file_operations ld_usb_fops = {
	.owner =	THIS_MODULE,
	.read  =	ld_usb_read,
	.write =	ld_usb_write,
	.open =		ld_usb_open,
	.release =	ld_usb_release,
	.poll =		ld_usb_poll,
	.llseek =	no_llseek,
};


/*
 * usb class driver info in order to get a minor number from the usb core,
 * and to have the device registered with the driver core
 */
static struct usb_class_driver ld_usb_class = {
......
	.fops =		&ld_usb_fops,
......
};

static int ld_usb_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
......
	//初始化read,write等待队列头
	init_waitqueue_head(&dev->read_wait);
	init_waitqueue_head(&dev->write_wait);

	//注册类,实现poll,供应用程序访问
......
	retval = usb_register_dev(intf, &ld_usb_class);
......
}

/**
 *	ld_usb_disconnect
 *
 *	Called by the usb core when the device is removed from the system.
 */
static void ld_usb_disconnect(struct usb_interface *intf)
{
......
		usb_deregister_dev(intf, &ld_usb_class);
......
		//唤醒poller
		wake_up_interruptible_all(&dev->read_wait);
		wake_up_interruptible_all(&dev->write_wait);
......
};

/* usb specific object needed to register this driver with the usb subsystem */
static struct usb_driver ld_usb_driver = {
	.name =		"ldusb",
	.probe =	ld_usb_probe,
	.disconnect =	ld_usb_disconnect,
	.id_table =	ld_usb_table,
};

module_usb_driver(ld_usb_driver);

四:区别

a.等待队列:在read、write函数中,初始化化一个等待队列,并且实现状态切换,进入睡眠状态。
b.轮询:在read、write中判断函数是否为非阻塞式访问,在poll函数根据pollin状态唤醒等待队列,读写数据。

标签:usb,......,struct,阻塞,usblp,IO,linux,poll,wait
来源: https://blog.csdn.net/m0_57123509/article/details/120276591

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有