開發利器——C語言必備實用第三方庫

本文轉載自本人頭條號: https://www.toutiao.com/i6926789516594479624/
轉載請註明出處,感謝!

對於廣大C語言開發者來講,缺少相似C++ STL和Boost的庫會讓開發受制於基礎庫的匱乏,也所以致使了開發效率的驟降。這也使得例如libevent這類事件庫(基礎組件庫)一時間大紅大紫。git

今天,碼哥給你們帶來一款基礎庫,這套庫不只僅提供了經常使用的數據結構、算法,如紅黑樹、斐波那契堆、隊列、KMP算法、RSA算法、各種哈希算法、數據恢復算法等等,還提供了多進程框架、多線程框架、跨平臺高性能事件等實用內容。注意:這是一款不依賴第三方的庫。github

除此之外,它也是筆者以前文章(Melang腳本語言)中的核心庫。這也就意味着,使用該庫,不只能夠快速得到上述內容,還可讓開發者所構建的系統很方便地引入腳本語言的功能。web

它就是——Melon算法

Github:https://github.com/Water-Melon/Melonvim

下面,碼哥便帶諸位一覽這個庫的功能。緩存

數據結構

Melon中包含以下數據結構的實現:bash

  • 雙向鏈表
  • 斐波那契堆
  • 哈希表
  • 隊列
  • 紅黑樹

其中:websocket

  • 雙向鏈表使用宏實現,能夠經過兩行宏函數便可完成雙向隊列插入和刪除操做的聲明和定義。
  • 斐波那契堆是一個最小堆,在庫中的事件功能中用於實現定時器的維護管理,固然,也能夠單獨使用。

以上結構幾乎都可在其對應名稱的頭文件中找到數據結構定義以及函數定義。數據結構

通常狀況下,數據結構的使用都是函數調用形式,所以也儘量下降了不一樣組件間的耦合度。多線程

算法

Melon中包含的算法以下:

  • 加密算法:AES、DES、3DES、RC四、RSA
  • 哈希算法:MD五、SHA一、SHA256
  • Base64
  • 大數計算
  • FEC
  • JSON
  • 矩陣運算
  • 裏德所羅門編碼
  • 正則匹配算法
  • KMP

如上算法基本都在其各自頭文件中能夠找到對應的函數聲明以及必要的數據結構定義。

其中,FEC與裏德所羅門編碼均屬於糾錯碼,FEC經常使用於RTP中作數據修復,而裏德所羅門編碼既能夠用於實時語音中丟包恢復,也能夠用於冗餘陣列(RAID)和其餘UDP丟包恢復的場景。關於裏德所羅門編碼,感興趣的讀者能夠閱讀碼哥以前的文章:神奇的數據恢復算法

其餘組件

前面的都是常規操做,這裏纔是重頭戲。

Melon中還包括以下實用組件:

  • 內存池
  • 數據鏈
  • TCP封裝
  • 事件機制
  • 文件緩存
  • HTTP處理
  • 腳本語言
  • 詞法分析器
  • websocket
  • 多進程框架
  • 多線程框架

因Melon做者Nginx中毒較深,因此Melon中部分機制與Nginx較爲類似。

內存池:這裏內存池不只支持對從堆中分配的內存進行管理,還支持對共享內存的管理。

數據鏈與TCP封裝:TCP封裝中包含了阻塞與非阻塞下的收發邏輯,並利用數據鏈結構來存放發送數據與接收數據。

事件機制:事件機制中不只支持epoll、select,還支持Kqueue,庫在編譯前會自行檢測平臺支持狀況。事件包含了:

  • 句柄(文件描述符)事件:讀、寫、出錯事件,以及超時事件(主要用於超時斷開連接);
  • 定時事件(與句柄超時是兩碼事);
  • 信號處理事件:這裏的信號處理並不是一個信號只能有一個處理事件,而是設置多少個處理函數就會執行多少個;

文件緩存:參考Nginx文件緩存,避免對同一文件的重複打開浪費文件描述符資源。

HTTP:包含了HTTP的接收解析和發送,該套接口依賴於數據鏈結構來進行處理,所以可配合TCP封裝一同使用。

腳本語言:內容較多,可另行參考:Melang腳本語言

詞法分析器:之因此這個單獨算一個功能組件,是由於在Melon中,配置文件解析就是使用該詞法分析器處理的。僅經過三行C代碼就能夠實現一個最最基礎的詞法分析器,這也歸功於C語言宏的強大。

websocket:該部分依賴於HTTP組件。

多進程:多進程採用一主多從模式,主進程作管理,從進程處理實際業務。主進程與從進程之間由socketpair相連,所以從進程異常退出,主進程會馬上拉起一個新的子進程,同時主子進程也能夠經過該socketpair進行數據通訊。除了自身子進程能夠管理,也能夠經過配置文件配置來拉起其餘程序做爲本身的子進程來管理,有些相似於supervisord。

多線程:多線程分爲兩類,一類是常規的線程池,另外一類是模塊化的線程。後者也是一主多從模型,主與子之間是經過socketpair進行通訊,而每個子線程都有其入口函數(相似main函數),每個子線程一般都是處理一類單一事務。

使用舉例

上面說了那麼多,下面就來看一個多進程的例子。

首先,咱們要先安裝Melon:

$ git clone https://github.com/Water-Melon/Melon.git
$ ./configure
$ make
$ sudo make install
$ sudo echo "/usr/local/melon/lib/" >> /etc/ld.so.conf
$ sudo ldconfig

安裝好後,Melon會被安裝在/usr/local/melon下。

接着,咱們建立一個名爲hello.c的源文件來完成咱們指望的功能:

#include <stdio.h>
#include "mln_core.h"
#include "mln_log.h"
#include "mln_event.h"

char text[1024];

static int global_init(void);
static void worker_process(mln_event_t *ev);
static void print_handler(mln_event_t *ev, void *data);

int main(int argc, char *argv[]) {
    struct mln_core_attr cattr;
    cattr.argc = argc;
    cattr.argv = argv;
    cattr.global_init = global_init;
    cattr.worker_process = worker_process;
    return mln_core_init(&cattr);
}

static int global_init(void) {
    //global variable init function
    int n = snprintf(text, sizeof(text)-1, "hello worldn");
    text[n] = 0;
    return 0;
}

static void worker_process(mln_event_t *ev) {
    //we can set event handler here
    //let's set a timer
    mln_event_set_timer(ev, 1000, text, print_handler);
}

static void print_handler(mln_event_t *ev, void *data) {
    mln_log(debug, "%sn", (char *)data);
    mln_event_set_timer(ev, 1000, data, print_handler);
}

這段代碼主要是初始化了一個全局變量,而後給每個子進程建立了一個定時事件,即每一秒中輸出一個hello world。

咱們先進行編譯連接生成可執行程序:

$ gcc -o hello hello.c -I /usr/local/melon/include/ -L /usr/local/melon/lib/ -lmelon

而後,咱們須要先修改Melon庫的配置文件:

$ sudo vim /usr/local/melon/conf/melon.conf

log_level "none";
//user "root";
daemon off;
core_file_size "unlimited";
//max_nofile 1024;
worker_proc 1;
thread_mode off;
framework off;
log_path "/usr/local/melon/logs/melon.log";
/*
 * Configurations in the 'exec_proc' are the
 * processes which are customized by user.
 *
 * Here is an example to show you how to
 * spawn a program.
 *     keepalive "/tmp/a.out" ["arg1" "arg2" ...]
 * The command in this example is 'keepalive' that
 * indicate master process to supervise this
 * process. If process is killed, master process
 * would restart this program.
 * If you don't want master to restart it, you can
 *     default "/tmp/a.out" ["arg1" "arg2" ...]
 *
 * But you should know that there is another
 * arugment after the last argument you write here.
 * That is the file descriptor which is used to
 * communicate with master process.
 */
exec_proc {
   // keepalive "/tmp/a";
}
thread_exec {
//    restart "hello" "hello" "world";
//    default "haha";
}

咱們作以下修改:

  • framework off; --> framework on;
  • worker_proc 1; --> worker_proc 3;

這樣,多進程框架將被啓用,且會產生三個子進程。

程序啓動後以下:

$ ./hello
Start up worker process No.1
Start up worker process No.2
Start up worker process No.3
02/08/2021 09:34:46 GMT DEBUG: hello.c:print_handler:39: PID:25322 hello world

02/08/2021 09:34:46 GMT DEBUG: hello.c:print_handler:39: PID:25323 hello world

02/08/2021 09:34:46 GMT DEBUG: hello.c:print_handler:39: PID:25324 hello world

02/08/2021 09:34:47 GMT DEBUG: hello.c:print_handler:39: PID:25322 hello world

02/08/2021 09:34:47 GMT DEBUG: hello.c:print_handler:39: PID:25323 hello world

02/08/2021 09:34:47 GMT DEBUG: hello.c:print_handler:39: PID:25324 hello world

...

這時,能夠ps看一下,一共存在四個hello進程,一個爲主,其他三個爲子進程。

小結

事實上,Melon並不會有過多條條框框須要開發者當心謹慎怕踩坑。與Skynet相似,Melon提供的絕大多數內容均可獨立使用,而沒必要必定與多進程多線程框架結合。所以,這也給了使用者極大的自由度。


Melon的官方QQ羣號:756582294

感謝閱讀,歡迎各位在評論區留言評論。

相關文章
相關標籤/搜索