标签:无效 轮询 nta 查看 阻塞 网络 删除 链接 containe
本章内容为驱动基石之一。
驱动只提供功能,不提供策略。
阻塞与非阻塞 都是应用程序主动访问的。从应用角度去解读阻塞与非阻塞。
原文:https://www.cnblogs.com/lizhuming/p/14912496.html
阻塞:
非阻塞:
实现阻塞的常用技能包括:(目的其实就是阻塞)
若需要实现阻塞式访问,可以使用休眠与唤醒机制。
相关函数其实在 等待队列 小节有说明了,现在只是函数汇总。
内核源码路径:include\linux\wait.h。
函数名 | 描述 |
---|---|
wait_event(wq, condition) | 休眠,直至 condition 为真;休眠期间不能被打断。 |
wait_event_interruptible(wq, condition) | 休眠,直至 condition 为真;休眠期间可被打断,包括信号。 |
wait_event_timeout(wq, condition, timeout) | 休眠,直至 condition 为真或超时;休眠期间不能被打断。 |
wait_event_interruptible_timeout(wq, condition, timeout) | 休眠,直至 condition 为真或超时;休眠期间可被打断,包括信号。 |
内核源码路径:include\linux\wait.h。
函数名 | 描述 |
---|---|
wake_up_interruptible(x) | 唤醒 x 队列中状态为“TASK_INTERRUPTIBLE”的线程,只唤醒其中的一个线程 |
wake_up_interruptible_nr(x, nr) | 唤醒 x 队列中状态为“TASK_INTERRUPTIBLE”的线程,只唤醒其中的 nr 个线程 |
wake_up_interruptible_all(x) | 唤醒 x 队列中状态为“TASK_INTERRUPTIBLE”的线程,唤醒其中的所有线程 |
wake_up(x) | 唤醒 x 队列中状态为“TASK_INTERRUPTIBLE”或“TASK_UNINTERRUPTIBLE”的线程,只唤醒其中的一个线程 |
wake_up_nr(x, nr) | 唤醒 x 队列中状态为“TASK_INTERRUPTIBLE”或“TASK_UNINTERRUPTIBLE”的线程,只唤醒其中 nr 个线程 |
wake_up_all(x) | 唤醒 x 队列中状态为“TASK_INTERRUPTIBLE”或“TASK_UNINTERRUPTIBLE”的线程,唤醒其中的所有线程 |
等待队列:
使用方法:
另外一种使用方法就是 在等待队列上睡眠。
等待队列头部结构体:
struct wait_queue_head {
spinlock_t lock;
struct list_head head;
};
typedef struct wait_queue_head wait_queue_head_t;
等待队列元素结构体:
struct wait_queue_entry {
unsigned int flags;
void *private;
wait_queue_func_t func;
struct list_head entry;
};
定义等待队列头部方法:wait_queue_head_t my_queue;
初始化等待队列头部源码:void init_waitqueue_head(wait_queue_head_t *q);
或
定义&初始化等待队列头部:使用宏 DECLARE_WAIT_QUEUE_HEAD。
定义等待队列元素源码:#define DECLARE_WAITQUEUE(name, tsk);
添加等待队列元素源码:void add_wait_queue(struct wait_queue_head *wq_head, struct wait_queue_entry *wq_entry);
移除等待队列元素源码:void add_wait_queue(struct wait_queue_head *wq_head, struct wait_queue_entry *wq_entry);
睡眠,直至事件发生:wait_event(wq_head, condition)
/**
* wait_event - sleep until a condition gets true
* @wq_head: the waitqueue to wait on
* @condition: a C expression for the event to wait for
*
* The process is put to sleep (TASK_UNINTERRUPTIBLE) until the
* @condition evaluates to true. The @condition is checked each time
* the waitqueue @wq_head is woken up.
*
* wake_up() has to be called after changing any variable that could
* change the result of the wait condition.
*/
#define wait_event(wq_head, condition) do { might_sleep(); if (condition) break; __wait_event(wq_head, condition); } while (0)
睡眠,直至事件发生或超时:wait_event_timeout(wq_head, condition, timeout)
等待事件发生,且可被信号中断唤醒:wait_event_interruptible(wq_head, condition)
等待事件发生或超时,且可被信号中断唤醒:wait_event_interruptible_timeout(wq_head, condition, timeout)
io_wait_event():
/*
* io_wait_event() -- like wait_event() but with io_schedule()
*/
#define io_wait_event(wq_head, condition) do { might_sleep(); if (condition) break; __io_wait_event(wq_head, condition); } while (0)
以下两个函数对应等待事件使用:
void wake_up(wait_queue_head_t *queue);
void wake_up_interruptible(wait_queue_head_t *queue);
函数源码:
sleep_on(wait_queue_head_t *q)
interruptible_sleep_on(wait_queue_head_t *q)
当用户应用程序以非阻塞的方式访问设备,设备驱动程序就要提供非阻塞的处理方式。
poll、epoll 和 select 可以用于处理轮询。这三个 API 均在 应用层 使用。
注意,轮询也是在APP实现轮询的。
select():
int select(int numfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
struct timeval{
long tv_sec; // 秒
long tv_usec; // 微妙
};
FD_CLR(int fd, fd_set *set); // 把 fd 对应的 set bit 清空。
FD_ISSET(int fd, fd_set *set); // 查看 d 对应的 set bit 是否被置 **1**。
FD_SET(int fd, fd_set *set); // 把 fd 对应的 set bit 置 **1**。
FD_ZERO(fd_set *set); // 把 set 全部清空。
fd_set 是有限制的,可以查看源码,修改也可。但是改大会影响系统效率。
由于 fd_set 是有限制的,所以当需要监测大量文件时,便不可用。
这时候,poll() 函数就应运而生。
poll() 和 select() 没什么区别,只是前者没有最大文件描述符限制。
int poll(struct pollfd *fds, nfds_t nfds, int timeout)
被监视的文件描述符格式:
struct pollfd{
int fd; /* 文件描述符 */
short events; /* 请求的事件 */
short revents; /* 返回的时间 */
}
可请求的事件 events:
宏 | 说明 |
---|---|
POLLIN | 有数据可读 |
POLLPRI | 有紧急的数据需要读取 |
POLLOUT | 可以写数据 |
POLLERR | 指定的文件描述符发生错误 |
POLLHUP | 指定的文件描述符被挂起 |
POLLNVAL | 无效的请求 |
POLLRDNORM | 等同于 POLLIN |
select() 和 poll() 会随着监测的 fd 数量增加,而出现效率低下的问题。
poll() 每次监测都需要历遍所有被监测的描述符。
epoll() 函数就是为大量并大而生的。在网络编程中比较常见。
epoll() 使用方法:
int epoll_creat(int size);
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
。int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
。epoll_event 结构体:
struct epoll_event{
uint32_t events; /* epoll 事件 */
epoll_data_t data; /* 用户数据 */
}
可请求的事件 events:
宏 | 说明 |
---|---|
EPOLLIN | 有数据可读 |
EPOLLPRI | 有紧急的数据需要读取 |
EPOLLOUT | 可以写数据 |
EPOLLERR | 指定的文件描述符发生错误 |
EPOLLHUP | 指定的文件描述符被挂起 |
EPOLLET | 设置 epoll 为边沿触发,默认触发模式为水平触发 |
EPOLLONESHOT | 一次性的监视,当监视完成后,还需要监视某个 fd,那就需要把 fd 重新添加到 epoll 中 |
当应用程序调用 select() 函数和 poll() 函数时,驱动程序会调用 file_operations 中的 poll。
unsigned int(*poll)(struct file *filp, struct poll_table_struct *wait)
poll_wait()
void poll_wait(struct file *filp, wait_queue_head_t *wait_address, poll_table *p)
标签:无效 轮询 nta 查看 阻塞 网络 删除 链接 containe
原文地址:https://www.cnblogs.com/lizhuming/p/14912496.html