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

时间:2010-01-13 01:00:54

标签: linux gcc g++ semaphore

我有多个使用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";
}

8 个答案:

答案 0 :(得分:37)

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

请注意也可使用的ipc工具 - 常用工具ipcmkipcrmipcs仅适用于过时的SysV信号量。它们特别适用于新的POSIX信号量。

但看起来还有其他东西可用于锁定事物,当应用程序以无法在信号处理程序中捕获的方式死亡时,操作系统会自动释放。两个示例:绑定到特定端口的侦听套接字或特定文件上的锁。

我决定锁定文件是我需要的解决方案。因此,我正在使用:

,而不是sem_wait()sem_post()来电
lockf( fd, F_LOCK, 0 )

lockf( fd, F_ULOCK, 0 )

当应用程序以任何方式退出时,文件将自动关闭,这也会释放文件锁定。等待“信号量”的其他客户端应用程序可以按预期自由进行。

谢谢你的帮助,伙计们。

答案 1 :(得分:6)

使用锁定文件而不是信号量,就像@Stéphane的解决方案,但没有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");

答案 2 :(得分:5)

这是管理信号量时的典型问题。某些程序使用单个进程来管理信号量的初始化/删除。通常这个过程就是这样,而不是别的。您的其他应用程序可以等到信号量可用。我已经看到使用SYSV类型API完成此操作,但没有使用POSIX。与提到的“ Duck ”类似,在semop()调用中使用SEM_UNDO标志。


但是,根据您提供的信息,我建议您不要使用信号量。特别是如果您的过程有被杀或崩溃的危险。尝试使用操作系统将自动清理的东西。

答案 3 :(得分:2)

您应该可以使用lsof从shell中找到它。那么你可以删除吗?

<强>更新

啊,是的...... man -k semaphore救援。

似乎你可以使用ipcrm来摆脱信号量。似乎你不是第一个遇到这个问题的人。

答案 4 :(得分:2)

你需要仔细检查,但我相信sem_post可以从信号处理程序中调用。如果你能够抓住一些导致这个过程失败的情况,这可能会有所帮助。

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

此外,semaphone通常列在/ dev / shm下,您可以删除它。

SysV信号量更适合这种情况。您可以指定SEM_UNDO,如果进程终止,系统将退出对进程所做信号量的更改。他们还能够告诉你改变信号量的最后一个进程ID。

答案 5 :(得分:1)

如果该过程被杀死,那么将没有任何直接的方法来确定它已经消失。

您可以在所有信号量上运行某种定期完整性检查 - 使用semctl(cmd = GETPID)查找触及您描述的状态中每个信号量的最后一个进程的PID,然后检查这个过程是否还在。如果没有,请执行清理。

答案 6 :(得分:0)

sem_unlink()之后立即执行sem_open()。在所有进程关闭资源(包括内部关闭)后,Linux将被删除。

答案 7 :(得分:0)

如果使用命名信号量,则可以使用lsoffuser中使用的算法。

请考虑以下因素:

1。每个名为POSIX信号量的文件都通常在以下路径下在tmpfs文件系统中创建一个文件:

/dev/shm/

2。每个进程在linux下的路径下都有一个map_files:

/proc/[PID]/map_files/

这些映射文件显示了进程存储器映射的哪一部分!

因此,使用这些步骤,您可以找到命名信号量是否仍被另一个进程打开:

1-(可选)查找命名信号量的确切路径(如果它不在/dev/shm下)

  • 首先在新进程中打开命名信号量,并将结果分配给指针
  • 找到指针在内存中的地址位置(通常以整数类型转换指针的地址)并将其转换为十六进制(即结果:0xffff1234),然后使用此路径:

    /proc/self/map_files/ffff1234-*

    应该只有一个文件可以满足这个条件。

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

2-遍历所有进程以查找其符号链接标记与指定信号量的完整路径匹配的映射文件。如果有一个,则该信号量是实际使用的,但是如果没有,则可以安全地取消链接已命名的信号量,然后重新打开以供使用。

更新

在步骤2中,当遍历所有进程时,而不是遍历文件夹map_file中的所有文件,最好使用文件/proc/[PID]/maps并搜索命名信号文件的完整路径。 (即/dev/shm/sem_xyz)。 通过这种方法,即使某些其他程序取消了链接已命名信号量,但该信号量仍在其他进程中使用,仍然可以找到该信号量,但在其文件路径的末尾附加了一个“(已删除)”标志。