49

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

8 回答 8

48

事实证明,没有办法可靠地恢复信号量。当然,任何人都可以post_sem()通过命名信号量使计数再次增加到零以上,但是如何判断何时需要进行这种恢复呢?提供的 API 太有限,并没有以任何方式表明何时发生这种情况。

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

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

我决定锁定文件是我需要的解决方案。因此,我使用的sem_wait()sem_post()

lockf( fd, F_LOCK, 0 )

lockf( fd, F_ULOCK, 0 )

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

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

于 2010-01-13T17:04:27.307 回答
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");
于 2013-03-08T23:34:53.870 回答
5

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


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

于 2010-01-13T14:26:42.927 回答
2

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

更新

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

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

于 2010-01-13T01:06:00.847 回答
2

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

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

此外,信号机通常列在 /dev/shm 下,您可以将其删除。

SysV 信号量更适合这种情况。您可以指定 SEM_UNDO,如果进程死亡,系统将在其中撤销对进程所做的信号量的更改。他们也有能力告诉你最后一个进程ID来改变信号量。

于 2010-01-13T02:51:20.143 回答
1

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

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

于 2010-01-13T01:18:01.610 回答
0

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

考虑这些:

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。在这种方法中,即使某些其他程序取消了命名信号量的链接,但该信号量仍在其他进程中使用,仍然可以找到它,但在其文件路径的末尾附加了一个“(已删除)”标志。

于 2019-08-04T05:57:21.630 回答
-1

只需sem_unlink()sem_open(). Linux 将在所有进程关闭资源后删除,包括内部关闭。

于 2011-03-16T19:48:56.750 回答