日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 人文社科 > 生活经验 >内容正文

生活经验

Select函数实现原理分析

發(fā)布時間:2023/11/27 生活经验 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Select函数实现原理分析 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

轉(zhuǎn)載自 http://blog.chinaunix.net/uid-20643761-id-1594860.html

select需要驅(qū)動程序的支持,驅(qū)動程序?qū)崿F(xiàn)fops內(nèi)的poll函數(shù)。select通過每個設備文件對應的poll函數(shù)提供的信息判斷當前是否有資源可用(如可讀或?qū)?,如果有的話則返回可用資源的文件描述符個數(shù),沒有的話則睡眠,等待有資源變?yōu)榭捎脮r再被喚醒繼續(xù)執(zhí)行。


下面我們分兩個過程來分析select:

1. select的睡眠過程

支持阻塞操作的設備驅(qū)動通常會實現(xiàn)一組自身的等待隊列如讀/寫等待隊列用于支持上層(用戶層)所需的BLOCK或NONBLOCK操作。當應用程序通過設備驅(qū)動訪問該設備時(默認為BLOCK操作),若該設備當前沒有數(shù)據(jù)可讀或?qū)?#xff0c;則將該用戶進程插入到該設備驅(qū)動對應的讀/寫等待隊列讓其睡眠一段時間,等到有數(shù)據(jù)可讀/寫時再將該進程喚醒。

select就是巧妙的利用等待隊列機制讓用戶進程適當在沒有資源可讀/寫時睡眠,有資源可讀/寫時喚醒。下面我們看看select睡眠的詳細過程。

select會循環(huán)遍歷它所監(jiān)測的fd_set內(nèi)的所有文件描述符對應的驅(qū)動程序的poll函數(shù)。驅(qū)動程序提供的poll函數(shù)首先會將調(diào)用select的用戶進程插入到該設備驅(qū)動對應資源的等待隊列(如讀/寫等待隊列),然后返回一個bitmask告訴select當前資源哪些可用。當select循環(huán)遍歷完所有fd_set內(nèi)指定的文件描述符對應的poll函數(shù)后,如果沒有一個資源可用(即沒有一個文件可供操作),則select讓該進程睡眠,一直等到有資源可用為止,進程被喚醒(或者timeout)繼續(xù)往下執(zhí)行。

下面分析一下代碼是如何實現(xiàn)的。
select的調(diào)用path如下:sys_select -> core_sys_select -> do_select

其中最重要的函數(shù)是do_select, 最主要的工作是在這里, 前面兩個函數(shù)主要做一些準備工作。do_select定義如下:

linux-4.0.1\fs\select.c

int do_select(int n, fd_set_bits *fds, struct timespec *end_time)
{ktime_t expire, *to = NULL;struct poll_wqueues table;poll_table *wait;int retval, i, timed_out = 0;unsigned long slack = 0;unsigned int busy_flag = net_busy_loop_on() ? POLL_BUSY_LOOP : 0;unsigned long busy_end = 0;rcu_read_lock();retval = max_select_fd(n, fds);rcu_read_unlock();if (retval < 0)return retval;n = retval;poll_initwait(&table);wait = &table.pt;if (end_time && !end_time->tv_sec && !end_time->tv_nsec) {wait->_qproc = NULL;timed_out = 1;}if (end_time && !timed_out)slack = select_estimate_accuracy(end_time);retval = 0;		//retval用于保存已經(jīng)準備好的描述符數(shù),初始為0for (;;) {unsigned long *rinp, *routp, *rexp, *inp, *outp, *exp;bool can_busy_loop = false;inp = fds->in; outp = fds->out; exp = fds->ex;rinp = fds->res_in; routp = fds->res_out; rexp = fds->res_ex;for (i = 0; i < n; ++rinp, ++routp, ++rexp) {		//遍歷每個描述符unsigned long in, out, ex, all_bits, bit = 1, mask, j;unsigned long res_in = 0, res_out = 0, res_ex = 0;in = *inp++; out = *outp++; ex = *exp++;all_bits = in | out | ex;if (all_bits == 0) {i += BITS_PER_LONG;		//如果這個字沒有待查找的描述符, 跳過這個長字(32位)continue;}for (j = 0; j < BITS_PER_LONG; ++j, ++i, bit <<= 1) {		//遍歷每個長字里的每個位struct fd f;if (i >= n)break;if (!(bit & all_bits))continue;f = fdget(i);if (f.file) {const struct file_operations *f_op;f_op = f.file->f_op;mask = DEFAULT_POLLMASK;if (f_op->poll) {wait_key_set(wait, in, out,bit, busy_flag);/* 在這里循環(huán)調(diào)用所監(jiān)測的fd_set內(nèi)的所有文件描述符對應的驅(qū)動程序的poll函數(shù) */mask = (*f_op->poll)(f.file, wait);}fdput(f);if ((mask & POLLIN_SET) && (in & bit)) {res_in |= bit;		//如果是這個描述符可讀, 將這個位置位retval++;			//返回描述符個數(shù)加1wait->_qproc = NULL;}if ((mask & POLLOUT_SET) && (out & bit)) {res_out |= bit;retval++;wait->_qproc = NULL;}if ((mask & POLLEX_SET) && (ex & bit)) {res_ex |= bit;retval++;wait->_qproc = NULL;}/* got something, stop busy polling */if (retval) {can_busy_loop = false;busy_flag = 0;/** only remember a returned* POLL_BUSY_LOOP if we asked for it*/} else if (busy_flag & mask)can_busy_loop = true;}}//返回結(jié)果if (res_in)*rinp = res_in;if (res_out)*routp = res_out;if (res_ex)*rexp = res_ex;cond_resched();}wait->_qproc = NULL;/* 到這里遍歷結(jié)束。retval保存了檢測到的可操作的文件描述符的個數(shù)。如果有文件可操作,則跳出for(;;)循環(huán),直接返回。若沒有文件可操作且timeout時間未到同時沒有收到signal,則執(zhí)行schedule_timeout睡眠。睡眠時間長短由__timeout決定,一直等到該進程被喚醒。那該進程是如何被喚醒的?被誰喚醒的呢?我們看下面的select喚醒過程*/if (retval || timed_out || signal_pending(current))break;if (table.error) {retval = table.error;break;}/* only if found POLL_BUSY_LOOP sockets && not out of time */if (can_busy_loop && !need_resched()) {if (!busy_end) {busy_end = busy_loop_end_time();continue;}if (!busy_loop_timeout(busy_end))continue;}busy_flag = 0;/** If this is the first loop and we have a timeout* given, then we convert to ktime_t and set the to* pointer to the expiry value.*/if (end_time && !to) {expire = timespec_to_ktime(*end_time);to = &expire;}if (!poll_schedule_timeout(&table, TASK_INTERRUPTIBLE,to, slack))timed_out = 1;}poll_freewait(&table);return retval;
}


2.? select的喚醒過程
前面介紹了select會循環(huán)遍歷它所監(jiān)測的fd_set內(nèi)的所有文件描述符對應的驅(qū)動程序的poll函數(shù)。驅(qū)動程序提供的poll函數(shù)首先會將調(diào)用select的用戶進程插入到該設備驅(qū)動對應資源的等待隊列(如讀/寫等待隊列),然后返回一個bitmask告訴select當前資源哪些可用。
一個典型的驅(qū)動程序poll函數(shù)實現(xiàn)如下:
(摘自《Linux Device Drivers – ThirdEdition》Page 165)
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;
}
將用戶進程插入驅(qū)動的等待隊列是通過poll_wait做的。
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在do_select內(nèi)poll_initwait(&table)被初始化為__pollwait,如下:
void poll_initwait(struct poll_wqueues *pwq)
{init_poll_funcptr(&pwq->pt, __pollwait);pwq->error = 0;pwq->table = NULL;pwq->inline_index = 0;
}

__pollwait定義如下:
/* Add a new entry */
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;get_file(filp);entry->filp = filp;entry->wait_address = wait_address;init_waitqueue_entry(&entry->wait, current);add_wait_queue(wait_address,&entry->wait);
}

通過init_waitqueue_entry初始化一個等待隊列項,這個等待隊列項關聯(lián)的進程即當前調(diào)用select的進程。然后將這個等待隊列項插入等待隊列wait_address。Wait_address即在驅(qū)動poll函數(shù)內(nèi)調(diào)用poll_wait(filp, &dev->inq,? wait);時傳入的該驅(qū)動的&dev->inq或者&dev->outq等待隊列。

注: 關于等待隊列的工作原理可以參考下面這篇文檔:
http://blog.chinaunix.net/u2/60011/showart_1334657.html

到這里我們明白了select如何當前進程插入所有所監(jiān)測的fd_set關聯(lián)的驅(qū)動內(nèi)的等待隊列,那進程究竟是何時讓出CPU進入睡眠狀態(tài)的呢?
進入睡眠狀態(tài)是在do_select內(nèi)調(diào)用schedule_timeout(__timeout)實現(xiàn)的。當select遍歷完fd_set內(nèi)的所有設備文件,發(fā)現(xiàn)沒有文件可操作時(即retval=0),則調(diào)用schedule_timeout(__timeout)進入睡眠狀態(tài)。

喚醒該進程的過程通常是在所監(jiān)測文件的設備驅(qū)動內(nèi)實現(xiàn)的,驅(qū)動程序維護了針對自身資源讀寫的等待隊列。當設備驅(qū)動發(fā)現(xiàn)自身資源變?yōu)榭勺x寫并且有進程睡眠在該資源的等待隊列上時,就會喚醒這個資源等待隊列上的進程。

舉個例子,比如內(nèi)核的8250 uart driver:
Uart是使用的Tty層維護的兩個等待隊列, 分別對應于讀和寫: (uart是tty設備的一種)
struct tty_struct {
???????? ……
???????? wait_queue_head_t write_wait;
???????? wait_queue_head_t read_wait;
???????? ……
}
當uart設備接收到數(shù)據(jù),會調(diào)用tty_flip_buffer_push(tty);將收到的數(shù)據(jù)push到tty層的buffer。
然后查看是否有進程睡眠的讀等待隊列上,如果有則喚醒該等待會列。
過程如下:
serial8250_interrupt -> serial8250_handle_port -> receive_chars -> tty_flip_buffer_push ->
flush_to_ldisc -> disc->receive_buf
在disc->receive_buf函數(shù)內(nèi):
if (waitqueue_active(&tty->read_wait)) //若有進程阻塞在read_wait上則喚醒
wake_up_interruptible(&tty->read_wait);

到這里明白了select進程被喚醒的過程。由于該進程是阻塞在所有監(jiān)測的文件對應的設備等待隊列上的,因此在timeout時間內(nèi),只要任意個設備變?yōu)榭刹僮?#xff0c;都會立即喚醒該進程,從而繼續(xù)往下執(zhí)行。這就實現(xiàn)了select的當有一個文件描述符可操作時就立即喚醒執(zhí)行的基本原理。

Referece:
1.?????? Linux Device Drivers – ThirdEdition
2.?????? 內(nèi)核等待隊列機制原理分析
http://blog.chinaunix.net/u2/60011/showart_1334657.html
3.?????? Kernel code : Linux 2.6.18_pro500 - Montavista

總結(jié)

以上是生活随笔為你收集整理的Select函数实现原理分析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 黄色网址在线视频 | 91色多多| 美日韩一区二区三区 | 高潮久久久| 老妇荒淫牲艳史 | 免费在线国产 | 人人舔人人 | 91蜜桃在线| 中文字幕在线观看第二页 | 偷拍一区二区三区 | 国产成人看片 | 屁屁影院一区二区三区 | 精品少妇无码av无码专区 | 亚洲精品黄色 | av在线播放不卡 | 久久久久国产精品视频 | 狠狠操免费视频 | 91精品国产综合久久久久久 | 中国女人一级片 | 国产日韩欧美一二三区 | 欧美顶级metart裸体全部自慰 | 超碰91在线观看 | 日韩美女视频网站 | www.四虎.| 国产成人激情 | 男女午夜影院 | 我想看一级黄色片 | 久久免费小视频 | 亚洲成人精 | 18久久 | 无罩大乳的熟妇正在播放 | 亚洲av无一区二区三区 | 久久国产小视频 | 99这里有精品视频 | av日韩在线免费观看 | av黄色大片| 精品自拍偷拍 | 蜜桃av中文字幕 | 亚洲国产aⅴ精品一区二区的游戏 | 免费黄色链接 | 亚洲不卡免费视频 | 精品国产一区二区三区av性色 | 可以看毛片的网站 | 福利精品在线 | 91草视频| 精人妻无码一区二区三区 | 六月丁香综合网 | 三级视频黄色 | 美女又爽又黄 | 少妇天天干| 黄色永久网站 | 91九色在线观看 | 亚洲成人av影片 | 欧洲在线观看 | 狠狠人妻久久久久久 | 天天干天天操天天插 | av88av| 青青青视频免费观看 | 激情欧美一区 | 欧美激精品 | 亚洲电影中文字幕 | 夫妻性生活黄色大片 | 免费荫蒂添的好舒服视频 | 日本少妇全体裸体洗澡 | 在线免费观看日本 | 顶级黑人搡bbw搡bbbb搡 | 在线观看91av | 国产激情四射 | 熟女少妇a性色生活片毛片 亚洲伊人成人网 | www亚洲精品| 真人抽搐一进一出视频 | 久久久久久久伊人 | 亚洲经典视频在线观看 | 69视频免费看 | 国产113页 | 看一级黄色大片 | 小优视频污 | 亚洲熟女乱色一区二区三区 | 亚洲永久免费观看 | 日本a天堂 | 丰满熟妇人妻中文字幕 | 婷婷国产视频 | 欧美射图| 少妇视频一区二区三区 | 91美女视频| 在线伊人网 | 黑人巨大精品欧美黑寡妇 | 欧美一区二区三区激情视频 | 亚洲a在线播放 | 一级激情视频 | 成人污污视频在线观看 | 亚洲精品91在线 | 成人精品视频99在线观看免费 | 伊人久久精品一区二区三区 | 日韩福利一区二区 | 一区三区视频在线观看 | 男人的天堂欧美 | 欧美在线一区二区 | 四虎黄网|