4

我正在编写一个将共享内存和信号量用于 ipc 的程序。有一个主服务器进程创建共享内存和信号量。任何数量的客户端进程都可以附加到共享内存并在允许时对其进行读写。信号量提供阻塞机制来控制读取和写入。一切正常,除非我尝试终止客户端。访问共享内存的信号量块位于线程中,并且在进程终止时,我无法释放信号量块,因此线程正确退出。我该怎么办?这是针对 Linux 的。

具体来说,有一个 shm 和两个 sem。第一个 sem 阻止写入,第二个阻止读取。当客户端有东西要写时,它会等待 write sem 为 0,然后将其设置为 1,写入,然后将 read sem 设置为 0,这会释放等待的服务器以读取客户端写入的内容。一旦读取,服务器将 write sem 设置回 0,并且下一个客户端开始写入。它挂在读取 sem 为 0 时释放的 semop 调用上。这个 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 );
}
4

4 回答 4

4

他说,这是针对Linux的。

如果你能准确地说出你是怎么做的,那将会很有用。我假设您在 sem_wait 或 sem_timedwait 中阻塞。如果您的线程在那里阻塞并且您想中断它,您可以使用 pthread_kill。

pthread_kill(blocking_thread_id, SIGUSR1);

当然,您需要设置正确的信号处理程序(man sigaction)来捕获 SIGUSR1,并且您需要检查 sem_wait() 的返回代码是否为 EINTR,在这种情况下,您可以做任何您想做的事情,因为您知道您被打断并且没拿到锁。

在您使用进程的情况下,您只需使用 kill() 而不是 pthread_kill() 提供进程 ID。(对不起,最初我误读并认为您正在使用线程)

于 2009-07-17T04:11:30.237 回答
1

我有两个半的答案给你。:)

首先,您的示例代码适用于我(在 Linux 上):pthread_kill在大约五秒钟后按预期成功EINTR提升了工作线程的睡眠状态,正如一些printf所揭示的那样,并记住了sleep的返回值。AFAICT,如果你想用信号中断一个特定的线程,你已经做到了。

第二,尝试SEM_UNDO。该标志可以在sembuf参数semop中传递的sem_flg成员中设置,顾名思义,它将在进程终止时撤消信号量调整。IIUC,当您杀死一个客户端时,该客户端会不恰当地锁定信号量。 就是为了这种情况而制作的。SEM_UNDO

最后,恭敬地,您是否可能在这里颠倒了信号量的逻辑?当我阅读您的问题时,零的 semval 表示“无资源”,而 semval 为 1 表示“资源锁定”(引用:“...[a client] 等待写入 sem 为 0,然后将其设置为 1 ,写...”)。但是,如果两个或更多写入客户端正在等待 SysV sem 降为零,那么当这种情况发生时,它们将一起被释放。这是一个相当令人不快的竞争条件,它至少可能导致意想不到的信号量减少和增加。

于 2009-07-20T05:06:15.997 回答
0

根据您的环境,您可能只能尝试使用超时的信号量。每次超时后检查是否已请求关闭线程并简单地放弃并关闭。

于 2009-07-17T05:40:07.857 回答
0

如果对受保护区域的操作对于您的目的而言可能持续太久,则使用阻塞互斥锁/信号量可能不是最好的主意。

您可以通过将读写请求放入队列(例如链表)并让队列中的第一个在受保护区域上操作并在进入该区域后将其从列表中删除来解决该问题。

在只读操作的情况下,只要第一个操作是只读的,您就可以访问其他进入受保护区域的读取。当第一个操作是写时,保护区域必须是空的才允许它访问。

列表修改必须受到互斥锁(或类似的东西)的保护,但这几乎是恒定的时间,您可能可以支付。

当线程坐在队列中时,每个线程都有其私有条件变量,您可以使用它来唤醒它们中的任何一个。条件变量也必须受到互斥锁的保护。您可以将条件变量、互斥锁等存储到一个结构中,并将它们放入一个数组或列表中,并存储每个线程的 ID,以便轻松找到您想要唤醒的线程。

一旦线程唤醒,它首先检查它必须唤醒的原因是什么。如果设置了退出标志,则线程知道退出。

于 2013-08-06T18:42:15.033 回答