阻塞和非阻塞IO(linux驱动开发篇)

1.简介

1.1 阻塞IO

阻塞IO就会将应用程序对应的线程挂起,直到设备资源可以才做为止。
(我在这里一直等你用完,在这个过程中我cpu没有精力去干其它的事情)
在这里插入图片描述
应用程序调用 read 函数从设备中读取数据,当设备不可用或数据未准备好的
时候就会进入到休眠态。等设备可用的时候就会从休眠态唤醒,然后从设备中读取数据返回给应用程序。

1.2 非阻塞IO

非阻塞IO,应用程序对应的线程不会挂起,它要么一直轮询等待,直到设备资源可以使用,要么就直接放弃。
(我隔一段时间来看看你有没有用完,在这个过程中我可以cpu可以去忙其它的事情)
在这里插入图片描述
应用程序使用非阻塞访问方式从设备读取数据,当设备不可用或数据未准备好的时候会立即向内核返回一个错误码,表示数据读取失败。应用程序会再次重新读取数据,这样一直往复循环,直到数据读取成功。

/*阻塞访问*/
int fd; 
int data = 0; 
fd = open("/dev/xxx_dev", O_RDWR); /* 阻塞方式打开 */
ret = read(fd, &data, sizeof(data)); /* 读取数据 */


/*非阻塞访问*/
int fd; 
int data = 0;
fd = open("/dev/xxx_dev", O_RDWR | O_NONBLOCK); /* 非阻塞方式打开 */
ret = read(fd, &data, sizeof(data)); /* 读取数据 */

2. 举列

2. 1 阻塞IO举例(等待队列)

阻塞访问的最大好处是当设备文件不可操作的时候进程可以进入休眠态,这样可以将CPU资源让出来。但是,当设备文件可以操作的时候就必须唤醒进程,一般子啊中断函数里面完成唤醒工作。Linux内提供了等待队列来实现阻塞进程的唤醒工作。

/*1.等待队列头*/
struct __wait_queue_head {
spinlock_t lock;
struct list_head task_list;
};
typedef struct __wait_queue_head wait_queue_head_t;

/*2、等待队列项*/
struct __wait_queue {
unsigned int flags;
void *private;
wait_queue_func_t func;
struct list_head task_list;
};
typedef struct __wait_queue wait_queue_t;

/*3、将队列项添加/移除等待队列头*/
void add_wait_queue(wait_queue_head_t *q, wait_queue_t *wait)

void remove_wait_queue(wait_queue_head_t *q, wait_queue_t *wait)

/*4、等待唤醒*/
void wake_up(wait_queue_head_t *q)
void wake_up_interruptible(wait_queue_head_t *q)

/*5、等待事件*/
//等待以 wq 为等待队列头的等待队列被唤醒,前提是 condition 条件必须满足(为真),否则一直阻塞 。设置为TASK_UNINTERRUPTIBLE 状态
wait_event(wq, condition)

//功能和 wait_event 类似,但是此函数可以添加超时时间,以 jiffies 为单位。此函数有返回值,如果返回 0 的话表示超时时间到,而且 condition为假。为 1 的话表示 condition 为真,也就是条件满足了。
wait_event_timeout(wq, condition, timeout)

//与 wait_event 函数类似,但是此函数将进程设置为 TASK_INTERRUPTIBLE,就是可以被信号打断。
wait_event_interruptible(wq, condition)

//与 wait_event_timeout 函数类似,此函数也将进程设置为 TASK_INTERRUPTIBLE,可以被信号打断。
wait_event_interruptible_timeout(wq,condition, timeout)

2.2 非阻塞IO举例子(轮询)

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

2.2.1 select

void main(void){ 
	int ret, fd; /* 要监视的文件描述符 */
	fd_set readfds; /* 读操作文件描述符集 */
	/*	struct timeval {
		long tv_sec; //秒
		long tv_usec; //微妙 
	};*/
	struct timeval timeout; /* 超时结构体 */
	
	fd = open("dev_xxx", O_RDWR | O_NONBLOCK); /* 非阻塞式访问 */
 
	/*
	void FD_ZERO(fd_set *set)		//FD_ZERO 用于将 fd_set 变量的所有位都清零,
	void FD_SET(int fd, fd_set *set)//FD_SET 用于将 fd_set 变量的某个位置 1,也就是向 fd_set 添加一个文件描述符,参数 fd 就是要加入的文件描述符
	void FD_CLR(int fd, fd_set *set)//FD_CLR 用户将 fd_set变量的某个位清零,也就是将一个文件描述符从 fd_set 中删除,参数 fd 就是要删除的文件描述符号
	int FD_ISSET(int fd, fd_set *set)//FD_ISSET 用于测试 fd_set 的某个位是否置 1,就		 是判断某个文件是否可以进行操作,参数 fd 就是要判断的文件描述符。
	*/
	FD_ZERO(&readfds); /* 清除 readfds */
	FD_SET(fd, &readfds); /* 将 fd 添加到 readfds 里面 */
 
	/* 构造超时时间 */
	timeout.tv_sec = 0;
	timeout.tv_usec = 500000; /* 500ms */
 
 	/*	@	nfds:要操作的文件描述符个数
 		@  	readfds 用于监视指定描述符集的读变化,也就是监视这些文件是否可以读取
 		@	writefs 用于监视这些文件是否可以进行写操作
 		@	exceptfds 用于监视这些文件的异常*/ 
	ret = select(fd + 1, &readfds, NULL, NULL, &timeout);
	switch (ret) {
	case 0: /* 超时 */
		printf("timeout!\r\n");
		break;
	case -1: /* 错误 */
		printf("error!\r\n");
		break;
		default: /* 可以读取数据 */
	if(FD_ISSET(fd, &readfds)) { /* 判断是否为 fd 文件描述符 */
		/* 使用 read 函数读取数据 */
	}
	break;
	} 
}

2.2.2 pool

  • 在单个线程中,select 函数能够监视的文件描述符数量有最大的限制,一般为 1024,可以修改内核将监视的文件描述符数量改大,但是这样会降低效率!这个时候就可以使用 poll 函数,poll 函数本质上和 select 没有太大的差别,但是 poll 函数没有最大文件描述符限制。
void main(void){ 
	int ret; 
	int fd; /* 要监视的文件描述符 */
	struct pollfd fds; 

	fd = open(filename, O_RDWR | O_NONBLOCK); /* 非阻塞式访问 */
 
	/* 构造结构体 */
	fds.fd = fd;
	fds.events = POLLIN; /* 监视数据是否可以读取 */

	ret = poll(&fds, 1, 500); /* 轮询文件是否可操作,超时 500ms */
	if (ret) { /* 数据有效 */
	......
	/* 读取数据 */
	......
	} else if (ret == 0) { /* 超时 */
	......
	} else if (ret < 0) { /* 错误 */
	......
	}
}

2.2.3 epool

  • 传统的 selcet 和 poll 函数都会随着所监听的 fd 数量的增加,出现效率低下的问题,且epoll 函数每次必须遍历所有的描述符来检查就绪的描述符,这个过程很浪费时间。为此epoll因运而生,epoll 就是为处理大并发而准备的,一般常常在网络编程中使用 epoll 函数。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

栋哥爱做饭

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

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

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

打赏作者

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

抵扣说明:

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

余额充值