【问题标题】:Is it correct to call semget() followed by semop() without calling semctl()?在不调用 semctl() 的情况下调用 semget() 后跟 semop() 是否正确?
【发布时间】:2017-03-30 19:45:22
【问题描述】:

我正在查看Beej's Guide to Unix IPC 中的信号量示例程序。

示例程序包含以下信号量初始化代码。我只发布一个与问题相关的 sn-p。要查看完整代码,请访问我上面提供的链接。

/*
** initsem() -- more-than-inspired by W. Richard Stevens' UNIX Network
** Programming 2nd edition, volume 2, lockvsem.c, page 295.
*/
int initsem(key_t key, int nsems)  /* key from ftok() */
{
    int i;
    union semun arg;
    struct semid_ds buf;
    struct sembuf sb;
    int semid;

    semid = semget(key, nsems, IPC_CREAT | IPC_EXCL | 0666);

    if (semid >= 0) { /* we got it first */
        sb.sem_op = 1; sb.sem_flg = 0;
        arg.val = 1;

        printf("press return\n"); getchar();

        for(sb.sem_num = 0; sb.sem_num < nsems; sb.sem_num++) { 
            /* do a semop() to "free" the semaphores. */
            /* this sets the sem_otime field, as needed below. */
            if (semop(semid, &sb, 1) == -1) {
                int e = errno;
                semctl(semid, 0, IPC_RMID); /* clean up */
                errno = e;
                return -1; /* error, check errno */
            }
        }

这是我无法理解的。一旦semget() 创建信号量并使用有效的信号量 ID 成功返回,信号量本身就未初始化并处于不确定状态。 semget 的手册页证实了这一点。

信号量初始化

新创建的集合中的信号量值是不确定的。 (POSIX.1-2001 和 POSIX.1-2008 在这一点上是明确的,尽管 POSIX.1-2008 指出该标准的未来版本可能 需要一个实现来将信号量初始化为 0。)尽管 Linux 与许多其他实现一样,将信号量值初始化为 0,但可移植应用程序不能依赖此:它 应该将信号量显式初始化为所需的值。

可以使用 semctl(2) SETVAL 或 SETALL 操作来完成初始化。在多个对等点不知道谁将是第一个初始化集合的情况下,检查关联的非零 sem_otime semctl(2) IPC_STAT 操作检索的数据结构可用于避免竞争。

但是在上面的代码中semctl() 没有被调用来初始化信号量。相反,semop() 是用sem_op = 1 调用的,根据semop 的手册页,它会执行以下操作。

如果 sem_op 是一个正整数,则操作将该值添加到信号量值 (semval)。此外,如果为此操作指定了 SEM_UNDO,则系统会从 sem_op 中减去 此信号量的信号量调整 (semadj) 值。这个操作总是可以进行的——它从不强制线程等待。调用进程必须对信号量集具有更改权限。

但如果没有初始化,semval 是不确定的。所以添加1 到它仍然是不确定的。

此代码不正确还是我的理解不正确?如果我的理解不正确,请您解释一下为什么这段代码是正确的?

【问题讨论】:

    标签: c ipc semaphore


    【解决方案1】:

    您的理解是正确的,代码不可移植。 POSIX.1-2008 specification of semget 不需要初始化信号量:

    创建后,与新的关联的 semid_ds 数据结构 信号量标识符初始化如下:

    • 在操作权限结构中sem_perm.cuidsem_perm.uidsem_perm.cgidsem_perm.gid应设置为 呼叫的有效用户ID和有效组ID 过程。

    • sem_perm.mode的低9位应设置为semflg的低9位。

    • 变量sem_nsems应设置为nsems的值。

    • 变量sem_otime应设置为0,sem_ctime应设置为当前时间,如IPC General Description中所述。

    • 与集合中每个信号量相关的数据结构不需要初始化semctl() 函数与命令 SETVAL 或 SETALL 可用于初始化每个信号量。

    【讨论】:

    • 需要注意的是,“不需要初始化”并不是说,实现是禁止初始化的。因此,发布的代码可能一直都在工作,因为它总是在将数据初始化为 0 的系统上运行。但下一个系统,代码被移植到,可能不会这样做,然后代码崩溃或行为不端以微妙的方式。
    【解决方案2】:

    免责声明

    这不应该是一个答案,但它不适合 cmets,因为它一直在增长......


    技术领域

    Timanswers 你的问题在技术领域非常好(这就是 SO 的全部意义)。然而,我今晚感觉哲学,我认为你怀疑的根源可能更微妙:我认为你的问题更多是关于上下文和作者,而不是这是关于代码的。


    大量个人意见

    根据您发布的内容:

    信号量初始化

    新创建的信号量的值是不确定的。(POSIX.1-2001 和 POSIX.1-2008 在这一点上是明确的,尽管 POSIX.1-2008 指出该标准的未来版本可能需要实现将信号量初始化为 0。)尽管 Linux 与许多其他实现一样,将信号量值初始化为 0,但可移植应用程序不能依赖此:它应该显式地将信号量初始化为所需的值。

    您正在阅读标题为“Beej's Guide to Unix IPC”的指南。当您阅读这样的标题时,应该会弹出一些非常重要的内容:

    1. 这是一个人对某事的看法 - 这是他的做事的方式。具体来说,一个名叫 Brian Hall 的人,绰号为“Beej”。

    2. 它明确表示它适用于 Unix(实际上是 Linux,见下文)。

    3. 如果您阅读Beej's resume(PDF),您会发现从他所掌握的领域范围来看,他并不是一个简单的程序员。他是一个修补匠,修补匠可以为所欲为。这就是为什么他们是聪明的家伙 - 他们犯了很多错误,因此他们比其他人学到的东西要多得多。

    因此,作者可以做任何他想做的事情,他可以在论文中写任何他想做的事情 - 包括使用平台特定的功能/错误/陷阱/标准/等。有一个 part in the article 明确地告诉您文章的 POSIXey 性质以及编译和运行此代码的平台:

    1.1.观众

    如果您了解 C 或 C++ 并且非常擅长使用 Unix 环境(或其他支持这些系统调用的 POSIXey 环境),那么这些文档适合您。

    (...)

    1.2.平台和编译器

    本文档中的示例在 Linux 下使用 gcc 编译。他们应该在任何有好的 Unix 编译器可用的地方编译。

    再次,更多的个人意见:他说他正在使用 Linuxgcc。所以,标题可以改写为:

    使用 GCC 进行 Linux IPC 编程的不可靠黑客指南

    这听起来很像Rusty Russell 的文章标题! (顺便说一句,另一个修补匠)。无论如何,有这样的标题,你真的会担心 POSIX 吗?

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-02-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多