zabbix源码分析之基础篇

zabbix_aggentd 源码分析之基础篇

zabbix 源码组织结构

zabbix是采用Automake方式构建的开源项目,服务和工具是通过C语言实现,实现来跨平台的能力,目前zabbix_server还不支持Windows系统。
主要的目录是:
src为C代码的源码目录,include为C代码的头文件,frontends为前端php代码。
src目录libs,modules和各功能主程序的目录,zabbix_agent就是本文需要分析的主目录。

zabbix编码规范和宏定义的解释

zabbix的代码中为了跨平台和开发的简洁采用了大量的宏定义,其中很多对于阅读代码而言又是一种煎熬:)

MAIN_ZABBIX_ENTRY
int MAIN_ZABBIX_ENTRY(int flags)

MAIN_ZABBIX_ENTRY定义来功能程序的开始入口,在这个入口之前,由真正的main函数调用,并且在调用之前需要检查参数,并对无需进入程序主功能的参数直接执行并处理。所以定义了MAIN_ZABBIX_ENTRY的主程序文件(例如:zabbix_agent.c),就是实现zabbix代理功能的主入口,命令行参数的检测,help信息的输出,服务的安装,等等都由具体的程序模块实现。
通过这个宏,就相当于定义了实现业务功能的main,无论是通过服务(Windows),还是通过deamon(*nix下的守护进程)启动业务功能,都可以直接调用MAIN_ZABBIX_ENTRY(0)启动业务功能。

ZBX_THREAD_ENTRY
#if defined(_WINDOWS)
    #define ZBX_THREAD_ENTRY(entry_name, arg_name)  \
        unsigned __stdcall entry_name(void *arg_name)
#else   /* not _WINDOWS */
    #define ZBX_THREAD_ENTRY(entry_name, arg_name)  \
        unsigned entry_name(void *arg_name)
#endif

ZBX_THREAD_ENTRY定义了功能线程的开始入口,如果需要调用一个开始线程,调用如下:
zbx_thread_start(collector_thread, thread_args);
而collector_thread的函数声明如下:
ZBX_THREAD_ENTRY(collector_thread, args)
这样通过宏,实际就是定义了函数collector_thread。直接定义函数不好吗,非要通过宏来定义一个函数的名称吗?对于跨平台系统而言,是必须的,如果不通过宏定义,那么对于windows上的stdcall的声明就需要特殊定义了,或者定义一个STDAPI的宏,实现Windows和*nix系统调用方式不同的生命方式(Windows系统定义为__stdcall,而*nix系统定义为空宏),相比较而言,zabbix采取的方法更好。

Zabbix的内存处理相关辅助函数

内存分配

zaibbx的内存实现代码在libs/common/misc.c
内存函数共4个:

#define zbx_calloc(old, nmemb, size)    zbx_calloc2(__FILE__, __LINE__, old, nmemb, size)
#define zbx_malloc(old, size)       zbx_malloc2(__FILE__, __LINE__, old, size)
#define zbx_realloc(src, size)      zbx_realloc2(__FILE__, __LINE__, src, size)
#define zbx_strdup(old, str)        zbx_strdup2(__FILE__, __LINE__, old, str)

实际的实现函数后面有数字2(利用宏实现对用户调用文件和行号进行跟踪,具体实现在misc.c中。
这里,咋们通过看一段代码就知道zabbix设计得有意思的地方:

void    *zbx_malloc2(const char *filename, int line, void *old, size_t size)
{
    int max_attempts;
    void    *ptr = NULL;

    /* old pointer must be NULL */
    if (NULL != old)
    {
        zabbix_log(LOG_LEVEL_CRIT, "[file:%s,line:%d] zbx_malloc: allocating already allocated memory. "
                "Please report this to Zabbix developers.",
                filename, line);
    }

    for (
        max_attempts = 10, size = MAX(size, 1);
        0 < max_attempts && NULL == ptr;
        ptr = malloc(size), max_attempts--
    );

    if (NULL != ptr)
        return ptr;

    zabbix_log(LOG_LEVEL_CRIT, "[file:%s,line:%d] zbx_malloc: out of memory. Requested " ZBX_FS_SIZE_T " bytes.",
            filename, line, (zbx_fs_size_t)size);

    exit(EXIT_FAILURE);
}

首先,他需要提供申请空间的指针,用于检测是否重复分配内存。调用函数是通过zbx_malloc调用实现内存分配,例如下面代码:

static ZBX_METRIC   *commands = NULL;
//...
commands = zbx_malloc(commands, sizeof(ZBX_METRIC));

commands是一个内存变量指针。
并且,在申请内存时尝试调用malloc函数10次,这样是否有实际意义?不得而知!
如果分配失败,直接退出进程,结束程序运行。

在代码中,还可以看到很多zbx_realloc的调用,用于动态扩展需要分配的空间。
例如下面代码添加一个需要采集的指标到运行时采集列表:

int add_metric(ZBX_METRIC *metric, char *error, size_t max_error_len)
{
    int i = 0;

    while (NULL != commands[i].key)
    {
        if (0 == strcmp(commands[i].key, metric->key))
        {
            zbx_snprintf(error, max_error_len, "key \"%s\" already exists", metric->key);
            return FAIL;    /* metric already exists */
        }
        i++;
    }

    commands[i].key = zbx_strdup(NULL, metric->key);
    commands[i].flags = metric->flags;
    commands[i].function = metric->function;
    commands[i].test_param = (NULL == metric->test_param ? NULL : zbx_strdup(NULL, metric->test_param));

    commands = zbx_realloc(commands, (i + 2) * sizeof(ZBX_METRIC));
    memset(&commands[i + 1], 0, sizeof(ZBX_METRIC));

    return SUCCEED;
}

这里用到了zbx_realloc来实现内存扩展,也就是,内存不是一次性分配好的,而是不断申请和分配的。内存分配开始是总是保留一个空位,数据开始填充在空位,然后重新分配一个空间(使用realloc实现内存复制),并设置最后一个空位为0值。
这种分配内存的模式在zabbix源码中很常见,熟悉后,阅读起来就会省不少时间。
这里再说一下zbx_free的实现

#define zbx_free(ptr)       \
                \
do              \
{               \
    if (ptr)        \
    {           \
        free(ptr);  \
        ptr = NULL; \
    }           \
}               \
while (0)

zbx_free就是通过C语言的free实现的,这里为什么需要采用一个do-while的写法呢?
这是在C中实现多行宏定义的一个通用方法,防止语句在宏扩张是产生语义错误,例如如果宏用在如下代码时:

#define zbx_free(p) \
    free(p);  \
    p = NULL;

int status = do_somthing();
if (status == 0) zbx_free(ptr);
else{
    //using p pointer to do somthing...
}

如果zbx_free没有采用do-while的写法,那么就会出现错误的宏扩展:

if (status == 0) free(p);
ptr = NULL;
else{
    // using p pointer to do somthing...
}

以上代码就会产生编译错误,最不好的情况是,直接出现运行时错误。原因就是宏使用时没有采用{}来包裹导致的问题,而采用do-while(0)时,该问题就可以避免。

字符串的处理

zabbix处理字符串也是采用封装的方式进行,主要包括如下几个函数:

#define zbx_strdup(old, str)        zbx_strdup2(__FILE__, __LINE__, old, str)

#define ZBX_STRDUP(var, str)    (var = zbx_strdup(var, str))

一般采用zbx_strdup函数实现字符串的复制。
例如前面的指标名称复制就是调用该函数实现复制的:

commands[i].key = zbx_strdup(NULL, metric->key);

zbx_strdup的实现任采用C库的strdup实现,不过处理的原指针的释放。

char    *zbx_strdup2(const char *filename, int line, char *old, const char *str)
{
    int retry;
    char    *ptr = NULL;

    zbx_free(old);

    for (retry = 10; 0 < retry && NULL == ptr; ptr = strdup(str), retry--)
        ;

    if (NULL != ptr)
        return ptr;

    zabbix_log(LOG_LEVEL_CRIT, "[file:%s,line:%d] zbx_strdup: out of memory. Requested " ZBX_FS_SIZE_T " bytes.",
            filename, line, (zbx_fs_size_t)(strlen(str) + 1));

    exit(EXIT_FAILURE);
}

并且,仍然采用多次尝试的方法复制字符串。

动态字符数组(dynamic string array)

首先声明,这个地方有点复杂(^_^)

函数的声明是这样:

void    zbx_strarr_init(char ***arr)
{
    *arr = zbx_malloc(*arr, sizeof(char *));
    **arr = NULL;
}

三个星号,你看看,太过复杂吧!一般人不用他!谁叫他是zabbix呢?
代码创建一个二维指针空间:

p -> [x1, x2, x3, ...]
      |   |   |     |
      v   v   v     v
    char*    char*  null

这里参数为三个*,是为了把两个的变量通过指针传递给zbx_strarr_init,以便初始化。

为更好的理解二维数组,我编写了以下简要的代码:

#include <stdlib.h>
#include <stdio.h>

void test_p(int **p)
{
    // 外部实际是一个指针(一维)
    // 通过**传递,是希望由该函数实现内存分配,并输出到外部的指针变量
    // 这里为了方便,使用了临时变量a,如果直接使用p实现赋值,
    // 就需要通过数组方式访问咯,例如*p[0], *p[1] *p[2]
    int *a = *p = malloc(sizeof(int)*3);
    *a++=1;
    *a++=2;
    *a++=3;
}

void test_pp(int ***p)
{
    // 二维指针的变量
    // 首先分配一维的指针变量
    // 然后对一维指针变量初始化数据内存
    int **a = *p = malloc(sizeof(int*) * 3);
    *a++ = malloc(sizeof(int) * 3);
    *a++ = malloc(sizeof(int) * 3);
    *a++ = malloc(sizeof(int) * 3);
    int **b = *p;
    printf("0x%lx\r\n", *b++);
    printf("0x%lx\r\n", *b++);
    printf("0x%lx\r\n", *b++);
    int **c = *p;
    int *c1 = *c++;
    int *c2 = *c++;
    int *c3 = *c++;
    *c1++ = 1; *c1++ = 2; *c1++ = 3;
    *c2++ = 4; *c2++ = 5; *c2++ = 6;
    *c3++ = 7; *c3++ = 8; *c3++ = 9;
}

void test_x(int *p){
        *p++=1;
        *p++=2;
        *p++=3;
}

int main(){
        printf("(1)*\r\n");
        int a[3] = {0};
        test_x(a);
        printf("%d %d %d\r\n", a[0], a[1], a[2]);

        printf("(2)**\r\n");
        int *b = 0;
        test_p(&b);
        printf("%d %d %d\r\n", b[0], b[1], b[2]);
        free(b);

        printf("(3) ***\r\n");
        int **c = 0;
        test_pp(&c);
        int *p;
        for (int i = 0; i < 3; i++){
                p = c[i];
                printf("addr is %lx\r\n", p);
                for (int j = 0; j < 3; j++){
                        printf("%d ", p[j]);
                }
                printf("\r\n");
                free(p);
        }
        free(c);
        return 0;
}

上面程序执行的结果是:

(1)*
1 2 3
(2)**
1 2 3
(3) ***
0x7fda91c025d0
0x7fda91c02600
0x7fda91c02610
addr is 7fda91c025d0
1 2 3
addr is 7fda91c02600
4 5 6
addr is 7fda91c02610
7 8 9

现在看zabbix的二维数组的初始化代码,实际上,只是分配咯一个元素的一位数组指针,并设置为空。

下面来看添加字符串实现:

void    zbx_strarr_add(char ***arr, const char *entry)
{
    int i;

    assert(entry);

    for (i = 0; NULL != (*arr)[i]; i++)
        ;

    *arr = zbx_realloc(*arr, sizeof(char *) * (i + 2));

    (*arr)[i] = zbx_strdup((*arr)[i], entry);
    (*arr)[++i] = NULL;
}

其实和前面的示例一样,不过这里通过zbx_realloc实现内存扩张。首先,寻找最后一个为null元素,i 表示元素个数-1,所以为了扩张一个元素,需要i+2个指针变量的区域。
然后,复制字符串到倒数一个指针位置,同时设置最后一个位置为null。

  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
Zabbix是一个基于WEB界面的提供分布式系统监视以及网络监视功能的企业级的开源解决方案。Zabbix能监视各种网络参数,保证服务器系统的安全运营;并提供柔软的通知机制以让系统管理员快速定位/解决存在的各种问题。Zabbix由2部分构成,Zabbix server与可选组件Zabbix agent。 Zabbix server可以通过SNMP,Zabbix agent,ping,端口监视等方法提供对远程服务器/网络状态的监视,数据收集等功能,它可以运行在Linux, Solaris, HP-UX, AIX, Free BSD, Open BSD, OS X等平台之上。 Zabbix agent需要安装在被监视的目标服务器上,它主要完成对硬件信息或与操作系统有关的内存,CPU等信息的收集。Zabbix agent可以运行在Linux ,Solaris, HP-UX, AIX, Free BSD, Open BSD, OS X, Tru64/OSF1, Windows NT4.0, Windows 2000/2003/XP/Vista)等系统之上。 Zabbix server可以单独监视远程服务器的服务状态;同时也可以与Zabbix agent配合,可以轮询Zabbix agent主动接收监视数据(trapping方式),同时还可被动接收Zabbix agent发送的数据(trapping方式)。 另外Zabbix server还支持SNMP (v1,v2),可以与SNMP软件(例如:net-snmp)等配合使用。 主要特点: - 安装与配置简单,学习成本低 - 支持多语言(包括中文) - 免费开源 - 自动发现服务器与网络设备 - 分布式监视以及WEB集中管理功能 - 可以无agent监视 - 用户安全认证和柔软的授权方式 - 通过WEB界面设置或查看监视结果 - email等通知功能 等等 更新说明: 增加一些安全补丁 修复一下bug
YOLO高分设计资源源码,详情请查看资源内容中使用说明 YOLO高分设计资源源码,详情请查看资源内容中使用说明 YOLO高分设计资源源码,详情请查看资源内容中使用说明 YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明
你可以按照下面的步骤来安装 Zabbix 源码并集成 Nginx: 1. 安装依赖项:首先,确保系统上已经安装了必要的依赖项。在终端中运行以下命令安装它们: ``` sudo apt-get update sudo apt-get install -y build-essential libssl-dev libcurl4-openssl-dev libxml2-dev libpcre3-dev zlib1g-dev ``` 2. 下载和编译 Nginx:使用以下命令下载 Nginx 源码: ``` wget http://nginx.org/download/nginx-<version>.tar.gz tar -zxvf nginx-<version>.tar.gz cd nginx-<version> ``` 注意:将 `<version>` 替换为你想要安装的 Nginx 版本号。 3. 配置和编译 Nginx:运行以下命令配置并编译 Nginx: ``` ./configure --prefix=/usr/local/nginx --with-http_ssl_module --with-http_gzip_static_module make sudo make install ``` 这将安装 Nginx 到 `/usr/local/nginx` 目录。 4. 配置 Nginx:创建一个新的 Nginx 配置文件 `zabbix.conf`,并在其中添加以下内容: ``` server { listen 80; server_name localhost; location / { root /usr/local/nginx/html; index index.html index.htm; } location /zabbix { alias /path/to/zabbix/frontend; index index.php; } location ~ \.php$ { fastcgi_pass 127.0.0.1:9000; # 修改为你的 PHP-FPM 地址和端口 fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $request_filename; include fastcgi_params; } } ``` 注意:将 `/path/to/zabbix/frontend` 替换为你实际安装 Zabbix 前端的路径。 5. 启动 Nginx:运行以下命令启动 Nginx 服务: ``` sudo /usr/local/nginx/sbin/nginx ``` 现在你已经成功安装并配置了 Nginx,并将 Zabbix 前端集成到了 Nginx 中。你可以通过访问服务器的 IP 地址或域名来访问 Zabbix 界面。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值