1. 程式人生 > >nginx的指令碼引擎(一)

nginx的指令碼引擎(一)

nginx的指令碼的語法和shell是很像的,我大致看了一下覺得挺有意思的,就想寫寫記錄一下。我沒看過shell指令碼的引擎,不知道nginx指令碼引擎和shell指令碼引擎像不像,但是我覺得nginx的指令碼引擎有點像C和彙編。

ngx_http_script_engine_t這個結構就代表了一段指令碼,ip指向的是編譯好的指令碼,sp指向的是一塊記憶體用來儲存指令碼執行的時候產生的一些中間值。ip/sp從名字看就已經很像彙編了instruction pointer/stack pointer指令暫存器和棧暫存器呀,當然是我瞎猜的,有時間的話可以查一下官方文件。程式碼段裡的各個指令長度不一定相同。

再來說說編譯過程,編譯過程是在nginx_http_script_engine_t建立之前執行的,我先畫出了整個圖是為了更好理解。舉個set指令編譯的的例子,比如你在腳本里有這樣的程式碼set $foo helloworld,指令碼編譯的步驟如下:

第一步:首先在cmcf->variables_keys和cmcf->variables裡增加一個變數foo,這個變數是可寫的。我之前寫的nginx的變數系統裡只說了變數的讀取方法,差別不大。

第二步:把ngx_http_script_value_code_t指令放到程式碼段裡(code欄位是一個回撥函式,賦值成ngx_http_script_value_code),把ngx_http_script_var_code_t指令放到程式碼段裡(code欄位是一個回撥函式,賦值成ngx_http_script_set_var_code)。

第三步:http請求來的時候會在rewrite階段會按順序執行ip指向的這一段程式碼,也就是執行ngx_http_script_value_code和ngx_http_script_set_var_code函式。

我們看一下這兩個函式做了什麼

void
ngx_http_script_value_code(ngx_http_script_engine_t *e)
{
    ngx_http_script_value_code_t  *code;

    code = (ngx_http_script_value_code_t *) e->ip;

    e->ip += sizeof(ngx_http_script_value_code_t);

    e->sp->len = code->text_len;
    e->sp->data = (u_char *) code->text_data;

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,
                   "http script value: \"%v\"", e->sp);

    e->sp++;
}
void
ngx_http_script_set_var_code(ngx_http_script_engine_t *e)
{
    ngx_http_request_t          *r;
    ngx_http_script_var_code_t  *code;

    code = (ngx_http_script_var_code_t *) e->ip;

    e->ip += sizeof(ngx_http_script_var_code_t);

    r = e->request;

    e->sp--;

    r->variables[code->index].len = e->sp->len;
    r->variables[code->index].valid = 1;
    r->variables[code->index].no_cacheable = 0;
    r->variables[code->index].not_found = 0;
    r->variables[code->index].data = e->sp->data;
}

第一條指令把“helloworld”這個字串放到了sp裡,第二條指令把值從sp裡取出來存到了變數系統的foo變數裡,任務完成,看起來很簡單。

set指令還可以這樣用set $foo $x$y,這就是所謂的變數插值,過程和上面這個類似,只不過第一條指令是先從變數系統裡取出$x和$y的值,再放入sp裡。

今天先寫這麼多,高興了再分析別的指令。