开发利器——C 语言必备实用第三方库

2021-02-10 16:20:49 +08:00
 monkeyNik

本文转载自本人头条号: https://www.toutiao.com/i6926789516594479624/

转载请注明出处,感谢!

对于广大 C 语言开发者来说,缺乏类似 C++ STL 和 Boost 的库会让开发受制于基础库的匮乏,也因此导致了开发效率的骤降。这也使得例如 libevent 这类事件库(基础组件库)一时间大红大紫。

今天,笔者给大家带来一款基础库,这套库不仅仅提供了常用的数据结构、算法,如红黑树、斐波那契堆、队列、KMP 算法、RSA 算法、各类哈希算法、数据恢复算法等等,还提供了多进程框架、多线程框架、跨平台高性能事件等实用内容。注意:这是一款不依赖第三方的库。

除此以外,它也是笔者之前文章(Melang 脚本语言)中的核心库。这也就意味着,使用该库,不仅可以快速获得上述内容,还可以让开发者所构建的系统很方便地引入脚本语言的功能。

它就是——Melon

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

下面,笔者便带诸位一览这个库的功能。

数据结构

Melon 中包含如下数据结构的实现:

其中:

以上结构几乎均可在其对应名称的头文件中找到数据结构定义以及函数定义。

一般情况下,数据结构的使用都是函数调用形式,因此也尽可能降低了不同组件间的耦合度。

算法

Melon 中包含的算法如下:

如上算法基本都在其各自头文件中可以找到对应的函数声明以及必要的数据结构定义。

其中,FEC 与里德所罗门编码均属于纠错码,FEC 常用于 RTP 中做数据修复,而里德所罗门编码既可以用于实时语音中丢包恢复,也可以用于冗余阵列( RAID )和其他 UDP 丢包恢复的场景。关于里德所罗门编码,感兴趣的读者可以阅读笔者之前的文章:神奇的数据恢复算法

其他组件

前面的都是常规操作,这里才是重头戏。

Melon 中还包括如下实用组件:

因 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 world\n");
    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, "%s\n", (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";
}

我们做如下修改:

这样,多进程框架将被启用,且会产生三个子进程。

程序启动后如下:

$ ./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

感谢阅读,欢迎各位在评论区留言评论。

5583 次点击
所在节点    推广
27 条回复
monkeyNik
2021-02-11 18:31:42 +08:00
哎...手抖了...😂
新的一年里,祝大家牛年大吉,合家团圆,事业有成,财运亨通。

感谢诸位!🎉
wtdg86ok
2021-02-11 20:37:19 +08:00
star 了,前辈真厉害~
codehz
2021-02-11 21:22:25 +08:00
@monkeyNik 加锁也没问题,主要问题出在设计“无锁”结构上,容易踩坑,弄出只有 x86 系列能跑的设计(
geekzhu
2021-02-13 13:22:11 +08:00
虽然不懂,但是感觉写这东西的都是大佬
monkeyNik
2021-02-14 15:22:37 +08:00
新文已经发布啦: https://www.v2ex.com/t/753269
关于 Melon 中多线程开发的内容,期待感兴趣的小伙伴来阅读
monkeyNik
2021-02-16 21:45:44 +08:00
各位实在不好意思,之前 QQ 群设置有问题导致无法搜索到,感谢反馈,已经可以搜索到了。
GitContract
2021-04-20 11:42:12 +08:00
好东西,顶

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/752832

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX