在线观看www成人影院-在线观看www日本免费网站-在线观看www视频-在线观看操-欧美18在线-欧美1级

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

epoll底層如何使用紅黑樹

科技綠洲 ? 來源:Linux開發架構之路 ? 作者:Linux開發架構之路 ? 2023-11-10 15:13 ? 次閱讀

epoll和poll的一個很大的區別在于,poll每次調用時都會存在一個將pollfd結構體數組中的每個結構體元素從用戶態向內核態中的一個鏈表節點拷貝的過程,而內核中的這個鏈表并不會一直保存,當poll運行一次就會重新執行一次上述的拷貝過程,這說明一個問題:poll并不會在內核中為要監聽的文件描述符長久的維護一個數據結構來存放他們,而epoll內核中維護了一個內核事件表,它是將所有的文件描述符全部都存放在內核中,系統去檢測有事件發生的時候觸發回調,當你要添加新的文件描述符的時候也是調用epoll_ctl函數使用EPOLL_CTL_ADD宏來插入,epoll_wait也不是每次調用時都會重新拷貝一遍所有的文件描述符到內核態。當我現在要在內核中長久的維護一個數據結構來存放文件描述符,并且時常會有插入,查找和刪除的操作發生,這對內核的效率會產生不小的影響,因此需要一種插入,查找和刪除效率都不錯的數據結構來存放這些文件描述符,那么紅黑樹當然是不二的人選。

接下來我們來看看epoll底層是如何使用紅黑樹的

我們知道epoll在添加一個文件描述符進行監聽或者刪除一個文件描述符時使用的是epoll_ctl函數,該函數底層調用的是sys_epoll_ctl函數,下面給出該函數的部分源碼

/*
 * The following function implements the controller interface for
 * the eventpoll file that enables the insertion/removal/change of
 * file descriptors inside the interest set.  It represents
 * the kernel part of the user space epoll_ctl(2).
 */
asmlinkage long
sys_epoll_ctl(int epfd, int op, int fd, struct epoll_event __user *event)
{
	int error;
	struct file *file, *tfile;
	struct eventpoll *ep;
	struct epitem *epi;
	struct epoll_event epds;

	DNPRINTK(3, (KERN_INFO "[%p] eventpoll: sys_epoll_ctl(%d, %d, %d, %p)n",
		     current, epfd, op, fd, event));

	error = -EFAULT;
	if (EP_OP_HASH_EVENT(op) &&
	    copy_from_user(&epds, event, sizeof(struct epoll_event)))
		goto eexit_1;

	/* Get the "struct file *" for the eventpoll file */
	error = -EBADF;
	file = fget(epfd);
	if (!file)
		goto eexit_1;

	/* Get the "struct file *" for the target file */
	tfile = fget(fd);
	if (!tfile)
		goto eexit_2;

	/* The target file descriptor must support poll */
	error = -EPERM;
	if (!tfile- >f_op || !tfile- >f_op- >poll)
		goto eexit_3;

	/*
	 * We have to check that the file structure underneath the file descriptor
	 * the user passed to us _is_ an eventpoll file. And also we do not permit
	 * adding an epoll file descriptor inside itself.
	 */
	error = -EINVAL;
	if (file == tfile || !IS_FILE_EPOLL(file))
		goto eexit_3;

	/*
	 * At this point it is safe to assume that the "private_data" contains
	 * our own data structure.
	 */
	ep = file- >private_data;

	down_write(&ep- >sem);

	/* Try to lookup the file inside our hash table */
	epi = ep_find(ep, tfile, fd);

在sys_epoll_ctl的參數中,op代表要進行的操作,fd表示要被操作的文件描述符。操作類型定義在下面著三個宏中

/* Valid opcodes to issue to sys_epoll_ctl() */
#define EPOLL_CTL_ADD 1
#define EPOLL_CTL_DEL 2
#define EPOLL_CTL_MOD 3

首先呢,會調用ep_find函數在內核事件表也就是紅黑樹中查找該fd是否已經存在,這里的結果會先保存在epi中,ep_find函數做了什么操作呢?這里就是我們第一個用到紅黑樹的地方:查找

先來看一下ep_find的實現:

/*
 * Search the file inside the eventpoll hash. It add usage count to
 * the returned item, so the caller must call ep_release_epitem()
 * after finished using the "struct epitem".
 */
static struct epitem *ep_find(struct eventpoll *ep, struct file *file, int fd)
{
	int kcmp;
	unsigned long flags;
	struct rb_node *rbp;
	struct epitem *epi, *epir = NULL;
	struct epoll_filefd ffd;

	EP_SET_FFD(&ffd, file, fd);
	read_lock_irqsave(&ep- >lock, flags);
	for (rbp = ep- >rbr.rb_node; rbp; ) {
		epi = rb_entry(rbp, struct epitem, rbn);
		kcmp = EP_CMP_FFD(&ffd, &epi- >ffd);
		if (kcmp > 0)
			rbp = rbp- >rb_right;
		else if (kcmp < 0)
			rbp = rbp- >rb_left;
		else {
			ep_use_epitem(epi);
			epir = epi;
			break;
		}
	}
	read_unlock_irqrestore(&ep- >lock, flags);

	DNPRINTK(3, (KERN_INFO "[%p] eventpoll: ep_find(%p) - > %pn",
		     current, file, epir));

	return epir;
}

這里的for循環就是一個紅黑樹的查找過程,我們可以看到這里查找的時候用到的一個變量是kcmp,這個kcmp的值就是我們的fd在紅黑樹中所用來排序的值。而且我們可以看到這個kcmp的值來源于宏函數EP_CMP_FFD我們來看一下這個宏函數的實現

/* Compare rb-tree keys */
#define EP_CMP_FFD(p1, p2) ((p1)- >file > (p2)- >file ? +1: 
			    ((p1)- >file < (p2)- >file ? -1: (p1)- >fd - (p2)- >fd))

根據該宏函數的實現我們看到在比較時其實使用的是一個epoll_filefd的結構體中的file成員來比較的,那么我們再進入epoll_filefd中查看一下

圖片

我們看到這里的file是一個struct file類型的指針,當我們比較兩個file類型的指針時比較的是他們的指針的值,也就是file結構體的地址。

根據源碼判斷,在紅黑樹中排序的根據是file的地址大小。至于為什么,目前還并不是很清楚,也存在我理解錯誤的可能,這里不是很確定。

查找完畢后,就要開始進行具體的操作了,這里會根據宏的值判斷應該進行的操作是插入,刪除,還是修改。這里給出sys_epoll_ctl的剩余源碼(和文章開頭給出的前半部分剛好銜接)

error = -EINVAL;
	switch (op) {
	case EPOLL_CTL_ADD:
		if (!epi) {
			epds.events |= POLLERR | POLLHUP;

			error = ep_insert(ep, &epds, tfile, fd);
		} else
			error = -EEXIST;
		break;
	case EPOLL_CTL_DEL:
		if (epi)
			error = ep_remove(ep, epi);
		else
			error = -ENOENT;
		break;
	case EPOLL_CTL_MOD:
		if (epi) {
			epds.events |= POLLERR | POLLHUP;
			error = ep_modify(ep, epi, &epds);
		} else
			error = -ENOENT;
		break;
	}

	/*
	 * The function ep_find() increments the usage count of the structure
	 * so, if this is not NULL, we need to release it.
	 */
	if (epi)
		ep_release_epitem(epi);

	up_write(&ep- >sem);

eexit_3:
	fput(tfile);
eexit_2:
	fput(file);
eexit_1:
	DNPRINTK(3, (KERN_INFO "[%p] eventpoll: sys_epoll_ctl(%d, %d, %d, %p) = %dn",
		     current, epfd, op, fd, event, error));

	return error;
}

我們看到這部分代碼里最主要的工作就是進行這個switch,case語句所做的判斷工作了,這里sys_epoll_ctl函數根據參數op的不同而調用不同的函數進行處理,我們以EPOLL_CTL_ADD宏舉例,該宏要進行的操作是插入一個新的文件描述符。

epoll底層的紅黑樹插入是調用ep_insert插入的,而ep_insert函數里面調用了ep_rbtree_insert來進行對紅黑樹中一個節點的插入。這兩個函數的聲明如下:

static void ep_rbtree_insert(struct eventpoll *ep, struct epitem *epi);
static int ep_insert(struct eventpoll *ep, struct epoll_event *event,
		     struct file *tfile, int fd);

我們忽略ep_insert函數其他的實現要點,直接查看它所調用的函數ep_retree_insert的實現

static void ep_rbtree_insert(struct eventpoll *ep, struct epitem *epi)
{
	int kcmp;
	struct rb_node **p = &ep- >rbr.rb_node, *parent = NULL;
	struct epitem *epic;

	while (*p) {
		parent = *p;
		epic = rb_entry(parent, struct epitem, rbn);
		kcmp = EP_CMP_FFD(&epi- >ffd, &epic- >ffd);
		if (kcmp > 0)
			p = &parent- >rb_right;
		else
			p = &parent- >rb_left;
	}
	rb_link_node(&epi- >rbn, parent, p);
	rb_insert_color(&epi- >rbn, &ep- >rbr);
}

可以看到這里在插入一個新節點時對于其在紅黑樹中的位置的選擇過程是用一個while循環來實現的,當該while循環退出后,說明我們已經找到了該節點應在的位置,接下來調用rb_link_node函數將該節點插入到紅黑樹中,該函數的實現很簡單,就是往一顆二叉樹中插入一個新的節點,實現如下

static inline void rb_link_node(struct rb_node * node, struct rb_node * parent,
				struct rb_node ** rb_link)
{
	node- >rb_parent = parent;
	node- >rb_color = RB_RED;
	node- >rb_left = node- >rb_right = NULL;

	*rb_link = node;
}

然后再調用rb_insert_color函數,這個函數實現的是對插入一個新節點之后的整個紅黑樹進行調整的過程,這里牽扯到紅黑樹的旋轉,不是我們本文的重點,只把代碼貼上,有興趣的同學可以下去自習。

void rb_insert_color(struct rb_node *node, struct rb_root *root)
{
	struct rb_node *parent, *gparent;

	while ((parent = node- >rb_parent) && parent- >rb_color == RB_RED)
	{
		gparent = parent- >rb_parent;

		if (parent == gparent- >rb_left)
		{
			{
				register struct rb_node *uncle = gparent- >rb_right;
				if (uncle && uncle- >rb_color == RB_RED)
				{
					uncle- >rb_color = RB_BLACK;
					parent- >rb_color = RB_BLACK;
					gparent- >rb_color = RB_RED;
					node = gparent;
					continue;
				}
			}

			if (parent- >rb_right == node)
			{
				register struct rb_node *tmp;
				__rb_rotate_left(parent, root);
				tmp = parent;
				parent = node;
				node = tmp;
			}

			parent- >rb_color = RB_BLACK;
			gparent- >rb_color = RB_RED;
			__rb_rotate_right(gparent, root);
		} else {
			{
				register struct rb_node *uncle = gparent- >rb_left;
				if (uncle && uncle- >rb_color == RB_RED)
				{
					uncle- >rb_color = RB_BLACK;
					parent- >rb_color = RB_BLACK;
					gparent- >rb_color = RB_RED;
					node = gparent;
					continue;
				}
			}

			if (parent- >rb_left == node)
			{
				register struct rb_node *tmp;
				__rb_rotate_right(parent, root);
				tmp = parent;
				parent = node;
				node = tmp;
			}

			parent- >rb_color = RB_BLACK;
			gparent- >rb_color = RB_RED;
			__rb_rotate_left(gparent, root);
		}
	}

	root- >rb_node- >rb_color = RB_BLACK;
}
聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 文件
    +關注

    關注

    1

    文章

    568

    瀏覽量

    24766
  • 函數
    +關注

    關注

    3

    文章

    4333

    瀏覽量

    62723
  • epoll
    +關注

    關注

    0

    文章

    28

    瀏覽量

    2967
收藏 人收藏

    評論

    相關推薦

    epoll的使用

    以下內容是參考華清遠見《linux/unix系統編程手冊》對epoll的一個個人總結,是我在華清遠見比較全面的總結。一、epoll的優點同I/O多路復用和信號驅動I/O一樣,linux的epoll
    發表于 05-11 13:22

    什么是“”看了就知道

    今天我們要說的就是就是一棵非嚴格均衡的二叉,均衡二叉又是在二叉搜索的基礎上增加了自動
    發表于 10-27 17:00

    一文詳解

    是一種自平衡的二叉查找,是一種高效的查找。它是由 Rudolf Bayer 于1972年發明,在當時被稱為對稱二叉 B
    的頭像 發表于 02-02 17:25 ?4228次閱讀
    一文詳解<b class='flag-5'>紅</b><b class='flag-5'>黑</b><b class='flag-5'>樹</b>

    poll&&epollepoll實現

    poll&&epollepoll實現
    發表于 05-14 14:34 ?2801次閱讀
    poll&&<b class='flag-5'>epoll</b>之<b class='flag-5'>epoll</b>實現

    詳解電源二叉到底是什么

    作為數據結構的基礎,分很多種,像 AVL 、二叉搜索....今天我想分享的是關于二
    的頭像 發表于 06-06 15:05 ?1w次閱讀
    詳解電源二叉<b class='flag-5'>樹</b>到底是什么

    魔3和鯊2買哪個好

    鯊2還是魔3?作為兩款同樣采用高通驍龍855移動平臺的游戲手機,鯊2和魔3不免會被消費者放在一起進行比較。那么,鯊2和
    的頭像 發表于 07-04 14:43 ?1.4w次閱讀

    鯊2和魔3哪個好

    魔3和鯊2哪個好?和鯊科技今年上半年力推的“鯊2”一樣,魔3也搭載了高通驍龍855移動平臺。那么,
    的頭像 發表于 06-30 09:20 ?2.1w次閱讀

    (Red Black Tree)是一種自平衡的二叉搜索

    平衡(Balance):就是當結點數量固定時,左右子樹的高度越接近,這棵二叉越平衡(高度越低)。而最理想的平衡就是完全二叉/滿二叉,高度最小的二叉
    的頭像 發表于 07-01 15:05 ?5742次閱讀
    <b class='flag-5'>紅</b><b class='flag-5'>黑</b><b class='flag-5'>樹</b>(Red Black Tree)是一種自平衡的二叉搜索<b class='flag-5'>樹</b>

    如何使用 go 實現

    二叉查找也叫二叉搜索,也叫二叉排序,它具有以下特點:1. 如果左子樹不為空,則左子樹上的結點的值都小于根節點;2. 如果右子樹不為空,則右子樹上的結點的值都大于根節點;3. 子樹同樣也要遵循以上兩點。
    的頭像 發表于 03-21 11:54 ?1312次閱讀

    是如何模擬2-3 B的操作邏輯的

    大家都聽說過,也都知道很厲害,是計算機里面評價非常高的數據結構。但是每當想學習
    的頭像 發表于 08-30 10:22 ?888次閱讀

    TiDB底層存儲結構LSM原理介紹

    隨著數據量的增大,傳統關系型數據庫越來越不能滿足對于海量數據存儲的需求。對于分布式關系型數據庫,我們了解其底層存儲結構是非常重要的。本文將介紹下分布式關系型數據庫 TiDB 所采用的底層存儲結構 LSM 的原理。
    的頭像 發表于 01-13 10:00 ?1009次閱讀

    epoll 的實現原理

    今兒我們就從源碼入手,來幫助大家簡單理解一下 epoll 的實現原理,并在后邊分析一下,大家都說 epoll 性能好,那到底是好在哪里。 epoll 簡介 1、epoll 的簡單使用
    的頭像 發表于 11-09 11:14 ?543次閱讀
    <b class='flag-5'>epoll</b> 的實現原理

    epoll的基礎數據結構

    一、epoll的基礎數據結構 在開始研究源代碼之前,我們先看一下 epoll 中使用的數據結構,分別是 eventpoll、epitem 和 eppoll_entry。 1、eventpoll 我們
    的頭像 發表于 11-10 10:20 ?814次閱讀
    <b class='flag-5'>epoll</b>的基礎數據結構

    的特點及應用

    比起理解的原理,更重要的是理解的應用場景,因為某些應用場景的需要,
    的頭像 發表于 11-10 11:16 ?737次閱讀
    <b class='flag-5'>紅</b><b class='flag-5'>黑</b><b class='flag-5'>樹</b>的特點及應用

    epoll源碼分析

    Linux內核提供了3個關鍵函數供用戶來操作epoll,分別是: epoll_create(), 創建eventpoll對象 epoll_ctl(), 操作eventpoll對象
    的頭像 發表于 11-13 11:49 ?1061次閱讀
    <b class='flag-5'>epoll</b>源碼分析
    主站蜘蛛池模板: 午夜痒痒网| a毛片网站| 色窝视频| 色.com| 欧美色视频日本片免费高清| 日本片免费观看一区二区| 日韩一级一片| 久久天堂网| 国产免费色视频| 哟交小u女国产精品视频| 天天操天天舔天天射| 欧美性色生活片天天看99| 国产99久9在线视频| 黄色国产在线观看| 男男失禁play 把尿bl| 字幕网中文aⅴ资源站| 四虎最新紧急入口4hu| 女性一级全黄生活片免费看| 国产农村妇女毛片精品久久| 在线观看精品国产入口| 可以免费看黄色的网站| 国产操女人| 亚洲婷婷综合色高清在线| 日本黄色大片免费| 国产精品久久久久久久久ktv| 天天狠天天插| 欧美极品另类xxx| 在线播放12p| 欧美二区三区| 一本久草| 午夜老司机永久免费看片| 天天色综合色| 老司机51精品视频在线观看| www.狠狠操.com| 日本加勒比在线播放| 黄色伊人网| 伊人久久成人成综合网222| 日本三级高清| xxx性欧美| 久久刺激视频| 美女被强插|