極客時(shí)間的GO語(yǔ)言進(jìn)階訓(xùn)練營(yíng)是很不錯(cuò),知識(shí)內(nèi)容涉及比較全面,從編程語(yǔ)言到中間件、系統(tǒng)設(shè)計(jì)再到架構(gòu)都安排了相關(guān)課程,老師們?cè)谡n程中不講語(yǔ)法和用法,重點(diǎn)傳遞設(shè)計(jì)原理和最佳實(shí)踐,講課的過(guò)程中貼合工作場(chǎng)景,分享真實(shí)的干貨案例,啟發(fā)學(xué)員的思維讓其自主進(jìn)行學(xué)習(xí),還幫學(xué)員建立系統(tǒng)大局觀,有助于學(xué)員深層次的提升。
成都創(chuàng)新互聯(lián)公司專(zhuān)業(yè)為企業(yè)提供吳江網(wǎng)站建設(shè)、吳江做網(wǎng)站、吳江網(wǎng)站設(shè)計(jì)、吳江網(wǎng)站制作等企業(yè)網(wǎng)站建設(shè)、網(wǎng)頁(yè)設(shè)計(jì)與制作、吳江企業(yè)網(wǎng)站模板建站服務(wù),10多年吳江做網(wǎng)站經(jīng)驗(yàn),不只是建網(wǎng)站,更提供有價(jià)值的思路和整體網(wǎng)絡(luò)服務(wù)。
會(huì)的。
1.字節(jié)跳動(dòng)并不會(huì)特別關(guān)心候選人使用什么編程語(yǔ)言,邏輯很簡(jiǎn)單,你Java特別厲害,那轉(zhuǎn)Go語(yǔ)言肯定不難。當(dāng)然,如果你覺(jué)得難,那大概率也通不過(guò)后面的面試。
2.在整個(gè)的面試流程中,至少會(huì)有3輪技術(shù)面,并且每一輪面試都會(huì)考算法。不管你是工程師,還是架構(gòu)師。
3.為什么要考這么多算法?其實(shí)核心是看候選人是不是足夠聰明。和Netflix一樣,字節(jié)跳動(dòng)招聘工程師的必要條件就是聰明。
4.怎么考算法呢?一般會(huì)分兩步,第一步是直接讓你說(shuō)思路,第二步是讓你直接上手寫(xiě)代碼。字節(jié)跳動(dòng)的算法題一般對(duì)應(yīng)的是LeetCode中級(jí)模式,要通過(guò)面試,你肯定得花時(shí)間好好準(zhǔn)備。
5.寫(xiě)算法代碼的時(shí)候,你可以用白板,也可以用電腦,都行。常見(jiàn)的模式是給你20分鐘時(shí)間,讓你寫(xiě)出來(lái)某道題的解法。當(dāng)然,肯定是越快做出來(lái)越好,這能說(shuō)明你的熟練程度。
為什么要學(xué)習(xí)GO語(yǔ)言,GO的優(yōu)勢(shì)是什么?
1、 Go有什么優(yōu)勢(shì)
Go的優(yōu)勢(shì)
1:性能
2:語(yǔ)言性能很重要
3:開(kāi)發(fā)者效率不要過(guò)于創(chuàng)新
4:并發(fā)性通道
5:快速的編譯時(shí)間
6:打造團(tuán)隊(duì)的能力
7:強(qiáng)大的生態(tài)系統(tǒng)
8:GOFMT,強(qiáng)制代碼格式
9:gRPC 和 Protocol Buffers
可直接編譯成機(jī)器碼,不依賴其他庫(kù),glibc的版本有一定要求,部署就是扔一個(gè)文件上去就完成了。
靜態(tài)類(lèi)型語(yǔ)言,但是有動(dòng)態(tài)語(yǔ)言的感覺(jué),靜態(tài)類(lèi)型的語(yǔ)言就是可以在編譯的時(shí)候檢查出來(lái)隱藏的大多數(shù)問(wèn)題,動(dòng)態(tài)語(yǔ)言的感覺(jué)就是有很多的包可以使用,寫(xiě)起來(lái)的效率很高。
Go 是一個(gè)開(kāi)源的編程語(yǔ)言,它能讓構(gòu)造簡(jiǎn)單、可靠且高效的軟件變得容易。想學(xué)習(xí)這門(mén)編程語(yǔ)言,首先要找到一份不錯(cuò)的教程,兄弟連go語(yǔ)言+區(qū)塊鏈培訓(xùn)最近新出了一套go語(yǔ)言的教程,老師講的非常不錯(cuò)!
伴隨著“區(qū)塊鏈”概念在全球范圍內(nèi)的熱議,金融、物流、征信、制造、零售等日常生活場(chǎng)景中也悄然加入了相關(guān)區(qū)塊鏈技術(shù)應(yīng)用。有專(zhuān)家表明,未來(lái)區(qū)塊鏈將與人們的生活息息相關(guān),區(qū)塊鏈技術(shù)與大眾日常生活融合是大勢(shì)所趨。
區(qū)塊鏈?zhǔn)袌?chǎng)的火熱引發(fā)了大量以區(qū)塊鏈技術(shù)型人員為基礎(chǔ)的人才性需求,區(qū)塊鏈人才受熱捧程度呈光速上升。據(jù)拉勾網(wǎng)發(fā)布的“2018年區(qū)塊鏈高薪清單”顯示,騰訊、小米、蘇寧、京東等國(guó)內(nèi)企業(yè)巨頭發(fā)布了眾多高薪區(qū)塊鏈崗需求,力圖探索區(qū)塊鏈相關(guān)技術(shù)與應(yīng)用。清單中同時(shí)指出,高薪崗位以區(qū)塊鏈相關(guān)技術(shù)型崗位需求為主,其中蘇寧和科達(dá)月薪最高已給到100k。
極大的技術(shù)型人才市場(chǎng)需求,必然會(huì)帶動(dòng)整個(gè)區(qū)塊鏈培訓(xùn)市場(chǎng)的爆發(fā)式涌現(xiàn)與增長(zhǎng)。培訓(xùn)模式大都可分為線上培訓(xùn)、傳統(tǒng)IT機(jī)構(gòu)培訓(xùn)及主打高端形式的線下短期訓(xùn)練營(yíng)等幾種形式,但市場(chǎng)火爆演進(jìn)過(guò)程中也充斥著種種區(qū)塊鏈培訓(xùn)亂象:講師資質(zhì)注水化、甚至是最基本的姓名都不敢公開(kāi),課程大綱不透明、授課質(zhì)量縮水化,課時(shí)安排不合理及培訓(xùn)收費(fèi)標(biāo)準(zhǔn)參差不齊等等。
在整個(gè)區(qū)塊鏈培訓(xùn)市場(chǎng)規(guī)?;l(fā)展之下,兄弟連教育攜手資深區(qū)塊鏈專(zhuān)家尹成及其清華水木未名團(tuán)隊(duì)成立區(qū)塊鏈學(xué)院,利用其專(zhuān)業(yè)強(qiáng)大的技術(shù)講師團(tuán)隊(duì)、細(xì)致全面的課程體系及海量真實(shí)性企業(yè)區(qū)塊鏈項(xiàng)目實(shí)戰(zhàn),旨在深耕區(qū)塊鏈教培領(lǐng)域,并為企業(yè)為社會(huì)培養(yǎng)更多專(zhuān)業(yè)型技術(shù)人才。
尹成 資深區(qū)塊鏈技術(shù)專(zhuān)家 兄弟連區(qū)塊鏈學(xué)院院長(zhǎng)畢業(yè)于清華大學(xué),曾擔(dān)任Google算法工程師,微軟區(qū)塊鏈領(lǐng)域全球最具價(jià)值專(zhuān)家,微軟Tech.Ed 大會(huì)金牌講師。精通C/C++、Python、Go語(yǔ)言、Sicikit-Learn與TensorFlow。擁有15年編程經(jīng)驗(yàn)與5年的教學(xué)經(jīng)驗(yàn),資深軟件架構(gòu)師,Intel軟件技術(shù)專(zhuān)家,著名技術(shù)專(zhuān)家,具備多年的世界頂尖IT公司微軟谷歌的工作經(jīng)驗(yàn)。具備多年的軟件編程經(jīng)驗(yàn)與講師授課經(jīng)歷, 并在人機(jī)交互、教育、信息安全、廣告、區(qū)塊鏈系統(tǒng)開(kāi)發(fā)諸多產(chǎn)品。具備深厚的項(xiàng)目管理經(jīng)驗(yàn)以及研發(fā)經(jīng)驗(yàn), 擁有兩項(xiàng)人工智能發(fā)明專(zhuān)利,與開(kāi)發(fā)電子貨幣部署到微軟Windows Azure的實(shí)戰(zhàn)經(jīng)驗(yàn)。教學(xué)講解深入淺出,使學(xué)員能夠做到學(xué)以致用。
epoll是linux中IO多路復(fù)用的一種機(jī)制,I/O多路復(fù)用就是通過(guò)一種機(jī)制,一個(gè)進(jìn)程可以監(jiān)視多個(gè)描述符,一旦某個(gè)描述符就緒(一般是讀就緒或者寫(xiě)就緒),能夠通知程序進(jìn)行相應(yīng)的讀寫(xiě)操作。當(dāng)然linux中IO多路復(fù)用不僅僅是epoll,其他多路復(fù)用機(jī)制還有select、poll,但是接下來(lái)介紹epoll的內(nèi)核實(shí)現(xiàn)。
events可以是以下幾個(gè)宏的集合:
epoll相比select/poll的優(yōu)勢(shì) :
epoll相關(guān)的內(nèi)核代碼在fs/eventpoll.c文件中,下面分別分析epoll_create、epoll_ctl和epoll_wait三個(gè)函數(shù)在內(nèi)核中的實(shí)現(xiàn),分析所用linux內(nèi)核源碼為4.1.2版本。
epoll_create用于創(chuàng)建一個(gè)epoll的句柄,其在內(nèi)核的系統(tǒng)實(shí)現(xiàn)如下:
sys_epoll_create:
可見(jiàn),我們?cè)谡{(diào)用epoll_create時(shí),傳入的size參數(shù),僅僅是用來(lái)判斷是否小于等于0,之后再也沒(méi)有其他用處。
整個(gè)函數(shù)就3行代碼,真正的工作還是放在sys_epoll_create1函數(shù)中。
sys_epoll_create - sys_epoll_create1:
sys_epoll_create1 函數(shù)流程如下:
sys_epoll_create - sys_epoll_create1 - ep_alloc:
sys_epoll_create - sys_epoll_create1 - ep_alloc - get_unused_fd_flags:
linux內(nèi)核中,current是個(gè)宏,返回的是一個(gè)task_struct結(jié)構(gòu)(我們稱之為進(jìn)程描述符)的變量,表示的是當(dāng)前進(jìn)程,進(jìn)程打開(kāi)的文件資源保存在進(jìn)程描述符的files成員里面,所以current-files返回的當(dāng)前進(jìn)程打開(kāi)的文件資源。rlimit(RLIMIT_NOFILE) 函數(shù)獲取的是當(dāng)前進(jìn)程可以打開(kāi)的最大文件描述符數(shù),這個(gè)值可以設(shè)置,默認(rèn)是1024。
相關(guān)視頻推薦:
支撐億級(jí)io的底層基石 epoll實(shí)戰(zhàn)揭秘
網(wǎng)絡(luò)原理tcp/udp,網(wǎng)絡(luò)編程epoll/reactor,面試中正經(jīng)“八股文”
學(xué)習(xí)地址:C/C++Linux服務(wù)器開(kāi)發(fā)/后臺(tái)架構(gòu)師【零聲教育】-學(xué)習(xí)視頻教程-騰訊課堂
需要更多C/C++ Linux服務(wù)器架構(gòu)師學(xué)習(xí)資料加群 812855908 獲?。ㄙY料包括C/C++,Linux,golang技術(shù),Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒體,CDN,P2P,K8S,Docker,TCP/IP,協(xié)程,DPDK,ffmpeg等),免費(fèi)分享
__alloc_fd的工作是為進(jìn)程在[start,end)之間(備注:這里start為0, end為進(jìn)程可以打開(kāi)的最大文件描述符數(shù))分配一個(gè)可用的文件描述符,這里就不繼續(xù)深入下去了,代碼如下:
sys_epoll_create - sys_epoll_create1 - ep_alloc - get_unused_fd_flags - __alloc_fd:
然后,epoll_create1會(huì)調(diào)用anon_inode_getfile,創(chuàng)建一個(gè)file結(jié)構(gòu),如下:
sys_epoll_create - sys_epoll_create1 - anon_inode_getfile:
anon_inode_getfile函數(shù)中首先會(huì)alloc一個(gè)file結(jié)構(gòu)和一個(gè)dentry結(jié)構(gòu),然后將該file結(jié)構(gòu)與一個(gè)匿名inode節(jié)點(diǎn)anon_inode_inode掛鉤在一起,這里要注意的是,在調(diào)用anon_inode_getfile函數(shù)申請(qǐng)file結(jié)構(gòu)時(shí),傳入了前面申請(qǐng)的eventpoll結(jié)構(gòu)的ep變量,申請(qǐng)的file-private_data會(huì)指向這個(gè)ep變量,同時(shí),在anon_inode_getfile函數(shù)返回來(lái)后,ep-file會(huì)指向該函數(shù)申請(qǐng)的file結(jié)構(gòu)變量。
簡(jiǎn)要說(shuō)一下file/dentry/inode,當(dāng)進(jìn)程打開(kāi)一個(gè)文件時(shí),內(nèi)核就會(huì)為該進(jìn)程分配一個(gè)file結(jié)構(gòu),表示打開(kāi)的文件在進(jìn)程的上下文,然后應(yīng)用程序會(huì)通過(guò)一個(gè)int類(lèi)型的文件描述符來(lái)訪問(wèn)這個(gè)結(jié)構(gòu),實(shí)際上內(nèi)核的進(jìn)程里面維護(hù)一個(gè)file結(jié)構(gòu)的數(shù)組,而文件描述符就是相應(yīng)的file結(jié)構(gòu)在數(shù)組中的下標(biāo)。
dentry結(jié)構(gòu)(稱之為“目錄項(xiàng)”)記錄著文件的各種屬性,比如文件名、訪問(wèn)權(quán)限等,每個(gè)文件都只有一個(gè)dentry結(jié)構(gòu),然后一個(gè)進(jìn)程可以多次打開(kāi)一個(gè)文件,多個(gè)進(jìn)程也可以打開(kāi)同一個(gè)文件,這些情況,內(nèi)核都會(huì)申請(qǐng)多個(gè)file結(jié)構(gòu),建立多個(gè)文件上下文。但是,對(duì)同一個(gè)文件來(lái)說(shuō),無(wú)論打開(kāi)多少次,內(nèi)核只會(huì)為該文件分配一個(gè)dentry。所以,file結(jié)構(gòu)與dentry結(jié)構(gòu)的關(guān)系是多對(duì)一的。
同時(shí),每個(gè)文件除了有一個(gè)dentry目錄項(xiàng)結(jié)構(gòu)外,還有一個(gè)索引節(jié)點(diǎn)inode結(jié)構(gòu),里面記錄文件在存儲(chǔ)介質(zhì)上的位置和分布等信息,每個(gè)文件在內(nèi)核中只分配一個(gè)inode。 dentry與inode描述的目標(biāo)是不同的,一個(gè)文件可能會(huì)有好幾個(gè)文件名(比如鏈接文件),通過(guò)不同文件名訪問(wèn)同一個(gè)文件的權(quán)限也可能不同。dentry文件所代表的是邏輯意義上的文件,記錄的是其邏輯上的屬性,而inode結(jié)構(gòu)所代表的是其物理意義上的文件,記錄的是其物理上的屬性。dentry與inode結(jié)構(gòu)的關(guān)系是多對(duì)一的關(guān)系。
sys_epoll_create - sys_epoll_create1 - fd_install:
總結(jié)epoll_create函數(shù)所做的事:調(diào)用epoll_create后,在內(nèi)核中分配一個(gè)eventpoll結(jié)構(gòu)和代表epoll文件的file結(jié)構(gòu),并且將這兩個(gè)結(jié)構(gòu)關(guān)聯(lián)在一塊,同時(shí),返回一個(gè)也與file結(jié)構(gòu)相關(guān)聯(lián)的epoll文件描述符fd。當(dāng)應(yīng)用程序操作epoll時(shí),需要傳入一個(gè)epoll文件描述符fd,內(nèi)核根據(jù)這個(gè)fd,找到epoll的file結(jié)構(gòu),然后通過(guò)file,獲取之前epoll_create申請(qǐng)eventpoll結(jié)構(gòu)變量,epoll相關(guān)的重要信息都存儲(chǔ)在這個(gè)結(jié)構(gòu)里面。接下來(lái),所有epoll接口函數(shù)的操作,都是在eventpoll結(jié)構(gòu)變量上進(jìn)行的。
所以,epoll_create的作用就是為進(jìn)程在內(nèi)核中建立一個(gè)從epoll文件描述符到eventpoll結(jié)構(gòu)變量的通道。
epoll_ctl接口的作用是添加/修改/刪除文件的監(jiān)聽(tīng)事件,內(nèi)核代碼如下:
sys_epoll_ctl:
根據(jù)前面對(duì)epoll_ctl接口的介紹,op是對(duì)epoll操作的動(dòng)作(添加/修改/刪除事件),ep_op_has_event(op)判斷是否不是刪除操作,如果op != EPOLL_CTL_DEL為true,則需要調(diào)用copy_from_user函數(shù)將用戶空間傳過(guò)來(lái)的event事件拷貝到內(nèi)核的epds變量中。因?yàn)?,只有刪除操作,內(nèi)核不需要使用進(jìn)程傳入的event事件。
接著連續(xù)調(diào)用兩次fdget分別獲取epoll文件和被監(jiān)聽(tīng)文件(以下稱為目標(biāo)文件)的file結(jié)構(gòu)變量(備注:該函數(shù)返回fd結(jié)構(gòu)變量,fd結(jié)構(gòu)包含file結(jié)構(gòu))。
接下來(lái)就是對(duì)參數(shù)的一些檢查,出現(xiàn)如下情況,就可以認(rèn)為傳入的參數(shù)有問(wèn)題,直接返回出錯(cuò):
當(dāng)然下面還有一些關(guān)于操作動(dòng)作如果是添加操作的判斷,這里不做解釋?zhuān)容^簡(jiǎn)單,自行閱讀。
在ep里面,維護(hù)著一個(gè)紅黑樹(shù),每次添加注冊(cè)事件時(shí),都會(huì)申請(qǐng)一個(gè)epitem結(jié)構(gòu)的變量表示事件的監(jiān)聽(tīng)項(xiàng),然后插入ep的紅黑樹(shù)里面。在epoll_ctl里面,會(huì)調(diào)用ep_find函數(shù)從ep的紅黑樹(shù)里面查找目標(biāo)文件表示的監(jiān)聽(tīng)項(xiàng),返回的監(jiān)聽(tīng)項(xiàng)可能為空。
接下來(lái)switch這塊區(qū)域的代碼就是整個(gè)epoll_ctl函數(shù)的核心,對(duì)op進(jìn)行switch出來(lái)的有添加(EPOLL_CTL_ADD)、刪除(EPOLL_CTL_DEL)和修改(EPOLL_CTL_MOD)三種情況,這里我以添加為例講解,其他兩種情況類(lèi)似,知道了如何添加監(jiān)聽(tīng)事件,其他刪除和修改監(jiān)聽(tīng)事件都可以舉一反三。
為目標(biāo)文件添加監(jiān)控事件時(shí),首先要保證當(dāng)前ep里面還沒(méi)有對(duì)該目標(biāo)文件進(jìn)行監(jiān)聽(tīng),如果存在(epi不為空),就返回-EEXIST錯(cuò)誤。否則說(shuō)明參數(shù)正常,然后先默認(rèn)設(shè)置對(duì)目標(biāo)文件的POLLERR和POLLHUP監(jiān)聽(tīng)事件,然后調(diào)用ep_insert函數(shù),將對(duì)目標(biāo)文件的監(jiān)聽(tīng)事件插入到ep維護(hù)的紅黑樹(shù)里面:
sys_epoll_ctl - ep_insert:
前面說(shuō)過(guò),對(duì)目標(biāo)文件的監(jiān)聽(tīng)是由一個(gè)epitem結(jié)構(gòu)的監(jiān)聽(tīng)項(xiàng)變量維護(hù)的,所以在ep_insert函數(shù)里面,首先調(diào)用kmem_cache_alloc函數(shù),從slab分配器里面分配一個(gè)epitem結(jié)構(gòu)監(jiān)聽(tīng)項(xiàng),然后對(duì)該結(jié)構(gòu)進(jìn)行初始化,這里也沒(méi)有什么好說(shuō)的。我們接下來(lái)看ep_item_poll這個(gè)函數(shù)調(diào)用:
sys_epoll_ctl - ep_insert - ep_item_poll:
ep_item_poll函數(shù)里面,調(diào)用目標(biāo)文件的poll函數(shù),這個(gè)函數(shù)針對(duì)不同的目標(biāo)文件而指向不同的函數(shù),如果目標(biāo)文件為套接字的話,這個(gè)poll就指向sock_poll,而如果目標(biāo)文件為tcp套接字來(lái)說(shuō),這個(gè)poll就是tcp_poll函數(shù)。雖然poll指向的函數(shù)可能會(huì)不同,但是其作用都是一樣的,就是獲取目標(biāo)文件當(dāng)前產(chǎn)生的事件位,并且將監(jiān)聽(tīng)項(xiàng)綁定到目標(biāo)文件的poll鉤子里面(最重要的是注冊(cè)ep_ptable_queue_proc這個(gè)poll callback回調(diào)函數(shù)),這步操作完成后,以后目標(biāo)文件產(chǎn)生事件就會(huì)調(diào)用ep_ptable_queue_proc回調(diào)函數(shù)。
接下來(lái),調(diào)用list_add_tail_rcu將當(dāng)前監(jiān)聽(tīng)項(xiàng)添加到目標(biāo)文件的f_ep_links鏈表里面,該鏈表是目標(biāo)文件的epoll鉤子鏈表,所有對(duì)該目標(biāo)文件進(jìn)行監(jiān)聽(tīng)的監(jiān)聽(tīng)項(xiàng)都會(huì)加入到該鏈表里面。
然后就是調(diào)用ep_rbtree_insert,將epi監(jiān)聽(tīng)項(xiàng)添加到ep維護(hù)的紅黑樹(shù)里面,這里不做解釋?zhuān)a如下:
sys_epoll_ctl - ep_insert - ep_rbtree_insert:
前面提到,ep_insert有調(diào)用ep_item_poll去獲取目標(biāo)文件產(chǎn)生的事件位,在調(diào)用epoll_ctl前這段時(shí)間,可能會(huì)產(chǎn)生相關(guān)進(jìn)程需要監(jiān)聽(tīng)的事件,如果有監(jiān)聽(tīng)的事件產(chǎn)生,(revents event-events 為 true),并且目標(biāo)文件相關(guān)的監(jiān)聽(tīng)項(xiàng)沒(méi)有鏈接到ep的準(zhǔn)備鏈表rdlist里面的話,就將該監(jiān)聽(tīng)項(xiàng)添加到ep的rdlist準(zhǔn)備鏈表里面,rdlist鏈接的是該epoll描述符監(jiān)聽(tīng)的所有已經(jīng)就緒的目標(biāo)文件的監(jiān)聽(tīng)項(xiàng)。并且,如果有任務(wù)在等待產(chǎn)生事件時(shí),就調(diào)用wake_up_locked函數(shù)喚醒所有正在等待的任務(wù),處理相應(yīng)的事件。當(dāng)進(jìn)程調(diào)用epoll_wait時(shí),該進(jìn)程就出現(xiàn)在ep的wq等待隊(duì)列里面。接下來(lái)講解epoll_wait函數(shù)。
總結(jié)epoll_ctl函數(shù):該函數(shù)根據(jù)監(jiān)聽(tīng)的事件,為目標(biāo)文件申請(qǐng)一個(gè)監(jiān)聽(tīng)項(xiàng),并將該監(jiān)聽(tīng)項(xiàng)掛人到eventpoll結(jié)構(gòu)的紅黑樹(shù)里面。
epoll_wait等待事件的產(chǎn)生,內(nèi)核代碼如下:
sys_epoll_wait:
首先是對(duì)進(jìn)程傳進(jìn)來(lái)的一些參數(shù)的檢查:
參數(shù)全部檢查合格后,接下來(lái)就調(diào)用ep_poll函數(shù)進(jìn)行真正的處理:
sys_epoll_wait - ep_poll:
ep_poll中首先是對(duì)等待時(shí)間的處理,timeout超時(shí)時(shí)間以ms為單位,timeout大于0,說(shuō)明等待timeout時(shí)間后超時(shí),如果timeout等于0,函數(shù)不阻塞,直接返回,小于0的情況,是永久阻塞,直到有事件產(chǎn)生才返回。
當(dāng)沒(méi)有事件產(chǎn)生時(shí)((!ep_events_available(ep))為true),調(diào)用__add_wait_queue_exclusive函數(shù)將當(dāng)前進(jìn)程加入到ep-wq等待隊(duì)列里面,然后在一個(gè)無(wú)限for循環(huán)里面,首先調(diào)用set_current_state(TASK_INTERRUPTIBLE),將當(dāng)前進(jìn)程設(shè)置為可中斷的睡眠狀態(tài),然后當(dāng)前進(jìn)程就讓出cpu,進(jìn)入睡眠,直到有其他進(jìn)程調(diào)用wake_up或者有中斷信號(hào)進(jìn)來(lái)喚醒本進(jìn)程,它才會(huì)去執(zhí)行接下來(lái)的代碼。
如果進(jìn)程被喚醒后,首先檢查是否有事件產(chǎn)生,或者是否出現(xiàn)超時(shí)還是被其他信號(hào)喚醒的。如果出現(xiàn)這些情況,就跳出循環(huán),將當(dāng)前進(jìn)程從ep-wp的等待隊(duì)列里面移除,并且將當(dāng)前進(jìn)程設(shè)置為T(mén)ASK_RUNNING就緒狀態(tài)。
如果真的有事件產(chǎn)生,就調(diào)用ep_send_events函數(shù),將events事件轉(zhuǎn)移到用戶空間里面。
sys_epoll_wait - ep_poll - ep_send_events:
ep_send_events沒(méi)有什么工作,真正的工作是在ep_scan_ready_list函數(shù)里面:
sys_epoll_wait - ep_poll - ep_send_events - ep_scan_ready_list:
ep_scan_ready_list首先將ep就緒鏈表里面的數(shù)據(jù)鏈接到一個(gè)全局的txlist里面,然后清空ep的就緒鏈表,同時(shí)還將ep的ovflist鏈表設(shè)置為NULL,ovflist是用單鏈表,是一個(gè)接受就緒事件的備份鏈表,當(dāng)內(nèi)核進(jìn)程將事件從內(nèi)核拷貝到用戶空間時(shí),這段時(shí)間目標(biāo)文件可能會(huì)產(chǎn)生新的事件,這個(gè)時(shí)候,就需要將新的時(shí)間鏈入到ovlist里面。
僅接著,調(diào)用sproc回調(diào)函數(shù)(這里將調(diào)用ep_send_events_proc函數(shù))將事件數(shù)據(jù)從內(nèi)核拷貝到用戶空間。
sys_epoll_wait - ep_poll - ep_send_events - ep_scan_ready_list - ep_send_events_proc:
ep_send_events_proc回調(diào)函數(shù)循環(huán)獲取監(jiān)聽(tīng)項(xiàng)的事件數(shù)據(jù),對(duì)每個(gè)監(jiān)聽(tīng)項(xiàng),調(diào)用ep_item_poll獲取監(jiān)聽(tīng)到的目標(biāo)文件的事件,如果獲取到事件,就調(diào)用__put_user函數(shù)將數(shù)據(jù)拷貝到用戶空間。
回到ep_scan_ready_list函數(shù),上面說(shuō)到,在sproc回調(diào)函數(shù)執(zhí)行期間,目標(biāo)文件可能會(huì)產(chǎn)生新的事件鏈入ovlist鏈表里面,所以,在回調(diào)結(jié)束后,需要重新將ovlist鏈表里面的事件添加到rdllist就緒事件鏈表里面。
同時(shí)在最后,如果rdlist不為空(表示是否有就緒事件),并且由進(jìn)程等待該事件,就調(diào)用wake_up_locked再一次喚醒內(nèi)核進(jìn)程處理事件的到達(dá)(流程跟前面一樣,也就是將事件拷貝到用戶空間)。
到這,epoll_wait的流程是結(jié)束了,但是有一個(gè)問(wèn)題,就是前面提到的進(jìn)程調(diào)用epoll_wait后會(huì)睡眠,但是這個(gè)進(jìn)程什么時(shí)候被喚醒呢?在調(diào)用epoll_ctl為目標(biāo)文件注冊(cè)監(jiān)聽(tīng)項(xiàng)時(shí),對(duì)目標(biāo)文件的監(jiān)聽(tīng)項(xiàng)注冊(cè)一個(gè)ep_ptable_queue_proc回調(diào)函數(shù),ep_ptable_queue_proc回調(diào)函數(shù)將進(jìn)程添加到目標(biāo)文件的wakeup鏈表里面,并且注冊(cè)ep_poll_callbak回調(diào),當(dāng)目標(biāo)文件產(chǎn)生事件時(shí),ep_poll_callbak回調(diào)就去喚醒等待隊(duì)列里面的進(jìn)程。
總結(jié)一下epoll該函數(shù): epoll_wait函數(shù)會(huì)使調(diào)用它的進(jìn)程進(jìn)入睡眠(timeout為0時(shí)除外),如果有監(jiān)聽(tīng)的事件產(chǎn)生,該進(jìn)程就被喚醒,同時(shí)將事件從內(nèi)核里面拷貝到用戶空間返回給該進(jìn)程。