0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

epoll的基礎(chǔ)數(shù)據(jù)結(jié)構(gòu)

科技綠洲 ? 來源:Linux開發(fā)架構(gòu)之路 ? 作者:Linux開發(fā)架構(gòu)之路 ? 2023-11-10 10:20 ? 次閱讀

一、epoll的基礎(chǔ)數(shù)據(jù)結(jié)構(gòu)

在開始研究源代碼之前,我們先看一下 epoll 中使用的數(shù)據(jù)結(jié)構(gòu),分別是 eventpoll、epitem 和 eppoll_entry。

1、eventpoll

我們先看一下 eventpoll 這個(gè)數(shù)據(jù)結(jié)構(gòu),這個(gè)數(shù)據(jù)結(jié)構(gòu)是我們在調(diào)用 epoll_create 之后內(nèi)核創(chuàng)建的一個(gè)句柄,表示了一個(gè) epoll 實(shí)例。后續(xù)如果我們再調(diào)用 epoll_ctl 和 epoll_wait 等,都是對這個(gè) eventpoll 數(shù)據(jù)進(jìn)行操作,這部分?jǐn)?shù)據(jù)會(huì)被保存在 epoll_create 創(chuàng)建的匿名文件 file 的 private_data 字段中。

* This structure is stored inside the "private_data" member of the file
* structure and represents the main data structure for the eventpoll
* interface.
*/
struct eventpoll {
/* Protect the access to this structure */
spinlock_t lock;

/*
* This mutex is used to ensure that files are not removed
* while epoll is using them. This is held during the event
* collection loop, the file cleanup path, the epoll file exit
* code and the ctl operations.
*/
struct mutex mtx;

/* Wait queue used by sys_epoll_wait() */
// 這個(gè)隊(duì)列里存放的是執(zhí)行 epoll_wait 從而等待的進(jìn)程隊(duì)列
wait_queue_head_t wq;

/* Wait queue used by file->poll() */
// 這個(gè)隊(duì)列里存放的是該 eventloop 作為 poll 對象的一個(gè)實(shí)例,加入到等待的隊(duì)列
// 這是因?yàn)?eventpoll 本身也是一個(gè) file, 所以也會(huì)有 poll 操作
wait_queue_head_t poll_wait;

/* List of ready file descriptors */
// 這里存放的是事件就緒的 fd 列表,鏈表的每個(gè)元素是下面的 epitem
struct list_head rdllist;

/* RB tree root used to store monitored fd structs */
// 這是用來快速查找 fd 的紅黑樹
struct rb_root_cached rbr;

/*
* This is a single linked list that chains all the "struct epitem" that
* happened while transferring ready events to userspace w/out
* holding ->lock.
*/
struct epitem *ovflist;

/* wakeup_source used when ep_scan_ready_list is running */
struct wakeup_source *ws;

/* The user that created the eventpoll descriptor */
struct user_struct *user;

// 這是 eventloop 對應(yīng)的匿名文件,充分體現(xiàn)了 Linux 下一切皆文件的思想
struct file *file;

/* used to optimize loop detection check */
int visited;
struct list_head visited_list_link;

#ifdef CONFIG_NET_RX_BUSY_POLL
/* used to track busy poll napi_id */
unsigned int napi_id;
#endif
};

2、epitem

每當(dāng)我們調(diào)用 epoll_ctl 增加一個(gè) fd 時(shí),內(nèi)核就會(huì)為我們創(chuàng)建出一個(gè) epitem 實(shí)例,并且把這個(gè)實(shí)例作為紅黑樹的一個(gè)子節(jié)點(diǎn),增加到 eventpoll 結(jié)構(gòu)體中的紅黑樹中,對應(yīng)的字段是 rbr。這之后,查找每一個(gè) fd 上是否有事件發(fā)生都是通過紅黑樹上的 epitem 來操作。

/*
* Each file descriptor added to the eventpoll interface will
* have an entry of this type linked to the "rbr" RB tree.
* Avoid increasing the size of this struct, there can be many thousands
* of these on a server and we do not want this to take another cache line.
*/
struct epitem {
union {
/* RB tree node links this structure to the eventpoll RB tree */
struct rb_node rbn;
/* Used to free the struct epitem */
struct rcu_head rcu;
};

/* List header used to link this structure to the eventpoll ready list */
// 將這個(gè) epitem 連接到 eventpoll 里面的 rdllist 的 list 指針
struct list_head rdllink;

/*
* Works together "struct eventpoll"->ovflist in keeping the
* single linked chain of items.
*/
struct epitem *next;

/* The file descriptor information this item refers to */
//epoll 監(jiān)聽的 fd
struct epoll_filefd ffd;

/* Number of active wait queue attached to poll operations */
// 一個(gè)文件可以被多個(gè) epoll 實(shí)例所監(jiān)聽,這里就記錄了當(dāng)前文件被監(jiān)聽的次數(shù)
int nwait;

/* List containing poll wait queues */
struct list_head pwqlist;

/* The "container" of this item */
// 當(dāng)前 epollitem 所屬的 eventpoll
struct eventpoll *ep;

/* List header used to link this item to the "struct file" items list */
struct list_head fllink;

/* wakeup_source used when EPOLLWAKEUP is set */
struct wakeup_source __rcu *ws;

/* The structure that describe the interested events and the source fd */
struct epoll_event event;
};

3、eppoll_entry

每次當(dāng)一個(gè) fd 關(guān)聯(lián)到一個(gè) epoll 實(shí)例,就會(huì)有一個(gè) eppoll_entry 產(chǎn)生。eppoll_entry 的結(jié)構(gòu)如下:

/* Wait structure used by the poll hooks */
struct eppoll_entry {
/* List header used to link this structure to the "struct epitem" */
struct list_head llink;

/* The "base" pointer is set to the container "struct epitem" */
struct epitem *base;

/*
* Wait queue item that will be linked to the target file wait
* queue head.
*/
wait_queue_entry_t wait;

/* The wait queue head that linked the "wait" wait queue item */
wait_queue_head_t *whead;
};

二、epoll底層原理

在高并發(fā)場景下,如果有100萬用戶同時(shí)與一個(gè)進(jìn)程保持著TCP連接,而每一時(shí)刻只有幾十個(gè)或幾百個(gè)TCP連接是活躍的(接收TCP包),也就是說在每一時(shí)刻進(jìn)程只需要處理這100萬連接中的一小部分連接。

對于這種場景,select或者poll事件驅(qū)動(dòng)方式采用了輪詢的方式操作系統(tǒng)收集有事件發(fā)生的TCP連接,把這100萬個(gè)連接告訴操作系統(tǒng)。但這里有一個(gè)明顯的問題,在某一時(shí)刻,進(jìn)程收集有事件的連接時(shí),其實(shí)這100萬連接中的大部分都是沒有事件發(fā)生的。因此如果每次收集事件時(shí),都把100萬連接的套接字傳給操作系統(tǒng),從用戶態(tài)內(nèi)存到內(nèi)核態(tài)內(nèi)存的大量復(fù)制,這無疑會(huì)產(chǎn)生巨大的開銷。而由操作系統(tǒng)內(nèi)核尋找這些連接上有沒有未處理的事件,將會(huì)是巨大的資源浪費(fèi),然后select和poll就是這樣做的,因此它們最多只能處理幾千個(gè)并發(fā)連接。

而epoll不這樣做,它在Linux內(nèi)核中申請了一個(gè)簡易的文件系統(tǒng),把原先的一個(gè)select或poll調(diào)用分成了3部分:

int epoll_create(int size);
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
int epoll_wait(int epfd, struct epoll_event *events,int maxevents, int timeout);
  1. 調(diào)用epoll_create建立一個(gè)epoll對象(在epoll文件系統(tǒng)中給這個(gè)句柄分配資源);
  2. 調(diào)用epoll_ctl向epoll對象中添加這100萬個(gè)連接的套接字;
  3. 調(diào)用epoll_wait收集發(fā)生事件的連接。

這樣只需要在進(jìn)程啟動(dòng)時(shí)建立1個(gè)epoll對象,并在需要的時(shí)候向它添加或刪除連接就可以了,因此,在實(shí)際收集事件時(shí),epoll_wait的效率就會(huì)非常高,因?yàn)檎{(diào)用epoll_wait時(shí)并沒有向它傳遞這100萬個(gè)連接,內(nèi)核也不需要去遍歷全部的連接。

1、epoll_create

我們在調(diào)用epoll_create時(shí),內(nèi)核除了幫我們在epoll文件系統(tǒng)里建了個(gè)file結(jié)點(diǎn),在內(nèi)核cache里建了個(gè)紅黑樹用于存儲(chǔ)以后epoll_ctl傳來的socket外,還會(huì)再建立一個(gè)rdllist雙向鏈表,用于存儲(chǔ)準(zhǔn)備就緒的事件,當(dāng)epoll_wait調(diào)用時(shí),僅僅觀察這個(gè)rdllist雙向鏈表里有沒有數(shù)據(jù)即可。有數(shù)據(jù)就返回,沒有數(shù)據(jù)就sleep,等到timeout時(shí)間到后即使鏈表沒數(shù)據(jù)也返回。所以,epoll_wait非常高效。

紅黑樹操作使用的是互斥鎖,在添加和刪除操作時(shí)需要加鎖。

雙向鏈表操作使用的是spinlock自旋鎖,當(dāng)沒有競爭到鎖資源時(shí),不會(huì)睡眠,加快了鏈表操作的速度,添加和刪除操作需要加鎖。

總之,紅黑樹存儲(chǔ)所監(jiān)控的文件描述符的節(jié)點(diǎn)數(shù)據(jù),就緒鏈表存儲(chǔ)就緒的文件描述符的節(jié)點(diǎn)數(shù)據(jù)

圖片

epoll_create工作流程

首先,epoll_create 會(huì)對傳入的 flags 參數(shù)做簡單的驗(yàn)證。

/* Check the EPOLL_* constant for consistency. */
BUILD_BUG_ON(EPOLL_CLOEXEC != O_CLOEXEC);

if (flags & ~EPOLL_CLOEXEC)
return -EINVAL;
/*

接下來,內(nèi)核申請分配 eventpoll 需要的內(nèi)存空間。

/* Create the internal data structure ("struct eventpoll").
*/
error = ep_alloc(&ep);
if (error < 0)
return error;

在接下來,epoll_create 為 epoll 實(shí)例分配了匿名文件和文件描述字,其中 fd 是文件描述字,file 是一個(gè)匿名文件。這里充分體現(xiàn)了 UNIX 下一切都是文件的思想。注意,eventpoll 的實(shí)例會(huì)保存一份匿名文件的引用,通過調(diào)用 fd_install 函數(shù)將匿名文件和文件描述字完成了綁定。

這里還有一個(gè)特別需要注意的地方,在調(diào)用 anon_inode_get_file 的時(shí)候,epoll_create 將 eventpoll 作為匿名文件 file 的 private_data 保存了起來,這樣,在之后通過 epoll 實(shí)例的文件描述字來查找時(shí),就可以快速地定位到 eventpoll 對象了。

最后,這個(gè)文件描述字作為 epoll 的文件句柄,被返回給 epoll_create 的調(diào)用者。

/*
* Creates all the items needed to setup an eventpoll file. That is,
* a file structure and a free file descriptor.
*/
fd = get_unused_fd_flags(O_RDWR | (flags & O_CLOEXEC));
if (fd < 0) {
error = fd;
goto out_free_ep;
}
file = anon_inode_getfile("[eventpoll]", &eventpoll_fops, ep,
O_RDWR | (flags & O_CLOEXEC));
if (IS_ERR(file)) {
error = PTR_ERR(file);
goto out_free_fd;
}
ep->file = file;
fd_install(fd, file);
return fd;

2、epoll_ctl

接下來,我們看一下一個(gè)套接字是如何被添加到 epoll 實(shí)例中的。這就要解析一下 epoll_ctl 函數(shù)實(shí)現(xiàn)了。

查找 epoll 實(shí)例

首先,epoll_ctl 函數(shù)通過 epoll 實(shí)例句柄來獲得對應(yīng)的匿名文件,這一點(diǎn)很好理解,UNIX 下一切都是文件,epoll 的實(shí)例也是一個(gè)匿名文件。

// 獲得 epoll 實(shí)例對應(yīng)的匿名文件
f = fdget(epfd);
if (!f.file)
goto error_return;

接下來,獲得添加的套接字對應(yīng)的文件,這里 tf 表示的是 target file,即待處理的目標(biāo)文件。

/* Get the "struct file *" for the target file */
// 獲得真正的文件,如監(jiān)聽套接字、讀寫套接字
tf = fdget(fd);
if (!tf.file)
goto error_fput;

再接下來,進(jìn)行了一系列的數(shù)據(jù)驗(yàn)證,以保證用戶傳入的參數(shù)是合法的,比如 epfd 真的是一個(gè) epoll 實(shí)例句柄,而不是一個(gè)普通文件描述符。

/* The target file descriptor must support poll */
// 如果不支持 poll,那么該文件描述字是無效的
error = -EPERM;
if (!tf.file->f_op->poll)
goto error_tgt_fput;
...

紅黑樹查找

接下來 epoll_ctl 通過目標(biāo)文件和對應(yīng)描述字,在紅黑樹中查找是否存在該套接字,這也是 epoll 為什么高效的地方。紅黑樹(RB-tree)是一種常見的數(shù)據(jù)結(jié)構(gòu),這里 eventpoll 通過紅黑樹跟蹤了當(dāng)前監(jiān)聽的所有文件描述字,而這棵樹的根就保存在 eventpoll 數(shù)據(jù)結(jié)構(gòu)中。

對于每個(gè)被監(jiān)聽的文件描述字,都有一個(gè)對應(yīng)的 epitem 與之對應(yīng),epitem 作為紅黑樹中的節(jié)點(diǎn)就保存在紅黑樹中。

紅黑樹是一棵二叉樹,作為二叉樹上的節(jié)點(diǎn),epitem 必須提供比較能力,以便可以按大小順序構(gòu)建出一棵有序的二叉樹。其排序能力是依靠 epoll_filefd 結(jié)構(gòu)體來完成的,epoll_filefd 可以簡單理解為需要監(jiān)聽的文件描述字,它對應(yīng)到二叉樹上的節(jié)點(diǎn)。

ep_insert

ep_insert 首先判斷當(dāng)前監(jiān)控的文件值是否超過了 /proc/sys/fs/epoll/max_user_watches 的預(yù)設(shè)最大值,如果超過了則直接返回錯(cuò)誤。接下來是分配資源和初始化動(dòng)作。

if (!(epi = kmem_cache_alloc(epi_cache, GFP_KERNEL)))
return -ENOMEM;

/* Item initialization follow here ... */
INIT_LIST_HEAD(&epi->rdllink);
INIT_LIST_HEAD(&epi->fllink);
INIT_LIST_HEAD(&epi->pwqlist);
epi->ep = ep;
ep_set_ffd(&epi->ffd, tfile, fd);
epi->event = *event;
epi->nwait = 0;
epi->next = EP_UNACTIVE_PTR;

再接下來的事情非常重要,ep_insert 會(huì)為加入的每個(gè)文件描述字設(shè)置回調(diào)函數(shù)。這個(gè)回調(diào)函數(shù)是通過函數(shù) ep_ptable_queue_proc 來進(jìn)行設(shè)置的。這個(gè)回調(diào)函數(shù)是干什么的呢?其實(shí),對應(yīng)的文件描述字上如果有事件發(fā)生,就會(huì)調(diào)用這個(gè)函數(shù),比如套接字緩沖區(qū)有數(shù)據(jù)了,就會(huì)回調(diào)這個(gè)函數(shù)。這個(gè)函數(shù)就是 ep_poll_callback。這里你會(huì)發(fā)現(xiàn),原來內(nèi)核設(shè)計(jì)也是充滿了事件回調(diào)的原理。

/*
* This is the callback that is used to add our wait queue to the
* target file wakeup lists.
*/
static void ep_ptable_queue_proc(struct file *file, wait_queue_head_t *whead,poll_table *pt)
{
struct epitem *epi = ep_item_from_epqueue(pt);
struct eppoll_entry *pwq;

if (epi>nwait >= 0 && (pwq = kmem_cache_alloc(pwq_cache, GFP_KERNEL))) {
init_waitqueue_func_entry(&pwq->wait, ep_poll_callback);
pwq->whead = whead;
pwq->base = epi;
if (epi->event.events & EPOLLEXCLUSIVE)
add_wait_queue_exclusive(whead, &pwq->wait);
else
add_wait_queue(whead, &pwq->wait);
list_add_tail(&pwq->llink, &epi->pwqlist);
epi->nwait++;
} else {
/* We have to signal that an error occurred */
epi->nwait = -1;
}
}

總而言之,當(dāng)我們使用epoll_ctl()函數(shù)注冊一個(gè)socket時(shí),內(nèi)核將會(huì)做這些事情:

  1. 分配一個(gè)紅黑樹節(jié)點(diǎn)對象epitem
  2. 添加等待事件到socket的等待隊(duì)列中
  3. 將epitem插入到epoll對象的紅黑樹中

3、epoll_wait

epoll_wait被調(diào)用時(shí)會(huì)觀察 eventpoll->rdllist 鏈表里有沒有數(shù)據(jù),有數(shù)據(jù)就返回,沒有數(shù)據(jù)就創(chuàng)建一個(gè)等待隊(duì)列項(xiàng),將其添加到 eventpoll 的等待隊(duì)列上(1.1節(jié)中的wait_queue_head_t),然后把自己阻塞掉就結(jié)束。

查找 epoll 實(shí)例

epoll_wait 函數(shù)首先進(jìn)行一系列的檢查,例如傳入的 maxevents 應(yīng)該大于 0。和前面介紹的 epoll_ctl 一樣,通過 epoll 實(shí)例找到對應(yīng)的匿名文件和描述字,并且進(jìn)行檢查和驗(yàn)證。

還是通過讀取 epoll 實(shí)例對應(yīng)匿名文件的 private_data 得到 eventpoll 實(shí)例。


/* The maximum number of event must be greater than zero */
if (maxevents <= 0 || maxevents > EP_MAX_EVENTS)
return -EINVAL;

/* Verify that the area passed by the user is writeable */
if (!access_ok(VERIFY_WRITE, events, maxevents * sizeof(struct epoll_event)))
return -EFAULT;
/* Get the "struct file *" for the eventpoll file */
f = fdget(epfd);
if (!f.file)
return -EBADF;

/*
* We have to check that the file structure underneath the fd
* the user passed to us _is_ an eventpoll file.
*/
error = -EINVAL;
if (!is_file_epoll(f.file))
goto error_fput;

4、總結(jié)

  • 執(zhí)行epoll_create()時(shí),創(chuàng)建了紅黑樹和就緒鏈表;
  • 執(zhí)行epoll_ctl()時(shí),如果增加socket句柄,則檢查在紅黑樹中是否存在,存在立即返回,不存在則添加到樹干上,然后向內(nèi)核注冊回調(diào)函數(shù),用于當(dāng)中斷事件來臨時(shí)向準(zhǔn)備就緒鏈表中插入數(shù)據(jù);
  • 執(zhí)行epoll_wait()時(shí)立刻返回準(zhǔn)備就緒鏈表里的數(shù)據(jù)即可;

三、epoll的兩種觸發(fā)模式

epoll有EPOLLLT和EPOLLET兩種觸發(fā)模式,LT是默認(rèn)的模式,ET是“高速”模式。

LT(水平觸發(fā))模式下,只要這個(gè)文件描述符還有數(shù)據(jù)可讀,每次 epoll_wait都會(huì)返回它的事件,提醒用戶程序去操作;

LT比ET多了一個(gè)開關(guān)EPOLLOUT事件(系統(tǒng)調(diào)用消耗,上下文切換)的步驟;對于監(jiān)聽的sockfd,最好使用水平觸發(fā)模式(參考nginx),邊緣觸發(fā)模式會(huì)導(dǎo)致高并發(fā)情況下,有的客戶端會(huì)連接不上,LT適合處理緊急事件;對于讀寫的connfd,水平觸發(fā)模式下,阻塞和非阻塞效果都一樣,不過為了防止特殊情況,還是建議設(shè)置非阻塞;LT的編程與poll/select接近,符合一直以來的習(xí)慣,不易出錯(cuò);

ET(邊緣觸發(fā))模式下,在它檢測到有 I/O 事件時(shí),通過 epoll_wait 調(diào)用會(huì)得到有事件通知的文件描述符,對于每一個(gè)被通知的文件描述符,如可讀,則必須將該文件描述符一直讀到空,讓 errno 返回 EAGAIN 為止,否則下次的 epoll_wait 不會(huì)返回余下的數(shù)據(jù),會(huì)丟掉事件。如果ET模式不是非阻塞的,那這個(gè)一直讀或一直寫勢必會(huì)在最后一次阻塞。

邊沿觸發(fā)模式很大程度上降低了同一個(gè)epoll事件被重復(fù)觸發(fā)的次數(shù),所以效率更高;對于讀寫的connfd,邊緣觸發(fā)模式下,必須使用非阻塞IO,并要一次性全部讀寫完數(shù)據(jù)。ET的編程可以做到更加簡潔,某些場景下更加高效,但另一方面容易遺漏事件,容易產(chǎn)生bug;

總之,ET和LT各有優(yōu)缺點(diǎn),需要根據(jù)業(yè)務(wù)場景選擇最合適的模式。

四、epoll的不足之處

1.定時(shí)的精度不夠,只到5ms級(jí)別,select可以到0.1ms;

2.當(dāng)連接數(shù)少并且連接都十分活躍的情況下,select和poll的性能可能比epoll好;

3.epoll_ctrl每次只能夠修改一個(gè)fd(kevent可以一次改多個(gè),每次修改,epoll需要一個(gè)系統(tǒng)調(diào)用,不能 batch 操作,可能會(huì)影響性能);

4.可能會(huì)在定時(shí)到期之前返回,導(dǎo)致還需要下一個(gè)epoll_wait調(diào)用;

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報(bào)投訴
  • TCP
    TCP
    +關(guān)注

    關(guān)注

    8

    文章

    1353

    瀏覽量

    79077
  • 文件
    +關(guān)注

    關(guān)注

    1

    文章

    566

    瀏覽量

    24745
  • 數(shù)據(jù)結(jié)構(gòu)

    關(guān)注

    3

    文章

    573

    瀏覽量

    40132
  • epoll
    +關(guān)注

    關(guān)注

    0

    文章

    28

    瀏覽量

    2956
收藏 人收藏

    評(píng)論

    相關(guān)推薦

    什么是數(shù)據(jù)結(jié)構(gòu)(Data Structrue)

    什么是數(shù)據(jù)結(jié)構(gòu)(Data Structrue) 一 名詞術(shù)語數(shù)據(jù):描述客觀事物的數(shù)字,字符以及一切能夠輸入到計(jì)算機(jī)中,并且能夠被計(jì)算機(jī)程序處理的符號(hào)的集合。數(shù)據(jù)元素:數(shù)據(jù)這個(gè)
    發(fā)表于 02-09 17:17

    數(shù)據(jù)結(jié)構(gòu)

    1.數(shù)據(jù)結(jié)構(gòu)的概念 所謂數(shù)據(jù)結(jié)構(gòu)是指由某一數(shù)據(jù)對象及該對象中所有數(shù)據(jù)成員之間的關(guān)系組成的集合。成員之間的關(guān)系有很多種,最常見的是前后件關(guān)系。 2.
    發(fā)表于 03-04 14:13

    常見的數(shù)據(jù)結(jié)構(gòu)

    `數(shù)據(jù)結(jié)構(gòu)在實(shí)際應(yīng)用中非常常見,現(xiàn)在各種算法基本都牽涉到數(shù)據(jù)結(jié)構(gòu),因此,掌握數(shù)據(jù)結(jié)構(gòu)算是軟件工程師的必備技能。一、什么是數(shù)據(jù)結(jié)構(gòu)數(shù)據(jù)結(jié)構(gòu),直
    發(fā)表于 05-10 07:58

    epoll_wait的事件返回的fd為錯(cuò)誤是怎么回事?

    event數(shù)據(jù)結(jié)構(gòu)中的data.fd2、在嵌入式Linux下執(zhí)行返回的 fd 為 0,在Ubuntu下運(yùn)行為4217881
    發(fā)表于 06-12 09:03

    數(shù)據(jù)結(jié)構(gòu)教程,下載

    1. 數(shù)據(jù)結(jié)構(gòu)的基本概念 2. 算法與數(shù)據(jù)結(jié)構(gòu)3. C語言的數(shù)據(jù)類型及其算法描述要點(diǎn)4. 學(xué)習(xí)算法與數(shù)據(jù)結(jié)構(gòu)的意義與方法
    發(fā)表于 05-14 17:22 ?0次下載
    <b class='flag-5'>數(shù)據(jù)結(jié)構(gòu)</b>教程,下載

    GPIB命令的數(shù)據(jù)結(jié)構(gòu)

    針對GPIB命令的結(jié)構(gòu),提出一種存儲(chǔ)GPIB命令的數(shù)據(jù)結(jié)構(gòu)。根據(jù)GPIB命令的層次關(guān)系的特點(diǎn),選擇數(shù)據(jù)結(jié)構(gòu)中“樹”的概念來存儲(chǔ)GPIB命令結(jié)點(diǎn);并考慮程序?qū)崿F(xiàn)的效率問題以及管理維護(hù)
    發(fā)表于 02-10 16:20 ?70次下載

    什么是數(shù)據(jù)結(jié)構(gòu)

    什么是數(shù)據(jù)結(jié)構(gòu) 1、數(shù)據(jù)類型和數(shù)據(jù)結(jié)構(gòu)·數(shù)據(jù)值:atomic data value: 不可再分解。如3、2、5等。nonatomicdata value: 可以再分解,其成分稱為
    發(fā)表于 08-13 13:56 ?1681次閱讀

    數(shù)據(jù)結(jié)構(gòu)與算法

    全國C語言考試公共基礎(chǔ)知識(shí)點(diǎn)——數(shù)據(jù)結(jié)構(gòu)與算法,該資料包含了有關(guān)數(shù)據(jù)結(jié)構(gòu)與算法的全部知識(shí)點(diǎn)。
    發(fā)表于 03-30 14:27 ?0次下載

    數(shù)據(jù)結(jié)構(gòu)

    數(shù)據(jù)結(jié)構(gòu)PPT教程
    發(fā)表于 02-27 16:43 ?0次下載

    數(shù)據(jù)結(jié)構(gòu)是什么_數(shù)據(jù)結(jié)構(gòu)有什么用

    數(shù)據(jù)結(jié)構(gòu)是計(jì)算機(jī)存儲(chǔ)、組織數(shù)據(jù)的方式。數(shù)據(jù)結(jié)構(gòu)是指相互之間存在一種或多種特定關(guān)系的數(shù)據(jù)元素的集合。通常情況下,精心選擇的數(shù)據(jù)結(jié)構(gòu)可以帶來更高
    發(fā)表于 11-17 14:45 ?1.6w次閱讀
    <b class='flag-5'>數(shù)據(jù)結(jié)構(gòu)</b>是什么_<b class='flag-5'>數(shù)據(jù)結(jié)構(gòu)</b>有什么用

    為什么要學(xué)習(xí)數(shù)據(jù)結(jié)構(gòu)數(shù)據(jù)結(jié)構(gòu)的應(yīng)用詳細(xì)資料概述免費(fèi)下載

    本文檔的主要內(nèi)容詳細(xì)介紹的是為什么要學(xué)習(xí)數(shù)據(jù)結(jié)構(gòu)?數(shù)據(jù)結(jié)構(gòu)的應(yīng)用詳細(xì)資料概述免費(fèi)下載包括了:數(shù)據(jù)結(jié)構(gòu)在串口通信當(dāng)中的應(yīng)用,數(shù)據(jù)結(jié)構(gòu)在按鍵監(jiān)測當(dāng)中的應(yīng)用
    發(fā)表于 09-11 17:15 ?13次下載
    為什么要學(xué)習(xí)<b class='flag-5'>數(shù)據(jù)結(jié)構(gòu)</b>?<b class='flag-5'>數(shù)據(jù)結(jié)構(gòu)</b>的應(yīng)用詳細(xì)資料概述免費(fèi)下載

    什么是數(shù)據(jù)結(jié)構(gòu)?為什么要學(xué)習(xí)數(shù)據(jù)結(jié)構(gòu)數(shù)據(jù)結(jié)構(gòu)的應(yīng)用實(shí)例分析

    本文檔的主要內(nèi)容詳細(xì)介紹的是什么是數(shù)據(jù)結(jié)構(gòu)?為什么要學(xué)習(xí)數(shù)據(jù)結(jié)構(gòu)?數(shù)據(jù)結(jié)構(gòu)的應(yīng)用實(shí)例分析包括了:數(shù)據(jù)結(jié)構(gòu)在串口通信當(dāng)中的應(yīng)用,數(shù)據(jù)結(jié)構(gòu)在按鍵
    發(fā)表于 09-26 15:45 ?14次下載
    什么是<b class='flag-5'>數(shù)據(jù)結(jié)構(gòu)</b>?為什么要學(xué)習(xí)<b class='flag-5'>數(shù)據(jù)結(jié)構(gòu)</b>?<b class='flag-5'>數(shù)據(jù)結(jié)構(gòu)</b>的應(yīng)用實(shí)例分析

    一文詳解epoll的實(shí)現(xiàn)原理

    本文以四個(gè)方面介紹epoll的實(shí)現(xiàn)原理,1.epoll數(shù)據(jù)結(jié)構(gòu);2.協(xié)議棧如何與epoll通信;3.epoll線程安全如何加鎖;4.ET與
    的頭像 發(fā)表于 08-01 13:28 ?4122次閱讀

    SystemVerilog中可以嵌套的數(shù)據(jù)結(jié)構(gòu)

    SystemVerilog中除了數(shù)組、隊(duì)列和關(guān)聯(lián)數(shù)組等數(shù)據(jù)結(jié)構(gòu),這些數(shù)據(jù)結(jié)構(gòu)還可以嵌套。
    的頭像 發(fā)表于 11-03 09:59 ?1606次閱讀

    NetApp的數(shù)據(jù)結(jié)構(gòu)是如何演變的

    混合和多云部署模型是企業(yè)IT組織的新常態(tài)。隨著這些復(fù)雜的環(huán)境,圍繞數(shù)據(jù)管理的新挑戰(zhàn)出現(xiàn)了。NetApp的數(shù)據(jù)管理愿景是一種無縫連接不同的數(shù)據(jù)結(jié)構(gòu)云,無論它們是私有環(huán)境、公共環(huán)境還是混合環(huán)境。數(shù)
    發(fā)表于 08-25 17:15 ?0次下載
    NetApp的<b class='flag-5'>數(shù)據(jù)結(jié)構(gòu)</b>是如何演變的