我正在编写一个程序,它为ipc使用共享内存和信号量。有一个主服务器进程可以创建共享内存和信号量。任何数量的客户端进程都可以附加到共享内存,并在允许时对其进行读写。信号量提供阻止机制来控制读取和写入。一切正常,除非我试图终止客户端。访问共享内存的信号量块在一个线程中,在进程终止时,我无法释放信号量块,因此线程正确退出。我该怎么做?这适用于Linux。
具体来说,有一个shm和两个sems。第一个sem阻止写入,第二个块阻止读取。当客户端有东西需要写入时,它会等待写入sem为0,然后将其设置为1,写入,然后将读取sem设置为0,从而释放等待服务器以读取客户端写入的内容。一旦读取,服务器将写入sem设置为0,并且下一个客户端可以写入。它挂在semop调用上,当read sem为0时释放。这个semop调用在一个线程中,我需要弄清楚如何在让主线程终止之前正确地退出该线程。
以下是我想要做但不起作用的例子(睡眠假装是挂着的semop电话):
#include <stdlib.h>
#include <errno.h>
#include <pthread.h>
#include <signal.h>
#include <stdio.h>
#include <unistd.h>
void termination_handler (int signum) {
printf( "Got Signal\n" );
}
void *threadfunc( void *parm ) {
struct sigaction action;
action.sa_handler = termination_handler;
sigemptyset( &action.sa_mask );
action.sa_flags = 0;
sigaction( SIGUSR1, &action, NULL );
printf("Thread executing\n");
sleep( 100 ); // pretending to be the semaphore
pthread_exit( NULL );
}
int main() {
int status;
pthread_t threadid;
int thread_stat;
status = pthread_create( &threadid, NULL, threadfunc, NULL );
if ( status < 0) {
perror("pthread_create failed");
exit(1);
}
sleep( 5 );
status = pthread_kill( threadid, SIGUSR1 );
if ( status < 0 )
perror("pthread_kill failed");
status = pthread_join( threadid, (void *)&thread_stat );
if ( status < 0 )
perror("pthread_join failed");
exit( 0 );
}
答案 0 :(得分:4)
他说,这是针对Linux的。
如果你能确切地说你是怎么做的话会很有用。我假设你在sem_wait或sem_timedwait中阻塞。如果您的线程在那里阻塞并且您想要中断它,则可以使用pthread_kill。
pthread_kill(blocking_thread_id, SIGUSR1);
当然,您需要设置正确的信号处理程序(man sigaction)以捕获SIGUSR1,并且您需要检查EINTR的sem_wait()的返回码,在这种情况下,您可以做任何您想知道的事情。被打断了,没有锁定。
如果您正在使用进程,则只使用kill()而不是pthread_kill()来提供进程ID。 (对不起,我最初误读并认为你在使用线程)
答案 1 :(得分:1)
我有两个半答案。 :)
首先,您的示例代码适用于我(在Linux上): pthread_kill 成功EINTR
提升工作线程的 sleep 正如大约五秒钟所预期的那样,正如一些 printf 所揭示的并记住 sleep 的返回值。 AFAICT,如果你想对特定线程进行信号中断,你就完成了它。
其次,请尝试SEM_UNDO
。此标志可以在 sembuf 参数 semop 中传递的 sem_flg 成员中设置,顾名思义,它将撤消信号量调整流程终止。 IIUC,当你杀死一个客户端时,该客户端不正确地锁定了一个信号量。 SEM_UNDO
就是针对这种情况而制定的。
最后,尊敬地说,你或许在这里颠倒了信号量的逻辑吗?当我读到你的问题时,semval为零表示“资源自由”,而semval为1是“资源锁定”(引用:“... [客户端]等待写入sem为0,然后设置写到1,写道......“)。但是,如果两个或多个写入客户端正在等待SysV sem降至零,那么当发生这种情况时,它们将全部释放。这是一种令人不快的竞争条件,这可能至少会导致意外的信号量减少和增量。
答案 2 :(得分:0)
根据您的环境,您可能只会尝试使用超时信号量。每次超时后检查是否已请求关闭线程,只需放弃并关闭。
答案 3 :(得分:0)
如果对受保护区域的操作可能会持续太久而无法用于您的目的,那么使用阻塞互斥锁/信号量可能不是最佳选择。
您可以通过将读取和写入请求放入队列(例如链接列表)来解决问题,并让队列中的第一个请求在受保护区域上运行,并在进入该区域后将其从列表中删除。
如果是只读操作,您可以访问其他读取进入受保护区域,只要第一个操作是只读即可。写入第一个操作时,受保护区域必须为空才允许其访问。
列表修改必须受到互斥锁(或类似的东西)的保护,但这几乎是恒定的时间,你可能可以支付它。
当线程位于队列中时,每个线程都有其私有条件变量,您可以使用它来唤醒它们中的任何一个。条件变量也必须由互斥锁保护。您可以将条件变量,互斥体等存储到结构中,并将其放入数组或列表中,并将每个存储的线程ID存储起来,以便您可以轻松找到要唤醒的线程。
一旦线程唤醒,它首先检查它必须唤醒的原因。如果设置了exit标志,则线程知道退出。