指向固定地址的函数指针

时间:2018-07-15 16:36:42

标签: c pointers minix

我正在浏览MINIX 3标头,并且在 include / signal.h 中有一些看似不寻常的定义:

/* Macros used as function pointers */
#define SIG_ERR ((sig_handler_t) -1)    /* error return */
#define SIG_DFL ((sig_handler_t) 0)     /* default signal handling */
#define SIG_IGN ((sig_handler_t) 1)     /* ignore signal */
#define SIG_HOLD ((sig_handler_t) 2)    /* block signal */ 
#define SIG_CATCH ((sig_handler_t) 3)   /* catch signal */

首先,SIG_ERR有效吗?第二(也是我的主要问题),源代码中还有其他指针映射到其中一些地址(例如NULL)。如果取消引用这些指针之一会发生什么?这些地址中的数据有效吗?

2 个答案:

答案 0 :(得分:1)

像这样的铸造不是C标准定义的,但是您的特定实现允许它以检查各种错误或返回码。

这些值不能被默认,但是可以将它们与为各种信号处理功能返回的值进行比较。

答案 1 :(得分:1)

这些不是内存地址,取消引用它们没有意义。它们是“魔术”值,指示这不是处理程序函数的地址,而是指示将信号处理状态设置为“运行此函数”以外的其他指令。

选择这些值是要与任何有效函数的地址区分开的,因为否则将无法判断signal函数的调用者是通过函数的地址还是魔术值。几乎所有具有MMU的系统都不会在第一页中映射任何内容,因此,页面大小以下的地址不能是功能变量的地址。例如,这使NULL成为地址0。

-1通常映射到可能的最高地址(全1),就像(unsigned)(-1)是全1一样。但这是一种实现选择(与(unsigned)(-1)不同,它是定义明确的,因为无符号整数是模2 N 定义的,其中 N 是位大小)。例如,在某些int是32位类型但地址具有64位的实现中,((sig_handler_t) -1)会映射到地址0xffffffff,这是函数的合理地址。 / p>

请注意,这些是操作系统实现者可以执行的操作,因为它们知道如何在特定平台上表示指针。指针的表示不是由C标准指定的(具体来说,将整数转换为指针的效果是实现定义的),并且约束因系统而异。作为C程序员,您不能做到这一点(更精确地说:您可以 ,但是除非您确切地知道自己在做什么,否则很可能会出错。您不仅需要了解特定平台如何表示指针以及如何将整数转换为指针,而且还必须了解编译器对代码进行的假设。可能需要使用特定的编译器或特定的编译器标志来编译OS代码,以启用必要的特定于实现的行为。

signal系统调用以如下方式使用它们(大大简化,但您知道了):

enum signal_disposition {
    SIGNAL_DISPOSITION_IGNORE,
    SIGNAL_DISPOSITION_KILL,
    SIGNAL_DISPOSITION_RUN_HANDLER,
    SIGNAL_DISPOSITION_STOP,
};

sighandler_t sys_signal(struct task *calling_task, int signum, sighandler_t handler)
{
    if (signum > SIGMAX || signum == SIGKILL) return SIG_ERR;
    sighandler_t previous_handler =
        calling_task->signal_disposition == SIGNAL_DISPOSITION_IGNORE ? SIG_IGN :
        calling_task->signal_disposition == SIGNAL_DISPOSITION_RUN_HANDLER ?
        calling_task->signal_handler[signum] :
        SIG_DFL;
    if (handler == SIG_DFL) {
        calling_task->signal_disposition[signum] =
            signum == SIGTSTP ? SIGNAL_DISPOSITION_STOP :
            signum == SIGALRM ? SIGNAL_DISPOSITION_IGNORE :
            SIGNAL_DISPOTITION_KILL;
        calling_task->signal_handler[signum] = NULL;
    } else if (handler == SIG_IGN) {
        calling_task->signal_disposition[signum] = SIGNAL_DISPOSITION_IGNORE;
        calling_task->signal_handler[signum] = NULL;
    } else {
        calling_task->signal_disposition[signum] = SIGNAL_DISPOSITION_RUN_HANDLER;
        calling_task->signal_handler[signum] = handler;
    }
    return previous_handler;
}

这是在内核中运行的相应代码,用于在进程中触发信号(再次大大简化):

void handle_signal(struct task *calling_task, int signum) {
    switch (calling_task->signal_disposition[signum]) {
    case SIGNAL_DISPOSITION_IGNORE:
        break;
    case SIGNAL_DISPOSITION_KILL:
        kill_task(task, signum);
        break;
    case SIGNAL_DISPOSITION_RUN_HANDLER:
        task->registers->r0 = signum;
        task->registers->pc = calling_task->signal_handler;
        wake_up(task);
        break;
    case SIGNAL_DISPOSITION_STOP:
        stop_task(task);
        break;
    }
}