编程技术分享平台

网站首页 > 技术教程 正文

Nginx源码分析(一)(nginx源码是什么语言)

xnh888 2024-09-22 17:27:53 技术教程 27 ℃ 0 评论

Nginx是一个高性能的静态HTTP服务器和反向代理服务器,Nginx本身不支持现在流行的 JSP、ASP、PHP等动态页面,但是它可以通过反向代理将请求发送到后端的服务器,例如 Tomcat、Apache等来完成动态页面的请求处理。

结合Nginx开发从入门到精通一书和Nginx源码学习服务器的高并发处理。

服务器的架构大同小异,而性能的差异主要来自对数据的处理方式上,也即进程模型和事件模型上。一个客户端请求的数据如何进行接收、存储、解析、返回是服务器做的最主要的工作。


##源码文件结构 源码位于src目录下,分为七个部分。

├── core #core module代码,nginx服务入口 
├── event #事件处理逻辑的封装
├── http #作为web/http/proxy server运行时核心模块
├── mail #作为pop3/imap/smtp proxy server核心模块
├── misc #一些utils,定义test个profiler外围模块逻辑
├── os #对各个平台抽象逻辑的封装
└── stream # 

最基础的数据结构

C++之所以不被用来写网站是因为,网站的核心是处理字符串。而C++的字符串处理实在比较弱,还要不断惦记GC和内存泄漏。

而Nginx首先封装了一下字符串的数据结构和API。定义位于nginx/src/core/nginx_string.h中。

typedef struct {
 size_t len;
 u_char *data;
} ngx_str_t;

Nginx中一个字符串被表示为指针data指示首地址+len指示长度的方式,这种方式唯一定位一个字符串。和标准的glibc API用\0的方式标识字符串结束不同,它有很多好处。

  • 通过长度表示字符串长度,减少计算字符串长度的步骤
  • 可以重复引用一段字符串内存,减少不必要的内存分配

这样的表示是不是似曾相识?在Redis的sds中有类似的处理。

struct sdshdr {
 unsigned int len;
 unsigned int free;
 char buf[];
};

再看看它的API,API以宏的方式定义。

#define ngx_string(str) { sizeof(str) - 1, (u_char *) str }

#define ngx_null_string { 0, NULL }

#define ngx_str_set(str, text) \

(str)->len = sizeof(text) - 1; (str)->data = (u_char *) text

#define ngx_str_null(str) (str)->len = 0; (str)->data = NULL

第一个宏定义一个nginx的字符串,用法是

int main()
{
 ngx_str_t stringA = ngx_string("Stop");
 printf("len: %d \n", stringA.len);
 printf("address: %p \n", stringA.data);
 char* p = (char*)("HELLO WORLD");
 printf("address: %p \n", p);
 printf("string %s \n", p);
 return 0; 
}

这里直接是宏展开,对结构体进行赋值。先生成了一个临时变量"stop",然后把临时变量的长度和地址赋值给结构体。ngx_string与ngx_null_string是{,}格式的,而结构体只能在初始化时进行整体赋值,因而API只能用于赋值时初始化。还需要注意str必须是常量字符串,因为sizeof是以\0为结束标志的。

打印结果

len: 4 
address: 0x8048530 
address: 0x804854d 
string HELLO WORLD

内存管理池ngx_pool_t

ngx_pool_t是一个资源管理池。它提供一种机制帮助管理一系列的资源(如内存、文件等),接管这些资源的所有权,负责资源的使用和释放。类似于C++中的shared_ptr智能指针。

ngx_pool_t定义在文件nginx/src/core/ngx_palloc.h中,相关的内存分配函数(如ngx_alloc)定义在nginx/src/os/unix/ngx_alloc.h中。

typedef struct ngx_pool_s ngx_pool_t; //core.h中
struct ngx_pool_s {
 ngx_pool_data_t d;
 size_t max;
 ngx_pool_t *current;
 ngx_chain_t *chain;
 ngx_pool_large_t *large;
 ngx_pool_cleanup_t *cleanup;
 ngx_log_t *log;
};

ngx_pool_data_t指示资源池数据块的位置信息。

size_t是数据块的最大值

整个内存管理如图所示:

再看一下相关的API函数。

  • ngx_alloc使用malloc进行内存分配,同时把错误信息写到log文件。
void * 
ngx_alloc(size_t size, ngx_log_t *log)
{
 void *p;
 p = malloc(size);
 if (p == NULL) {
 ngx_log_error(NGX_LOG_EMERG, log, ngx_errno,
 "malloc(%uz) failed", size);
 }
 ngx_log_debug2(NGX_LOG_DEBUG_ALLOC, log, 0, 
 "malloc: %p:%uz", p, size);
 return p;
}
  • ngx_create_pool使用posix_memalign()申请大小为NGX_POOL_ALIGNMENT字节对其的内存。
ngx_pool_t *
ngx_create_pool(size_t size, ngx_log_t *log)
{
 ngx_pool_t *p;
 p = ngx_memalign(NGX_POOL_ALIGNMENT, size, log);
 if (p == NULL) {
 return NULL;
 }
 p->d.last = (u_char *) p + sizeof(ngx_pool_t);
 p->d.end = (u_char *) p + size;
 p->d.next = NULL;
 p->d.failed = 0;
 size = size - sizeof(ngx_pool_t);
 p->max = (size < NGX_MAX_ALLOC_FROM_POOL) ? \
 size : NGX_MAX_ALLOC_FROM_POOL;
 p->current = p;
 p->chain = NULL;
 p->large = NULL;
 p->cleanup = NULL;
 p->log = log;
 return p;
}

p->d.last位移ngx_pool_t大小指向数据区段未使用部分的开始。

p->d.last指向数据区段未使用部分的结束。

可见分配的size大小的内存一部分要分给ngx_pool_t结构。

p->max表示分配的最大内存为NGX_MAX_ALLOC_FROM_POOL。

#define NGX_MAX_ALLOC_FROM_POOL (ngx_pagesize -1)
ngx_pagesize = getpagesize();

最大限制为一个分页大小,在x86上其返回值应为4096Bytes = 4KB。

零声学院专门整理了《互联网后端架构师技术大纲》,有兴趣的同学可以关注私信我!更多免费学习资料等你来取。欢迎大家点赞、转发、关注!

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表