nginx源码初读(1)--让烦恼从数据结构开始(ngx_cdecl/ngx_int/ngx_log)
拋去所有的模塊和各種定義的數據結構,對于一個沒看過這么大工程的小白來說,太亂!亂的不要不要,光是各種數據結構的含義,就讓我要抓狂了。好吧,我并不是說它結構不好,相反我感覺代碼寫的太棒了。。就是一時間。。接受不了。廢話不說了,讓煩惱開始吧,唉╮(╯▽╰)╭
第一點:ngx_cdecl
int ngx_cdecl main(int argc, char *const *argv);- 1
- 1
是的,就是這個ngx_cdecl,之前知道在源碼里面有__cdecl和__stdcall等東西,在這兒的ngx_cdecl又是什么,按理說是一樣的,但是查到定義后是這樣的:
#define ngx_cdecl- 1
- 1
對,就是一個空的define,那它有啥用?當然有用,而且用得很好,要不怎么說人家是好代碼呢,后路留的多好,避免以后填坑。nginx中使用這個宏是為了跨平臺支持,方便調整函數調用方式。在遇到問題時,可以修改上面的定義為:
#define ngx_cdecl stdcal- 1
- 1
要不怎么說多看代碼有好處呢,這就是思想啊。好了再解釋下cdecl和stdcall:?
__cdecl:C Declaration的縮寫,表示C語言默認的函數調用方法:所有參數從右到左依次入棧,這些參數由調用者清除,稱為手動清棧。被調用函數不會要求調用者傳遞多少參數,調用者傳遞過多或者過少的參數,甚至完全不同的參數都不會產生編譯階段的錯誤。?
調用函數的代碼和被調函數必須采用相同的函數的調用約定,程序才能正常運行。?
__cdecl和__stdcall的區別:__cdecl是調用者清理參數占用的堆棧,__stdcall是被調函數清理參數占用的堆棧。假設函數A是__stdcall,函數B調用函數A。你必須通過函數聲明告訴編譯器,函數A是__stdcall。編譯器自然會產生正確的調用代碼。如果函數A是__stdcall,但在引用函數A的地方,你卻告訴編譯器,函數A是__cdecl方式,編譯器產生__cdecl方式的代碼,與函數A的調用約定不一致,就會發生錯誤。?
注意事項:由于__stdcall的被調函數在編譯時就必須知道傳入參數的準確數目(被調函數要清理堆棧),所以不能支持變參數函數,例如printf。而且如果調用者使用了不正確的參數數目,會導致堆棧錯誤。
第二點:ngx_int_t & ngx_uint_t
typedef intptr_t ngx_int_t; typedef uintptr_t ngx_uint_t;- 1
- 2
- 1
- 2
在stdint.h中找到intptr的定義:
117 /* Types for `void *' pointers. */ 118 #if __WORDSIZE == 64 119 # ifndef __intptr_t_defined 120 typedef long int intptr_t; 121 # define __intptr_t_defined 122 # endif 123 typedef unsigned long int uintptr_t; 124 #else 125 # ifndef __intptr_t_defined 126 typedef int intptr_t; 127 # define __intptr_t_defined 128 # endif 129 typedef unsigned int uintptr_t; 130 #endif- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
定義中的注釋說明,這兩個類型是定義用來當作指針使用的,因為指針的長度和長整型一直是一樣的,由于指針就是一個數組索引的存在,系統內核在操作內存時,就是將內存當做一個大數組,而指針就是數組索引/下標,內核程序員使用這種特殊的整型來接受內存地址值、操作內存相比使用指針更加直觀,不容易犯錯。?
intptr_t 這個類型可以被安全的在 void * 和 整數間轉換,對于寫跨 64 位平臺的程序非常重要。也就是說,當你需要把指針作為一個整數來運算時,轉換成 intptr_t 才是安全的,可以在運算完畢安全的轉回指針類型,也避免了對指針解引用產生的bug。
由代碼中的宏可以看出,intptr_t的長度是適應不同平臺的,當編譯環境是64位時,intptr_t是long int,否則就是int。
那么nginx中使用它來typedef出ngx_int_t是想要干嗎?為啥不用int來??
從類型名來看很容易理解為普通的int型,我想nginx使用它是因為intptr在定義的時候就自適應平臺,根據平臺來變化自己的長度,作者就不用自己再定義一次了。
第三點:ngx_log_t
20 typedef struct ngx_log_s ngx_log_t;45 typedef u_char *(*ngx_log_handler_pt) (ngx_log_t *log, u_char *buf, size_t len);46 typedef void (*ngx_log_writer_pt) (ngx_log_t *log, ngx_uint_t level,47 u_char *buf, size_t len); // 看log.c中程序len的計算都是通過指針相減 48 49 50 struct ngx_log_s {51 ngx_uint_t log_level; /* 日志級別,有define:0~8的值和對應的string數組* cycle->log->log_level = NGX_LOG_INFO;* if (log->log_level < level && !debug_connection) break;* log->log_level |= d;*/52 ngx_open_file_t *file;/* 日志文件,結構內有文件標志fd和文件名name* n = ngx_write_fd(log->file->fd, errstr, p - errstr);* if (log->file->fd == ngx_stderr) { wrote_stderr = 1;}* log->file = ngx_conf_open_file(cycle, &error_log);* if (new_log->file == NULL) { return NGX_CONF_ERROR;}*/53 54 ngx_atomic_uint_t connection;/* 原子單元鏈接,定義為整形變量(與平臺有關)* if (log->connection) {p = ngx_slprintf(p, last, "*%uA ", log->connection);}*/55 56 time_t disk_full_time;/* 寫入full filesystem時間,代碼中經常與ngx_time()比較* if (ngx_time() == log->disk_full_time) {/** on FreeBSD writing to a full filesystem with enabled softupdates* may block process for much longer time than writing to non-full* filesystem, so we skip writing to a log for one second*/goto next;}* if (n == -1 && ngx_errno == NGX_ENOSPC) {log->disk_full_time = ngx_time();}*/57 58 ngx_log_handler_pt handler;/* 日志處理器,見45行定義* if (level != NGX_LOG_DEBUG && log->handler) {p = log->handler(log, p, last - p);}* cln->handler = ngx_log_memory_cleanup;*/59 void *data; /* 日志數據* cln->data = new_log;*/60 61 ngx_log_writer_pt writer; /* 日志寫入函數,見46行定義* if (log->writer) {log->writer(log, level, errstr, p - errstr);goto next;}*/62 void *wdata; /* 日志內容/* 日志寫入函數,見46行定義* if (log->writer) {log->writer(log, level, errstr, p - errstr);goto next;}*/63 64 /*65 * we declare "action" as "char *" because the actions are usually66 * the static strings and in the "u_char *" case we have to override67 * their types all the time68 */69 70 char *action; /* 操作* c->log->action = "waiting for request";* c->log->action = "reading client request headers";* c->log->action = "keepalive";* log->action = "closing request";* p = ngx_snprintf(buf, len, " while %s", log->action);*/ 71 72 ngx_log_t *next;73 };- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
為啥要先struct又在另一個頭文件里typedef,因為現在用得是c,必須struct XXX來使用,所以typedef封裝一下,讓用戶不知道它的具體結構。
總結
以上是生活随笔為你收集整理的nginx源码初读(1)--让烦恼从数据结构开始(ngx_cdecl/ngx_int/ngx_log)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: http 状态码 504 502
- 下一篇: http 长连接 短连接