Nginx基础. HTTP过滤模块添加方式探讨

在学习Nginx中的过滤模块开发时, 遇到了使用全局变量和静态全局变量构成的单向函数链表, 对于我这种新手来说, 有些无法理解.

首先, 关于全局变量与静态全局变量, 下面贴出一些注意的地方:

从分配内存空间看:
          全局变量、静态局部变量、静态全局变量都在静态存储区分配空间,而局部变量在栈分配空间。
区别:
          全局变量本身就是静态存储方式,静态全局变量当然也是静态存储方式。这两者在存储方式上没有什么不同。区别在于非静态全局变量的作用域是整个源程序,当一个源程序由多个源文件组成时,非静态的全局变量在各个源文件中都是有效的。而静态全局变量则限制了其作用域,即只在定义该变量的源文件内有效,在同一源程序的其他源文件中不能使用它。由于静态全局变量的作用域局限于一个源文件内,只能为该源文件内的函数公用,因此可以避免在其他源文件中引起错误。


所以, 自己按照Nginx中构建链表的方式也写了一个小小的实验程序如下.

整个框架目的:

目的是为了能实现一系列相同类型的函数(即可以以同用一个函数指针指向的众多函数)

如果想要添加能执行的函数(这里我们称为模块), 只需要编写新的文件而不需要改动原有的代码


框架结构体:

首先, 这是头文件. 用于制定每个模块需要的一些信息.

只是这里因为程序太小太简陋, 所以不需要什么信息. 只需要每个模块的初始化函数. 初始化函数的用途会在下面讲

//from  "conf.h"
#include <stdio.h>

#ifndef CONF_H
#define CONF_H

//形成这个函数链表的每个(函数)元素的类型
typedef int (*function_init)();
//每个模块真正工作的函数的指针
typedef void (*function_job)();

typedef struct{//每个模块结构体当前只包含必须的初始化函数
    function_init init;
}module_t;

#endif


框架自带的模块.
//from "module_A.c"
#include "conf.h"

//这个function_top_job函数是整个框架用来遍历模块函数的入口, 换句话说, 就是函数链表的入口
extern function_job function_top_job;
//这个必须是静态全局变量, 才能让每个模块具有相同的变量名字但指向不同的函数
static function_job function_next_job;

static void module_A_job();
static int module_A_init();

//对于模块A的定义.
module_t module_A = {
    module_A_init
};
//模块A的真正工作函数, 其中要包含其调用下一个函数元素的代码
static void
module_A_job(){
    printf("I am from module A\n");
        //看是否执行到链表尾部. 如果还有则继续执行
    if(function_next_job)
        function_next_job();
}

//模块A的初始化函数. 从这里明显可以看出和Nginx中相同的用法
//将每个模块插入在链表头部, 所以后初始化的函数会被优先执行
static int
module_A_init(){
        //静态变量指向原来的整个框架的链表首部
    function_next_job = function_top_job;
        //整个框架的链表首部被重新赋值
    function_top_job = module_A_job;
}


模块整合
我们知道, 每个模块都需要执行它的初始化函数之后才会被放到链表函数中去, 那么我们不可能在每次添加新模块的时候都
到main函数中去添加新模块的初始化函数调用代码, 所以就需要使用下面这个数组
在Nginx中, 类似的文件叫做 /nginx/obj/ngx_modules.c, 这个文件是由configure文件生成的. 因为我还不会编写这个东西...所以每次添加模块的话, 还
需要到这里来做一些小小的修改. 不过这个是单独的文件, 所以改起来比较清晰方便.
//from "modules.c"
#include "conf.h"

//其他文件的全局变量
extern module_t module_A;

//将其他文件的模块结构体放到这个模块数组中来
module_t *modules[] = {
    &module_A,
    NULL            //放一个NULL在最后也就方便遍历, 不需要知道到底有多少元素在这个modules数组中
};



最后, 整个框架需要一个main函数:
//from "main.c"
#include "conf.h"

//定义框架的函数链表入口
function_job function_top_job = NULL;

//所有模块都被放在这个模块指针数组中
extern module_t *modules[];

int main()
{
    int i;
        //执行每个模块的初始化函数. 形成函数链表
    for(i=0; modules[i]; i++)
        modules[i]->init();
    
        //从入口开始执行, 遍历每个函数元素
    function_top_job();
    return 0;
}


编译执行:
好了, 基本的一个框架已经完成了. 如果在编译后执行, 那么输出的内容就是:
$ gcc -o exe conf.h module_A.c modules.c main.c
$ ./exe
I am from module A


添加新模块
现在来添加新的模块. 添加新的模块在原本的设计下也就显得十分简单了.

比如, 现在想要添加模块B和模块C

//from "module_B.c"
#include "conf.h"

extern function_job function_top_job;
static function_job function_next_job;

static void module_B_job();
static int module_B_init();

module_t module_B = {
    module_B_init
};

static void
module_B_job(){
    printf("I am from module B\n");
    if(function_next_job)
        function_next_job();
}

static int
module_B_init(){
    function_next_job = function_top_job;
    function_top_job = module_B_job;
}


//from "module_C.c"
#include "conf.h"

extern function_job function_top_job;
static function_job function_next_job;

static void module_C_job();
static int module_C_init();

module_t module_C = {
    module_C_init
};

static void
module_C_job(){
    printf("I am from module C\n");
    if(function_next_job)
        function_next_job();
}

static int
module_C_init(){
    function_next_job = function_top_job;
    function_top_job = module_C_job;
}


除了添加这两个源文件外, 还需要去modules.c文件中进行简单的修改, 改成下面这个样子:
#include "conf.h"

extern module_t module_A;
extern module_t module_B;
extern module_t module_C;

module_t *modules[] = {
    &module_A,
    &module_B,
    &module_C,
    NULL
};



最后编译执行:
$ gcc -o exe conf.h module_A.c modules.c main.c module_B.c module_C.c 
$ ./exe
I am from module C
I am from module B
I am from module A

可以发现, 函数的执行顺序与插入操作是相符合的.
好了, 可以发现, 添加的东西与原来模块的格式相同, 这里内容虽然也差不多, 但完全可以改成完全不同的操作. 只是格式必须要遵守.
另外需要修改的modules.c 也不是很困难...
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值