返回

shm_open 如何确定 NAME_MAX?动态获取共享内存名称长度

Linux

如何为 shm_open 正确确定 NAME_MAX?

使用 shm_open 创建共享内存对象时,name 参数的长度至关重要,它不应超过 NAME_MAX 限制。然而,直接使用 limits.h 中定义的 NAME_MAX 可能并不可靠。如果系统允许不同文件系统具有不同的限制,这个值可能未定义,需要采取其他方法确定最大长度。这时,我们通常会考虑使用 pathconf,但它需要一个具体的路径。那对于 shm_open 使用的共享内存对象来说,应该使用什么路径呢?本文探讨如何解决这一难题,提供一些可靠的方法。

问题根源

shm_openname 参数并不像普通文件路径那样直接关联到某个具体的文件系统位置,通常,它在 Linux 系统下,会被映射到 /dev/shm 下。但是,/dev/shm 可能不是唯一的,也有可能是其他目录,甚至使用内存文件系统(如 tmpfs)。 因此,依赖一个固定的硬编码路径是不可取的。同时, shm_open 的手册页建议,为确保可移植性,name 的最大长度应该不超过 NAME_MAX 个字符。 pathconf 方法能确定给定路径的文件名最大长度,而直接使用 255 并非长久之计。 我们需要找到一个更动态和准确的解决方案。

解决方案一: 使用 "/dev/shm" 的 pathconf

尽管不总是绝对,/dev/shm 通常是共享内存对象的常见存储位置,即使并非固定路径,但至少具有参考意义,我们可以使用它作为pathconf 的输入路径。

操作步骤:

  1. 使用 pathconf 函数,以 "/dev/shm" 作为路径。
  2. 检测 pathconf 返回的 NAME_MAX 值。
  3. 如果返回的值大于 0,就使用该值;否则,尝试其他方法(例如 fallback 为一个已知的小的数值,如255,作为默认长度,这种兜底的方式可以防止潜在问题,当然可以根据自身情况而调整。)

代码示例:

#include <unistd.h>
#include <errno.h>
#include <stdio.h>
#include <limits.h>
#include <stdlib.h>


int get_shm_name_max() {
    long name_max = pathconf("/dev/shm", _PC_NAME_MAX);
    if (name_max == -1) {
       if (errno == EINVAL) {
           // 如果 EINVAL 表示没有限制,则我们仍然需要设置最大长度值,
           // 手册里说的255长度限制可以满足绝大部分要求。
          fprintf(stderr, "Warning: pathconf does not support _PC_NAME_MAX, fallback to default limit 255.\n");
          return 255;
      } else{
            perror("pathconf error");
           return -1;
      }
   }else {
       if(name_max > 0){
            return (int)name_max;
       }else {
           // fallback,一般情况下不可能执行到这里
           fprintf(stderr, "Warning: pathconf _PC_NAME_MAX returns 0 or negative values, fallback to default limit 255.\n");
           return 255;
       }
  }
}
int main() {
    int shm_max = get_shm_name_max();

   if (shm_max == -1) {
       printf("Failed to get shared memory name limit\n");
   }
    else
    {
        printf("The Maximum shared memory object name length is: %d \n",shm_max);
    }
    return 0;
}

解释说明:

代码中,pathconf("/dev/shm", _PC_NAME_MAX) 调用,会尝试从 "/dev/shm" 文件系统的属性中检索 NAME_MAX 。 如果返回值为 -1, 那么就需要检查 errno 值, EINVAL 错误代表这个文件系统不支持 _PC_NAME_MAX。 如果是这种情况, fallback 使用默认值255,这样可以提供一个合理的长度,保证共享内存对象名称的安全性。 代码中会根据 pathconf 的返回值打印获取到的 NAME_MAX 值或者相应的错误信息。

注意: 使用 "/dev/shm" 不一定通用, 只是大部分系统的实现是如此。如果你需要非常高的可移植性,那么下面的方法可能更合适。

解决方案二: 依赖已知上限 (fallback)

如果你希望最大化程序的可移植性,同时确保能够适用于那些没有提供 pathconf 或者 /dev/shm 不可用的情况,那么可以采用兜底策略。 确定一个合理的上限,并且保证大多数系统的兼容。因为 shm_open 手册中声明,名字长度最好不要超过 255,那我们完全可以选取255 作为默认值,并直接采用。

代码示例:

#include <limits.h>
#include <stdio.h>

int get_default_shm_name_max() {
    // Manual specify the default NAME_MAX of shm name
    return 255;
}

int main()
{
   int shm_max =  get_default_shm_name_max();
  printf("The default Maximum shared memory object name length is: %d \n", shm_max);

    return 0;
}

解释说明:

这种方法更加简单明了, 它直接定义了一个返回固定值的函数。这部分逻辑可以整合在其他方法中做 fallback ,保证程序的鲁棒性,减少程序崩溃的可能性。 值得注意,这种方法虽然简洁高效,但在某些极为特殊的文件系统上可能不兼容。 然而对于绝大多数系统而言,这都一个不错的默认值。

安全建议:

  • 尽量不要使用硬编码的方式,优先使用 pathconf 进行动态检测,但同时要做好 fallback , 使用合理的值做兜底。
  • 命名共享内存对象时,注意名称不要过长,控制名称的长度在可接受的范围之内,减少问题发生的可能性。

了解了如何准确获取 NAME_MAX 限制之后,就能更加安全可靠地使用 shm_open 函数了。