IO模型:阻塞和非阻塞

一、五种IO模型------读写外设数据的方式

  1. 阻塞: 不能操作就睡觉

    IO模型:阻塞和非阻塞_第1张图片

  2. 非阻塞:不能操作就返回错误

    IO模型:阻塞和非阻塞_第2张图片

  3. 多路复用:委托中介监控

    IO模型:阻塞和非阻塞_第3张图片

  4. 信号驱动:让内核如果能操作时发信号,在信号处理函数中操作

    IO模型:阻塞和非阻塞_第4张图片

  5. 异步IO:向内核注册操作请求,内核完成操作后发通知信号

    IO模型:阻塞和非阻塞_第5张图片

二、阻塞与非阻塞

应用层:

​ open时由O_NONBLOCK指示read、write时是否阻塞

​ open以后可以由fcntl函数来改变是否阻塞:

flags = fcntl(fd,F_GETFL,0);
flags |= O_NONBLOCK;
fcntl(fd, F_SETFL, flags);

驱动层:通过等待队列

wait_queue_head_t //等待队列头数据类型

init_waitqueue_head(wait_queue_head_t *pwq) //初始化等待队列头
    
wait_event_interruptible(wq,condition)
/*
功能:条件不成立则让任务进入浅度睡眠,直到条件成立醒来
    wq:等待队列头
    condition:C语言表达式
返回:正常唤醒返回0,信号唤醒返回非0(此时读写操作函数应返回-ERESTARTSYS)
*/
        
wait_event(wq,condition) //深度睡眠

wake_up_interruptible(wait_queue_head_t *pwq)
        
wake_up(wait_queue_head_t *pwq)
    
    
/*
1. 读、写用不同的等待队列头rq、wq
2. 无数据可读、可写时调用wait_event_interruptible(rq、wq,条件)
3. 写入数据成功时唤醒rq,读出数据成功唤醒wq
*/

示例:
mychar.h

#ifndef MY_CHAR_H
#define MY_CHAR_H


#include 

#define MY_CHAR_MAGIC 'c'

#define MYCHAR_IOCTL_GET_MAXLEN _IOR(MY_CHAR_MAGIC, 1, int *)
#define MYCHAR_IOCTL_GET_CURLEN _IOR(MY_CHAR_MAGIC, 2, int *)


#endif 

mychar.c

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include "mychar.h"

#define BUF_LEN 100

int major = 11;					//主设备号
int minor = 0;					//次设备号
int char_num = 1;				//设备号数量

struct mychar_dev 
{
	struct cdev mydev;
	char mydev_buf[BUF_LEN];
	int curlen;

	wait_queue_head_t rq;
	wait_queue_head_t wq;
};
struct mychar_dev gmydev;

int mychar_open (struct inode *pnode, struct file *pfile)//打开设备
{
	pfile->private_data = (void *) (container_of(pnode->i_cdev, struct mychar_dev, mydev));
	printk("open\n");
	return 0;
}

int mychar_close(struct inode *pnode, struct file *pfile)//关闭设备
{
	printk("close\n");
	return 0;
}

ssize_t mychar_read (struct file *pfile, char __user *puser, size_t count, loff_t *p_pos) {

	struct mychar_dev *pmydev = (struct mychar_dev *)pfile->private_data;
	int size = 0;
	int ret = 0;

	/* 判断是否有数据可读 */
	if(pmydev->curlen <= 0) {

		if(pfile->f_flags & O_NONBLOCK) { //非阻塞
			printk("O_NONBLOCK Not Data Read\n");
			return -1;

		} else { //阻塞

			/* 睡眠 当curlen>0 时返回 */
			ret = wait_event_interruptible(pmydev->rq, pmydev->curlen > 0);
			if(ret) {
				return -ERESTARTSYS;
			}
		}
	}


	// 确定要读取的数据长度,如果请求大于设备当前数据长度,则读取全部可用数据
	if (count > pmydev->curlen) {
		size = pmydev->curlen;
	}
	else {
		size = count;
	}

	// 将设备数据复制到用户空间缓冲区
	ret = copy_to_user(puser, pmydev->mydev_buf, size);
	if(ret) {
		printk("copy_to_user failed\n");
		return -1;
	}

	// 移动设备内部缓冲区,去除已读取的数据
	memcpy(pmydev->mydev_buf, pmydev->mydev_buf + size, pmydev->curlen - size);

	pmydev->curlen -= size;

	wake_up_interruptible(&pmydev->wq);
	
	// 返回实际读取的字节数
	return size;
}

ssize_t mychar_write (struct file *pfile, const char __user *puser, size_t count, loff_t *p_pos) {

	struct mychar_dev *pmydev = (struct mychar_dev *)pfile->private_data;
	int size = 0;
	int ret = 0;

	if(pmydev->curlen >= BUF_LEN) {

		if(pfile->f_flags & O_NONBLOCK) { //非阻塞
			printk("O_NONBLOCK Can Not Write Data\n");
			return -1;

		} else { //阻塞
			ret = wait_event_interruptible(pmydev->wq, pmydev->curlen < BUF_LEN);
			if(ret) {
				return -ERESTARTSYS;
			}
		}
		
	}


	// 确定要写入的数据长度,如果请求大于设备缓冲区剩余空间,则写入剩余空间大小
	if (count > BUF_LEN - pmydev->curlen) {
		size = BUF_LEN - pmydev->curlen;
	}
	else {
		size = count;
	}

	// 从用户空间复制数据到设备缓冲区
	ret = copy_from_user(pmydev->mydev_buf + pmydev->curlen, puser, size);
	if(ret) {
		printk("copy_from_user failed\n");
		return -1;
	}

	// 更新设备缓冲区中的数据长度
	pmydev->curlen += size;


	/* 唤醒读阻塞 */
	wake_up_interruptible(&pmydev->rq);

	 // 返回实际写入的字节数
	return size;
}

long mychar_ioctl(struct file *pfile, unsigned int cmd, unsigned long arg) 
{
	struct mychar_dev *pmydev = (struct mychar_dev *)pfile->private_data;
	int __user *pret = (int *)arg;
	int maxlen = BUF_LEN;
	int ret = 0;

	switch(cmd) {
	case MYCHAR_IOCTL_GET_MAXLEN:
		ret = copy_to_user(pret, &maxlen, sizeof(int));
		if(ret) {
			printk("copy_from_user failed\n");
			return -1;
		}
		break;

	case MYCHAR_IOCTL_GET_CURLEN:
		ret = copy_to_user(pret, &pmydev->curlen, sizeof(int));
		if(ret) {
			printk("copy_from_user failed\n");
			return -1;
		}
		break;

	default:
		printk("The is a know\n");
		return -1;
	}
	return 0;
}

struct file_operations myops = {
	.owner = THIS_MODULE,
	.open = mychar_open,
	.read = mychar_read,
	.write = mychar_write,
	.unlocked_ioctl = mychar_ioctl,
};

int __init mychar_init(void) 
{
	int ret = 0;
	dev_t devno = MKDEV(major, minor);

	/* 手动申请设备号 */
	ret = register_chrdev_region(devno, char_num, "mychar");
	if (ret) {
		/* 动态申请设备号 */
		ret = alloc_chrdev_region(&devno, minor, char_num, "mychar");
		if(ret){
			printk("get devno failed\n");
			return -1;
		}
		/*申请成功 更新设备号*/
		major = MAJOR(devno);
	}
	
	/* 给struct cdev对象指定操作函数集 */
	cdev_init(&gmydev.mydev, &myops);

	/* 将struct cdev对象添加到内核对应的数据结构中 */
	gmydev.mydev.owner = THIS_MODULE;
	cdev_add(&gmydev.mydev, devno, char_num);

	/* 初始化 */
	init_waitqueue_head(&gmydev.rq);
	init_waitqueue_head(&gmydev.wq);


	return 0;
}

void __exit mychar_exit(void) 
{

	dev_t devno = MKDEV(major, minor);
	printk("exit %d\n", devno);
	
	/* 从内核中移除一个字符设备 */
	cdev_del(&gmydev.mydev);

	/* 回收设备号 */
	unregister_chrdev_region(devno, char_num);

}

MODULE_LICENSE("GPL");
module_init(mychar_init);
module_exit(mychar_exit);

testmychar_blockwait.c

#include 
#include 
#include 
#include 
#include 
#include 

#include "mychar.h"
int main(int argc, char *argv[])
{
	int fd = -1;
	char buf[100];

	if(argc < 2) {
		printf("The argument is too few\n");
		return -1;
	}

	fd = open(argv[1], O_RDWR);
	if(fd < 0) {
		perror("open");
		return -1;
	}

	memset(buf, 'C', sizeof(buf));
	if ( (write(fd, buf, strlen(buf)) ) < 0 ) {
		printf("write failed\n");
		return -1;
	}

	close(fd);
	fd = -1;
	return 0;
}

testmychar_blockread.c

#include 
#include 
#include 
#include 
#include 
#include 

#include "mychar.h"
int main(int argc, char *argv[])
{
	int fd = -1;
	char buf[32] = "";
	int ret = 0;

	if(argc < 2) {
		printf("The argument is too few\n");
		return -1;
	}

	fd = open(argv[1], O_RDWR);
	if(fd < 0) {
		perror("open");
		return -1;
	}

	if ( (read(fd, buf, sizeof(buf)) ) < 0 ) {
		printf("read failed\n");
		return -1;
	} else {
		printf("buf = %s\n", buf);
	}

	close(fd);
	fd = -1;
	return 0;
}

运行结果:
IO模型:阻塞和非阻塞_第6张图片
IO模型:阻塞和非阻塞_第7张图片

你可能感兴趣的:(驱动开发,linux)