首先看一下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; wait爲poll_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);
}
執行一些移除等待對列項,和釋放內存的操作。