標簽:無效 輪詢 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