Ubuntu 下 nginx-1.24.0 源码分析 - ngx_vslprintf 函数

news/2025/2/7 13:09:02/

ngx_vslprintf 声明

ngx_vslprintf的声明在 ngx_string.h 中:

u_char *ngx_vslprintf(u_char *buf, u_char *last, const char *fmt, va_list args);

ngx_vslprintf 实现

ngx_string.c 中 ngx_vslprintf 函数的定义

u_char *
ngx_vslprintf(u_char *buf, u_char *last, const char *fmt, va_list args)
{u_char                *p, zero;int                    d;double                 f;size_t                 slen;int64_t                i64;uint64_t               ui64, frac;ngx_msec_t             ms;ngx_uint_t             width, sign, hex, max_width, frac_width, scale, n;ngx_str_t             *v;ngx_variable_value_t  *vv;while (*fmt && buf < last) {/** "buf < last" means that we could copy at least one character:* the plain character, "%%", "%c", and minus without the checking*/if (*fmt == '%') {i64 = 0;ui64 = 0;zero = (u_char) ((*++fmt == '0') ? '0' : ' ');width = 0;sign = 1;hex = 0;max_width = 0;frac_width = 0;slen = (size_t) -1;while (*fmt >= '0' && *fmt <= '9') {width = width * 10 + (*fmt++ - '0');}for ( ;; ) {switch (*fmt) {case 'u':sign = 0;fmt++;continue;case 'm':max_width = 1;fmt++;continue;case 'X':hex = 2;sign = 0;fmt++;continue;case 'x':hex = 1;sign = 0;fmt++;continue;case '.':fmt++;while (*fmt >= '0' && *fmt <= '9') {frac_width = frac_width * 10 + (*fmt++ - '0');}break;case '*':slen = va_arg(args, size_t);fmt++;continue;default:break;}break;}switch (*fmt) {case 'V':v = va_arg(args, ngx_str_t *);buf = ngx_sprintf_str(buf, last, v->data, v->len, hex);fmt++;continue;case 'v':vv = va_arg(args, ngx_variable_value_t *);buf = ngx_sprintf_str(buf, last, vv->data, vv->len, hex);fmt++;continue;case 's':p = va_arg(args, u_char *);buf = ngx_sprintf_str(buf, last, p, slen, hex);fmt++;continue;case 'O':i64 = (int64_t) va_arg(args, off_t);sign = 1;break;case 'P':i64 = (int64_t) va_arg(args, ngx_pid_t);sign = 1;break;case 'T':i64 = (int64_t) va_arg(args, time_t);sign = 1;break;case 'M':ms = (ngx_msec_t) va_arg(args, ngx_msec_t);if ((ngx_msec_int_t) ms == -1) {sign = 1;i64 = -1;} else {sign = 0;ui64 = (uint64_t) ms;}break;case 'z':if (sign) {i64 = (int64_t) va_arg(args, ssize_t);} else {ui64 = (uint64_t) va_arg(args, size_t);}break;case 'i':if (sign) {i64 = (int64_t) va_arg(args, ngx_int_t);} else {ui64 = (uint64_t) va_arg(args, ngx_uint_t);}if (max_width) {width = NGX_INT_T_LEN;}break;case 'd':if (sign) {i64 = (int64_t) va_arg(args, int);} else {ui64 = (uint64_t) va_arg(args, u_int);}break;case 'l':if (sign) {i64 = (int64_t) va_arg(args, long);} else {ui64 = (uint64_t) va_arg(args, u_long);}break;case 'D':if (sign) {i64 = (int64_t) va_arg(args, int32_t);} else {ui64 = (uint64_t) va_arg(args, uint32_t);}break;case 'L':if (sign) {i64 = va_arg(args, int64_t);} else {ui64 = va_arg(args, uint64_t);}break;case 'A':if (sign) {i64 = (int64_t) va_arg(args, ngx_atomic_int_t);} else {ui64 = (uint64_t) va_arg(args, ngx_atomic_uint_t);}if (max_width) {width = NGX_ATOMIC_T_LEN;}break;case 'f':f = va_arg(args, double);if (f < 0) {*buf++ = '-';f = -f;}ui64 = (int64_t) f;frac = 0;if (frac_width) {scale = 1;for (n = frac_width; n; n--) {scale *= 10;}frac = (uint64_t) ((f - (double) ui64) * scale + 0.5);if (frac == scale) {ui64++;frac = 0;}}buf = ngx_sprintf_num(buf, last, ui64, zero, 0, width);if (frac_width) {if (buf < last) {*buf++ = '.';}buf = ngx_sprintf_num(buf, last, frac, '0', 0, frac_width);}fmt++;continue;#if !(NGX_WIN32)case 'r':i64 = (int64_t) va_arg(args, rlim_t);sign = 1;break;
#endifcase 'p':ui64 = (uintptr_t) va_arg(args, void *);hex = 2;sign = 0;zero = '0';width = 2 * sizeof(void *);break;case 'c':d = va_arg(args, int);*buf++ = (u_char) (d & 0xff);fmt++;continue;case 'Z':*buf++ = '\0';fmt++;continue;case 'N':
#if (NGX_WIN32)*buf++ = CR;if (buf < last) {*buf++ = LF;}
#else*buf++ = LF;
#endiffmt++;continue;case '%':*buf++ = '%';fmt++;continue;default:*buf++ = *fmt++;continue;}if (sign) {if (i64 < 0) {*buf++ = '-';ui64 = (uint64_t) -i64;} else {ui64 = (uint64_t) i64;}}buf = ngx_sprintf_num(buf, last, ui64, zero, hex, width);fmt++;} else {*buf++ = *fmt++;}}return buf;
}

作用:

这个函数是 Nginx 源码中的一个关键函数,用于格式化字符串。

它类似于标准 C 语言中的 sprintf 函数,但更加灵活和强大,能够处理多种数据类型,并且可以控制输出的格式。

它在 Nginx 中用于生成日志信息、错误信息等,是 Nginx 内部进行字符串处理的重要工具。

它的主要功能是根据提供的格式字符串和参数,生成格式化的字符串,并将其存储在指定的缓冲区中。


函数参数:

u_char *buf:目标缓冲区的起始地址,用于存储格式化后的字符串。

u_char *last:目标缓冲区的结束地址,用于限制输出的长度,防止缓冲区溢出。

const char *fmt:格式字符串,定义了如何格式化输入的参数。

va_list args:可变参数列表,包含需要格式化的数据。


返回值:

函数的返回值是 u_char * 类型,它指向目标缓冲区 buf 的当前写入位置。

这个返回值非常重要,它是缓冲区中下一个可以写入的字符位置

这使得函数可以被多次调用,每次从上次写入的位置继续写入,从而实现高效的字符串拼接。

在函数的整个执行过程中,buf 不断地向后移动,每次写入一个字符后,buf 的值就会增加 1。

当函数处理完所有的格式化指令后,buf 指向的是缓冲区中最后一个写入的字符的下一个位置。


主要逻辑:

循环处理格式字符串

函数通过一个 while 循环逐个字符处理格式字符串 fmt,直到遇到字符串结束符 \0 或者缓冲区满(buf < last

处理普通字符:

如果当前字符不是 %,则直接将其复制到目标缓冲区 buf 中。

处理格式化指令:

如果当前字符是 %,则进入复杂的格式化处理逻辑:

解析宽度和填充字符:

通过循环读取数字字符,解析出格式化宽度 width,并确定填充字符('0'' ')。

解析格式化选项:通过 switch 语句解析格式化选项,如 'u'(无符号整数)、'm'(最大宽度)、'X'(十六进制大写)、'x'(十六进制小写)、'.'(小数点精度)等。

处理具体格式化类型:

根据格式化类型(如 'V''v''s''i''d''f' 等),从可变参数列表 args 中取出对应的值,并调用相应的函数(如 ngx_sprintf_strngx_sprintf_num)进行格式化处理。

特殊处理:

对于浮点数 'f',会先处理符号位,然后分别处理整数部分和小数部分,并控制小数部分的精度。

其他特殊字符:

'c'(字符)、'Z'(字符串结束符)、'N'(换行符)等,也会进行特殊处理。


关键函数调用:

ngx_sprintf_str

用于格式化字符串

Ubuntu 下 nginx-1.24.0 源码分析 - ngx_sprintf_str 函数-CSDN博客


ngx_sprintf_num

用于格式化数字。

Ubuntu 下 nginx-1.24.0 源码分析 - ngx_sprintf_num 函数-CSDN博客


详解:

    u_char                *p, zero;int                    d;double                 f;size_t                 slen;int64_t                i64;uint64_t               ui64, frac;ngx_msec_t             ms;ngx_uint_t             width, sign, hex, max_width, frac_width, scale, n;ngx_str_t             *v;ngx_variable_value_t  *vv;

u_char *p

这是一个指向 u_char 类型的指针,在函数中,它会被用来临时存储或操作字符串。

u_char zero;

这是一个 u_char 类型的变量,用于存储一个字符。在函数中,它会被用来表示填充字符(如 '0'' '),具体取决于格式化要求。

int d;

这是一个整数类型的变量,用于存储临时的整数值。在函数中,它可能会被用来处理字符类型的数据(如 ASCII 值)。

double f;

这是一个双精度浮点数类型的变量,用于处理浮点数格式化。在函数中,它会被用来存储从可变参数列表中取出的浮点数值。

size_t slen;

这是一个无符号整数类型,通常用于表示长度或大小。在函数中,它会被用来存储字符串的最大长度或其他相关的长度信息。

int64_t i64;

这是一个 64 位有符号整数类型的变量,用于处理有符号整数格式化。在函数中,它会被用来存储从可变参数列表中取出的有符号整数值。

uint64_t ui64;

这是一个 64 位无符号整数类型的变量,用于处理无符号整数格式化。在函数中,它会被用来存储从可变参数列表中取出的无符号整数值。

uint64_t frac;

这也是一个 64 位无符号整数类型的变量,用于处理浮点数的小数部分。在函数中,它会被用来存储浮点数的小数部分的值。

ngx_msec_t ms;

这是一个 Nginx 自定义的类型,通常用于表示毫秒时间。在函数中,它会被用来处理与时间相关的格式化。

ngx_msec_t

这个类型的定义在哪里?

在 os/unix/ngx_time.h 中:

typedef ngx_rbtree_key_t      ngx_msec_t;

ngx_core.h 中引入了 ngx_time.h

#include <ngx_time.h>

ngx_msec_t 的本质是 ngx_rbtree_key_t 类型

ngx_rbtree_key_t 类型的定义是什么呢?

它的定义在 ngx_rbtree.h 中:

typedef ngx_uint_t  ngx_rbtree_key_t;

所以它的本质是 ngx_uint_t 类型

 ngx_core.h 中引入了 ngx_rbtree.h

#include <ngx_rbtree.h>

ngx_uint_t 的定义是什么?

gx_uint_t 的定义是 ngx_config.h 中

typedef uintptr_t       ngx_uint_t;

intptr_t 是一种整数类型,它保证可以容纳指针的值,帮助我们安全地在指针和整数之间进行转换

在不同的系统和编译器中,指针的大小可能不同(比如 32 位系统和 64 位系统)

intptr_t 确保在这些系统上都能正确工作。

可以通过引入 #include <unistd.h>来使用 intptr_t 类型

uintptr_t 是 intptr_t 的无符号形式

ngx_uint_t width;

这是一个无符号整数类型的变量,用于存储格式化宽度。

ngx_uint_t sign;

这是一个无符号整数类型的变量,用于存储符号标志。在函数中,它会被用来表示是否需要显示符号(如正负号)。

ngx_uint_t hex;

这是一个无符号整数类型的变量,用于存储十六进制标志。在函数中,它会被用来表示是否需要以十六进制格式输出。

ngx_uint_t max_width;

这是一个无符号整数类型的变量,用于存储最大宽度。在函数中,它会被用来限制输出的最大宽度。

ngx_uint_t frac_width;

这是一个无符号整数类型的变量,用于存储小数部分的宽度。在函数中,它会被用来控制浮点数小数部分的精度。

ngx_uint_t scale;

这是一个无符号整数类型的变量,用于存储缩放因子。在函数中,它会被用来计算浮点数小数部分的缩放值。

ngx_uint_t n;

这是一个无符号整数类型的变量,用于临时存储计数或其他整数值。在函数中,它会被用来进行循环计数或其他计算。

ngx_str_t *v;

这是一个指向 ngx_str_t 类型的指针。ngx_str_t 是 Nginx 自定义的结构体,通常用于表示字符串。

在函数中,它会被用来从可变参数列表中取出字符串类型的参数。

ngx_str_t 

声明在 ngx_string.h 中:

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

ngx_variable_value_t *vv;

这是一个指向 ngx_variable_value_t 类型的指针。ngx_variable_value_t 是 Nginx 自定义的结构体,通常用于表示变量的值。

在函数中,它会被用来从可变参数列表中取出变量类型的参数。

ngx_variable_value_t

类型的 定义在 ngx_string.h:

typedef struct {unsigned    len:28;unsigned    valid:1;unsigned    no_cacheable:1;unsigned    not_found:1;unsigned    escape:1;u_char     *data;
} ngx_variable_value_t;

while (*fmt && buf < last) {

逐个解析格式化字符串(fmt)中的字符,并根据格式说明符将参数数据写入目标缓冲区(buf

*fmt:检查格式化字符串是否尚未结束(\0)。

buf < last:确保目标缓冲区仍有空间(last指向缓冲区末尾的下一个位置,因此buf必须严格小于last才允许写入)。

if (*fmt == '%') {

当格式化字符串(fmt)遇到%字符时,表示进入一个格式说明符的解析过程,

该过程负责提取参数、应用格式规则(如宽度、精度、符号等),并将数据安全写入缓冲区。

            i64 = 0;ui64 = 0;zero = (u_char) ((*++fmt == '0') ? '0' : ' ');width = 0;sign = 1;hex = 0;max_width = 0;frac_width = 0;slen = (size_t) -1;

这段代码的作用是为处理 % 后面的格式化指令做准备,初始化一些关键变量,确保后续的处理逻辑能够正确执行

初始化变量:

i64 = 0;
初始化一个 64 位有符号整数变量 i64,用于存储后续可能的有符号整数。

ui64 = 0;
初始化一个 64 位无符号整数变量 ui64,用于存储后续可能的无符号整数。

zero = (u_char) ((*++fmt == '0') ? '0' : ' ');                                                           检查格式化字符串的下一个字符是否是 '0'。                                                                                如果是 '0',则将 zero 设置为 '0',表示后续的数字格式化会用零填充;否则设置为 ' ',表示用空格填充。fmt 指针会向前移动一位。

width=0;                                    初始化宽度变量 width,用于存储格式化指令中指定的宽度(例如 %5d 中的 5)。

sign = 1;
初始化符号标志 sign,默认值为 1,表示后续的数字是有符的。                                                  如果后续遇到 u 或其他无符号修饰符,会将 sign 设置为 0

hex = 0;
初始化十六进制标志 hex,默认值为 0,表示后续的数字不是十六进制格式。                              如果后续遇到 xX,会将 hex 设置为 12

max_width = 0;
初始化最大宽度变量 max_width,用于存储格式化指令中可能的最大宽度限制。

frac_width = 0;
初始化小数部分宽度变量 frac_width,用于存储浮点数格式化指令中指定的小数部分宽度(例如 %.2f 中的 2)。

slen = (size_t) -1;
初始化字符串长度变量 slen,默认值为 (size_t) -1,表示字符串长度未指定。如果后续遇到 *,会从 va_list args 中取出实际的长度值。

while (*fmt >= '0' && *fmt <= '9') {width = width * 10 + (*fmt++ - '0');}

这段代码的作用是解析格式说明符中的宽度字段(例如`%10d`中的`10`),并将其转换为整数值存储在`width`变量中。width 用于控制后续格式化输出的宽度

for ( ;; ) {

无限循环:通过for ( ;; )实现,内部通过break退出。 

逐字符处理:每次循环处理一个字符(*fmt),通过switch-case分支处理不同修饰符。

通过continue继续解析更多修饰符,或通过break退出循环

switch (*fmt) {

switch (*fmt):根据当前字符 *fmt 的值,选择执行不同的代码分支。

*fmt 是当前格式化字符串中的字符。

            case 'u':sign = 0;fmt++;continue;

这段代码是一个 case 分支,用于处理格式化字符串中的 u 修饰符。

u 修饰符表示后续的整数是无符号的。

代码通过设置 sign 变量为 0 来标记这一点,并将 fmt 指针向前移动一位,然后继续处理下一个字符。

            case 'm':max_width = 1;fmt++;continue;

处理格式化字符串中的 m 修饰符

当前字符 *fmt'm'。这意味着格式化指令中有一个最大宽度的限制。

max_width 设置为 1

max_width 是一个标志变量,用来标记是否有限制最大宽度的要求。

max_width 被设置为 1,后续的代码就知道在格式化输出时需要考虑最大宽度的限制

为什么要这样做:                                                                                                                           某些数据类型(如整数)的最大可能值需要固定的字符宽度。例如,32位整数的最大值是 2147483647,需要 10 个字符,加上负号可能需要 11 个字符。max_width = 1 告诉程序后续要自动计算这个最大宽度,而不是用用户指定的值。

fmt 指针向前移动一位,准备处理下一个字符

continue;跳过当前循环的剩余部分,直接进入下一次循环,这意味着在处理完 'm' 修饰符后,代码不会执行 switch 语句后面的代码,而是直接继续处理下一个字符

为什么需要跳过:因为 m 可能只是格式说明符中的一个修饰符,后面可能还有其他字符需要处理(比如 %mlx 中的 l 和 x)。continue 会让程序继续解析后面的字符。

            case 'X':hex = 2;sign = 0;fmt++;continue;

处理格式化字符串中的 X 修饰符

在格式化字符串中,%X 表示以大写十六进制格式输出一个数字。

这段代码的作用是告诉程序接下来我们要以大写十六进制格式输出一个数字

hex 是一个标志变量,用来标记当前的格式化指令是否要求以十六进制格式输出。

在这里,hex = 2 表示要求以大写十六进制格式输出。如果 hex1,则表示小写十六进制;如果是 0,则表示不是十六进制格式

sign = 0 表示当前的数字是无符号的。因为十六进制通常用于表示无符号数字,所以这里把 sign 设置为 0

fmt++,代码跳过当前的 'X' 字符,准备处理下一个字符

continue;

这行代码的作用是跳过当前循环的剩余部分,直接进入下一次循环

            case 'x':hex = 1;sign = 0;fmt++;continue;

与上一段代码类似

            case '.':fmt++;while (*fmt >= '0' && *fmt <= '9') {frac_width = frac_width * 10 + (*fmt++ - '0');}break;

处理格式化字符串中的小数点(.)和它后面跟着的数字。

这些数字表示小数部分的宽度,比如在 %5.2f 中,2 表示小数部分显示两位。

fmt++,代码跳过当前的 '.' 字符,准备处理小数点后面的数字

while (*fmt >= '0' && *fmt <= '9')

这是一个循环,用来检查当前字符是否是一个数字

*fmt >= '0' && *fmt <= '9':这个条件检查当前字符是否在 '0''9' 之间,也就是是否是一个数字

如果是数字,循环继续执行;如果不是数字,循环结束

frac_width = frac_width * 10 + (*fmt++ - '0');

这行代码的作用是把小数点后面的数字解析出来,并存储到 frac_width 中。

frac_width * 10:把当前的 frac_width 值乘以 10,相当于把数字左移一位。

比如,如果 frac_width2,乘以 10 后变成 20

(*fmt++ - '0'):把当前字符转换成对应的数字。比如,字符 '3' 的 ASCII 值是 51,'0' 的 ASCII 值是 48,所以 '3' - '0' 的结果是 3

fmt++:把 fmt 指针向前移动一位,继续处理下一个字符。

break;

退出 switch 语句,然后会退出 for 循环

            case '*':slen = va_arg(args, size_t);fmt++;continue;

处理格式化字符串中的 * 修饰符

* 表示格式化指令中的宽度或精度由一个变量指定,而不是直接写在格式化字符串中。代码会从 va_list args 中取出这个变量,并将其存储到 slen

    switch (*fmt) {case 'V':v = va_arg(args, ngx_str_t *);buf = ngx_sprintf_str(buf, last, v->data, v->len, hex);fmt++;continue;

进入下一个 switch 

前面的 switch:处理的是格式化指令中的修饰符,比如宽度、精度、是否是无符号数等。它的目的是解析这些修饰符,并设置相应的标志变量

这个 switch:在解析完所有修饰符后,用来处理数据类型的转换

处理格式化字符串中的 V 修饰符。V 表示需要插入一个字符串,这个字符串的地址存储在参数列表 args 中。代码会从 args 中取出这个字符串的地址,并将其内容格式化到输出缓冲区 buf

v = va_arg(args, ngx_str_t *);

这行代码从参数列表 args 中取出一个 ngx_str_t 类型的指针,并将其存储到变量 v 中。

ngx_str_t 是一个结构,通常包含两个字段:

  data:指向字符串内容的指针。

  len:字符串的长度。

buf = ngx_sprintf_str(buf, last, v->data, v->len, hex);

这行代码调用 ngx_sprintf_str 函数,将字符串 v->data 格式化到输出缓冲区 buf 中。

ngx_sprintf_str 是一个函数,用于将字符串格式化到缓冲区中

它的参数包括:

  buf:当前的输出缓冲区指针。

  last:缓冲区的末尾指针,用于防止缓冲区溢出。

  v->data:要插入的字符串内容。

  v->len:要插入的字符串长度。

  hex:一个标志,表示是否以十六进制格式输出

调用 ngx_sprintf_str 后,buf 指针会移动到刚刚写入的字符串的末尾,准备写入下一个内容。

        case 'v':vv = va_arg(args, ngx_variable_value_t *);buf = ngx_sprintf_str(buf, last, vv->data, vv->len, hex);fmt++;continue;

处理格式化字符串中的 v 修饰符。

v 表示需要插入一个字符串,这个字符串的地址存储在参数列表 args 中。

代码会从 args 中取出这个字符串的地址,并将其内容格式化到输出缓冲区 buf 中。

vv = va_arg(args, ngx_variable_value_t *);

这行代码从参数列表 args 中取出一个 ngx_variable_value_t 类型的指针,并将其存储到变量 vv 中。

ngx_variable_value_t

是一个结构体

  data:指向字符串内容的指针。

  len:字符串的长度。

buf = ngx_sprintf_str(buf, last, vv->data, vv->len, hex);

这行代码调用 ngx_sprintf_str 函数,将字符串 vv->data 格式化到输出缓冲区 buf 中。

        case 's':p = va_arg(args, u_char *);buf = ngx_sprintf_str(buf, last, p, slen, hex);fmt++;continue;

处理格式化字符串中的 s 修饰符。

s 表示需要插入一个普通的字符串,这个字符串的地址存储在参数列表 args 中。

代码会从 args 中取出这个字符串的地址,并将其内容格式化到输出缓冲区 buf 中。

p = va_arg(args, u_char *);

这行代码从参数列表 args 中取出一个 u_char * 类型的指针,并将其存储到变量 p 中。

buf = ngx_sprintf_str(buf, last, p, slen, hex);

这行代码调用 ngx_sprintf_str 函数,将字符串 p 格式化到输出缓冲区 buf

        case 'O':i64 = (int64_t) va_arg(args, off_t);sign = 1;break;

%O 是 Nginx 自定义的格式符,专用于处理 off_t 类型的数据

off_t 通常表示文件偏移量(比如读写文件时的位置)

off_t

off_t 是一个用于表示文件偏移量的类型。它是一个有符号整数类型。在 C 语言中,文件偏移量是指从文件的开头到文件的某个特定位置之间的字节数。例如,如果一个文件的大小为 1024 字节,那么偏移量可以是 0(文件开头)、512(文件中间位置)等。

它的具体大小(如是 32 位还是 64 位)取决于系统架构和编译器。在 32 位系统上,off_t 通常是 32 位的,而在 64 位系统上,off_t 通常是 64 位的。这是因为 64 位系统可以支持更大的文件大小,需要更大的偏移量范围来表示文件中的位置。

off_t 类型通常定义在 <sys/types.h> 头文件中

i64 = (int64_t) va_arg(args, off_t);

从可变参数列表(args)中提取一个 off_t 类型的参数

off_t 的类型可能因系统不同而变化(例如在 32 位系统可能是 32 位,64 位系统是 64 位)。

sign = 1;

需要处理正负号(比如负数前面加 -

off_t 可以是负数(比如返回错误时的 -1),所以必须保留符号信息

break;

跳出 switch (*fmt) 的整个选择结构

执行完这行后,代码会继续执行 switch 之后的公共逻辑 (ngx_sprintf_num:用于数字转换为字符串)

case 'P':i64 = (int64_t) va_arg(args, ngx_pid_t);sign = 1;break;

%P 是 Nginx 自定义的格式符,专门用于打印进程ID(PID)

用 ngx_pid_t 而不是直接 int

pid_t 类型在不同系统中可能不同(如在 32 位系统是 int,64 位系统可能是 long

Nginx 通过 ngx_pid_t 封装了这一差异,确保代码可移植

将 ngx_pid_t 强制转换为 int64_t(固定 64 位有符号整数),统一后续处理逻辑

ngx_pid_t

ngx_process.h 中可以找到 ngx_pid_t 的定义,它本质上是 pid_t

ngx_core.h 引入了 ngx_process.h

pid_t是一个用来表示进程ID的类型。它是一个整数类型,专门用来存储进程的唯一标识符

pid_t的声明在<sys/types.h>头文件中

进程ID不可能是负数吧?这里为什么还要用 sign=1

某些系统 API 可能返回 -1 表示错误(例如 fork() 失败)

            case 'T':i64 = (int64_t) va_arg(args, time_t);sign = 1;break;

%T 是 Nginx 自定义的格式符,用于处理 time_t 类型的时间戳。

time_t 是什么?

它是 C 标准库中表示时间的类型,通常是从 1970-01-01 开始的秒数(Unix 时间戳)。

在 32 位系统可能是 int32_t(最大到 2038 年),64 位系统是 int64_t

time_t 

要使用 time_t,需要在代码中引入头文件 <time.h>

time_t 的具体类型由编译器和系统决定,可能是 longlong long 或其他整数类型。例如,在32位系统中可能是32位整数,而在64位系统中可能是64位整数。

为什么要把 time_t 转成 int64_t

time_t 的大小可能因系统不同而变化(32 位或 64 位)

转换为 int64_t 确保统一处理,避免溢出(例如 2038 年后 32 位时间戳会溢出)

case 'M':ms = (ngx_msec_t) va_arg(args, ngx_msec_t);if ((ngx_msec_int_t) ms == -1) {sign = 1;i64 = -1;} else {sign = 0;ui64 = (uint64_t) ms;}break;

%M 是 Nginx 自定义的格式符,用于处理 ngx_msec_t 类型的毫秒时间。

在 Nginx 中,-1 表示「无限时间」或「无效值」

if ((ngx_msec_int_t) ms == -1)

ngx_msec_int_t是 ngx_msec_t 对应的有符号类型

ngx_msec_t 被定义为无符号类型,直接判断 ms == -1 会永远为假

通过强制转换为有符号类型,可以正确检测 -1 的特殊值

ngx_msec_int_t

是 ngx_msec_t 的有符号形式

sign = 1; i64 = -1;

当检测到 ms 是 -1 时,表示需要输出 -1

设置 sign = 1:告诉后续代码这是有符号数,需要处理负号

i64 = -1:将值固定为 -1(后续会转换为字符串 "-1"

else { sign = 0; ui64 = (uint64_t) ms; }

当 ms 不是 -1 时,表示合法的毫秒数

sign = 0:标记为无符号数(数值非负)

ui64 = ms:将毫秒值存入无符号变量

case 'z':if (sign) {i64 = (int64_t) va_arg(args, ssize_t);} else {ui64 = (uint64_t) va_arg(args, size_t);}break;

%z 是 Nginx 自定义的格式符,专门处理 size_t 和 ssize_t 类型的数据

size_t 和 ssize_t

size_t 是一个无符号整数类型,

通常用于表示对象的大小或数组的索引。

ssize_t 是有符号的,因此可以表示负数,通常用于错误处理,比如返回-1表示错误。

可以通过引入 <sys/types.h> 使用这2个类型

使用size_t而非固定类型(如intlong)能提高代码在不同平台间的兼容性。

case 'i':if (sign) {i64 = (int64_t) va_arg(args, ngx_int_t);} else {ui64 = (uint64_t) va_arg(args, ngx_uint_t);}if (max_width) {width = NGX_INT_T_LEN;}break;

%i 是 Nginx 自定义的格式符,用于处理其内部整数类型 ngx_int_t 和 ngx_uint_t

ngx_int_t 和 ngx_uint_t 是跨平台类型,可能是 int32_t 或 int64_t,具体取决于编译配置。

if (max_width) 

max_width 由格式字符串中的修饰符设置(例如 %10i 中的 10 是宽度,而 %mi 可能设置 max_width=1)。

%mi 中的 m 表示使用最大宽度(即 max_width=1

NGX_INT_T_LEN

Nginx 定义的常量,表示 ngx_int_t 类型的最大字符长度

例如,如果 ngx_int_t 是 64 位,其最大值为 -9223372036854775808(20 字符),则 NGX_INT_T_LEN 可能是 20

当 max_width 启用时,强制将宽度设为该类型可能的最大长度

NGX_INT_T_LEN

NGX_INT_T_LEN 的定义在 ngx_config.h

#if (NGX_PTR_SIZE == 4)
#define NGX_INT_T_LEN   NGX_INT32_LEN
#define NGX_MAX_INT_T_VALUE  2147483647#else
#define NGX_INT_T_LEN   NGX_INT64_LEN
#define NGX_MAX_INT_T_VALUE  9223372036854775807
#endif

它具体的定义取决于 NGX_PTR_SIZE

NGX_PTR_SIZE 定义在 objs/ngx_auto_config.h 中

#ifndef NGX_PTR_SIZE
#define NGX_PTR_SIZE  8
#endif

所以 #if (NGX_PTR_SIZE == 4) 这个条件不成立

所以 NGX_INT_T_LEN 的定义是:

#define NGX_INT_T_LEN   NGX_INT64_LEN

NGX_INT64_LEN 的定义是什么?在哪里?

在 ngx_config.h 中:

#define NGX_INT64_LEN   (sizeof("-9223372036854775808") - 1)

"-9223372036854775808":这是一个字符串,表示64位整数的最小值。64位整数的范围是从-9223372036854775808到9223372036854775807,这个字符串的长度就是64位整数的最大长度。

-1:因为字符串的长度包括了结尾的空字符\0,所以减去1,得到实际的数字长度。

case 'd':if (sign) {i64 = (int64_t) va_arg(args, int);} else {ui64 = (uint64_t) va_arg(args, u_int);}break;

%d 主要处理有符号整数(如 int),但 Nginx 扩展了它的能力

通过 sign 标志,允许 %d 同时兼容有符号和无符号整数的打印

case 'l':if (sign) {i64 = (int64_t) va_arg(args, long);} else {ui64 = (uint64_t) va_arg(args, u_long);}break;

%l 是 Nginx 自定义的格式符,用于处理 long 和 unsigned long 类型的数据。

例如:

  • %ul → 无符号(sign=0

  • %l 默认有符号(sign=1

i64 = (int64_t) va_arg(args, long);

为什么要转成 int64_t

跨平台一致性:

  long 的大小因系统而异(32 位系统通常是 4 字节,64 位系统是 8 字节)转换后统一处理

case 'D':if (sign) {i64 = (int64_t) va_arg(args, int32_t);} else {ui64 = (uint64_t) va_arg(args, uint32_t);}break;

%D 是 Nginx 自定义的格式符,专门处理精确 32 位的整数(无论系统是 32 位还是 64 位)

  • 典型场景:

    • 处理网络协议中的固定长度字段(如 IP 地址的 32 位表示)

    • 与外部系统交互时确保数据宽度一致(如二进制文件格式)

为什么要转成 int64_t

  • 如果直接操作 int32_t,负数的符号位在 64 位系统中可能错误扩展(例如 -1 在 32 位是 0xFFFFFFFF,转为 64 位可能变成 0xFFFFFFFFFFFFFFFF)。

  • 转换为 int64_t 会保留原始值的符号和大小(例如 -1 → -1)。

  • 后续代码只需处理 int64_t 类型,无需考虑原始数据类型,统一处理

case 'L':if (sign) {i64 = va_arg(args, int64_t);} else {ui64 = va_arg(args, uint64_t);}break;

%L 是 Nginx 自定义的格式符,专门处理 64 位整数

case 'A':if (sign) {i64 = (int64_t) va_arg(args, ngx_atomic_int_t);} else {ui64 = (uint64_t) va_arg(args, ngx_atomic_uint_t);}if (max_width) {width = NGX_ATOMIC_T_LEN;}break;

%A 是 Nginx 自定义的格式符,专门用于处理原子整数类型,原子操作专用

  • 用途场景:

    • 打印多线程环境下的原子计数器(如请求计数、连接数)。

    • 调试原子变量的值(例如检查锁的状态)。

ngx_atomic_int_t 和 ngx_atomic_uint_t

定义在 ngx_atomic.h

#if (NGX_HAVE_LIBATOMIC)#define AO_REQUIRE_CAS
#include <atomic_ops.h>#define NGX_HAVE_ATOMIC_OPS  1typedef long                        ngx_atomic_int_t;
typedef AO_t                        ngx_atomic_uint_t;
typedef volatile ngx_atomic_uint_t  ngx_atomic_t;

#if (NGX_HAVE_LIBATOMIC)

这个条件编译指令检查是否定义了NGX_HAVE_LIBATOMIC宏。

只有在定义了NGX_HAVE_LIBATOMIC这个宏的时候,下面的代码才会被包含进来

这个宏通常在Nginx的配置阶段生成,表示当前系统是否安装了libatomic库。

如果有的话,Nginx就会使用这个库提供的原子操作函数。

libatomic 是一个 跨平台原子操作库,其核心作用是为不同硬件架构提供统一的原子操作接口,Nginx用它来实现原子操作的可移植性。

<atomic_ops.h>libatomic库的头文件,包含平台无关的原子操作函数和类型定义(如AO_t

AO_tatomic_ops库定义的无符号原子类型,其长度和内存对齐由库自动适配当前平台。

AO_t重定义为ngx_atomic_uint_t,是为了隐藏实现细节,提高代码可移植性。如果未来更换原子操作库,只需修改此处类型定义

AO_t通常是一个无符号整型,其具体位数取决于平台,比如在32位系统上是32位,64位系统上是64位,这样能够保证原子操作的效率。

为什么需要原子类型?

线程安全:
这些类型保证在多线程/多进程环境下,读写操作是原子的(不会被中断)。

NGX_ATOMIC_T_LEN

 是原子类型的最大字符长度(例如 32 位原子数最大是 -2147483648,长度是 11)

NGX_ATOMIC_T_LEN 定义

在 os/unix/ngx_atomic.h :

#if (NGX_PTR_SIZE == 8)
#define NGX_ATOMIC_T_LEN            (sizeof("-9223372036854775808") - 1)
#else
#define NGX_ATOMIC_T_LEN            (sizeof("-2147483648") - 1)
#endif

同 NGX_INT_T_LEN 一样是 (sizeof("-9223372036854775808") - 1)


case 'f':f = va_arg(args, double);          // 提取浮点数if (f < 0) {                       // 处理负号*buf++ = '-';f = -f;}ui64 = (int64_t) f;                // 取整数部分frac = 0;                           // 小数部分初始化if (frac_width) {                   // 如果有小数位数要求scale = 1;for (n = frac_width; n; n--) {  // 计算10的N次方(如保留3位小数,scale=1000)scale *= 10;}// 计算小数部分并四舍五入frac = (uint64_t) ((f - (double) ui64) * scale + 0.5);// 处理进位(如0.9995保留3位会进位到1.000)if (frac == scale) {ui64++;frac = 0;}}// 写入整数部分buf = ngx_sprintf_num(buf, last, ui64, zero, 0, width);if (frac_width) {                    // 写入小数部分if (buf < last) {*buf++ = '.';}buf = ngx_sprintf_num(buf, last, frac, '0', 0, frac_width);}fmt++;continue;

1. 提取浮点数

f = va_arg(args, double);

作用:从参数列表中提取一个双精度浮点数(如 3.1415


2. 处理负号

if (f < 0) {*buf++ = '-';  // 写入负号f = -f;        // 转为正数处理
}

如果是负数,先在缓冲区写 -

将 f 转为正数,简化后续处理


3. 分离整数和小数部分

ui64 = (int64_t) f;  // 取整数部分(如123.456 → 123)
frac = 0;             // 初始化小数部分为0

(int64_t) f 会直接截断小数(例如 3.999 → 3


4. 处理小数位数

if (frac_width) {// 1. 计算缩放因子(如保留3位 → scale=1000)scale = 1;for (n = frac_width; n; n--) {scale *= 10;}// 2. 计算四舍五入后的小数值frac = (uint64_t) ((f - ui64) * scale + 0.5);// 3. 处理进位(如0.9995 → 1.000)if (frac == scale) {ui64++;   // 整数部分加1frac = 0; // 小数清零}
}

frac_width 是格式字符串中指定的保留小数位数(如 %.3f → frac_width=3 

  • 示例:
    对 3.1415926 保留3位小数:

    1. scale = 1000

    2. 小数部分 0.1415926 → 0.1415926 * 1000 = 141.5926 → 加上 0.5 后四舍五入 → 142

    3. 最终 frac=142 → 输出 3.142


5. 写入整数部分

buf = ngx_sprintf_num(buf, last, ui64, zero, 0, width);


6. 写入小数部分

if (frac_width) {if (buf < last) {  // 检查缓冲区是否还有空间*buf++ = '.';   // 写入小数点}// 将小数部分按指定位数写入(如frac=142 → 在小数点后写入"142")buf = ngx_sprintf_num(buf, last, frac, '0', 0, frac_width);
}


7. 收尾处理

fmt++;     // 移动到格式字符串的下一个字符
continue; // 跳过循环剩余代码


#if !(NGX_WIN32)
case 'r':i64 = (int64_t) va_arg(args, rlim_t);sign = 1;break;
#endif

第1行:#if !(NGX_WIN32)
作用:这是条件编译指令,意思是“如果当前不是Windows系统”。
Nginx需要跨平台兼容,但rlim_t这个类型是Unix/Linux系统中特有的(用于表示系统的资源限制值,如ulimit)。Windows没有这个类型和对应的系统调用,所以通过条件编译屏蔽了Windows平台的这段代码,避免编译错误。


 rlim_t

rlim_t 用于表示Unix/Linux系统中 进程资源限制的数值,典型场景包括:

  • 定义进程可创建文件的最大大小(RLIMIT_FSIZE
  • 限制进程栈内存大小(RLIMIT_STACK
  • 控制最大打开文件数(RLIMIT_NOFILE
  • 管理CPU时间配额(RLIMIT_CPU

使用 rlim_t 及相关函数时,必须引入 <sys/resource.h> 头文件

底层类型:在POSIX标准中,rlim_t 通常是 无符号64位整数,但在不同系统可能不同


第2行:case 'r':
作用:匹配格式符%r
当Nginx的格式化字符串中遇到%r时,会进入这个分支处理对应的参数。


第3行:i64 = (int64_t) va_arg(args, rlim_t);
从可变参数列表args中提取一个rlim_t类型的值,并强制转换为int64_t(64位有符号整数)。
1️⃣ rlim_t是Unix系统中表示资源限制的类型(例如通过getrlimit()获取进程的文件描述符数量限制)。
2️⃣ 强制转换是为了后续统一处理数值格式(比如处理负数),因为rlim_t可能是longlong long类型,与平台相关,转成固定宽度的int64_t保证兼容性。


第4行:sign = 1;
作用:设置符号标记为需要处理正负号。
资源限制值可能是负数或无意义(例如表示“无限制”),设置sign=1后,后续代码会检查i64是否为负,如果是负数,会在输出时添加负号-


第5行:break;
作用:跳出switch语句


第6行:#endif
作用:结束条件编译块。
与开头的#if !(NGX_WIN32)配对,确保Windows平台下完全忽略这段代码。


case 'p':ui64 = (uintptr_t) va_arg(args, void *);hex = 2;sign = 0;zero = '0';width = 2 * sizeof(void *);break;

第1行:case 'p':
作用:匹配格式符%p,用于输出指针地址
Nginx会通过这个分支处理指针的格式化。


第2行:ui64 = (uintptr_t) va_arg(args, void *);
作用:
1️⃣ va_arg(args, void *):从可变参数列表args中提取一个指针
2️⃣ (uintptr_t):将指针转换为无符号整数类型uintptr_t(保证能完整存储指针值)。


 


第3行:hex = 2;
作用:标记后续以大写十六进制格式输出。
在代码中,hex=1表示小写十六进制(如1a3f),hex=2表示大写(如1A3F)。指针地址通常用大写,例如Linux内核和调试工具(如GDB)的默认行为。


第4行:sign = 0;
指针地址永远是非负数(内存地址从0x0开始),不需要处理正负号


第5行:zero = '0';
作用:设置填充字符为0
统一指针地址的显示风格,例如0x00007ffeeb4a8d20(用0填充左侧空白),而不是空格填充(如0x 7ffeeb4a8d20)。


第6行:width = 2 * sizeof(void *);
作用:计算指针地址的完整十六进制位数。
1️⃣ sizeof(void *):获取指针类型的字节数(32位系统为4字节,64位系统为8字节)。
2️⃣ 2 *:每个字节用2个十六进制字符表示
示例:

  • 32位系统:4字节 × 2 = 8字符 → 地址格式化为0x00000000

  • 64位系统:8字节 × 2 = 16字符 → 地址格式化为0x0000000000000000


case 'c':d = va_arg(args, int);*buf++ = (u_char) (d & 0xff);fmt++;continue;

第1行:case 'c':
作用:匹配格式符%c,用于输出单个字符。


第2行:d = va_arg(args, int);
作用:从可变参数列表args中提取一个int类型的参数。
1️⃣ 在C语言中,char类型参数传递给可变参数函数时会被提升为int(例如字符'A'实际以整数65传递)。
2️⃣ 提取为int是为了兼容这种隐式类型提升,确保能正确处理所有字符值(包括charint参数)。


第3行:*buf++ = (u_char) (d & 0xff);
作用:将整数d转换为1个字节的字符,写入缓冲区。
1️⃣ d & 0xff:取d的最低8位(即一个字节),屏蔽高位数据(例如传入0x1234会被截断为0x34)。

  • 为什么要屏蔽高位?

    • 确保即使传入的int值超出char范围(如256),也只保留有效字节(类似(char)d但更安全)。
      2️⃣ (u_char):将结果转为无符号字符(避免符号扩展问题,例如char可能被当作负数)。
      3️⃣ *buf++:写入缓冲区并移动指针


第4行:fmt++;
作用:移动格式字符串指针到下一个字符。
当前格式符%c已经处理完毕,需要继续解析后续字符


第5行:continue;
作用:跳过switch语句的剩余代码,直接回到while循环开头。
break的区别:

  • break:退出整个switch,继续执行switch之后的代码(例如后续的数值格式化逻辑)。

  • continue:直接回到while (*fmt && buf < last)循环的开头,立即处理下一个格式字符。
    为什么这里用continue
    因为%c的代码逻辑已经完全结束(不需要执行switch后的公共数值处理代码),而其他格式符(如%d%x)可能需要继续执行公共代码。


case 'Z':*buf++ = '\0';fmt++;continue;

第1行:case 'Z':
作用:匹配格式符%Z,用于在缓冲区中写入一个空字符(\0)。

 


第2行:*buf++ = '\0';
作用:
1️⃣ *buf = '\0':在当前缓冲区位置写入空字符(字符串终止符)。


第3行:fmt++;
作用:移动格式字符串指针到下一个字符。
当前格式符%Z已经处理完毕,继续解析后续字符


第4行:continue;
作用:跳过switch语句的剩余代码,直接回到while循环开头。
%Z仅插入空字符,不需要执行后续的数值格式化逻辑(如处理符号、十六进制转换等),因此用continue直接进入下一轮循环。


case 'N':
#if (NGX_WIN32)*buf++ = CR;if (buf < last) {*buf++ = LF;}
#else*buf++ = LF;
#endiffmt++;continue;

第1行:case 'N':
作用:匹配格式符%N,用于输出换行符(类似\n)。
Nginx用%N统一跨平台换行符的差异,类似C语言的\n,但实际行为因平台而异。


第2行:#if (NGX_WIN32)
作用:判断当前是否为Windows系统。
Windows和Unix系统的换行符不同:

  • Windows:换行符是CRLF(Carriage Return + Line Feed,即\r\n)。

  • Unix/Linux/macOS:换行符是LF\n)。


Windows分支代码:
第3行:*buf++ = CR;
作用:写入CR(Carriage Return,ASCII码0x0D,即\r)。
这是Windows换行符的第一个字符。


第4行:if (buf < last) {
作用:检查缓冲区是否还有空间。
写入CR后缓冲区指针buf已移动,需确认剩余空间是否至少1字节。


第5行:*buf++ = LF;
作用:写入LF(Line Feed,ASCII码0x0A,即\n)。
完成Windows换行符\r\n的第二个字符。若缓冲区已满(buf == last),则跳过此操作。


非Windows分支代码:
第7行:*buf++ = LF;
作用:直接写入LF(对应Unix的\n)。
无需CR,单字符即可表示换行。


公共逻辑:
第9行:fmt++;
作用:移动格式字符串指针到下一个字符。


第10行:continue;
作用:跳过switch后续代码,直接处理下一个格式字符。


总结:这段代码通过%N实现了跨平台换行符的自动适配:

  • Windows → \r\n

  • Unix → \n
     


CR、 LF

定义在 ngx_core.h:

​​​​​​​#define LF     (u_char) '\n'
#define CR     (u_char) '\r'
#define CRLF   "\r\n"


case '%':*buf++ = '%';fmt++;continue;default:*buf++ = *fmt++;continue;

case '%'%%转义)

代码作用:当格式字符串中出现连续两个%(即%%)时,输出单个%
逐行解释:
 *buf++ = '%';

  • 向缓冲区写入一个%字符(转义输出)。

  • 直接进入下一轮循环,避免执行其他无关逻辑。


default(未识别的格式符)

代码作用:当遇到未知的格式符(例如%k)时,原样输出该字符。

跳过后续处理,直接处理下一个字符。


if (sign) {if (i64 < 0) {*buf++ = '-';ui64 = (uint64_t) -i64;} else {ui64 = (uint64_t) i64;}
}

第1行:if (sign)

作用:检查是否需要处理数值的符号位。
 

  • sign=1:表示当前处理的数值是有符号类型

  • sign=0:表示数值是无符号类型,直接跳过符号处理。


第2行:if (i64 < 0)

作用:判断数值是否为负数。

如果i64是负数(例如-123),需要输出负号-并取其绝对值,否则直接使用原值
 


第3行:*buf++ = '-';

作用:向缓冲区写入负号-


第4行:ui64 = (uint64_t) -i64;

作用:将负数的绝对值转换为无符号整数。

-i64:对负数取反

(uint64_t):转换为无符号类型,以便后续统一处理数值格式化


第5-6行:else { ui64 = (uint64_t) i64; }

作用:如果数值非负,直接将其转换为无符号整数。


buf = ngx_sprintf_num(buf, last, ui64, zero, hex, width);
fmt++;

第1行:buf = ngx_sprintf_num(...)

作用:调用函数将数值ui64格式化为字符串,并写入缓冲区。
参数解析:
1️⃣ buf:当前缓冲区写入位置(函数返回后更新到新位置)。
2️⃣ last:缓冲区末尾地址(防止溢出)。
3️⃣ ui64:待格式化的无符号数值(绝对值和进制转换已完成)。
4️⃣ zero:填充字符('0'' '),用于宽度不足时补位。
5️⃣ hex:进制模式(0=十进制,1=小写十六进制,2=大写十六进制)。
6️⃣ width:最小输出宽度(若实际位数不足,左侧填充zero字符)。


第2行:fmt++

作用:移动格式字符串指针到下一字符。

当前格式符已处理完毕,fmt指向格式符后的字符

通过fmt++跳到下一个待处理字符,继续主循环


else {*buf++ = *fmt++;
}

将格式字符串当前字符(如abc)复制到缓冲区

同时移动buffmt指针到下一位置

用于处理普通字符(非%的字符),例如格式字符串"hello"会原样写入缓冲区



http://www.ppmy.cn/news/1570073.html

相关文章

vscode修改自定义模板

vscode修改自定义模板 打开命令面板&#xff1a;你可以通过快捷键 CtrlShiftP&#xff08;Windows/Linux&#xff09;或CmdShiftP&#xff08;macOS&#xff09;打开命令面板。输入并选择“首选项: 配置用户代码片段”&#xff1a;在命令面板中输入 Preferences:Configure Use…

PEP8代码规范

文章目录 波浪线 波浪线 不同UI下可能会有差异&#xff0c;但可做通用参考 红色 代码错误&#xff0c;必须处理&#xff01;代码才能正常运行 – 代码未顶格 – 代码未写完灰色 建议改进&#xff0c;不会影响代码正常执行&#xff0c;可以通过ide的格式规范调整 – 注释缺少空…

可靠冲突性多视角学习:通过证据驱动的多视角融合提供决策可靠性

2024年2月28日&#xff0c;由多位作者联合提出了一种名为可靠冲突性多视角学习&#xff08;RCML&#xff09;的框架&#xff0c;其核心贡献在于为含有冲突性实例的多视角数据提供决策结果与可靠性评估。该方法通过证据驱动的多视角融合&#xff08;ECML&#xff09;策略&#x…

【分布式架构理论3】分布式调用(2):API 网关分析

文章目录 一、API 网关的作用1. 业务层面&#xff1a;简化调用复杂性2. 系统层面&#xff1a;屏蔽客户端调用差异3. 其他方面&#xff1a; 二、API 网关的技术原理1. 协议转换2. 链式处理3. 异步请求机制1. Zuul1&#xff1a;同步阻塞处理2. Zuul2&#xff1a;异步非阻塞处理 三…

HTTP异步Client源码解析

我们知道Netty作为高性能通信框架&#xff0c;优点在于内部封装了管道的连接通信等操作&#xff0c;用户只需要调用封装好的接口&#xff0c;便可以很便捷的进行高并发通信。类似&#xff0c;在Http请求时&#xff0c;我们通过调用HttpClient&#xff0c;内部使用java NIO技术&…

使用 cipher /w 清除磁盘删除残留数据(Windows) - 随笔

cipher命令是Windows 系统自带的一个用于管理文件加密和磁盘数据清除的工具。通过 cipher /w 命令&#xff0c;可以清除磁盘上已删除文件的残留数据&#xff0c;确保这些数据无法被恢复。以下是一个简易的批处理脚本&#xff0c;用于清除指定磁盘上的加密数据。 echo off :: 清…

排序算法与查找算法

1.十大经典排序算法 我们希望数据以一种有序的形式组织起来&#xff0c;无序的数据我们要尽量将其变得有序 一般说来有10种比较经典的排序算法 简单记忆为Miss D----D小姐 时间复杂度 &#xff1a;红色<绿色<蓝色 空间复杂度&#xff1a;圆越大越占空间 稳定性&…

DeepSeek 遭 DDoS 攻击背后:DDoS 攻击的 “千层套路” 与安全防御 “金钟罩”

当算力博弈升级为网络战争&#xff1a;拆解DDoS攻击背后的技术攻防战——从DeepSeek遇袭看全球网络安全新趋势 在数字化浪潮席卷全球的当下&#xff0c;网络已然成为人类社会运转的关键基础设施&#xff0c;深刻融入经济、生活、政务等各个领域。从金融交易的实时清算&#xf…