1. 程式人生 > >Linux內核poll內部實現

Linux內核poll內部實現

ask 結構 trie ide 驅動 初始 return code 自己

前言

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內部實現