nginx配置解析之缓冲区管理

        nginx服务器的master进程在解析nginx.conf时,会使用一个4k大小的缓存区存放部分配置文件信息。nginx会从配置文件中读取4k大小的内容到缓冲区,之后对缓冲区中的内容进行逐个字符扫描,从而解析出配置项、配置值。例如server_name  127.0.0.1; 则逐个字符扫描得到配置项server_name, 配置值127.0.0.1。当然nginx不会每次都去读取文件,而是读取4k大小的内容到缓冲区中,然后对缓冲区的内容进行解析。如果缓冲区的内容都解析完了,才会从配置文件中重新读取4k大小的内容到缓冲区中。

        因此这个缓冲区可以被重复利用。那为什么不一次性读取配置文件中的所有内容到缓冲区,然后对缓冲区的内容进行解析呢? 因为在极端情况下,如果nginx.conf内容非常大,例如超过1G,则把这1G内容一次性读取到内容缓冲区中,太占用内存资源。因此为了解决配置文件过大问题,使用一个4K的缓冲区进行管理,限制每次读取到缓冲区数据大小。  以下是缓冲区可能存在的几种状态。


一、开始解析配置时,ngx_conf_parse函数会从配置中读取4K大小的内容内容到缓冲(前提是nginx.conf配置文件大小超过4k)。函数调用后内存布局如下,缓冲区的数据全部为待解析数据,pos,start指向缓冲区的开始位置;last,end指向缓冲区的结束位置。接下里nginx会对这个缓冲区中的数据进行解析,从而获取到配置项、配置值。

nginx配置解析之缓冲区管理_第1张图片

二、当nginx解析完一部分数据后,一部分数据已经解析成“配置项 配置值;"了,另一部分数据则已经扫描到了,但不足以组装成一行数据(也就是配置项 配置值;); 还有一部分则表示完全没有扫描到。

nginx配置解析之缓冲区管理_第2张图片

已解析数据: 这部分的数据表示已经能够组装成一各个独立行数据,也就是("配置项   配置值;")格式。例如:

 work_process 2;

 error_log  logs/error.log;

这部分已解析数据已经保存到了ngx_conf_s结构中的args数组中了,因此这部分空间可以被重新利用。

已扫描数据: 这部分数据表示不能够组装成一个个独立行数据,也就是("配置项   配置值;")格式。例如:本来一个完整的行格式为:worker_connections  1024;

但实际上只解析到了worker_conectio这一小部分数据,因此不能构成一个完整的格式。这部分数据称为已扫描数据,还不能被删除。nginx使用了局部变量start,start与pos之间的这部分数据就是已扫描数据。

未扫描数据: 这部分数据表示nginx还没有解析到这部分数据,pos与last之间的这部分数据称为未扫描数据。


三、当缓冲区里的字符都处理完成时,需要从配置文件中读取新的内容到缓冲区中,此时的临界状态为:

nginx配置解析之缓冲区管理_第3张图片

四、已解析的数据已经保存到数组中了,在缓存中没有意义了。这部分数据可以被删除。而start与pos之间这部分数据,由于还不能为组装成一个完成的格式,因此还不能被删除。但由于缓冲期已经解析完了,因此需要从配置文件中读取新的数据到缓冲区中。此时需要把这部分已经扫描的数据移动到缓冲区最开始的位置。然后从配置文件中读取新数据到这个已扫描缓冲区之后。

nginx配置解析之缓冲区管理_第4张图片

如果配置文件剩余数据没有4K大小,也就是读取后的内容不能填满整个缓冲区。那这种场景下缓冲区布局如下,此时空白空域表示缓冲区未满。

nginx配置解析之缓冲区管理_第5张图片

        以上是在解析nginx.conf配置文件时,可能存在的5种缓冲区状态。在理清了配置文件在缓冲区中的内存布局后,接下来分析nginx如何解析配置文件就相对容易些了。ngx_conf_parse函数是解析配置文件的入口,内部会调用ngx_conf_read_token函数,这个函数负责从配置文件中读取数据到缓冲区,以及对缓冲区中的数据进行扫描得到完整的"配置项 配置值;"格式的内容;以下代码片段就是上面5种可能的缓冲区内存布局的代码实现。

ngx_int_t ngx_conf_read_token(ngx_conf_t *cf)
{
	for(;;)
	{
		//是否需要从配置文件读取配置到缓冲区条件。也就是缓冲区的所有内容都已经被扫描到了。
		//一部分数据已经解析为配置项配置值格式。 另一部分扫描到了,但还没解析为配置项配置值格式。
		//说明缓冲区内容已经不能够组装完整的配置项配置值内容格式,需要从文件中读取数据到缓冲区
        if (b->pos >= b->last) 
		{
            if (cf->conf_file->file.offset >= file_size) 
			{
				//如果文件已经读取完成,但数组中参数个数不为0,说明配置文件格式错误。
				//因为每解析完一行配置,会把数组中的参数保存到内容结构中。之后会把这个参数数组元素清空。
				//以便存放下一行解析出的参数信息。文件都结束了,但数组元素还存在,说明配置有误
                if (cf->args->nelts > 0) 
				{

                    if (cf->conf_file->file.fd == NGX_INVALID_FILE) 
					{
                        return NGX_ERROR;
                    }
                    return NGX_ERROR;
                }
                return NGX_CONF_FILE_DONE;
            }

			//len长度的空间表示已经扫描完成,但没有解析成一个个配置项配置值格式
			//start为局部变量(pos与start之间的数据为已扫描数据)
            len = b->pos - start;
			
			//已经扫描但未组装成一个配置项配置值格式的内容。说明这一行长度太大了。配置有问题
            if (len == NGX_CONF_BUFFER) 
			{
                cf->conf_file->line = start_line;
                return NGX_ERROR;
            }

			//len表示上一次读取后,还有多少内容未解析。并将这些未解析的内容移动到缓冲区头部
			//下一次读取的内容就可以放到缓冲区的len长度后面
            if (len) 
			{
                ngx_memmove(b->start, start, len);
            }
			//计算配置文件剩余大小
            size = (ssize_t) (file_size - cf->conf_file->file.offset);

			//每一次最多读取多少字节内容,如果文件很大,大于缓冲区4096大小,则每次最多
			//从文件中读取4096字节的内容
            if (size > b->end - (b->start + len)) 
			{
                size = b->end - (b->start + len);
            }

			//从文件中读取数据到缓冲区
            n = ngx_read_file(&cf->conf_file->file, b->start + len, size,
                              cf->conf_file->file.offset);

            b->pos = b->start + len;
            b->last = b->pos + n;
            start = b->start;
        }
	}
}	
	



你可能感兴趣的:(nginx源码分析,nginx源码分析)