我正在浏览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
)。如果取消引用这些指针之一会发生什么?这些地址中的数据有效吗?
答案 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;
}
}