c语言 - 具有嵌套功能的 OSX "Illegal instruction 4"



这里的目标是捕获SIGINT以关闭小型套接字服务器上的服务器套接字。我尝试使用嵌套函数来保持代码的整洁。但是

当我执行Ctrl-C(SIGINT,对吧?)时,我得到Illegal instruction: 4。在阅读了这篇文章之后,我尝试将-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 %dn", *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;
}

虽然我不能具体告诉你发生了什么,但gcc文档有一个概括:

如果您试图通过嵌套函数在包含函数出口,所有地狱都会释放。

将函数指针传递给signal()可以做到这一点,在包含函数退出后调用本地函数。因此,您不应该将嵌套函数指针传递给signal()

您可能只应该为处理程序使用一个普通函数,它设置了一个标志。

static volatile int do_exit;
void sigint_handler(int sig)
{
   do_exit = 1;
}

在服务器中,通常有某种主循环,例如选择或轮询,你的主循环,空的while循环现在可以变成

while (!do_exit) { 
  pause(); 
}

(请注意,当进程退出时,操作系统会自动关闭套接字;)

"那就不要那样做。"

GCC的嵌套函数-in-C扩展不提供真正的闭包。当你取sigint_handler的地址时,一个"蹦床"(一小段自我修改的代码)会被写入堆栈;一旦register_sigint_handler退出,蹦床就被摧毁;随后尝试调用蹦床(由内核调用,以便发送信号)会导致未定义的行为。

根据定义,信号处理程序是全局处理程序因此,试图避免在信号处理程序中使用全局变量原则上是不正确的。想象一下,您将如何使此代码处理两个服务器套接字:您只能注册一个SIGINT处理程序,因此它必须以某种方式关闭这两个套接字。

当进程终止时,所有打开的文件描述符都会自动关闭因此,没有必要先手动关闭它们。此外,^C上成功退出是违反惯例的如果此程序是由一个监督进程驱动的,该进程会想知道(通过waitpid的状态码)它是因为SIGINT而退出的。把这两件事放在一起,你根本不应该有这个信号处理程序

(如果您需要在退出时对活动套接字执行操作,则这种情况将停止。例如,如果您想在退出时向每个活动客户端连接写入一些内容,则需要一个信号处理程序。但此时,您希望信号处理程序提醒主事件循环,并在那里执行操作。)

(考虑使用libevent来做这类事情,而不是自己做所有底层的goo。)

最新更新