栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 系统运维 > 运维 > Linux

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

Linux 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

阻塞和非阻塞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内提供了等待队列来实现阻塞进程的唤醒工作。

struct __wait_queue_head {
spinlock_t lock;
struct list_head task_list;
};
typedef struct __wait_queue_head wait_queue_head_t;


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;


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)


void wake_up(wait_queue_head_t *q)
void wake_up_interruptible(wait_queue_head_t *q)


//等待以 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 timeout; 
	
	fd = open("dev_xxx", O_RDWR | O_NONBLOCK); 
 
	
	FD_ZERO(&readfds); 
	FD_SET(fd, &readfds); 
 
	
	timeout.tv_sec = 0;
	timeout.tv_usec = 500000; 
 
 	 
	ret = select(fd + 1, &readfds, NULL, NULL, &timeout);
	switch (ret) {
	case 0: 
		printf("timeout!rn");
		break;
	case -1: 
		printf("error!rn");
		break;
		default: 
	if(FD_ISSET(fd, &readfds)) { 
		
	}
	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); 
	if (ret) { 
	......
	
	......
	} else if (ret == 0) { 
	......
	} else if (ret < 0) { 
	......
	}
}
2.2.3 epool

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

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/730265.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号