<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                [TOC] 轉載:http://www.ilovecpp.com/2018/04/20/lievent-trick/ ## 概述 閱讀libevent源碼時,搞懂它的網絡處理邏輯,應該是一件比較容易的事兒。但是我們不應該局限于此,深入每一處細節,體會作者的設計意圖才是學習libevent的最佳”食用”方式。學習掌握這些開源庫里邊的通用編程技法,對提高我們的編程能力大有好處。 ## 數據封裝 libevent作為一個開源庫,要盡量降低與庫使用者的耦合度。這就要求庫的模塊設計要盡量少的暴露其實現細節,這也是我們在面向對象編程里經常提到的——數據封裝。封裝的最終目的是實現信息隱藏(Information Hiding)。 libevent是基于C語言的,在C語言中沒有類(class)的概念,只有struct的概念。struct對數據成員的訪問并沒有如C++般精細的權限控制(public/private/protected)。所有的數據成員對外都是可見的,struct對數據的包裝只能看作做”打包”而不是”封裝”。 libevent大量的采用了不透明指針,來實現對數據的封裝。對于庫使用者libevent僅提供了`event.h`,在這個文件里邊我們完全看不到event的內部細節,只能看到一個event結構的聲明。 ``` struct event #ifdef EVENT_IN_DOXYGEN_ {/*Empty body so that doxygen will generate documentation here.*/} #endif ``` event結構的完整定義放在了`event_struct.h`文件中。其中的數據成員,我們放到后邊的博客里邊再分析,這里僅作演示。作為庫使用者而言,只能通過lievent提供的操作函數對event的數據成員進行訪問。比如要計算struct event的大小,我們是不能直接進行sizeof操作的,如果你堅持這么做的話,編譯器會立馬罷工,給你返回一個:”invalid application of ‘sizeof’ to incomplete type ‘struct event’ “,只能通過庫作者提供的`event_get_struct_event_size()`接口達到同樣的目的。這樣就對struct event實現了信息隱藏,達到了封裝的效果。 指向這種看不到內部數據的結構體指針,有一個專業的術語——不透明指針(opaque pointer)。這種技術在設計模式中被稱為`橋接模式`(Bridge pattern),有時候也被稱為”Pimpl技法”或者”編譯防火墻”或者”D-Pointer”。 這樣做還有一個好處就是二進制兼容。 ## 二進制兼容 二進制兼容的意思就是,在升級庫的時候不必重新編譯使用了該庫的軟件。Qt在這方面做的就很出色,可以參閱Qt里邊的`D-Pointer`技術[https://wiki.qt.io/D-Pointer](https://wiki.qt.io/D-Pointer)。 由于我們不直接依賴`struct event`的內部結構,如果新的版本中libevent往event的數據結構中新添加、修改了一個成員,event.h頭文件并不需要跟著修改,那么我們僅僅需要替換掉libevent的動態庫,而不需要重新編譯整個的項目。 這部分就不展開講了,因為網上有一篇陳碩的文章寫的肯定會比我寫的要清楚得多,原文在這里:[https://blog.csdn.net/Solstice/article/details/6233478](https://blog.csdn.net/Solstice/article/details/6233478)。 ## 多態 你沒有看錯,多態并不是面向對象語言的專利。C++之父Bjarne Stroustrup曾經給多態下的定義是”providing a single interface to entities of different types”。根據這個定義,C語言也能模擬出”多態”的行為,即使libevent這里沒有繼承的關系。 多態可以分為子類型多態(Subtype polymorphism)和包含多態(inclusion polymorphism),嚴格來說我以為,我們模擬出來的多態應當屬于包含多態。其實多態的類型還有很多:[https://en.wikipedia.org/wiki/Polymorphism\_(computer\_science)](https://en.wikipedia.org/wiki/Polymorphism_(computer_science)),有興趣可以深入探究。 C語言規定了在struct中只能定義變量,不可以定義函數。但是稍加變通,可以使用函數指針來代替。在C++中,是通過一個虛函數表([https://blog.csdn.net/haoel/article/details/1948051/](https://blog.csdn.net/haoel/article/details/1948051/)) 實現函數的動態綁定的。我們只需要在C語言中模擬出一個”虛函數表”,來實現多態。libevent正是這么干的。 ``` struct eventop { const char *name; void *(*init)(struct event_base *); int (*add)(struct event_base *, evutil_socket_t fd, short old, short events, void *fdinfo); int (*del)(struct event_base *, evutil_socket_t fd, short old, short events, void *fdinfo); int (*dispatch)(struct event_base *, struct timeval *); void (*dealloc)(struct event_base *); int need_reinit; enum event_method_feature features; size_t fdinfo_len; }; struct event_base { /** Function pointers and other data to describe this event_base's * backend. */ const struct eventop *evsel; ... } ``` event\_base中的第一個成員變量evsel,代表了libevent將要采用的多路復用后端。它是`const struct eventop *`,而`struct eventop`這個結構則包含了5個函數指針。每種多路復用機制都需要封裝成這五個函數接口的形式,提供其初始化、釋放以及對事件的注冊、注銷和分發。 ` ` `struct eventop`本質上可以看成一張”虛函數”表 ``` /* Array of backends in order of preference. */ static const struct eventop *eventops[] = { #ifdef EVENT__HAVE_EVENT_PORTS &evportops, #endif #ifdef EVENT__HAVE_WORKING_KQUEUE &kqops, #endif #ifdef EVENT__HAVE_EPOLL &epollops, #endif #ifdef EVENT__HAVE_DEVPOLL &devpollops, #endif #ifdef EVENT__HAVE_POLL &pollops, #endif #ifdef EVENT__HAVE_SELECT &selectops, #endif #ifdef _WIN32 &win32ops, #endif NULL }; ``` eventops數組里存放的就是`struct eventop`所有可取的值。 ``` struct event_base* event_base_new_with_config(const struct event_config *cfg) { ... for (i = 0; eventops[i] && !base->evbase; i++) { ... base->evsel = eventops[i]; base->evbase = base->evsel->init(base); } ... } ``` event\_base\_new\_with\_config中將eventops數組內的值,動態的賦值給base->evsel。這樣就實現了在不同的平臺之上,將不同的多路復用后端賦給了evesl。因而evsel->init、evsel->dispatch等操作才會在不同的平臺上,展現出不同的行為——多態。 此外即使經過預處理后,eventops數組的值只有一個,evsel的值也是運行期在event\_base\_new\_with\_config中才被確定的,依然是動態多態(dynamic polymorphism)。 ## 不重復造輪子 C語言的標準庫里并沒有像C++中的STL,要使用`list`、`queue`等數據結構只能靠自己。libevent并沒有重復造輪子,而是使用的glibc提供的list、tail queue等數據結構。Linux上這些數據結構都包含在頭文件`/usr/include/sys/queue.h`中,操作都是通過宏函數實現的。因此要想復用這些數據結構僅僅需要拷貝一份頭文件即可,libevent源碼里的`.\compat\sys\queue.h`就是拷貝的glibc上的。 這些數據結構都是經受過時間的考驗的,肯定比我們一般人實現出來的數據結構要穩定、高效得多。這些數據結構的用法,不多贅述了,Linux manual page上有非常詳細的使用說明和example:[http://man7.org/linux/man-pages/man3/queue.3.html](http://man7.org/linux/man-pages/man3/queue.3.html)。 除了是使用宏實現的意外,這些數據結構還有一個特點,prev使用的是`二級指針`。以List為例,libevent中是這樣使用的: ``` #define LIST_HEAD(name, type) \ struct name { \ struct type *lh_first; /* first element */ \ } #define LIST_HEAD_INITIALIZER(head) \ { NULL } #define LIST_ENTRY(type) \ struct { \ struct type *le_next; /* next element */ \ struct type **le_prev; /* address of previous next element */ \ } ... LIST_HEAD (event_dlist, event); struct event { union { struct { LIST_ENTRY (event) ev_io_next; struct timeval ev_timeout; } ev_io; /* used by signal events */ struct { LIST_ENTRY (event) ev_signal_next; short ev_ncalls; /* Allows deletes in callback */ short *ev_pncalls; } ev_signal; } ev_; ... }; ``` event\_dlist表示一個event鏈表,示意圖如下: ![](https://baixiangcpp.oss-cn-shanghai.aliyuncs.com/blog/libevent-trick/evlist.png) 需要注意的是`le_prev`,它指向的不是前一個event節點,而是指向前一個event節點里的le\_next指針的指針。這樣做有一個好處,當刪除一個event節點的時候,不需要判斷le\_prev指針是否為空,可以大膽的對其解引用,因為它不可能為空,即使它是list里第一個元素,le\_prev指向了event\_dlist里邊的lh\_fisrst。 `le_prev`這樣的設計,正是Linus大神推崇的”core low-level coding”:[https://coolshell.cn/articles/8990.html](https://coolshell.cn/articles/8990.html)。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看