代码之家  ›  专栏  ›  技术社区  ›  Daniel Walker

初始值为0的sem_open使用EINVAL失败

  •  0
  • Daniel Walker  · 技术社区  · 4 年前

    我有一个在Linux上运行的简单示例:

    #include <fcntl.h>
    #include <stdio.h>
    #include <sys/stat.h>
    
    #include <semaphore.h>
    
    #define SEM_NAME "/some/sem/name"
    
    int main() {
        sem_t *sem;
    
        sem = sem_open(SEM_NAME, O_CREAT, S_IRUSR, 0);
        if ( sem == SEM_FAILED ) {
            perror("sem_open");
            return 1;
        }
    
        sem_close(sem);
        sem_unlink(SEM_NAME);
        return 0;
    }
    

    其输出为:

    sem_open: Invalid argument
    

    以下是可能的原因 EINVAL 如手册页中所列:

    EINVAL :值大于SEM_value_MAX。

    EINVAL :name只包含“/”,后面没有其他字符。

    在我的例子中, value 为0并且 name 显然不仅仅 "/" 。我缺了什么?

    0 回复  |  直到 3 年前
        1
  •  3
  •   Daniel Walker    4 年前

    the POSIX sem_open() documentation :

    这个 name 参数符合路径名的构造规则,除了 <slash> 前导以外的字符 <斜线> 名称中的字符是实现定义的 ,并且name参数的长度限制是由实现定义的,不必与路径名限制相同 {PATH_MAX} {NAME_MAX} 。如果名称以 <斜线> 字符,然后处理调用 sem_open() 具有相同名称值的应引用相同的信号量对象,只要该名称尚未删除即可。如果名称不是以 <斜线> 角色,效果是实现定义的。

    假设您使用的是Linux系统 sem_open() man page 状态:

    sem_open() 创建一个新的POSIX信号量或打开一个现有的 信号灯。信号量由名称标识。有关的详细信息 名称的构造,请参见 sem_overview(7)

    这个 sem_overview(7) man page 状态:

    命名信号量

    命名信号量由表单的名称标识 /somename ;也就是说,一个以null结尾的字符串,最多为 NAME_MAX-4 (即251)个字符,由一个首字母组成 斜杠,后跟一个或多个字符, 都没有 是斜线。

    注意粗体部分:“没有一个是斜杠”。

    你的

    #define SEM_NAME "/some/sem/name"
    

    定义了一个无效的信号量名称。

        2
  •  2
  •   Rachid K.    3 年前

    附带说明一下,在Linux/glibc下,名称管理如下(glibc-2.34的源代码)__ shm_get_name() (文件 sysdeps/pthread/sem_open.c ):

    sem_t *
    __sem_open (const char *name, int oflag, ...)
    {
      int fd;
      sem_t *result;
    
      /* Check that shared futexes are supported.  */
      int err = futex_supports_pshared (PTHREAD_PROCESS_SHARED);
      if (err != 0)
        {
          __set_errno (err);
          return SEM_FAILED;
        }
    
      struct shmdir_name dirname;
      if (__shm_get_name (&dirname, name, true) != 0)
        {
          __set_errno (EINVAL);
          return SEM_FAILED;
        }
    [...]
    

    函数在中定义 posix/shm目录.c 。它首先去掉名称中的标题“/”,并接受它,除非名称的其余部分仍有“/”字符(参见对 memchr() 在最后的检查中):

    int
    __shm_get_name (struct shmdir_name *result, const char *name, bool sem_prefix)
    {
      while (name[0] == '/')
        ++name;
      size_t namelen = strlen (name);
    
      struct alloc_buffer buffer
        = alloc_buffer_create (result->name, sizeof (result->name));
      alloc_buffer_copy_bytes (&buffer, SHMDIR, strlen (SHMDIR));
      if (sem_prefix)
        alloc_buffer_copy_bytes (&buffer, "sem.", strlen ("sem."));
      alloc_buffer_copy_bytes (&buffer, name, namelen + 1);
      if (namelen == 0 || memchr (name, '/', namelen) != NULL
          || alloc_buffer_has_failed (&buffer))
        return -1;
      return 0;
    }
    

    因此,这是后一个函数,它使OP的程序失败,因为删除标题时“/some/sem/name”仍然包含“/”字符。中的以下错误分支 sem_open() 输入代码进行设置 errno EINVAL 和返回 SEM_FAILED :

      if (__shm_get_name (&dirname, name, true) != 0)
        {
          __set_errno (EINVAL);
          return SEM_FAILED;
        }
    

    总之,在这种情况下(Linux/glibc),本手册可以用以下内容完成:

    EINVAL 如果名称中第一个非“/”字符后有“/”个字符,则返回。