Linux 阻塞和非阻塞IO

Linux 阻塞和非阻塞IO

阻塞IO

当应用程序对设备驱动进行操作的时候,如果不能获取到设备资源,那么阻塞式IO就会将应用程序对应的线程挂起,直到设备资源可以获取为止。
应用程序调用read函数从设备中读取数据,当设备不可用或数据未准备好的时候就会进入到休眠态。等设备可用的时候就会从休眠态唤醒,然后从设备中读取数据返回给应用程序。

int fd; 
int data = 0; 

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

非阻塞IO

对于非阻塞IO,应用程序对应的线程不会挂起,它会一直轮询等待,直到设备资源可以使用,要么就直接放弃。
应用程序使用非阻塞访问方式从设备读取数据,当设备不可用或数据未准备好的时候会立即向内核返回一个错误码,表示数据读取失败。应用程序会再次读取数据,这样一直往复循环,直到数据读取成功。

int fd; 
int data = 0; 

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

阻塞IO和等待队列

阻塞访问最大的好处就是当设备文件不可操作的时候进程可以进入休眠态,这样可以将CPU资源让出来。但是,当设备文件可以操作的时候必须唤醒进程,一般在中断函数里面完成唤醒工作。
Linux内核提供了等待队列(wait queue)来实现阻塞进程的唤醒工作,如果我们要在驱动中使用等待队列,必须创建并初始化一个等待队列头(wait_queue_head_t)。
等待队列头:等待队列的头部
等待队列项:每个访问设备的进程都是一个队列项
//初始化等待队列头
void init_waitqueue_head(wait_queue_head_t *q)
//初始化一个等待队列项 
DECLARE_WAITQUEUE(name, tsk)
//将队列项添加到等待队列头 
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)
//等待事件
wait_event(wq, condition)
wait_event_timeout(wq, condition, timeout)
wait_event_interruptible(wq, condition)
wait_event_interruptible_timeout(wq, condition, timeout)

非阻塞IO和poll机制

poll,epoll,select可以用于处理轮询。

//select函数
int select(int nfds, 
	fd_set *readfds, 
	fd_set *writefds,
	fd_set *exceptfds, 
	struct timeval *timeout)
//poll函数
int poll(struct pollfd *fds, 
	nfds_t nfds, 
	int timeout)
//epoll函数
int epoll_create(int size)

int epoll_ctl(int epfd, 
	int op, 
	int fd,
	struct epoll_event *event)
	 
struct epoll_event {
	uint32_t events; /* epoll 事件 */
	epoll_data_t data; /* 用户数据 */
};

int epoll_wait(int epfd, 
	struct epoll_event *events,
	int maxevents, 
	int timeout)

你可能感兴趣的:(Linux驱动,linux,运维,服务器)