ngx_output_chain_align_file_buf

发布时间:2026/7/20 10:35:30
ngx_output_chain_align_file_buf 1 定义ngx_output_chain_align_file_buf 函数 定义在 src/core/ngx_output_chain.cstaticngx_int_tngx_output_chain_align_file_buf(ngx_output_chain_ctx_t*ctx,off_tbsize){size_tsize;ngx_buf_t*in;inctx-in-buf;if(in-fileNULL||!in-file-directio){returnNGX_DECLINED;}ctx-directio1;size(size_t)(in-file_pos-(in-file_pos~(ctx-alignment-1)));if(size0){if(bsize(off_t)ctx-bufs.size){returnNGX_DECLINED;}size(size_t)bsize;}else{size(size_t)ctx-alignment-size;if((off_t)sizebsize){size(size_t)bsize;}}ctx-bufngx_create_temp_buf(ctx-pool,size);if(ctx-bufNULL){returnNGX_ERROR;}/* * we do not set ctx-buf-tag, because we do not want * to reuse the buf via ctx-free list */#if(NGX_HAVE_ALIGNED_DIRECTIO)ctx-unaligned1;#endifreturnNGX_OK;}2 目的1 设计意图ngx_output_chain_align_file_buf的核心职责是为 DirectIO 场景下可能存在的文件偏移未对齐问题分配一个小的临时缓冲区来读取未对齐的头部数据使后续读取的起始位置对齐到磁盘扇区边界。该函数属于 Nginx输出过滤链的缓冲区分配阶段。Nginx 的ngx_output_chain定义于同一文件是输出数据处理的核心引擎负责将上游传递的数据缓冲区ctx-in链经过必要的拷贝/变换后通过output_filter传递给下游过滤器。当ngx_output_chain发现当前没有可用的输出缓冲区ctx-buf NULL时会首先调用本函数尝试对齐分配。DirectIO直接 I/O允许应用程序绕过操作系统的页缓存直接与磁盘设备交互从而消除双重缓冲的开销。其代价是在部分操作系统尤其是 Linux上DirectIO 要求读写缓冲区的内存地址和文件偏移都对齐到文件系统扇区大小通常 512 字节XFS 文件系统可达 4096 字节。如果文件偏移未对齐Linux 会返回EINVAL错误而非自动回退到普通读取。本函数的引入正是为了解决这一问题当检测到使用了 DirectIO 且当前文件偏移未对齐时它计算需要补齐多少字节才能到达下一个对齐边界分配一个恰好大小的临时缓冲区用于吃掉这些非对齐数据之后的读取位置即自然对齐。这一设计使得 Nginx 可以在享受 DirectIO 性能优势的同时无需上游模块保证数据边界恰好对齐。3 详解1 函数签名staticngx_int_tngx_output_chain_align_file_buf(ngx_output_chain_ctx_t*ctx,off_tbsize)1 返回值ngx_int_t返回值值含义NGX_DECLINED-5无需对齐处理或无 DirectIO调用方应走普通缓冲区分配路径NGX_ERROR-1内存分配失败调用方应中止当前请求NGX_OK0成功分配了对齐用的临时缓冲区ctx-buf已指向该缓冲区且ctx-directio 12 函数名ngx_output_chain_align_file_buf词段含义ngxNginx 标准前缀output_chain所属模块输出链处理逻辑align核心操作对齐alignmentfile适用场景文件类型的缓冲区区别于内存/mmap 等buf产出物对齐用的临时缓冲区3 参数列表参数名类型含义来源约束ctxngx_output_chain_ctx_t*输出链上下文持有所有累积状态调用方ngx_output_chain非空ctx-in必须非空bsizeoff_t当前输入缓冲区的数据大小字节ngx_output_chain内部通过ngx_buf_size计算bsize 02 逻辑流程ngx_output_chain_align_file_buf(ctx, bsize) ├─ [1] 非 DirectIO 或非文件缓冲区 │ └─ in-file NULL || !in-file-directio → return NGX_DECLINED ├─ [2] 标记 DirectIO 模式 │ └─ ctx-directio 1 ├─ [3] 计算文件偏移的未对齐量 │ └─ size file_pos - (file_pos ~(alignment - 1)) ├─ [4] 已对齐路径 │ └─ size 0 │ ├─ [4.1] 数据量充足 │ │ └─ bsize bufs.size → return NGX_DECLINED直接读入大缓冲区 │ └─ [4.2] 数据量不足一个完整缓冲区 │ └─ size bsize → 分配小临时缓冲区 → return NGX_OK └─ [5] 未对齐路径 └─ size 0 └─ size min(alignment - size, bsize) → 分配对齐补齐缓冲区 → return NGX_OK{size_tsize;ngx_buf_t*in;}局部变量声明。size分配给临时缓冲区的大小字节后续语义根据分支变化未对齐路径下为补齐到对齐边界的字节数已对齐路径下为bsize。in来自ctx-in-buf的快捷引用1 非 DirectIO 或非文件缓冲区inctx-in-buf;if(in-fileNULL||!in-file-directio){returnNGX_DECLINED;}进入条件函数入口无条件执行。处理逻辑先从输入缓冲区获取对ngx_file_t的引用。in-file NULL表示该缓冲区不关联任何文件例如纯内存缓冲区、临时内存缓冲区等in-file-directio是ngx_file_t中的位字段定义于src/core/ngx_file.h:unsigneddirectio:1;该标记由ngx_http_copy_filter_module或其他调用方在配置阶段根据用户指令设置表示本次响应输出打开了 DirectIO。任一条件不满足直接返回NGX_DECLINED。这个返回值的语义是本函数不适用当前场景请走普通缓冲区分配逻辑。调用方ngx_output_chainsrc/core/ngx_output_chain.c收到NGX_DECLINED后会跳过ctx-buf此时为 NULL未被本函数设置转而尝试从ctx-free链表复用已有缓冲区或调用ngx_output_chain_get_buf分配新的输出缓冲区。设计意图本函数只在 DirectIO 的文件缓冲区场景下才有用武之地。非 DirectIO 场景下操作系统的块设备层会自动处理对齐问题无需额外干预。将是否需要对齐的判断前置于此使函数的主体逻辑专注于纯粹的对齐计算 分配语义。2 标记 DirectIO 模式ctx-directio1;进入条件通过了 [1] 的检查即确定当前处于 DirectIO 文件缓冲区场景。处理逻辑ctx-directio是ngx_output_chain_ctx_t中的位字段定义于src/core/ngx_buf.h:unsigneddirectio:1;将其设置为1向下游声明当前输出链处于 DirectIO 模式。此标记有两个下游影响影响后续缓冲区分配策略影响缓冲区复用策略3 计算文件偏移的未对齐量size(size_t)(in-file_pos-(in-file_pos~(ctx-alignment-1)));进入条件DirectIO 已确认开启。处理逻辑这是整个函数最关键的位运算。先拆解右侧子表达式ctx-alignment对齐粒度来自 Nginx 配置的directio_alignment指令默认值NGX_CONF_UNSET即 0实际运行时会与上级配置合并为有效值512src/http/ngx_http_core_module.c通过ngx_conf_merge_off_value合并。通常设置为磁盘扇区大小512 字节。ctx-alignment - 1构造对齐掩码。例如 alignment 512 时得到 511二进制0000000111111111。~(ctx-alignment - 1)逐位取反。例如 alignment 512 时得到1111111111100000000高位全 1低 9 位全 0。in-file_pos ~(ctx-alignment - 1)将当前文件偏移的低 N 位清零即将file_pos向下对齐到最近的 alignment 整数倍边界。in-file_pos - (...)原偏移量减去已对齐的偏移量得到未对齐的尾部字节数即当前读取位置偏离上一个对齐边界的距离。以具体数值为例假设ctx-alignment 512in-file_pos 1000file_pos 1000 (二进制: ...0001111101000) ~(alignment - 1) ~511 (二进制: ...1110000000000) file_pos ~(alignment -1) 512 (向下对齐到 512) 未对齐量 1000 - 512 488这个值表示当前文件读取位置距离上一个 512 字节对齐边界有 488 字节——也就是说要使下一次读取从 1024下一个对齐边界开始需要先读取 512 - 488 24 字节的补齐数据。4 已对齐路径if(size0){if(bsize(off_t)ctx-bufs.size){returnNGX_DECLINED;}size(size_t)bsize;进入条件size 0即当前file_pos已对齐到alignment边界。ngx_bufs_t定义src/core/ngx_buf.htypedefstruct{ngx_int_tnum;size_tsize;}ngx_bufs_t;其中ctx-bufs.size是输出缓冲区的标准大小由output_buffers指令配置表示一组输出缓冲区的单个大小。4.1 数据量充足if(bsize(off_t)ctx-bufs.size){returnNGX_DECLINED;}进入条件文件偏移已对齐且待处理的数据量bsize≥ 标准缓冲区大小。处理逻辑数据量足够填满一个标准缓冲区无需分配补齐用的小缓冲区。返回NGX_DECLINED调用方将转而分配一个标准大小的缓冲区通过ngx_output_chain_get_buf直接读取数据。设计意图当前已对齐 数据量 标准缓冲区 → 直接使用大缓冲区读取保证 I/O 效率。分配小缓冲区会浪费一次 I/O 系统调用。4.2 数据量不足一个完整缓冲区size(size_t)bsize;进入条件文件偏移已对齐且bsize ctx-bufs.size数据量不足填满一个标准缓冲区。处理逻辑将size设为bsize即只需分配一个恰好容纳剩余数据的小缓冲区。后续步骤中会用这个 size 调用ngx_create_temp_buf分配临时缓冲区。5 未对齐路径}else{size(size_t)ctx-alignment-size;if((off_t)sizebsize){size(size_t)bsize;}}进入条件size 0即当前file_pos未对齐到alignment边界。处理逻辑第一步size alignment - size。此时变量size的语义发生变化——从未对齐偏移量变为需要读取多少字节才能到达下一个对齐边界。沿用前文的例子alignment512, file_pos1000, 偏移量488需要对齐补齐量 512 - 488 24这意味着只需读取 24 字节的填补数据文件偏移就从 1000 变为 1024下一次读取就对齐了。第二步if (size bsize)。如果对齐补齐量大于实际可用数据量则以实际数据量为准。这对应数据尾部场景剩余数据不足到达下一个对齐边界且这是最后一批数据不存在下一次读取。例如file_pos 1000bsize 20。此时需要 24 字节才能对齐到 1024但只剩下 20 字节数据。最终size 20仅读取实际剩余数据。6 分配对齐用临时缓冲区ctx-bufngx_create_temp_buf(ctx-pool,size);if(ctx-bufNULL){returnNGX_ERROR;}进入条件对已对齐路径的 [4.2] 或未对齐路径的 [5]size已被确定为临时缓冲区的分配大小。ngx_create_temp_buf函数概述定义在src/core/ngx_buf.c。该函数从指定内存池分配一个ngx_buf_t结构体及其数据区。处理逻辑将分配结果赋给ctx-buf。ctx-buf是ngx_output_chain_ctx_t的输出缓冲区指针定义于src/core/ngx_buf.h:79。之后的ngx_output_chain_copy_buf函数会使用ctx-buf作为目标缓冲区将文件数据从ctx-in-buf拷贝到此缓冲区中。若NULL返回NGX_ERROR调用方ngx_output_chain会立即中止处理并向上传播错误。7 标记未对齐状态平台相关/* * we do not set ctx-buf-tag, because we do not want * to reuse the buf via ctx-free list */源码注释解释了为何不设置ctx-buf-tagctx-tag是缓冲区复用机制的关键标识。同一个 tag 值的缓冲区在完成使用后会被归还到ctx-free链表中供后续复用。本函数分配的临时缓冲区是一个一次性的补齐缓冲区——它仅用于读取 DirectIO 下未对齐头部的那几个字节读完后并不会再次被需要。不设置 tag 意味着该缓冲区不会被加入 free 链表避免了内存池中堆积大量尺寸特殊的小缓冲区这些缓冲区的大小可能远小于标准缓冲区大小复用价值极低。#if(NGX_HAVE_ALIGNED_DIRECTIO)ctx-unaligned1;#endifNGX_HAVE_ALIGNED_DIRECTIO是编译期特性检测宏仅在 Linux 平台上定义为1见auto/unix。原因在于Linux 的 DirectIO 是硬对齐——如果参数未对齐read()/write()直接返回EINVAL必须由应用层自行处理。而 FreeBSD、Solaris、macOS 的 DirectIO 是软对齐——参数未对齐时操作系统会透明回退到普通读取无需应用层额外处理NGX_HAVE_ALIGNED_DIRECTIO宏定义auto/unix→auto/have编译期写入ngx_auto_config.h#ifndefNGX_HAVE_ALIGNED_DIRECTIO#defineNGX_HAVE_ALIGNED_DIRECTIO1#endif仅当系统为 Linux 时定义auto/unix:208-209if [ $ngx_found yes -a $NGX_SYSTEM Linux ]; then haveNGX_HAVE_ALIGNED_DIRECTIO . auto/have; fi。ctx-unaligned位字段设置后实际生效位置有两处同文件读取后line 553-561在ngx_output_chain_copy_buf的从文件读取数据到内存缓冲区路径中读取完成后如果ctx-unaligned为真调用ngx_directio_off(in-file-fd)暂时关闭DirectIO。因为后续的读取已经对齐可能仍然是 DirectIO 模式但本次对齐补齐读取小量数据时关闭 DirectIO 可以让内核走缓存路径避免 DirectIO 的最小传输粒度限制。恢复 DirectIOline 596-609数据读取完成后调用ngx_directio_on(in-file-fd)重新开启 DirectIO并恢复此前的errno值因为ngx_directio_on的fcntl可能会覆盖errno需用ngx_set_errno恢复确保上层能正确获取实际 I/O 操作的错误状态。8 成功返回returnNGX_OK;进入条件临时缓冲区分配成功。此时函数已完成了以下工作ctx-directio 1声明 DirectIO 模式启用ctx-buf指向分配的临时缓冲区尺寸为补齐对齐所需的字节数或实际数据量ctx-unaligned 1如果平台需要标记当前读取为非对齐读取。调用方ngx_output_chain在收到NGX_OK后流程继续——ctx-buf已就绪接着调用ngx_output_chain_copy_buf(ctx)将输入缓冲区的数据文件内容拷贝到此临时缓冲区中然后通过output_filter传递给下游过滤器。整体设计意图总结视角说明安全避免了在不支持软对齐的 Linux 系统上因 DirectIO 对齐不满足而导致的EINVAL错误。如果跳过此函数直接在 Linux 上以未对齐偏移执行 DirectIO 读取内核会拒绝该操作导致请求失败。性能只分配最小必要的缓冲区通常几十到几百字节而非完整大小缓冲区节省内存。补齐小缓冲区只使用一次即丢弃不进入 free 链表复用避免内存池中堆积尺寸特异的碎片。在非 Linux 平台上直接编译消除不产生任何开销。架构将对齐处理的细节封装在模块内部上游模块如ngx_http_copy_filter_module只需设置ctx-alignment和打开 DirectIO 即可无需关心底层对齐逻辑。本函数与ngx_output_chain_get_buf负责标准缓冲区分配和ngx_output_chain_copy_buf负责实际数据拷贝构成了完整的分配-对齐-拷贝三角协作关系。