Linux IO復用區別與epoll詳解

轉載:http://blog.csdn.net/hacker00011000/article/details/52160590

一、select、poll、epoll之間的區別總結[整理]?
  select,poll,epoll都是IO多路復用的機制。I/O多路復用就通過一種機制,可以監視多個描述符,一旦某個描述符就緒(一般是讀就緒或者寫就緒),能夠通知程序進行相應的讀寫操作。但select,poll,epoll本質上都是同步I/O,因為他們都需要在讀寫事件就緒后自己負責進行讀寫,也就是說這個讀寫過程是阻塞的,而異步I/O則無需自己負責進行讀寫,異步I/O的實現會負責把數據從內核拷貝到用戶空間。關于這三種IO多路復用的用法,前面三篇總結寫的很清楚,并用服務器回射echo程序進行了測試。連接如下所示:

select:http://www.cnblogs.com/Anker/archive/2013/08/14/3258674.html

poll:http://www.cnblogs.com/Anker/archive/2013/08/15/3261006.html

epoll:http://www.cnblogs.com/Anker/archive/2013/08/17/3263780.html

  今天對這三種IO多路復用進行對比,參考網上和書上面的資料,整理如下:

1、select實現

select的調用過程如下所示:

(1)使用copy_from_user從用戶空間拷貝fd_set到內核空間

(2)注冊回調函數__pollwait

(3)遍歷所有fd,調用其對應的poll方法(對于socket,這個poll方法是sock_poll,sock_poll根據情況會調用到tcp_poll,udp_poll或者datagram_poll)

(4)以tcp_poll為例,其核心實現就是__pollwait,也就是上面注冊的回調函數。

(5)__pollwait的主要工作就是把current(當前進程)掛到設備的等待隊列中,不同的設備有不同的等待隊列,對于tcp_poll來說,其等待隊列是sk->sk_sleep(注意把進程掛到等待隊列中并不代表進程已經睡眠了)。在設備收到一條消息(網絡設備)或填寫完文件數據(磁盤設備)后,會喚醒設備等待隊列上睡眠的進程,這時current便被喚醒了。

(6)poll方法返回時會返回一個描述讀寫操作是否就緒的mask掩碼,根據這個mask掩碼給fd_set賦值。

(7)如果遍歷完所有的fd,還沒有返回一個可讀寫的mask掩碼,則會調用schedule_timeout是調用select的進程(也就是current)進入睡眠。當設備驅動發生自身資源可讀寫后,會喚醒其等待隊列上睡眠的進程。如果超過一定的超時時間(schedule_timeout指定),還是沒人喚醒,則調用select的進程會重新被喚醒獲得CPU,進而重新遍歷fd,判斷有沒有就緒的fd。

(8)把fd_set從內核空間拷貝到用戶空間。

總結:

select的幾大缺點:

(1)每次調用select,都需要把fd集合從用戶態拷貝到內核態,這個開銷在fd很多時會很大

(2)同時每次調用select都需要在內核遍歷傳遞進來的所有fd,這個開銷在fd很多時也很大

(3)select支持的文件描述符數量太小了,默認是1024

2 poll實現

  poll的實現和select非常相似,只是描述fd集合的方式不同,poll使用pollfd結構而不是select的fd_set結構,其他的都差不多。

關于select和poll的實現分析,可以參考下面幾篇博文:

http://blog.csdn.net/lizhiguo0532/article/details/6568964#comments

http://blog.csdn.net/lizhiguo0532/article/details/6568968

http://blog.csdn.net/lizhiguo0532/article/details/6568969

http://www.ibm.com/developerworks/cn/linux/l-cn-edntwk/index.html?ca=drs-

http://linux.chinaunix.net/techdoc/net/2009/05/03/1109887.shtml

3、epoll

  epoll既然是對select和poll的改進,就應該能避免上述的三個缺點。那epoll都是怎么解決的呢?在此之前,我們先看一下epoll和select和poll的調用接口上的不同,select和poll都只提供了一個函數——select或者poll函數。而epoll提供了三個函數,epoll_create,epoll_ctl和epoll_wait,epoll_create是創建一個epoll句柄;epoll_ctl是注冊要監聽的事件類型;epoll_wait則是等待事件的產生。

  對于第一個缺點,epoll的解決方案在epoll_ctl函數中。每次注冊新的事件到epoll句柄中時(在epoll_ctl中指定EPOLL_CTL_ADD),會把所有的fd拷貝進內核,而不是在epoll_wait的時候重復拷貝。epoll保證了每個fd在整個過程中只會拷貝一次。

  對于第二個缺點,epoll的解決方案不像select或poll一樣每次都把current輪流加入fd對應的設備等待隊列中,而只在epoll_ctl時把current掛一遍(這一遍必不可少)并為每個fd指定一個回調函數,當設備就緒,喚醒等待隊列上的等待者時,就會調用這個回調函數,而這個回調函數會把就緒的fd加入一個就緒鏈表)。epoll_wait的工作實際上就是在這個就緒鏈表中查看有沒有就緒的fd(利用schedule_timeout()實現睡一會,判斷一會的效果,和select實現中的第7步是類似的)。

  對于第三個缺點,epoll沒有這個限制,它所支持的FD上限是最大可以打開文件的數目,這個數字一般遠大于2048,舉個例子,在1GB內存的機器上大約是10萬左右,具體數目可以cat /proc/sys/fs/file-max察看,一般來說這個數目和系統內存關系很大。

總結:

(1)select,poll實現需要自己不斷輪詢所有fd集合,直到設備就緒,期間可能要睡眠和喚醒多次交替。而epoll其實也需要調用epoll_wait不斷輪詢就緒鏈表,期間也可能多次睡眠和喚醒交替,但是它是設備就緒時,調用回調函數,把就緒fd放入就緒鏈表中,并喚醒在epoll_wait中進入睡眠的進程。雖然都要睡眠和交替,但是select和poll在“醒著”的時候要遍歷整個fd集合,而epoll在“醒著”的時候只要判斷一下就緒鏈表是否為空就行了,這節省了大量的CPU時間。這就是回調機制帶來的性能提升。

(2)select,poll每次調用都要把fd集合從用戶態往內核態拷貝一次,并且要把current往設備等待隊列中掛一次,而epoll只要一次拷貝,而且把current往等待隊列上掛也只掛一次(在epoll_wait的開始,注意這里的等待隊列并不是設備等待隊列,只是一個epoll內部定義的等待隊列)。這也能節省不少的開銷。

參考資料:

http://www.cnblogs.com/apprentice89/archive/2013/05/09/3070051.html

http://www.linuxidc.com/Linux/2012-05/59873p3.htm

http://xingyunbaijunwei.blog.163.com/blog/static/76538067201241685556302/

http://blog.csdn.net/kkxgx/article/details/7717125

https://banu.com/blog/2/how-to-use-epoll-a-complete-example-in-c/epoll-example.c

二、epoll詳解

前言:?
I/O多路復用有很多種實現。在linux上,2.4內核前主要是select和poll,自Linux 2.6內核正式引入epoll以來,epoll已經成為了目前實現高性能網絡服務器的必備技術。盡管他們的使用方法不盡相同,但是本質上卻沒有什么區別。本文將重點探討將放在EPOLL的實現與使用詳解。?
為什么會是EPOLL?
1、select的缺陷?
高并發的核心解決方案是1個線程處理所有連接的“等待消息準備好”,這一點上epoll和select是無爭議的。但select預估錯誤了一件事,當數十萬并發連接存在時,可能每一毫秒只有數百個活躍的連接,同時其余數十萬連接在這一毫秒是非活躍的。select的使用方法是這樣的: 返回的活躍連接 ==select(全部待監控的連接)。 什么時候會調用select方法呢?在你認為需要找出有報文到達的活躍連接時,就應該調用。所以,調用select在高并發時是會被頻繁調用的。這樣,這個頻繁調用的方法就很有必要看看它是否有效率,因為,它的輕微效率損失都會被“頻繁”二字所放大。它有效率損失嗎?顯而易見,全部待監控連接是數以十萬計的,返回的只是數百個活躍連接,這本身就是無效率的表現。被放大后就會發現,處理并發上萬個連接時,select就完全力不從心了。 此外,在Linux內核中,select所用到的FD_SET是有限的,即內核中有個參數__FD_SETSIZE定義了每個FD_SET的句柄個數。

/linux/posix_types.h:
#define __FD_SETSIZE         1024
  • 1
  • 2

其次,內核中實現 select是用輪詢方法,即每次檢測都會遍歷所有FD_SET中的句柄,顯然,select函數執行時間與FD_SET中的句柄個數有一個比例關系,即 select要檢測的句柄數越多就會越費時。看到這里,您可能要要問了,你為什么不提poll?筆者認為select與poll在內部機制方面并沒有太大的差異。相比于select機制,poll只是取消了最大監控文件描述符數限制,并沒有從根本上解決select存在的問題。 接下來我們看張圖,當并發連接為較小時,select與epoll似乎并無多少差距。可是當并發連接上來以后,select就顯得力不從心了。?
主流I/O復用機制的benchmark:?
這里寫圖片描述

2、epoll高效的奧秘:?
epoll精巧的使用了3個方法來實現select方法要做的事:?
新建epoll描述符==epoll_create() epoll_ctrl(epoll描述符,添加或者刪除所有待監控的連接) 返回的活躍連接 ==epoll_wait( epoll描述符 ) 與select相比,epoll分清了頻繁調用和不頻繁調用的操作。例如,epoll_ctrl是不太頻繁調用的,而epoll_wait是非常頻繁調用的。這時,epoll_wait卻幾乎沒有入參,這比select的效率高出一大截,而且,它也不會隨著并發連接的增加使得入參越發多起來,導致內核執行效率下降。 筆者在這里不想過多貼出epoll的代碼片段。如果大家有興趣,可以參考文末貼出的博文鏈接和Linux相關源碼。

要深刻理解epoll,首先得了解epoll的三大關鍵要素:mmap、紅黑樹、鏈表?
epoll是通過內核與用戶空間mmap同一塊內存實現的。mmap將用戶空間的一塊地址和內核空間的一塊地址同時映射到相同的一塊物理內存地址(不管是用戶空間還是內核空間都是虛擬地址,最終要通過地址映射映射到物理地址),使得這塊物理內存對內核和對用戶均可見,減少用戶態和內核態之間的數據交換。

epoll上就是相當減少了epoll監聽的句柄從用戶態copy到內核態,內核可以直接看到epoll監聽的句柄,效率高

紅黑樹將存儲epoll所監聽的套接字。上面mmap出來的內存如何保存epoll所監聽的套接字,必然也得有一套數據結構,epoll在實現上采用紅黑樹去存儲所有套接字,當添加或者刪除一個套接字時(epoll_ctl),都在紅黑樹上去處理,紅黑樹本身插入和刪除性能比較好,時間復雜度O(lgN)。

這里寫圖片描述

下面幾個關鍵數據結構的定義

struct epitem
{struct rb_node rbn;            //用于主結構管理的紅黑樹struct list_head rdllink;       //事件就緒隊列struct epitem *next;           //用于主結構體中的鏈表struct epoll_filefd ffd;         //每個fd生成的一個結構int nwait;                struct list_head pwqlist;     //poll等待隊列struct eventpoll *ep;          //該項屬于哪個主結構體struct list_head fllink;         //鏈接fd對應的file鏈表struct epoll_event event;  //注冊的感興趣的事件,也就是用戶空間的
}
struct eventpoll
{spin_lock_t lock;            //對本數據結構的訪問struct mutex mtx;            //防止使用時被刪除wait_queue_head_t wq;        //sys_epoll_wait() 使用的等待隊列wait_queue_head_t poll_wait; //file->poll()使用的等待隊列struct list_head rdllist;    //事件滿足條件的鏈表struct rb_root rbr;          //用于管理所有fd的紅黑樹struct epitem *ovflist;      //將事件到達的fd進行鏈接起來發送至用戶空間
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

3、添加以及返回事件:

通過epoll_ctl函數添加進來的事件都會被放在紅黑樹的某個節點內,所以,重復添加是沒有用的。當把事件添加進來的時候時候會完成關鍵的一步,那就是該事件都會與相應的設備(網卡)驅動程序建立回調關系,當相應的事件發生后,就會調用這個回調函數,該回調函數在內核中被稱為:ep_poll_callback,這個回調函數其實就所把這個事件添加到rdlist這個雙向鏈表中。一旦有事件發生,epoll就會將該事件添加到雙向鏈表中。那么當我們調用epoll_wait時,epoll_wait只需要檢查rdlist雙向鏈表中是否有存在注冊的事件,效率非常可觀。這里也需要將發生了的事件復制到用戶態內存中即可。

4、epoll_wait的工作流程:

epoll_wait調用ep_poll,當rdlist為空(無就緒fd)時掛起當前進程,直到rdlist不空時進程才被喚醒。 文件fd狀態改變(buffer由不可讀變為可讀或由不可寫變為可寫),導致相應fd上的回調函數ep_poll_callback()被調用。 ep_poll_callback將相應fd對應epitem加入rdlist,導致rdlist不空,進程被喚醒,epoll_wait得以繼續執行。 ep_events_transfer函數將rdlist中的epitem拷貝到txlist中,并將rdlist清空。 ep_send_events函數(很關鍵),它掃描txlist中的每個epitem,調用其關聯fd對用的poll方法。此時對poll的調用僅僅是取得fd上較新的events(防止之前events被更新),之后將取得的events和相應的fd發送到用戶空間(封裝在struct epoll_event,從epoll_wait返回)。

需要注意的是:epoll并不是在所有的應用場景都會比select和poll高很多。尤其是當活動連接比較多的時候,回調函數被觸發得過于頻繁的時候,epoll的效率也會受到顯著影響!所以,epoll特別適用于連接數量多,但活動連接較少的情況。

5、epoll使用方式的注意點:?
5.1:文件描述符的創建

 #include <sys/epoll.h>int epoll_create ( int size );
  • 1
  • 2

在epoll早期的實現中,對于監控文件描述符的組織并不是使用紅黑樹,而是hash表。這里的size實際上已經沒有意義?
5.2:注冊監控事件

#include <sys/epoll.h>
int epoll_ctl ( int epfd, int op, int fd, struct epoll_event *event );
  • 1
  • 2

函數說明: fd:要操作的文件描述符 op:指定操作類型 操作類型: EPOLL_CTL_ADD:往事件表中注冊fd上的事件 EPOLL_CTL_MOD:修改fd上的注冊事件 EPOLL_CTL_DEL:刪除fd上的注冊事件 event:指定事件,它是epoll_event結構指針類型 epoll_event定義:

struct epoll_event
{__unit32_t events;    // epoll事件epoll_data_t data;     // 用戶數據
};
  • 1
  • 2
  • 3
  • 4
  • 5

結構體說明: events:描述事件類型,和poll支持的事件類型基本相同(兩個額外的事件:EPOLLET和EPOLLONESHOT,高效運作的關鍵) data成員:存儲用戶數據

typedef union epoll_data
{void* ptr;              //指定與fd相關的用戶數據int fd;                 //指定事件所從屬的目標文件描述符uint32_t u32;uint64_t u64;
} epoll_data_t;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

5.3:epoll_wait函數

#include <sys/epoll.h>
int epoll_wait ( int epfd, struct epoll_event* events, int maxevents, int timeout );
  • 1
  • 2

返回:成功時返回就緒的文件描述符的個數,失敗時返回-1并設置errno timeout:指定epoll的超時時間,單位是毫秒。當timeout為-1是,epoll_wait調用將永遠阻塞,直到某個時間發生。當timeout為0時,epoll_wait調用將立即返回。 maxevents:指定最多監聽多少個事件 events:檢測到事件,將所有就緒的事件從內核事件表中復制到它的第二個參數events指向的數組中。

6、EPOLLONESHOT事件:?
使用場合: 一個線程在讀取完某個socket上的數據后開始處理這些數據,而數據的處理過程中該socket又有新數據可讀,此時另外一個線程被喚醒來讀取這些新的數據。 于是,就出現了兩個線程同時操作一個socket的局面。可以使用epoll的EPOLLONESHOT事件實現一個socket連接在任一時刻都被一個線程處理。 作用: 對于注冊了EPOLLONESHOT事件的文件描述符,操作系統最多出發其上注冊的一個可讀,可寫或異常事件,且只能觸發一次。 使用: 注冊了EPOLLONESHOT事件的socket一旦被某個線程處理完畢,該線程就應該立即重置這個socket上的EPOLLONESHOT事件,以確保這個socket下一次可讀時,其EPOLLIN事件能被觸發,進而讓其他工作線程有機會繼續處理這個sockt。 效果: 盡管一個socket在不同事件可能被不同的線程處理,但同一時刻肯定只有一個線程在為它服務,這就保證了連接的完整性,從而避免了很多可能的競態條件。

7、LT與ET模式:?
在這里,筆者強烈推薦《徹底學會使用epoll》系列博文,這是筆者看過的,對epoll的ET和LT模式講解最為詳盡和易懂的博文。下面的實例均來自該系列博文。限于篇幅原因,很多關鍵的細節,不能完全摘錄。?
******Code*******?
Code1:?
這里寫圖片描述?
這里寫圖片描述?
Result1:

這里寫圖片描述

當用戶輸入一組字符,這組字符被送入buffer,字符停留在buffer中,又因為buffer由空變為不空,所以ET返回讀就緒,輸出”welcome to epoll’s world!”。 之后程序再次執行epoll_wait,此時雖然buffer中有內容可讀,但是根據我們上節的分析,ET并不返回就緒,導致epoll_wait阻塞。(底層原因是ET下就緒fd的epitem只被放入rdlist一次)。 用戶再次輸入一組字符,導致buffer中的內容增多,根據我們上節的分析這將導致fd狀態的改變,是對應的epitem再次加入rdlist,從而使epoll_wait返回讀就緒,再次輸出“Welcome to epoll’s world!”。

接下來我們將上面程序的第11行做如下修改:

ev.events=EPOLLIN;    //默認使用LT模式
  • 1

這里寫圖片描述?
程序陷入死循環,因為用戶輸入任意數據后,數據被送入buffer且沒有被讀出,所以LT模式下每次epoll_wait都認為buffer可讀返回讀就緒。導致每次都會輸出”welcome to epoll’s world!”

Code2:?
這里寫圖片描述?
這里寫圖片描述

Result2:?
這里寫圖片描述?
本程序依然使用LT模式,但是每次epoll_wait返回讀就緒的時候我們都將buffer(緩沖)中的內容read出來,所以導致buffer再次清空,下次調用epoll_wait就會阻塞。所以能夠實現我們所想要的功能——當用戶從控制臺有任何輸入操作時,輸出”welcome to epoll’s world!”

Code3:?
這里寫圖片描述?
這里寫圖片描述

Result3:?
這里寫圖片描述?
程序依然使用ET,但是每次讀就緒后都主動的再次MOD IN事件,我們發現程序再次出現死循環,也就是每次返回讀就緒。但是注意,如果我們將MOD改為ADD,將不會產生任何影響。別忘了每次ADD一個描述符都會在epitem組成的紅黑樹中添加一個項,我們之前已經ADD過一次,再次ADD將阻止添加,所以在次調用ADD IN事件不會有任何影響。

Code4:?
這里寫圖片描述?
這里寫圖片描述

Result4:?
這里寫圖片描述?
這個程序的功能是只要標準輸出寫就緒,就輸出“welcome to epoll’s world”。我們發現這將是一個死循環。下面具體分析一下這個程序的執行過程:?
首先初始buffer為空,buffer中有空間可寫,這時無論是ET還是LT都會將對應的epitem加入rdlist,導致epoll_wait就返回寫就緒。 程序想標準輸出輸出”welcome to epoll’s world”和換行符,因為標準輸出為控制臺的時候緩沖是“行緩沖”,所以換行符導致buffer中的內容清空,這就對應第二節中ET模式下寫就緒的第二種情況——當有舊數據被發送走時,即buffer中待寫的內容變少得時候會觸發fd狀態的改變。所以下次epoll_wait會返回寫就緒。如此循環往復。

Code5:?
這里寫圖片描述?
這里寫圖片描述

Result5:?
這里寫圖片描述?
與程序四相比,程序五只是將輸出語句的printf的換行符移除。我們看到程序成掛起狀態。因為第一次epoll_wait返回寫就緒后,程序向標準輸出的buffer中寫入“welcome to epoll’s world!”,但是因為沒有輸出換行,所以buffer中的內容一直存在,下次epoll_wait的時候,雖然有寫空間但是ET模式下不再返回寫就緒。回憶第一節關于ET的實現,這種情況原因就是第一次buffer為空,導致epitem加入rdlist,返回一次就緒后移除此epitem,之后雖然buffer仍然可寫,但是由于對應epitem已經不再rdlist中,就不會對其就緒fd的events的在檢測了。

Code6:?
這里寫圖片描述?
這里寫圖片描述

Result6:?
這里寫圖片描述?
程序六相對程序五僅僅是修改ET模式為默認的LT模式,我們發現程序再次死循環。這時候原因已經很清楚了,因為當向buffer寫入”welcome to epoll’s world!”后,雖然buffer沒有輸出清空,但是LT模式下只有buffer有寫空間就返回寫就緒,所以會一直輸出”welcome to epoll’s world!”,當buffer滿的時候,buffer會自動刷清輸出,同樣會造成epoll_wait返回寫就緒。

Code7:?
這里寫圖片描述?
這里寫圖片描述

Result7:?
這里寫圖片描述?
程序七相對于程序五在每次向標準輸出的buffer輸出”welcome to epoll’s world!”后,重新MOD OUT事件。所以相當于每次都會返回就緒,導致程序循環輸出。

8、分析:?
經過前面的案例分析,我們已經了解到,當epoll工作在ET模式下時,對于讀操作,如果read一次沒有讀盡buffer中的數據,那么下次將得不到讀就緒的通知,造成buffer中已有的數據無機會讀出,除非有新的數據再次到達。對于寫操作,主要是因為ET模式下fd通常為非阻塞造成的一個問題——如何保證將用戶要求寫的數據寫完。?
要解決上述兩個ET模式下的讀寫問題,我們必須實現:?
對于讀,只要buffer中還有數據就一直讀; 對于寫,只要buffer還有空間且用戶請求寫的數據還未寫完,就一直寫。

9、ET模式下的accept問題?
請思考以下一種場景:在某一時刻,有多個連接同時到達,服務器的 TCP 就緒隊列瞬間積累多個就緒連接,由于是邊緣觸發模式,epoll 只會通知一次,accept 只處理一個連接,導致 TCP 就緒隊列中剩下的連接都得不到處理。在這種情形下,我們應該如何有效的處理呢??
解決的方法是:解決辦法是用 while 循環抱住 accept 調用,處理完 TCP 就緒隊列中的所有連接后再退出循環。如何知道是否處理完就緒隊列中的所有連接呢? accept 返回 -1 并且 errno 設置為 EAGAIN 就表示所有連接都處理完。?
關于ET的accept問題,這篇博文的參考價值很高,如果有興趣,可以鏈接過去圍觀一下。

9.1、ET模式為什么要設置在非阻塞模式下工作?
因為ET模式下的讀寫需要一直讀或寫直到出錯(對于讀,當讀到的實際字節數小于請求字節數時就可以停止),而如果你的文件描述符如果不是非阻塞的,那這個一直讀或一直寫勢必會在最后一次阻塞。這樣就不能在阻塞在epoll_wait上了,造成其他文件描述符的任務餓死。

10、小結?
LT:水平觸發,效率會低于ET觸發,尤其在大并發,大流量的情況下。但是LT對代碼編寫要求比較低,不容易出現問題。LT模式服務編寫上的表現是:只要有數據沒有被獲取,內核就不斷通知你,因此不用擔心事件丟失的情況。?
ET:邊緣觸發,效率非常高,在并發,大流量的情況下,會比LT少很多epoll的系統調用,因此效率高。但是對編程要求高,需要細致的處理每個請求,否則容易發生丟失事件的情況。?
從本質上講:與LT相比,ET模型是通過減少系統調用來達到提高并行效率的。?
epoll使用的梳理與總結到這里就告一段落了。限于篇幅原因,很多細節都被略過了。后面參考給出的鏈接,強烈推薦閱讀。疏謬之處,萬望斧正!


本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/384298.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/384298.shtml
英文地址,請注明出處:http://en.pswp.cn/news/384298.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

簡單圖和多重圖

一、簡單圖 ?? ① 不存在重復邊&#xff1b; ?? ② 不存在頂點到自身的邊&#xff1b; 二、多重圖 ??① 某兩結點之間邊數多于一條&#xff1b; ??② 允許頂點通過一條邊和自己關聯&#xff1b;

C++筆記:select多路復用機制

轉載&#xff1a;http://blog.csdn.net/qdx411324962/article/details/42499535 函數作用&#xff1a; 系統提供select函數來實現多路復用輸入/輸出模型。select系統調用是用來讓我們的程序監視多個文件句柄的狀態變化的。程序會停在select這里等待&#xff0c;直到被監視的文件…

交叉編譯執行應用程序出現:No such file or directory

問題分析 當我在arm板子上執行交叉編譯過的程序的時候發現了這個錯誤。通過百度查詢基本都是缺少32位庫什么的,但是都不能解決問題。 然后我用ll指令&#xff0c;也排除了權限的原因。 我們用ldd指令發現&#xff0c;它不是動態執行的&#xff0c;雖然我們可以使用-static指…

select、poll、epoll 比較

轉載&#xff1a;http://blog.csdn.net/dodo_328/article/details/39081183 1.Selet&#xff1a;本質上是通過設置或者檢查存放fd標志位的數據結構來進行下一步處理。 缺點&#xff1a;1 單個進程可監視的fd數量被限制&#xff0c;因為受描述符集合fd_set限制&#xff0c;fd數量…

C庫函數 File

C庫函數常用的有&#xff1a;fopen, fclose, fread, fwrite, fgets, fputs, fscanf, fprintf, fseek, fgetc, fputc, ftell, feof, flush等&#xff0c; 當使用fopen打開一個文件時通常返回一個文件指針 FILE *fp。FILE類型是一個結構體&#xff0c;包含文件描述符&#xff08;…

Unix 網絡編程(四)- 典型TCP客服服務器程序開發實例及基本套接字API介紹

轉載&#xff1a;http://blog.csdn.net/michael_kong_nju/article/details/43457393 寫在開頭&#xff1a; 在上一節中我們學習了一些基礎的用來支持網絡編程的API&#xff0c;包括“套接字的地址結構”、“字節排序函數”等。這些API幾乎是所有的網絡編程中都會使用的一些&…

C庫函數與系統函數的關系

轉載于:https://www.cnblogs.com/lr1402585172/p/10464933.html

Unix網絡編程(六)高級I/O技術之復用技術 select

轉載&#xff1a;http://blog.csdn.net/michael_kong_nju/article/details/44887411 I/O復用技術 本文將討論網絡編程中的高級I/O復用技術&#xff0c;將從下面幾個方面進行展開&#xff1a; a. 什么是復用技術呢&#xff1f; b. 什么情況下需要使用復用技術呢&#xff1f; c. …

open、read、write、文件類型

open&#xff0c;打開一個文件、創建一個文件或判斷一個文件是否存在。 頭文件&#xff1a;<sys/types.h> <sys/stat.h> <fcntl.h> 重載函數有&#xff1a;int open(const char *pathname, int flags) int open(const char *pathname, int flags, mode_t m…

用模板寫單鏈表 尹成

轉載&#xff1a;http://blog.csdn.net/itcastcpp/article/details/39081953 為了加深對模板的理解&#xff0c;我們今天一起用模板寫一個單鏈表&#xff0c;希望通過這個例子&#xff0c;能夠幫助大家加深對模板的體會&#xff0c;具體如下&#xff1a; SList.hpp內容&#xf…

lseek、stat、access、chmod、strtol、truncate、unlink

lseek&#xff0c;可實現計算文件長度&#xff0c;以及文件擴展。 int ret lseek(fd, 0, SEEK_END); //文件長度printf("file lendth %d\n", ret); int ret lseek(fd, 2000, SEEK_END); //文件拓展2000個byte 在文件末尾偏移2000printf("return va…

inode淺談

索引節點inode&#xff1a;保存的其實是實際的數據的一些信息&#xff0c;這些信息稱為“元數據”(也就是對文件屬性的描述)。例如&#xff1a;文件大小&#xff0c;設備標識符&#xff0c;用戶標識符&#xff0c;用戶組標識符&#xff0c;文件模式&#xff0c;擴展屬性&#x…

Openssl-MD5

http://blog.csdn.net/sunspider107/article/details/7395904 MD5是最常用的一個信息摘要算法&#xff0c;雖然現在慢慢被SHA1算法替代&#xff0c;但還是應用廣泛。 MD5的計算結果是16個字節。 int MD5_Init(MD5_CTX *c); 初始化MD5 Context參數&#xff1b; c: MD5 context; …

opendir、readdir以及使用

opendir&#xff0c;打開一個目錄。 函數原型&#xff1a;DIR *opendir(const char *name) DIR *fopendir(int fd) DIR是一個結構指針&#xff0c;是一個內部結構&#xff0c;保存所打開的目錄信息。函數出錯返回NULL readdir&#xff0c;讀目錄 ,<dirent.h> 函數原型&am…

Linux下C語言使用openssl庫進行MD5校驗

http://blog.csdn.net/cassie_huang/article/details/53212933 作者&#xff1a;無腦仔的小明 出處&#xff1a;http://www.cnblogs.com/wunaozai/ 我們以一個字符串為例&#xff0c;新建一個文件filename.txt&#xff0c;在文件內寫入hello &#xff0c;然后在Linux下可以使…

dup、dup2、fcntl

dup、dup2&#xff0c;復制文件描述符 int dup(int oldfd);  //返回文件描述表中沒有被占用的最小可用的描述符&#xff0c;新舊描述符作用相同 int dup2(int oldfd, int newfd);  //如果new已經被打開&#xff0c;先關閉再拷貝就會指向同一個文件&#xff0c;如果old和new…

進程

創建子進程&#xff1a;fork調用&#xff0c; 一次fork調用返回兩個值&#xff0c;1、返回子進程的pid&#xff08;非負整數&#xff09; 2、返回0 父進程的fork返回子進程的id&#xff0c;子進程的fork返回0&#xff08;表示執行成功&#xff09; 創建單個子進程&#xff1a; …

Ubuntu在vmware虛擬機無法上網的解決方法

http://blog.csdn.net/xueyushenzhou/article/details/50460183 在vmware中安裝Ubuntu之后&#xff0c;我們希望基本的功能如上網、傳輸文件等功能都是可用的&#xff0c;但是經常遇到不能上網的情況。使用筆記本時&#xff0c;我們經常希望能通過無線網卡上網&#xff0c;但是…

exec函數族

fork創建子進程后執行的是和父進程相同的程序&#xff08;但有可能執行不同的代碼分支&#xff09;&#xff0c;子進程往往要調用一種exec函數以執行另一個程序。當進程調用一種exec函數時&#xff0c;該進程的用戶空間代碼和數據完全被新程序替換&#xff0c;從新程序的啟動例…

IO 多路復用之poll總結

http://www.cnblogs.com/Anker/p/3261006.html IO多路復用之poll總結 1、基本知識 poll的機制與select類似&#xff0c;與select在本質上沒有多大差別&#xff0c;管理多個描述符也是進行輪詢&#xff0c;根據描述符的狀態進行處理&#xff0c;但是poll沒有最大文件描述符數量的…