gpt4 book ai didi

c - 使用 pthreads 时无法捕获 SIGINT 信号

转载 作者:行者123 更新时间:2023-12-01 12:30:29 25 4
gpt4 key购买 nike

我制作了一个使用多线程处理多个客户端的聊天服务器。我有一个无限运行并等待新客户的 while 循环。我想在我按下ctrl+c之后就出来了。所以,我正在尝试捕捉 SIGINT 信号,如前所述 here .但是我无法退出该程序。我在 Linux 的终端上工作。

服务器.c

//for running type ./a.out anyportnumber
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <pthread.h>
#include <signal.h>
int s2;
int arr[100];
int tc = 0;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
volatile sig_atomic_t flag = 1;
void handler(int signal)
{
flag = 0;
}
void sendtoall(char *msg,int s1)
{
int i;
pthread_mutex_lock(&mutex);
for(i = 0; i < tc; i++) {
if(arr[i] != s1)
write(arr[i],msg,strlen(msg));
}
pthread_mutex_unlock(&mutex);
}
void *function(void *s)
{
int s1;
int n;
char rmsg[500];
s1 = *(int *)s;
while((n = read(s1,rmsg,500)) > 0) {
rmsg[n] = '\0';
sendtoall(rmsg,s1);
bzero(rmsg,500);
}
pthread_exit(NULL);
}
int main(int arrc,char *argv[])
{
struct sockaddr_in server,client;
int s1,len;
int n;
int port;
pthread_t t1;
char message[500];
port = atoi(argv[1]);
bzero((char *)&server,sizeof(server));
server.sin_port = htons(port);
server.sin_addr.s_addr = INADDR_ANY;
server.sin_family = AF_INET;
s1 = socket(AF_INET,SOCK_STREAM,0);
if(s1 == -1) {
perror("socket not created\n");
exit(1);
}
if(bind(s1,(struct sockaddr *)&server,sizeof(struct sockaddr)) == -1) {
perror("socket not binded\n");
exit(1);
}
if(listen(s1,5) == -1) {
perror("unable to listen");
exit(1);
}
len = sizeof(struct sockaddr_in);
signal(SIGINT, handler);
while(flag) {
s2 = accept(s1,(struct sockaddr *)&client,&len);
pthread_create(&t1,NULL,function,(void *)&s2);
arr[tc] = s2;
tc++;
}
close(s1);
close(s2);
return 0;

}

最佳答案

通过中断处理程序设置的标志捕获信号不适用于信号需要可靠地中断阻塞系统调用(在您的情况下为接受)的情况。问题是信号可能恰好在进入阻塞系统之前到达:在检查标志之后但在信号中断给定系统调用的状态之前。因此,即使设置了标志,系统调用也会阻止程序的执行。

此外,当多个线程允许信号时,只有一个线程捕获信号,并且未指定是哪个线程。在你的情况下,信号可能没有被主线程捕获,所以 accept 根本没有被中断。

虽然第二个问题(与多线程程序有关)很容易通过阻塞除主线程之外的所有线程中的信号来克服,但第一个问题需要特殊的方法。可能的:

signalfd() 结合 poll()/select()

最复杂的方法,但几乎适用于所有情况。信号被“转换”为文件描述符,该文件描述符与系统调用等待的文件描述符结合在一起。生成的文件描述符集用于轮询:

// Preparations
sigset_t s;
sigemptyset(&s);
sigaddset(&s, SIGINT);
sigprocmask(SIGBLOCK, &s, NULL); // For multithreaded program *pthread_sigmask* should be used.
int fd_int = signalfd(0, &s, 0); // When signal arises, this file becomes readable

// Usage
fd_set fds;
FD_ZERO(&fds);
FD_SET(fd_int, &fds);
FD_SET(s1, &fds);
int nfds = MAX(fd_int, s1) + 1;
select(nfds, &fds, NULL, NULL, NULL);
if(FD_ISSET(fd_int, &fds)) {
// Signal is arrived
...
}
else {
// New connection request is received
accept(s1, ...);
...
}

请注意,该信号对所有线程都被阻塞,包括主线程。

在信号处理程序中完成并退出

最简单的方法,但用途有限。如果所有终止操作都是信号安全的(请参阅 man singnal(7) 以了解在信号处理程序中允许调用的函数的完整列表),则该操作可能由信号处理程序本身执行,然后退出程序:

void handler(int signal)
{
close(s1);
close(s2);
_exit(0); // This function is thread-safe, unlike to *exit*.
}

但在多线程程序的情况下,这种方法通常不适用,因为函数 thread_join 不是信号安全的。

在信号处理程序中更改系统调用参数的状态

所以系统调用会立即返回,不会阻塞。最简单的状态更改是关闭系统调用工作的文件描述符:

void handler(int signal)
{
flag = 0;
close(s1); // Close file descriptor which is used by the system call.
}

while(flag)
{
s2 = accept(s1, ...);
if(s2 == -1 && !flag) {
// Signal is catched
break;
}
...
}

请注意,在多线程程序的情况下,除了主线程之外,所有线程的信号都应该被显式阻塞。否则,在一个线程中关闭文件而其他线程读取它不需要中断读取线程。

此外,在多线程程序的情况下,应该考虑到,如果一些其他线程创建(打开)文件描述符,它可以重用一个,在信号处理程序中关闭,就在它被关闭之前在系统调用中使用。

关于c - 使用 pthreads 时无法捕获 SIGINT 信号,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34549327/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com