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

b0zn9rqh  于 2022-11-13  发布在  其他
关注(0)|答案(8)|浏览(196)

我有多个用g++编译的应用程序,运行在Ubuntu中,我使用命名信号量来协调不同的进程。
除以下情况外,所有工程均良好:如果其中一个进程调用sem_wait()sem_timedwait()来递减信号量,然后在它有机会调用sem_post()之前崩溃或被终止-9,那么从那一刻起,命名的信号量是“不可用的”。
所谓“不可用”,我的意思是信号量计数现在为零,而本应将其递增回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";
}
hfsqlsce

hfsqlsce1#

事实证明,没有一种方法可以可靠地恢复信号量。当然,任何人都可以对指定的信号量执行post_sem()操作,以使计数再次增加到零以上,但如何判断何时需要进行这样的恢复?提供的API太有限,并且无法以任何方式指示何时发生了这种情况。
要注意也可以使用的ipc工具--常用工具ipcmkipcrmipcs只用于过时的SysV信号量,特别是它们不能与新的POSIX信号量一起使用。
但看起来还有其他东西可以用来锁定东西,当应用程序以信号处理程序无法捕捉的方式死亡时,操作系统会自动释放这些东西。绑定到特定端口的侦听套接字或特定文件上的锁。
我决定锁定文件是我需要的解决方案。因此,我使用的不是sem_wait()sem_post()调用,而是:

lockf( fd, F_LOCK, 0 )

lockf( fd, F_ULOCK, 0 )

当应用程序以任何方式退出时,文件会自动关闭,这也会释放文件锁定。等待“信号量”的其他客户端应用程序随后可以按预期自由地继续运行。
谢谢你们的帮助,伙计们。
最新消息:
12年后,我想我应该指出posix互斥体确实有一个“健壮”属性,这样,如果互斥体的所有者被杀死或退出,下一个锁定互斥体的用户将获得EOWNERDEAD的非错误返回值,允许恢复互斥锁。这将使它类似于文件和套接字锁定解决方案。有关详细信息,请查看pthread_mutexattr_setrobust()pthread_mutex_consistent()。谢谢,雷尼尔·托伦贝克,这个提示。

nimxete2

nimxete22#

使用一个锁文件代替信号量,很像@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");
yb3bgrhw

yb3bgrhw3#

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

**但是,**根据您提供的信息,我建议您不要使用信号量。特别是当您的进程有被终止或崩溃的危险时。请尝试使用操作系统会自动为您清理的内容。

lztngnrs

lztngnrs4#

您需要仔细检查,但我相信sem_post可以从信号处理程序中调用。如果您能够捕捉到一些导致进程停止的情况,这可能会有所帮助。
与互斥锁不同的是,任何进程或线程(有权限)都可以向信号量发送信息。您可以编写一个简单的实用程序来重置它。想必您知道系统何时出现死锁。您可以关闭它并运行实用程序。
此外,信号通常列在/dev/shm下,您可以删除它。
SysV信号量更适合这种情况。您可以指定SEM_UNDO,如果进程终止,系统将取消进程对信号量所做的更改。它们还可以告诉您最后一个进程ID以更改信号量。

n1bvdmb6

n1bvdmb65#

你应该可以用lsof从shell中找到它。然后你可以删除它吗?

更新

啊,是的...... man -k semaphore来拯救。
看起来你可以用ipcrm来去掉一个信号量。看起来你不是第一个遇到这个问题的人。

wgxvkvu9

wgxvkvu96#

如果进程被终止,那么将没有任何直接的方法来确定它已经消失。
你可以对所有的信号量进行某种周期性的完整性检查--使用semctl(cmd=GETPID)来找到最后一个进程的PID,这个进程在你描述的状态下接触了每个信号量,然后检查这个进程是否还在,如果没有,就执行清理。

cld4siwp

cld4siwp7#

如果使用命名信号量,则可以使用类似于lsoffuser中使用的算法。
请考虑以下几点:
1.每个命名的POSIX信号量在tmpfs文件系统中创建一个文件,通常位于以下路径下:

/dev/shm/

2.每个进程在linux中都有一个map_files,路径为:

/proc/[PID]/map_files/

这些Map文件,显示了一个进程内存Map到什么的哪一部分!
因此,使用这些步骤,您可以发现命名的信号量是否仍由另一个进程打开:
1-(可选)查找已命名信号量的确切路径(如果它不在/dev/shm下)

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

/proc/self/map_files/ffff1234-*
应该只有一个文件满足此标准。

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

2-遍历所有进程以找到一个Map文件,该文件的符号链接目标与指定信号量的完整路径相匹配。如果存在一个Map文件,则该信号量真实的上正在使用,但如果没有Map文件,则可以安全地取消指定信号量的链接,然后重新打开它以供使用。

更新

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

rjee0c15

rjee0c158#

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

相关问题