當前位置:編程學習大全網 - 源碼下載 - FS竊取源代碼

FS竊取源代碼

Epoll是linux中IO復用的壹種機制,I/O復用是壹個進程通過它可以監控多個描述符的機制,壹旦壹個描述符就緒(通常是讀或寫就緒),就可以通知程序執行相應的讀寫操作。當然linux中的IO復用不僅僅是epoll,還有select、poll等其他復用機制,但接下來介紹epoll的內核實現。

事件可以是以下宏的集合:

epoll相對於選擇/輪詢的優勢:

與epoll相關的內核代碼在fs/eventpoll.c文件中。這裏分別分析了epoll_create、epoll_ctl、epoll_wait三個函數在內核中的實現。分析中使用的linux內核的源代碼是版本4.1.2。

Epoll_create用於創建Epoll的句柄,在內核系統中實現如下:

sys_epoll_create:

可以看出,當我們調用epoll_create時,傳入的size參數只是用來判斷是否小於等於0,然後就沒有其他用途了。

整個函數只有三行代碼,真正的工作還是放在sys_epoll_create1函數中。

sys _ epoll _ create-& gt;sys_epoll_create1:

sys_epoll_create1的功能流程如下:

sys _ epoll _ create-& gt;sys _ epoll _ create 1-& gt;ep_alloc:

sys _ epoll _ create-& gt;sys _ epoll _ create 1-& gt;EP _ alloc-& gt;get_unused_fd_flags:

在linux內核中,current是壹個宏,它返回壹個task_struct結構的變量(我們稱之為進程描述符),這個變量代表當前的進程。進程打開的文件資源存儲在進程描述符的files成員中,所以current->;文件返回的當前進程打開的文件資源。rlimit(RLIMIT_NOFILE)函數獲取當前進程可以打開的文件描述符的最大數量。該值可以設置,默認值為1024。

相關視頻推薦:

支撐億級io的底層基石Epoll,在實戰中揭示真相。

網絡原理tcp/udp,網絡編程epoll/reactor,面試時壹本正經的“八股文”。

學習地址:C/C++Linux服務器開發/後臺架構師零語音教育-學習視頻教程-騰訊課堂

需要更多C/C++ Linux服務器架構師學習資料加群812855908(資料包括C/C++、Linux、golang技術、Nginx、ZeroMQ、MySQL、Redis、fastdfs、MongoDB、ZK、流媒體、CDN、P2P、K8S、Docker、TCP/IP等

__alloc_fd的工作是在[start,end]之間為進程分配壹個可用的文件描述符(註意:這裏start是0,end是進程可以打開的文件描述符的最大數量),這裏就不贅述了。代碼如下:

sys _ epoll _ create-& gt;sys _ epoll _ create 1-& gt;EP _ alloc-& gt;get _ unused _ FD _ flags-& gt;__alloc_fd:

然後,epoll_create1將調用anon_inode_getfile來創建文件結構,如下所示:

sys _ epoll _ create-& gt;sys _ epoll _ create 1-& gt;匿名信息節點獲取文件:

anon_inode_getfile函數將首先分配壹個文件結構和壹個dentry結構,然後用壹個匿名inode節點anon_inode_inode將文件結構掛鉤。需要註意的是,調用anon_inode_getfile函數申請文件結構時,較早申請的eventpoll結構的ep變量,申請的文件-->;Private_data會指向這個ep變量,同時anon_inode_getfile函數返回後,EP->;File將指向該函數應用的文件結構變量。

簡單說壹下file/dentry/inode,當壹個進程打開壹個文件的時候,內核會給進程分配壹個文件結構,表示打開的文件在進程的上下文中,然後應用會通過壹個int文件描述符來訪問這個結構。實際上,內核進程維護的是壹個文件結構數組,文件描述符就是數組中對應文件結構的下標。

Dentry結構(稱為“目錄條目”)記錄了文件的各種屬性,如文件名、訪問權限等。每個文件只有壹個dentry結構,然後壹個進程可以多次打開壹個文件,多個進程也可以打開同壹個文件。在這些情況下,內核會申請多個文件結構,建立多個文件上下文。但是,對於同壹個文件,無論打開多少次,內核只會給該文件分配壹個dentry。因此,文件結構和條目結構之間是多對壹的關系

同時,每個文件不僅有壹個dentry目錄條目結構,還有壹個索引節點的inode結構,記錄了文件在存儲介質上的位置和分布,在內核中每個文件只分配壹個inode。Dentry和inode描述了不同的目標。壹個文件可能有幾個文件名(如鏈接文件),通過不同的文件名訪問同壹個文件可能是不同的。Dentry文件表示邏輯意義上的文件並記錄其邏輯屬性,而inode結構表示物理意義上的文件並記錄其物理屬性。dentry和inode結構之間是多對壹的關系

sys _ epoll _ create-& gt;sys _ epoll _ create 1-& gt;fd _安裝:

總結壹下epoll_create函數的作用:調用epoll_create後,在內核中分配壹個eventpoll結構和壹個表示epoll文件的file結構,將兩個結構關聯在壹起,同時返回壹個也與file結構關聯的epoll文件描述符fd。當應用程序操作epoll時,它需要傳入壹個epoll文件描述符fd。根據這個fd,內核可以找到epoll的文件結構,然後通過該文件可以獲得epoll_create所申請的eventpoll的結構變量,所有與epoll相關的重要信息都存儲在這個結構中。接下來,epoll接口函數的所有操作都在eventpoll結構變量上執行。

所以epoll_create的作用就是為進程建立壹個從epoll文件描述符到內核中eventpoll結構變量的通道。

Epoll_ctl接口用於添加/修改/刪除文件監聽事件。內核代碼如下:

sys_epoll_ctl:

根據epoll_ctl接口的介紹,op是epoll操作(添加/修改/刪除事件)的動作,ep_op_has_event(op)判斷是否不是刪除操作。If OP!= EPOLL_CTL_DEL為真,那麽就需要調用copy_from_user函數將事件從用戶空間復制到內核的epds變量中。因為,只有刪除操作,內核不需要使用進程傳入的event事件。

然後連續兩次調用fdget,獲取epoll文件和被監控文件(以下簡稱目標文件)的文件結構變量(註意:該函數返回fd結構變量,fd結構包含文件結構)。

下壹步是檢查參數。在以下情況下,可以認為傳入的參數有問題,直接返回錯誤:

當然,如果是加法運算,對於運算動作還是有壹些判斷的。這裏就不解釋了,比較簡單,可以自己看。

在ep中,維護了壹個紅黑樹。每增加壹個註冊的事件,就應用壹個具有epitem結構的變量來表示事件監聽器,然後插入ep的紅黑樹中。在epoll_ctl中,會調用ep_find函數從ep的紅黑樹中尋找目標文件所代表的監聽項,返回的監聽項可能為空。

接下來,switch區域的代碼是整個epoll_ctl函數的核心。切換op有三種情況:添加(EPOLL_CTL_ADD)、刪除(EPOLL_CTL_DEL)和修改(EPOLL_CTL_MOD)。這裏我就以加法為例來說明壹下。其他兩種情況類似。我知道如何添加監聽事件,其他刪除和修改監聽事件也可以外推。

向目標文件添加監控事件時,您必須首先確保目標文件沒有在當前ep中被監控。如果存在(epi不為空),將返回-EEXIST錯誤。否則參數正常,然後默認設置目標文件的POLLERR和POLLHUP監控事件,然後調用ep_insert函數將目標文件的監控事件插入到ep維護的紅黑樹中:

sys _ epoll _ CTL-& gt;ep_insert:

如前所述,對目標文件的監控是由壹個具有epitem結構的監聽器變量來維護的,所以在ep_insert函數中,首先調用kmem_cache_alloc函數,從slab分配器中分配壹個epitem結構監聽器,然後初始化該結構。這裏沒什麽好說的。接下來讓我們看看函數調用ep_item_poll:

sys _ epoll _ CTL-& gt;EP _ insert-& gt;ep _ item _輪詢:

在ep_item_poll函數中,調用目標文件的poll函數,針對不同的目標文件指向不同的函數。如果目標文件是套接字,輪詢指向sock_poll,而如果目標文件是tcp套接字,輪詢是tcp_poll函數。雖然poll指向的函數可能不壹樣,但是它們的作用都是壹樣的,就是獲取目標文件當前生成的事件位,將監聽項綁定到目標文件的poll鉤子上(最重要的是註冊poll回調函數EP _ PTABLE _ QUEEN _ PROC)。這個操作完成後,以後目標文件生成事件時會調用EP _ PTABLE _ QUEEN _ PROC回調函數。

接下來調用list_add_tail_rcu將當前監聽項添加到目標文件的f_ep_links鏈表中,也就是目標文件的epoll鉤子鏈表,所有監聽目標文件的監聽項都會添加到鏈表中。

然後調用ep_rbtree_insert,將epi監聽器添加到ep維護的紅黑樹中。這裏不做解釋,代碼如下:

sys _ epoll _ CTL-& gt;EP _ insert-& gt;ep_rbtree_insert:

如前所述,ep_insert調用ep_item_poll來獲取目標文件生成的事件位。在調用epoll_ctl之前的壹段時間內,可能有相關進程需要監控的事件。如果有被監視的事件,(Revents &:event-& gt;Events為真),且與目標文件相關的監聽項沒有鏈接到ep的準備鏈表rdlist,監聽項被添加到ep的準備鏈表rdlist,rdlist鏈接epoll描述符所監控的所有就緒目標文件的監聽項。而且,如果有任務等待生成事件,調用wake_up_locked函數喚醒所有等待的任務,處理相應的事件。當壹個進程調用epoll_wait時,它出現在ep的wq等待隊列中。接下來,解釋epoll_wait函數。

總結壹下epoll_ctl函數:該函數根據監控到的事件為目標文件申請監聽項,並將監聽項掛入eventpoll結構的紅黑樹中。

Epoll_wait等待事件,內核代碼如下:

sys_epoll_wait:

第壹個是檢查流程傳入的壹些參數:

檢查完所有參數後,調用ep_poll函數進行實際處理:

sys _ epoll _ wait-& gt;ep_poll:

ep_poll第壹件事就是處理等待時間。超時超時以毫秒為單位,超時大於0,表示等待超時時間超時。如果超時等於0,函數不會阻塞,直接返回。如果它小於0,它將被永久阻止,直到事件發生時才會返回。

當沒有事件生成時((!Ep_events_available(ep))為true),調用__add_wait_queue_exclusive函數將當前進程添加到EP->;Wq在隊列中等待,然後在壹個無限for循環中,先調用set _ current _ state(task _ interruptible)將當前進程設置為可中斷睡眠狀態,然後當前進程放棄cpu進入睡眠,直到另壹個進程調用wake_up或者有中斷信號進來喚醒這個進程,它才會執行下壹個代碼。

如果進程被喚醒,首先檢查是否有事件,或者是否超時或者被其他信號喚醒。如果出現這些情況,跳出循環,將當前進程從EP->;Wp從等待隊列中移除,當前進程設置為TASK_RUNNING就緒狀態。

如果有事件,調用ep_send_events函數將事件轉移到用戶空間。

sys _ epoll _ wait-& gt;EP _ poll-& gt;ep _發送_事件:

Ep_send_events沒有工作,但是真正的工作在ep_scan_ready_list函數中:

sys _ epoll _ wait-& gt;EP _ poll-& gt;EP _ send _ events-& gt;ep_scan_ready_list:

Ep_scan_ready_list首先將Ep就緒列表中的數據鏈接到壹個全局txlist,然後清除ep就緒列表,還將ep的ovflist列表設置為NULL。ovflist是壹個單鏈表,它是壹個接受就緒事件的備份鏈表。當內核進程將事件從內核復制到用戶空間時,目標文件可能會在此期間生成新事件。這時候就需要將新的時間鏈放入ovlist。

然後,調用存儲過程回調函數(這裏將調用ep_send_events_proc函數)將事件數據從內核復制到用戶空間。

sys _ epoll _ wait-& gt;EP _ poll-& gt;EP _ send _ events-& gt;EP _ scan _ ready _ list-& gt;ep_send_events_proc:

ep_send_events_proc回調函數循環獲取被監控項的事件數據。對於每個被監控的項目,它調用ep_item_poll來獲取被監控目標文件的事件。如果獲得了事件,它調用__put_user函數將數據復制到用戶空間。

回到ep_scan_ready_list函數,上面提到了在執行存儲過程回調函數的過程中,目標文件可能會生成新的事件鏈接到ovlist中,所以在回調之後,需要再次將ovlist中的事件添加到rdllist ready事件列表中。

同時,在最後,如果rdlist不為空(表示是否有ready事件)且進程正在等待事件,則調用wake_up_locked再次喚醒內核進程來處理事件的到來(流程同前,即把事件復制到用戶空間)。

至此,epoll_wait的進程結束了,但是有壹個問題,就是上面提到的進程在調用epoll_wait之後會休眠,但是這個進程什麽時候會被喚醒呢?調用epoll_ctl為目標文件註冊監聽器項時,為目標文件的監聽器項註冊壹個EP _ PTABLE _ QUEEN _ PROC回調函數。EP _ PTABLE _ QUEEN _ PROC回調函數將進程添加到目標文件的喚醒列表中,並註冊壹個ep_poll_callbak回調函數。當目標文件生成壹個事件時,ep_poll_callbak回調將喚醒等待隊列中的進程。

總結壹下epoll函數:epoll_wait函數會讓調用它的進程進入睡眠狀態(timeout為0時除外)。如果有被監控的事件,進程將被喚醒,事件將從內核復制到用戶空間,並返回到進程。

  • 上一篇:Go 內存模型 並發可見性
  • 下一篇:如何評價高校927事件?
  • copyright 2024編程學習大全網