Linux內核poll內部實現
前言
poll機制用於實現IO多路復用。所謂IO多路復用,通俗的講,其實就是線程復用,使其能在一個線程上處理多個IO。
用戶空間
用戶通過調用用戶空間的poll函數使用該機制。
驅動部分的實現
用戶如果要在自己的驅動中實現poll機制,則必須實現:
struct file_operations中的
unsigned int (poll) (struct file , struct poll_table_struct *) 函數
該函數主要調用:
static inline void poll_wait(struct file * filp, wait_queue_head_t * wait_address, poll_table *p)
用於將一個wait_queue加入到poll_table中
然後檢查相應狀態,設置並返回給用戶
eg(LDD 3):
static unsigned int scull_p_poll(struct file *filp, poll_table *wait) { struct scull_pipe *dev = filp->private_data; unsigned int mask = 0; /* * The buffer is circular; it is considered full * if "wp" is right behind "rp" and empty if the * two are equal. */ down(&dev->sem); poll_wait(filp, &dev->inq, wait); poll_wait(filp, &dev->outq, wait); if (dev->rp != dev->wp) mask |= POLLIN | POLLRDNORM; /* readable */ if (spacefree(dev)) mask |= POLLOUT | POLLWRNORM; /* writable */ up(&dev->sem); return mask; }
內核實現分析
下面以內核版本4.10.0進行分析
在內核,實現為do_sys_poll(/fs/select.c)
int do_sys_poll(struct pollfd __user *ufds, unsigned int nfds, struct timespec64 *end_time) { .................................. poll_initwait(&table); fdcount = do_poll(head, &table, end_time); poll_freewait(&table); .................................. }
do_sys_poll 函數主要就是創建一個 struct poll_wqueues 的結構體,然後調用 do_poll。
其中 struct poll_wqueues結構體:
struct poll_wqueues {
poll_table pt;
struct poll_table_page *table;
struct task_struct *polling_task;
int triggered;
int error;
int inline_index;
struct poll_table_entry inline_entries[N_INLINE_POLL_ENTRIES];
};
do_poll(/fs/select)函數,主要是一個for循環,會將struct poll_wqueues 的 poll_table pt作為參數,傳遞給相應文件的poll函數。如果檢查到任意一個文件能使用,就返回,否則就等待,直到有可用的文件為止。
然後文件的poll函數,調用poll_wait函數
poll_wait函數,接著調用__pollwait函數.
static void __pollwait(struct file *filp, wait_queue_head_t *wait_address,
poll_table *p)
{
struct poll_wqueues *pwq = container_of(p, struct poll_wqueues, pt);
struct poll_table_entry *entry = poll_get_entry(pwq);
if (!entry)
return;
entry->filp = get_file(filp);
entry->wait_address = wait_address;
entry->key = p->_key;
init_waitqueue_func_entry(&entry->wait, pollwake);
entry->wait.private = pwq;
add_wait_queue(wait_address, &entry->wait);
}
分析,可知,獲取一個poll_table_entry,對其進行初始化,然後將當前進程加入到等待隊列中,等待事件的發生。
總結
對一個程序的分析,最重要的是對其數據結構的分析。
當用戶調用poll函數時,內核會創建一個struct poll_wqueues,然後將其中的poll_table 傳遞給文件的poll函數,然後由poll函數調用poll_wait,將當前調用進程加入等待隊列中,等待事件的發生。
當一個進程,poll多個文件描述符時,該進程會被加載進多個等待隊列中。
Linux內核poll內部實現