解码Nginx:数组(Array)

本文介绍Nginx中数组的设计理念与实现细节,包括使用预分配内存块以提高性能,以及ngx_array_create、ngx_array_destroy等核心函数的工作原理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

源码文件

src/core/ngx_array.h
src/core/ngx_array.c

设计思路

数组是非常常用的数据结构。为降低使用时的时间开销,Nginx对数组设计做了不少简化:

  1. 使用从给定内存池中预分配好的储备元素区块;
  2. 储备元素区块无空闲元素供分配时,尝试从原内存池中分配新的空闲元素,或扩充储备元素区块以完成分配;
  3. “分配”,或“压入”,只是取得新的空闲元素的首址,不负责创建实际元素;
  4. “释放”只是调整原内存池的字段,不负责清理实际元素,或者干脆推迟释放时机;
  5. 使用者可以直接访问数组的各个字段,以取得最佳性能。

数组适用于元素较小、或元素生命周期较短,扩充储备元素区不会跨越内存池中多个ngx_pool_data_t结构体的场景。

数据结构

        ngx_array_create(pool, n, size)            ngx_array_push()/            ...
         |                                         ngx_array_push_n()
         |                                          |
         +- ngx_palloc()-----\                  /---?- ngx_palloc()------\
         |                   |                  |                        |
         \- ngx_palloc()-------------------\    |                        |
                             |             |    |                        |
          ngx_array_t        V             |    |                        |
         +-------------------+             |    |                        |
  /----- | elts              | ======================\                   |
  |      +-------------------+             |    |    H                   |
  |      | nelts             | -------\    |    |    H                   |
  |      +-------------------+        |    |    |    H                   |
  |  /-- | size        bytes |        |    |    |    H                   |
  |  |   +-------------------+        |    |    |    H                   |
  |  |   | nalloc        = n | --\    |    |    |    H                   |
  |  |   +-------------------+   |    |    |    |    H                   |
  |  |   | pool              |   |    |    |    |    H                   |
  |  |   +-------------------+   |    |    |    |    H                   |
  |  |                           |    |    |    |    H                   |
  |  |                           |    |    |    |    H                   |
  |  |                       /-------------/    |    H                   |
  |  |                       |   |    |         |    H                   | 
  |  |                       |   |    |         |    H                   |
  |  |                       V   |    |         |    V                   V
  \----> +-------------------+  -+-  -+-        |    +-------------------+   
     |   | elem 0            |   A    A         |    | elem 0            |
     |   /                   /   |    |         |    /                   /
     |   /                   /   |    |         |    /                   /
     |   |                   |   |    |         |    |                   |
     |   +-------------------+   |    |         |    +-------------------+
     |   | elem 1            |   |    |         |    | elem 1            |
     |   /                   /   |    |         |    /                   /
     |   /                   /   |    |         |    /                   /
     |   |                   |   |    |         |    |                   |
    -+-  +-------------------+   |    |         |    +-------------------+
     A   | elem 2            |   |    |         |    | elem 2            |
     |   /                   /   |    |         |    /                   /
     |   /                   /   |    |         |    /                   /
     V   |                   |   |    V         |    |                   |
    ---  +-------------------+   |   ---        |    +-------------------+
         | unpushed          |   |              |    | elem 3            |
         / elements          /   |              |    /                   /
         /                   /   |              |    /                   /
         /                   /   |              |    |                   |
         /                   /   |              |    +-------------------|
         /                   /   |              |    | elem 4            |
         /                   /   |              |    /                   /
         /                   /   |              |    /                   /
         |                   |   V              |    |                   |
         +-------------------+  ---  <----------/    +-------------------+
         | extended          |                       | elem 5            |
         / area              /                       /                   /
         / (in place)        /                       /                   /
         /                   /                       |                   /
         /                   /                       +-------------------|
         /                   /                       | unpushed          |
         /                   /                       / elements          /
         /                   /                       /                   /
         |        = size * n |                       |                   |
         +-------------------+                       +-------------------+

接口函数

ngx_array_create(pool, n, size)

ngx_array_create()负责动态创建一个数组,动作序列如下:

  1. 从pool中分配ngx_array_t结构体;
  2. 从pool中分配能容纳n个size大小的元素的储备元素区块;
  3. 适当地初始化ngx_array_t结构体。

ngx_array_destroy(array)

ngx_array_destroy()负责“释放”数组使用的内存块(实际为推迟释放时机),但不清理其中的各个元素。动作序列如下:

  1. 如果储备元素区块位于pool的末尾,直接移动pool->current->d.last指针,指向“释放”后的地址;
  2. 如果ngx_array_t结构体位于pool的末尾,直接移动pool->current->d.lat指针,指向“释放”后的地址;
  3. 否则什么也不做。

ngx_array_push(array)/ngx_array_push_n(array, n)

ngx_array_push()负责向数组中“压入”一个元素(实际为取得一个空闲元素的首址),动作序列如下:

  1. 若数组中还有空闲元素,直接取得其首址并返回;
  2. 若数组的储备元素区块位于pool的储备内存块中最末位置,且其还有size大小的剩余空间,则直接分配新的空闲元素并返回;
  3. 重新分配新的储备元素区块(原区块的2倍大小)并复制已有元素,将新区块首址存入elts字段后分配新的空闲元素并返回。

ngx_array_push_n()与前者相似,可以一次返回n个新的空闲元素的切片首址。


转自:http://www.ituring.com.cn/article/19056  

其他资料 :

http://blog.youkuaiyun.com/livelylittlefish/article/details/6599056

http://blog.youkuaiyun.com/sunhappy2200/article/details/5915189

<think>好的,我现在需要解决的问题是如何在Linux的Bash脚本中接收一个特定的字符串参数,并将其拆分为数组。用户提供的字符串是"nginx: master,nginx: worker,apple,banana",期望输出数组("nginx: master" "nginx: worker" "apple" "banana")。首先,我得理解用户的需求,即将逗号分隔的字符串转换为数组,但要注意元素中可能包含冒号和空格,比如"nginx: master"这样的部分。 首先,我需要确定如何将字符串按逗号分隔成数组。Bash中默认的IFS(内部字段分隔符)是空格、制表符和换行符,所以直接使用IFS=','可能有效。不过,用户给的例子中的元素包含逗号后面的空格,比如"nginx: master"中的逗号后面有一个空格,所以分割的时候需要处理这种情况。不过,根据示例输入,字符串中的分隔符是逗号,而每个元素内部的空格是元素本身的一部分,不需要处理,只需要按逗号分割即可。但需要确认用户输入的字符串中的逗号是否可能被转义或存在其他情况。 接下来,用户希望将字符串作为参数传递给脚本。假设脚本接收第一个参数,也就是$1,然后将该参数按逗号分割成数组。这时候需要考虑参数中可能包含空格,所以传递参数的时候需要用引号括起来,否则Bash会将其拆分为多个参数。 那么,脚本的大致步骤应该是: 1. 接收输入参数。 2. 设置IFS为逗号,进行分割。 3. 处理可能的空格问题,比如分割后的元素是否需要去除首尾空格?比如,如果输入是"nginx: master, nginx: worker",分割后的第二个元素会有前导空格,是否需要处理?根据用户的示例,输出中的元素没有前导或尾随空格,所以可能输入字符串中的逗号分隔符后面没有空格,或者用户希望保留原始空格。需要看示例输入中的字符串是否有这种情况。例如,用户给出的例子中的输入是"nginx: master,nginx: worker,apple,banana",逗号后面没有空格,所以分割后的元素不会有前导空格。因此,可能不需要额外处理。 但是,如果用户的输入可能存在逗号后面跟随空格的情况,例如"nginx: master, nginx: worker",那么分割后的第二个元素会有前导空格,这时候需要去除。但用户示例的输出显示元素是"nginx: worker",没有前导空格,所以可能需要根据具体情况处理。但根据用户提供的输入字符串,逗号后面没有空格,所以可能不需要处理。 现在,编写脚本的思路是: 将传入的第一个参数按逗号分割成数组。可以使用IFS=',' read -ra array <<< "$1"来实现。 测试例子:假设输入字符串是"nginx: master,nginx: worker,apple,banana",分割后的数组元素应该是"nginx: master"、"nginx: worker"、"apple"、"banana"。 测试这个命令: IFS=',' read -ra arr <<< "nginx: master,nginx: worker,apple,banana" 然后检查数组元素是否正确。执行这个命令后,arr数组的内容应该是四个元素,分别是"nginx: master","nginx: worker","apple","banana"。注意,这里的read命令会将字符串按逗号分割,并且保留元素中的空格,比如"nginx: master"中的空格会被保留,这正是需要的。 所以,正确的做法是: 在脚本中,接收第一个参数,然后使用IFS设置为逗号来分割成数组。 但需要注意,参数传递时是否正确传递。例如,如果用户调用脚本时,参数需要用引号括起来,否则Bash会将字符串中的空格分割成多个参数。 例如,用户调用脚本时应该是: ./script.sh "nginx: master,nginx: worker,apple,banana" 而不是不带引号,否则参数会被拆分成多个部分。 因此,脚本应该正确处理$1参数,然后分割。 现在,编写脚本的具体步骤: 1. 接收参数,保存到变量: input="$1" 2. 设置IFS为逗号,分割到数组: IFS=',' read -ra my_array <<< "$input" 或者更准确的方式,考虑到可能存在的空白问题,或者输入字符串可能带有空格,使用双引号包裹变量展开: IFS=',' read -ra my_array <<< "$input" 测试这个命令是否正确分割。 例如,输入字符串是"a,b,c,d",分割成四个元素。对于用户提供的例子,是否正确? 是的。假设input变量正确获取了参数,那么分割后的数组元素应该正确。 之后,为了验证数组内容,可以遍历打印每个元素: for element in "${my_array[@]}"; do echo "[$element]" done 预期输出: [nginx: master] [nginx: worker] [apple] [banana] 如果正确的话,那么脚本就完成了。 但需要注意,如果输入参数中存在逗号后面跟随空格,比如"nginx: master, nginx: worker",分割后的第二个元素会有前导空格,这时候可能需要处理。但用户提供的例子中没有这种情况,所以可能不需要处理。但如果用户希望处理这种情况,可能需要先替换掉逗号后面的空格,或者在分割后去除每个元素的前后空格。 不过根据用户给出的输入和输出示例,不需要处理空格,所以当前的方法是正确的。 综上,Bash脚本可以写成: #!/bin/bash input="$1" IFS=',' read -ra arr <<< "$input" # 打印数组元素,验证结果 printf '%s\n' "${arr[@]}" 或者,如果需要将数组格式化为要求的输出形式,可以使用: echo "("${arr[@]@Q}")" 这里的${var@Q}会在Bash中把变量引用转换为被引用的格式,适合输出。例如,每个元素会被单引号括起来,或者根据需要调整。不过用户要求的输出形式是用双引号包围,但可能更简单的显示方式是将数组直接打印出来,每个元素用空格分隔,外面用括号包围。例如: echo "(${arr[*]})" 但这样可能在某些情况下导致元素中的空格被错误处理,所以更好的方法是使用循环打印每个元素,或者用@Q转换。 例如,使用: echo "("$(printf '"%s" ' "${arr[@]}")")" 这样可以生成每个元素用双引号包围,空格分隔的数组形式。但需要处理最后的空格问题。 或者,使用循环逐个添加元素到字符串中: result="(" for element in "${arr[@]}"; do result+=" \"$element\"" done result+=" )" echo "$result" 这样生成的字符串会是( "nginx: master" "nginx: worker" "apple" "banana" ) 不过,用户给出的示例输出显示数组("nginx: master" "nginx: worker" "apple" "banana"),即元素之间空格分隔,用双引号包围,整个数组用括号括起来。因此,可能需要调整输出格式。 例如: #!/bin/bash input="$1" IFS=',' read -ra arr <<< "$input" # 构造输出字符串 output="(" for elem in "${arr[@]}"; do output+=" \"$elem\"" done output+=" )" echo "$output" 这样处理后的输出应该符合用户的要求。例如,输入参数为"nginx: master,nginx: worker,apple,banana"时,输出是: ( "nginx: master" "nginx: worker" "apple" "banana" ) 但是否每个元素的双引号是必须的?在Bash中,当数组元素包含空格时,需要用引号括起来,但用户给出的示例输出显示数组元素用双引号包围,例如("nginx: master" ...),因此可能需要添加双引号。 不过,另一种情况是,用户可能只需要数组元素正确分割,输出时每个元素是否带引号可能取决于显示方式。例如,当用declare -p arr命令输出时,Bash会自动添加引号。例如: declare -a arr=([0]="nginx: master" [1]="nginx: worker" [2]="apple" [3]="banana") 如果用户希望输出数组的声明形式,可以使用declare -p,但如果是希望以简单的空格分隔形式显示,那么可能不需要。 但根据用户给出的示例输出,元素是被双引号包围的,因此可能需要生成这样的字符串。例如,输出结果应该严格符合("nginx: master" "nginx: worker" "apple" "banana")。 因此,在构造输出字符串时,每个元素应该用双引号包裹,并且整体用括号包围,元素之间用空格分隔。如上面的脚本。 测试这个脚本,当输入参数是"nginx: master,nginx: worker,apple,banana"时,output变量会变成: ( "nginx: master" "nginx: worker" "apple" "banana" ) 这正是用户想要的。 不过,如果元素本身包含双引号或者特殊字符,可能需要转义,但根据用户提供的例子,这种情况不存在,所以暂时不需要处理。 因此,最终的脚本应该如下: #!/bin/bash input="$1" IFS=',' read -ra arr <<< "$input" output="(" for elem in "${arr[@]}"; do output+=" \"$elem\"" done output+=" )" echo "$output" 或者,更简洁的写法: #!/bin/bash IFS=',' read -ra arr <<< "$1" printf '(%s)' "$(printf '"%s" ' "${arr[@]}")" 不过,这样可能末尾会多一个空格,例如输出:( "nginx: master" "nginx: worker" "apple" "banana" ),其中末尾有一个空格。但用户示例中的输出末尾是否有空格?用户示例的输出数组("nginx: master" "nginx: worker" "apple" "banana"),元素之间没有多余空格。所以可能需要处理。 例如,使用循环构造数组,每个元素用双引号包裹,并用空格连接,最后去除末尾空格: output="(" for elem in "${arr[@]}"; do output+="\"$elem\" " done output="${output% }" # 去除末尾空格 output+=")" echo "$output" 或者,使用printf: elements=$(printf '"%s" ' "${arr[@]}") echo "(${elements% })" 这样更简洁。例如: #!/bin/bash input="$1" IFS=',' read -ra arr <<< "$input" elements=$(printf '"%s" ' "${arr[@]}") echo "(${elements% })" 这样,elements变量是各个元素被双引号包围,后面带空格,然后${elements% }去除最后一个空格,得到正确的格式。 测试一下: 当arr数组包含四个元素时,printf '"%s" ' 会生成"nginx: master" "nginx: worker" "apple" "banana "(注意最后有一个空格),然后${elements% }去掉末尾的一个空格,得到正确的字符串,再包裹在括号中。 这样输出的结果应该符合用户的要求。 综上,这个脚本可以正确完成任务。现在需要验证各个步骤是否正确。 另外,需要考虑参数是否为空的情况,或者没有提供参数时的处理。不过用户的问题中可能假设参数已正确传递,因此可能不需要处理错误情况。 总结步骤: 1. 将输入字符串按逗号分割成数组。 2. 将数组的每个元素用双引号包裹,并用空格连接。 3. 包裹整个结果在括号中,并输出。 因此,最终的Bash脚本可以是: #!/bin/bash input="$1" IFS=',' read -ra arr <<< "$input" # 将数组元素格式化为带双引号的字符串 elements="" for elem in "${arr[@]}"; do elements+="\"$elem\" " done # 去除末尾空格并包裹在括号中 echo "(${elements% })" 或者更简洁的版本: #!/bin/bash IFS=',' read -ra arr <<< "$1" echo "($(printf '"%s" ' "${arr[@]}" | sed 's/ $//'))" 这里使用sed替换掉最后的空格。 或者,使用参数扩展: elements=$(printf '"%s" ' "${arr[@]}") echo "(${elements%% })" 但%%只能处理最长的匹配,可能需要用%来去除末尾空格。 所以正确的写法是使用: elements="${elements% }" 因此,更安全的写法是: #!/bin/bash input="$1" IFS=',' read -ra arr <<< "$input" elements=$(printf '"%s" ' "${arr[@]}") elements="${elements% }" # 去除最后一个空格 echo "($elements)" 这样,输出结果就正确了。 测试这个脚本: 输入参数为"nginx: master,nginx: worker,apple,banana" 分割后的数组是四个元素:nginx: master, nginx: worker, apple, banana。 printf '"%s" ' 会生成每个元素加上双引号和空格,最后elements变量为"nginx: master" "nginx: worker" "apple" "banana "(最后有一个空格),然后经过elements="${elements% }"处理后,变为"nginx: master" "nginx: worker" "apple" "banana",然后包裹在括号中,得到正确的输出。 综上,这个脚本是正确的。</think>要实现在Bash脚本中将字符串参数`"nginx: master,nginx: worker,apple,banana"`转换为数组`("nginx: master" "nginx: worker" "apple" "banana")`,可按以下步骤操作: --- ### 步骤详解 1. **接收参数** 脚本通过`$1`获取输入字符串: ```bash input="$1" ``` 2. **按逗号分割字符串为数组** 使用`IFS=','`设置分隔符为逗号,并通过`read -ra`将字符串存入数组: ```bash IFS=',' read -ra arr <<< "$input" ``` 3. **格式化输出数组** 遍历数组元素并用双引号包裹,最后包裹在括号中: ```bash elements=$(printf '"%s" ' "${arr[@]}") # 每个元素添加双引号和空格 elements="${elements% }" # 移除末尾多余的空格 echo "($elements)" ``` --- ### 完整脚本 ```bash #!/bin/bash input="$1" IFS=',' read -ra arr <<< "$input" # 分割字符串为数组 elements=$(printf '"%s" ' "${arr[@]}") # 添加双引号 elements="${elements% }" # 去除末尾空格 echo "($elements)" # 输出格式化结果 ``` --- ### 验证示例 执行命令: ```bash ./script.sh "nginx: master,nginx: worker,apple,banana" ``` 输出结果为: ``` ("nginx: master" "nginx: worker" "apple" "banana") ``` --- ### 关键点说明 - **参数传递**:需用引号包裹输入字符串,确保Bash将其视为单个参数。 - **数组分割**:`IFS=','`确保按逗号分割,保留元素内部空格(如`nginx: master`)。 - **格式处理**:通过`printf`添加双引号,并移除末尾多余空格,确保输出符合要求。 此方法高效且直接,适用于元素含空格的场景。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值