这里的目标是捕获SIGINT以关闭小型套接字服务器上的服务器套接字。我试图使用嵌套函数来保持代码清洁。但...
当我执行Ctrl-C(SIGINT,对吗?)时,我得到Illegal instruction: 4
。阅读this post后,我尝试将-mmacosx-version-min=10.8
添加到编译标志中,因为我在10.8。执行Ctrl-C时出现相同的错误。
这里有两个问题:为什么我会收到“非法指令4”?如何在不使用全局变量的情况下关闭服务器套接字?
我的软件:
Mac OSX 10.8.4
GCC 4.2.1
以下是我编译的方式:
gcc -fnested-functions main.c
以下是代码:
#include <sys/socket.h>
#include <unistd.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
void register_sigint_handler(int *serverSocket)
{
void sigint_handler(int signal) {
printf("Shutting down...\n");
printf("Server socket was %d\n", *serverSocket);
close(*serverSocket);
exit(0);
}
signal(SIGINT, &sigint_handler);
}
int main(void) {
int serverSocket = 0, guestSocket = 0;
register_sigint_handler(&serverSocket);
serverSocket = socket(PF_INET, SOCK_STREAM, 0);
while (1) {}
close(serverSocket);
return 0;
}
答案 0 :(得分:6)
虽然我不能具体告诉你发生了什么,gcc docs有一个概括:
如果您尝试通过其地址后调用嵌套函数 包含功能退出,所有地狱都破裂了。
将函数指针传递给signal()就可以了,在包含函数退出后调用本地函数。所以你不应该将嵌套函数指针传递给signal()
你应该只为处理程序使用普通函数,它设置一个标志。
static volatile int do_exit;
void sigint_handler(int sig)
{
do_exit = 1;
}
在服务器中,通常有某种主循环,例如选择或轮询,你的主循环,空的while循环现在可以变为
while (!do_exit) {
pause();
}
(请注意,当进程退出时,操作系统会自动关闭套接字;)
答案 1 :(得分:5)
GCC的嵌套函数在C扩展中不提供真正的闭包。当你取sigint_handler
的地址时,一个“trampoline”(一小段自修改代码)被写入堆栈;一旦register_sigint_handler
退出,蹦床就会被摧毁;随后尝试调用trampoline(由内核调用信号)导致未定义的行为。
根据定义,信号处理程序是全局处理的。因此,原则上不正确,试图避免在信号处理程序中使用全局变量。想象一下,为了使这个代码能够处理两个服务器套接字,您需要做什么:您只能注册一个SIGINT
处理程序,因此它必须以某种方式关闭两个套接字。
当您的流程终止时,所有打开的文件描述符都会自动关闭。因此,首先要手动关闭它们无需。此外,在 ^ C 上成功退出是违反惯例的。如果该程序是由监督程序驱动的,那么该程序就会想知道(通过{{ 1}}的状态代码)由于waitpid
而退出。把这两件事放在一起,你根本不应该有这个信号处理程序。
(如果您需要在退出时对 某些东西活动套接字,那就停止了。例如,如果你想在退出时为每个活动的客户端连接写一些东西,你需要一个信号处理程序。但是那时你想让信号处理程序警告主事件循环,然后在那里工作。)
(考虑使用libevent
来做这类事情而不是自己做所有低级别的goo。)