poll系統調用源碼簡要分析

 首先看一下select的大致執行過程:

用戶空間select --->內核空間sys_select--->core_sys_select---->do_select---->驅動程序poll

最主要的地方在do_select函數中,該函數中的一個大的調用關係如下:

int do_select(int n, fd_set_bits *fds, s64 *timeout)

{

      struct poll_wqueues table;

       poll_table *wait;

      int retval, i;

      ......

      poll_initwait(&table);

      wait = &table.pt;

     ......

     retval = 0;

       for (;;)

      {

......

             set_current_state(TASK_INTERRUPTIBLE);

             ......

             for (i = 0; i < n; ++rinp, ++routp, ++rexp)

            {

                      ......

                 const struct file_operations *f_op = NULL;

                 struct file *file = NULL;

                 ......

                 for (j = 0; j < __NFDBITS; ++j, ++i, bit <<= 1)

               {

                             ......

                     mask = (*f_op->poll)(file, retval ? NULL : wait);

                       ......

               }

            ......

       }

      if( ......)

      {

           ......

          break;

       }

__timeout = schedule_timeout(__timeout);

   }

    __set_current_state(TASK_RUNNING);

    poll_freewait(&table);

     return retval;

}

省略號爲刪除的一些代碼。

對上面的執行過程做一個分析:

table poll_wqueues結構體。

struct poll_wqueues

{

poll_table pt;

struct poll_table_page * table;

int error;

int inline_index;

struct poll_table_entry inline_entries[N_INLINE_POLL_ENTRIES];

};

poll_table也爲一個結構體:

typedef struct poll_table_struct

{

poll_queue_proc qproc;

} poll_table;

其中poll_queue_proc爲一個函數指針:

typedef void (*poll_queue_proc)(struct file *, wait_queue_head_t *, struct poll_table_struct *);

 

do_select函數調用poll_initwait函數,__pollwait爲一個函數名,既函數指針。

void poll_initwait(struct poll_wqueues *pwq)

{

init_poll_funcptr(&pwq->pt, __pollwait);

pwq->error = 0;

pwq->table = NULL;

pwq->inline_index = 0;

}

static inline void init_poll_funcptr(poll_table *pt, poll_queue_proc qproc)

{

pt->qproc = qproc;

}

到這裏,table.pt.qproc = __pollwait

然後wait = &table.pt; waitpoll_table結構體的指針。

for( ; ; )循環中,執行set_current_state(TASK_INTERRUPTIBLE);函數將當前進程的狀態設置成TASK_INTERRUPTIBLE

for (j = 0; j < __NFDBITS; ++j, ++i, bit <<= 1)循環中執行mask = (*f_op->poll)(file, retval ? NULL : wait);驅動程序中的poll函數,該函數進一步調用 poll_wait函數。

static inline void poll_wait(struct file * filp, wait_queue_head_t * wait_address, poll_table *p)

{

if (p && wait_address)

p->qproc(filp, wait_address, p);

}

其中p->qproc(filp, wait_address, p);是爲調用__pollwait函數。

代碼如下:

static void __pollwait(struct file *filp, wait_queue_head_t *wait_address, poll_table *p)

{

struct poll_table_entry *entry = poll_get_entry(p);

if (!entry)

return;

/*poll_table_entry結構體進行一些列的賦值,初始化*/

get_file(filp);

entry->filp = filp;

entry->wait_address = wait_address;

init_waitqueue_entry(&entry->wait, current); /*初始化等待隊列項*/

add_wait_queue(wait_address, &entry->wait);/*添加至等待隊列頭*/

}

poll_table_page結構體如下:

struct poll_table_page

{

struct poll_table_page * next;

struct poll_table_entry * entry;

struct poll_table_entry entries[0];

};

static struct poll_table_entry *poll_get_entry(poll_table *_p)

{

struct poll_wqueues *p = container_of(_p, struct poll_wqueues, pt);

struct poll_table_page *table = p->table;

if (p->inline_index < N_INLINE_POLL_ENTRIES)

return p->inline_entries + p->inline_index++;

if (!table || POLL_TABLE_FULL(table))

{

struct poll_table_page *new_table;

/*分配一頁內存*/

new_table = (struct poll_table_page *) __get_free_page(GFP_KERNEL);

if (!new_table) /*如果內存沒有分配成功*/

{

p->error = -ENOMEM;

__set_current_state(TASK_RUNNING);

return NULL;

}

new_table->entry = new_table->entries;

new_table->next = table;

p->table = new_table;

table = new_table;

}

/*返回table->entry++struct poll_table_entry entries[0]所在位置的指針*/

return table->entry++;

}

poll_table_entry 結構體

struct poll_table_entry

{

struct file * filp;

wait_queue_t wait;

wait_queue_head_t * wait_address;

};

返回__pollwait然後進行一系列賦值,初始化操作。

至此也瞭解了爲什麼使用poll系統調用需要定義等待隊列頭。由於以上都是在for (i = 0; i < n; ++rinp, ++routp, ++rexp)循環中,故每個傳入的文件描述符的文件都通過__pollwait函數穿件了一個poll_table_entry結構體,該結構體內包含等待對立的成員,poll系統調用的實現是依賴與等待隊列的。

然後滿足一定的條件退出for (i = 0; i < n; ++rinp, ++routp, ++rexp)循環,執行

__timeout = schedule_timeout(__timeout); 使當前進程睡眠。

然後當有進程喚醒該等待隊列時,例如設備寫入了數據可以被讀取,則寫系統調用調用wakeup或同類函數喚醒該等待隊列。然後程序退出for( ; ; )循環。然後執行__set_current_state(TASK_RUNNING);將當前進程狀態改爲TASK_RUNNING(不知爲何還要將進程改爲TASK_RUNNING狀態,個人感覺在wakeup函數中就已經改了,畢竟在wait_event函數最後並沒有在此修改進程狀態)

最後調用poll_freewait(&table);

void poll_freewait(struct poll_wqueues *pwq)

{

struct poll_table_page * p = pwq->table;

int i;

for (i = 0; i < pwq->inline_index; i++)

free_poll_entry(pwq->inline_entries + i);

while (p)

{

struct poll_table_entry * entry;

struct poll_table_page *old;

entry = p->entry;

do

{

entry--;

free_poll_entry(entry);

} while (entry > p->entries);

old = p;

p = p->next;

free_page((unsigned long) old);

}

}

static void free_poll_entry(struct poll_table_entry *entry)

{

        remove_wait_queue(entry->wait_address, &entry->wait);

        fput(entry->filp);

}

執行一些移除等待對列項,和釋放內存的操作。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章