如何终止正在等待信号量操作的线程
我正在编写一个使用 ipc 共享内存和信号量的程序。 有一个主服务器进程创建共享内存和信号量。 任意数量的客户端进程都可以连接到共享内存,并在允许的情况下对其进行读写。 信号量提供阻塞机制来控制读取和写入。 一切正常,除了当我尝试终止客户端时。 访问共享内存的信号量块位于线程中,并且在进程终止时我无法释放信号量块,以便线程正确退出。 我该怎么办呢? 这是针对 Linux 的。
具体来说,有1个shm和2个sem。 第一个 sem 阻止写入,第二个 sem 阻止读取。 当客户端有东西要写时,它会等待写入 sem 为 0,然后将其设置为 1,写入,然后将读取 sem 设置为 0,这会释放等待的服务器以读取客户端写入的内容。 一旦读取,服务器将写入 sem 设置回 0,并且队列中的下一个客户端开始写入。 它挂在一个 semop 调用上,当读取 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 );
}
I am writing a program that uses shared memory and semaphores for ipc. There is one main server process that creates the shared memory and semaphores. Any number of client processes can attach to the shared memory and read and write to it when allowed. The semaphores provide the blocking mechanism to control reads and writes. Everything works fine except when a I try to terminate a client. The semaphore block to access the shared memory is in a thread and on process termination I have no way to release the semaphore block so the thread exits correctly. How would I go about this? This is for Linux.
To be specific, there is one shm and two sems. The first sem blocks writing, and the second blocks reading. When a client has something to write, it waits for the write sem to be 0, then sets it to 1, writes, then sets the read sem to 0 which releases the waiting server to read what the client wrote. once read the server sets the write sem back to 0 and the next client in line gets to write. It hangs on a semop call which releases when read sem is 0. This semop call is in a thread and I need to figure out how to exit that thread correctly before letting the main thread terminate.
Here is an example of what i want to do but isn't working (the sleep is pretending to be the hanging semop call):
#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 );
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
他说,这是针对Linux的。
如果你能准确地说出你是怎么做的,那将会很有用。 我假设您在 sem_wait 或 sem_timedwait 中阻塞。 如果你的线程阻塞在那里并且你想中断它,你可以使用pthread_kill。
当然,您需要设置适当的信号处理程序(man sigaction)来捕获 SIGUSR1,并且需要检查 sem_wait() 的返回码是否为 EINTR,在这种情况下,您可以做任何您想做的事情,知道您被中断了并且没有拿到锁。
如果您正在使用进程,您只需使用kill()而不是pthread_kill()来提供进程ID。 (抱歉,最初我误读并认为你正在使用线程)
He said, this is for Linux.
It would be useful if you could say exactly how are you doing it. I assume you are blocking in sem_wait or sem_timedwait. If your thread is blocking there and you want to interrupt it, you can use pthread_kill.
Of course, you need to setup the proper signal handler (man sigaction) to catch SIGUSR1 and you need to check the return code of sem_wait() for EINTR, in which case you can do whatever you want to do knowing that you were interrupted and did not get the lock.
In the case you are using processes you would use simply kill() and not pthread_kill() providing the process id. (sorry, initially I misread and thought you were using threads)
我有两个半的答案给你。 :)
首先,您的示例代码对我有用(在 Linux 上):pthread_kill 成功
EINTR
提高了工作线程的睡眠 em> 大约五秒后,正如预期的那样,通过几个 printf 并记住 sleep 的返回值来显示。 AFAICT,如果您想用信号中断特定线程,那么您已经做到了。其次,尝试
SEM_UNDO
。 该标志可以在 sembuf 参数 semop 中传递的 sem_flg 成员中设置,顾名思义,它将撤消信号量调整进程终止。 IIUC,当你杀死一个客户端时,该客户端会留下一个信号量不适当的锁定。SEM_UNDO
就是为了这种情况而设计的。最后,请恕我直言,您是否在这里颠倒了信号量的逻辑? 当我读到你的问题时,semval 为零表示“资源空闲”,semval 为 1 表示“资源锁定”(引用:”...[a client] 等待写入 sem 为 0,然后设置它为 1,写入...")。 但是,如果两个或多个写入客户端正在等待 SysV sem 降至零,则当发生这种情况时,它们将全部被释放。 这是一个相当令人不快的竞争条件,它至少可能导致意外的信号量减少和增加。
I have two and a half answers for you. :)
First, your example code works for me (on Linux): the pthread_kill successfully
EINTR
upts the worker thread's sleep as expected after about five seconds, as revealed with a few printfs and remembering the return value of sleep. AFAICT, if you want to signal-interrupt a specific thread, you've done it.Second, try
SEM_UNDO
. This flag may be set in the sem_flg member passed in the sembuf argument semop, and it will, as the name suggests, undo semaphore adjustments upon process termination. IIUC, when you kill a client, that client leaves a semaphore inappropriately locked.SEM_UNDO
was made for just this circumstance.Finally and respectfully, have you perhaps inverted the logic of semaphores here? As I read your question, a semval of zero indicates "resource free" and a semval of one is "resource locked" (quote: "...[a client] waits for the write sem to be 0, then sets it to 1, writes..."). However, if two or more writing clients are waiting for a SysV sem to drop to zero, they will all be released together when that occurs. That's rather an unpleasant race condition, which might at the least result in unanticipated semaphore decrements and increments.
根据您的环境,也许您只能尝试超时获取信号量。 每次超时后,检查是否已请求关闭线程,然后简单地放弃并关闭。
Depending on your environment perhaps you could only try to take the semaphore with a timeout. After each timeout check if a close thread has been requested and simply give up and shutdown.
如果保护区上的操作对于您的目的来说可能持续太长时间,那么使用阻塞互斥体/信号量可能不是最好的主意。
您可以通过将读写请求放入队列(例如链表)中来解决该问题,让队列中的第一个对受保护区域进行操作,并在进入该区域后将其从列表中删除。
如果是只读操作,您可以访问进入受保护区域的其他读取操作,只要第一个操作是只读的。 当第一次写操作时,保护区必须为空才允许访问。
列表修改必须受到互斥体(或类似的东西)的保护,但这几乎是恒定的时间,您可能可以为此付出代价。
当线程位于队列中时,每个线程都有其私有条件变量,您可以使用它来唤醒其中的任何一个。 条件变量也必须受到互斥体的保护。 您可以将条件变量、互斥体等存储到一个结构中,并将它们放入一个数组或列表中,并存储每个线程的 ID,以便很容易找到您想要唤醒的线程。
一旦线程唤醒,它首先检查它必须唤醒的原因是什么。 如果设置了退出标志,则线程知道退出。
It may not be the best idea to use blocking mutexes/semaphores if operations on protected area may last too long for your purposes.
You can solve the issue by putting read and write requests to a queue (linked list, for example) and let the first in the queue to operate on protected area and remove it from the list once it enters the area.
In case of read-only op you may access other reads enter the protected area as well as long as the first operation is read only. When the first operation is write, protected area must be empty before allowing it to access.
List modifications must be protected by mutex (or something alike) but that is near constant time and you probably can pay that.
When threads are sitting in a queue, each has its private condition variable which you can use to wake up any of them. Condition variable must be protected by mutex too. You may store condition variable, mutex etc into a structure and put the into an array or list and store thread id with each so that it will be easy to find the thread you want to wake up.
Once thread wakes up, it first checks what was the reason it had to wake up. If exit flag is set, then the thread know to exit.