我有一个处理accept(2)
呼叫的循环。我希望能够在将SIGINT
发送到程序时执行一些清理工作。我的第一个想法是使用signal
函数。
void signal_handler(int signal) {
printf("Caught signal\n");
}
int main() {
signal(SIGINT, &signal_handler);
// ...
int accept_fd = accept(sock, NULL, NULL);
if (accept_fd == -1) {
close(sock);
perror("accept");
return 1;
}
// ...
}
然而,这只是打印“捕获信号”然后程序继续。
如果我修改main
使用sigaction
,该程序按预期工作。
int main() {
struct sigaction sa;
sa.sa_handler = &signal_handler;
sa.sa_flags = 0;
sigemptyset(&sa.sa_mask);
sigaction(SIGINT, &sa, NULL);
// ...
int accept_fd = accept(sock, NULL, NULL);
if (accept_fd == -1) {
close(sock);
perror("accept");
return 1;
}
// ...
}
发送SIGINT后,我得到Caught Signal
,然后是accept: Interrupted system call
。来自accept(2)
的手册页
错误
...
EINTR系统调用被有效连接到达之前捕获的信号中断;见信号(7)。
我知道sigaction
更现代,我应该在signal
上使用它,但我很好奇为什么它提供了这种功能上的差异。
下面我列出了每个案例的完整可用示例程序。
#include <netdb.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>
#define BUFFER_SIZE 32
void signal_handler(int signal) {
printf("Caught signal\n");
}
int main() {
signal(SIGINT, &signal_handler);
struct addrinfo hints;
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
struct addrinfo *addr_info;
int info_result = getaddrinfo("localhost", "8080", &hints, &addr_info);
if (info_result != 0) {
printf("Getting address failed\n");
return 1;
}
int sock = socket(addr_info->ai_family, addr_info->ai_socktype, addr_info->ai_protocol);
if (sock == -1) {
printf("Socket Failed\n");
return 1;
}
int bind_result = bind(sock, addr_info->ai_addr, addr_info->ai_addrlen);
if (bind_result == -1) {
close(sock);
printf("Bind Failed\n");
return 1;
}
int listen_result = listen(sock, 8);
if (listen_result == -1) {
close(sock);
printf("Listen Failed\n");
return 1;
}
printf("Waiting...\n");
int accept_fd = accept(sock, NULL, NULL);
if (accept_fd == -1) {
close(sock);
perror("accept");
return 1;
}
printf("Got fd %d\n", accept_fd);
char *buffer = malloc(BUFFER_SIZE * sizeof(char));
int n;
while ((n = read(accept_fd, buffer, BUFFER_SIZE)) > 0) {
printf("%.*s\n", n, buffer);
}
close(sock);
}
#include <netdb.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>
#define BUFFER_SIZE 32
void signal_handler(int signal) {
printf("Caught signal\n");
}
int main() {
struct sigaction sa;
sa.sa_handler = &signal_handler;
sa.sa_flags = 0;
sigemptyset(&sa.sa_mask);
sigaction(SIGINT, &sa, NULL);
struct addrinfo hints;
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
struct addrinfo *addr_info;
int info_result = getaddrinfo("localhost", "8080", &hints, &addr_info);
if (info_result != 0) {
printf("Getting address failed\n");
return 1;
}
int sock = socket(addr_info->ai_family, addr_info->ai_socktype, addr_info->ai_protocol);
if (sock == -1) {
printf("Socket Failed\n");
return 1;
}
int bind_result = bind(sock, addr_info->ai_addr, addr_info->ai_addrlen);
if (bind_result == -1) {
close(sock);
printf("Bind Failed\n");
return 1;
}
int listen_result = listen(sock, 8);
if (listen_result == -1) {
close(sock);
printf("Listen Failed\n");
return 1;
}
printf("Waiting...\n");
int accept_fd = accept(sock, NULL, NULL);
if (accept_fd == -1) {
close(sock);
perror("accept");
return 1;
}
printf("Got fd %d\n", accept_fd);
char *buffer = malloc(BUFFER_SIZE * sizeof(char));
int n;
while ((n = read(accept_fd, buffer, BUFFER_SIZE)) > 0) {
printf("%.*s\n", n, buffer);
}
close(sock);
}
在BSD和Linux上,signal()
相当于sigaction()
,sa_flags
设置为SA_RESTART
。如果在sigaction()
代码中设置该标志,它的行为与signal()
代码相同。如果这不是你想要的,那么你必须只使用sigaction()
。
来自Linux man page的注释(也适用于BSD和OS X):
在BSD上,当调用信号处理程序时,信号处理不会被重置,并且在处理程序执行时阻止信号的其他实例被传递。此外,如果信号处理程序中断,某些阻塞系统调用会自动重启(参见signal(7))。 BSD语义等同于使用以下标志调用sigaction(2):
sa.sa_flags = SA_RESTART;