代码之家  ›  专栏  ›  技术社区  ›  Stéphane

当将信号量减为零的进程崩溃时,如何恢复信号量?

  •  44
  • Stéphane  · 技术社区  · 16 年前

    我有多个用g++编译的应用程序,运行在Ubuntu中。我使用命名信号量来协调不同进程。

    一切正常 除了 在以下情况下:如果其中一个进程调用 sem_wait() sem_timedwait() 减少信号量,然后在它有机会呼叫之前崩溃或被杀死-9 sem_post() ,则从那一刻起,命名的信号量“不可用”。

    所谓“不可用”,我的意思是信号量计数现在为零,并且本应将其增加回1的进程已经死亡或被终止。

    我找不到合适的人选 sem_*() API,它可能会告诉我上次递减的进程已崩溃。

    我是不是遗漏了一个API?

    sem_t *sem = sem_open( "/testing",
        O_CREAT     |   // create the semaphore if it does not already exist
        O_CLOEXEC   ,   // close on execute
        S_IRWXU     |   // permissions:  user
        S_IRWXG     |   // permissions:  group
        S_IRWXO     ,   // permissions:  other
        1           );  // initial value of the semaphore
    

    以下是我如何减少它:

    struct timespec timeout = { 0, 0 };
    clock_gettime( CLOCK_REALTIME, &timeout );
    timeout.tv_sec += 5;
    
    if ( sem_timedwait( sem, &timeout ) )
    {
        throw "timeout while waiting for semaphore";
    }
    
    7 回复  |  直到 16 年前
        1
  •  47
  •   Stéphane    16 年前

    事实证明,没有一种方法可以可靠地恢复信号量。当然,任何人都可以 post_sem() 要使计数再次增加到零以上,请发送到指定的信号量,但是如何判断何时需要这样的恢复?提供的API太有限,并且没有以任何方式指示何时发生这种情况。

    注意ipc工具也可以使用——常用工具 ipcmk , ipcrm ,及 ipcs 仅适用于过时的SysV信号量。它们特别不适用于新的POSIX信号量。

    sem_wait() sem_post() 呼叫,我正在使用:

    lockf( fd, F_LOCK, 0 )
    

    lockf( fd, F_ULOCK, 0 )
    

    谢谢你们的帮助,伙计们。

        2
  •  6
  •   Raffi Khatchadourian    12 年前

    使用锁文件而不是信号量,很像@Stphane的解决方案,但没有flock()调用。您只需使用独占锁打开文件:

    //call to open() will block until it can obtain an exclusive lock on the file.
    errno = 0;
    int fd = open("/tmp/.lockfile", 
        O_CREAT | //create the file if it's not present.
        O_WRONLY | //only need write access for the internal locking semantics.
        O_EXLOCK, //use an exclusive lock when opening the file.
        S_IRUSR | S_IWUSR); //permissions on the file, 600 here.
    
    if (fd == -1) {
        perror("open() failed");
        exit(EXIT_FAILURE);
    }
    
    printf("Entered critical section.\n);
    //Do "critical" stuff here.
    
    //exit the critical section
    errno = 0;
    if (close(fd) == -1) {
        perror("close() failed");
        exit(EXIT_FAILURE);
    }
    
    printf("Exited critical section.\n");
    
        3
  •  5
  •   Steve Lazaridis    16 年前

    这是管理信号量时的典型问题。有些程序使用单个进程来管理信号量的初始化/删除。通常这个过程只做这个,其他什么都不做。您的其他应用程序可以等待信号量可用。我见过使用SYSV类型的API实现这一点,但没有使用POSIX。类似于 鸭子 '在semop()调用中使用SEM_UNDO标志。


    但是 根据您提供的信息,我建议您不要使用信号量。尤其是当您的进程有被杀死或崩溃的危险时。尝试使用操作系统将自动为您清理的东西。

        4
  •  2
  •   Carl Smotricz    16 年前

    您应该能够使用 lsof . 那么你可以删除它吗?

    啊,是的。。。 man -k semaphore 去营救。

    ipcrm

        5
  •  2
  •   Duck    16 年前

    您需要再次检查,但我相信可以从信号处理程序调用sem_post。如果您能够捕捉到导致流程中断的某些情况,这可能会有所帮助。

    与互斥不同,任何进程或线程(具有权限)都可以发布到信号量。您可以编写一个简单的实用程序来重置它。大概你知道你的系统什么时候死锁了。您可以将其关闭并运行实用程序。

    SysV信号量更适合这种情况。您可以指定SEM_UNDO,在该命令中,如果进程死亡,系统将撤销对该信号量所做的更改。它们还可以告诉您更改信号量的最后一个进程id。

        6
  •  1
  •   martin clayton egrunin    16 年前

    如果进程被终止,那么就没有任何直接的方法来确定它是否消失了。

    您可以对您使用的所有信号量进行某种定期的完整性检查 semctl (cmd=GETPID)查找在您描述的状态下触及每个信号量的最后一个进程的PID,然后检查该进程是否仍然存在。如果没有,则进行清理。

        7
  •  0
  •   AshkanVZ    6 年前

    如果使用命名的信号量,那么可以使用与中使用的算法类似的算法 lsof fuser .

    考虑这些因素:

    /dev/shm/
    

    2.每个进程在linux中都有一个map_文件,路径如下:

    /proc/[PID]/map_files/
    

    这些映射文件显示了进程内存映射到哪个部分!

    /dev/shm )

    • 首先在新进程中打开命名的信号量,并将结果分配给指针
    • 0xffff1234 )编号,然后使用此路径:

      /proc/self/map_files/ffff1234-*

      应该只有一个文件满足此条件。

    • 获取该文件的符号链接目标。它是命名信号量的完整路径。

    2-迭代所有进程以查找其符号链接标记集与命名信号量的完整路径匹配的映射文件。如果有,则该信号量处于实际使用中,但如果没有,则可以安全地取消命名信号量的链接,并重新打开它以供使用。

    在步骤2中,当迭代所有进程时,而不是迭代文件夹中的所有文件 map_file ,最好使用该文件 /proc/[PID]/maps 并搜索指定信号量文件的完整路径(即: /dev/shm/sem_xyz )里面。

        8
  •  -1
  •   sra Jon    14 年前

    简单地做一个 sem_unlink() 紧接着 sem_open()