[英]How can the value of an argument in a function called from a signal handler be different from the value passed in?
更新的问题-下面的第一个版本
我已经设置了一个自定义信号处理程序来缓冲从 CAN 接口中删除的帧。 在处理程序中,我首先阻塞信号,最后使用pthread_sigmask()
解除阻塞。
在bufferMessageDirect()
中,检查接口 ID iface_id
,如果出现错误,因为这通常只能是我的错,所以调用assert(0)
。
在我的程序运行了一段时间后,但确切时间有所不同,随着 stream 的 CAN 帧到达,此assert()
被触发并执行结束。 当我检查信号处理程序堆栈帧时, iface_id
是0
这是有效的,但是传递给bufferMessageDirect()
的参数can_interface_id
是-1
。
我有一个传入的 CAN 帧的转储,它们没有任何异常。 信号处理程序的堆栈帧表明转换后的 CAN 消息符合预期,接口 ID 为0
。 在bufferMessageDirect()
的堆栈帧中,接口 ID 为-1
。
请您就可能导致此问题的原因提供建议?
我添加了对pthread_sigmask()
的调用,因为我认为,也许信号在其处理程序已经在进行中时正在触发。
/************************************************************** EXTERNAL DATA */
extern can_metadata_t can_metadata;
extern internal_time_t otherthing_heartbeat[THING_QUANTITY];
extern internal_time_t something_heartbeat[THING_QUANTITY];
/************************************************************** INTERNAL DATA */
STATIC volatile ssize_t bytes_read = 0;
STATIC volatile int socket_desc = 0;
STATIC volatile struct can_frame frame = {0};
STATIC volatile uint32_t receiver_id = 0u;
STATIC volatile uint32_t command_id = 0u;
STATIC volatile uint32_t function_id = 0u;
STATIC volatile int32_t iface_id = 0;
STATIC volatile cand_result_t d_result = CAND_RESULT_OK;
STATIC volatile canh_result_t h_result = CANH_RESULT_OK;
STATIC volatile internal_time_t *const heartbeats[2] =
{
otherthing_heartbeat,
something_heartbeat,
};
// ============================================================================
void
myHandler(int const signal_number,
siginfo_t *const p_signal_info,
void *const p_ucontext)
{
uint8_t ii = 0u;
uint8_t thing_id = 0u;
bool is_something = 0u;
can_message_t message = {0};
int std_result = 0;
/* Mark as unwanted */
(void) p_ucontext;
if ((HANDLERS_SIGNUM_CAN_RX != signal_number) ||
(NULL == p_signal_info))
{
/* No penalty for these conditions */
return;
}
else
{
/* Block this signal */
std_result = pthread_sigmask(SIG_BLOCK, &signal_set_can, NULL);
/* This result is asserted because the only failure is if the SIG_BLOCK
* action is invalid
*/
assert(0 == std_result);
socket_desc = p_signal_info->si_fd;
bytes_read = read(socket_desc, &frame, sizeof(frame));
if (bytes_read != sizeof(frame))
{
// ...
goto unblock_signal;
}
}
/* Is this an error frame? */
if ((frame.can_id & CAN_ERR_FLAG) != 0u)
{
// ...
goto unblock_signal;
}
/* Is this a frame with an 11-bit ID? */
else if ((frame.can_id & CAN_EFF_FLAG) == 0u)
{
// ...
goto unblock_signal;
}
/* Is this a frame with a 29-bit ID? */
else
{
function_id = frame.can_id & CAN_EFF_MASK;
command_id = function_id;
receiver_id = function_id;
function_id &= MASK_FUNCTION_ID;
function_id >>= POSITION_FUNCTION_ID;
command_id &= MASK_COMMAND_ID;
command_id >>= POSITION_COMMAND_ID;
receiver_id &= MASK_RECEIVER_ID;
thing_id = (frame.can_id & MASK_THING_ID) - 1u;
is_something = frame.can_id & MASK_RECEIVER_IS_SOMETHING;
/* Update the housekeeping stats */
if (function_id < FUNCTIONCODE_QUANTITY)
{
delivered_for_function_id[function_id]++;
}
else
{
delivered_for_function_id[FUNCTIONCODE_QUANTITY]++;
}
}
/* Handle emergency messages */
if (FUNCTIONCODE_EMGC == function_id)
{
// ...
goto unblock_signal;
}
/* Handle heartbeats */
if (FUNCTIONCODE_HB == function_id)
{
// Gets time from CLOCK_MONOTONIC_RAW and converts to microseconds
(void) getRawTimeFormatInternal(&(heartbeats[is_something][thing_id]));
goto unblock_signal;
}
/* Discard anything but Responses */
if (FUNCTIONCODE_RESPONSE != function_id)
{
// ...
goto unblock_signal;
}
/* Make the busy something available again */
if (true == is_something)
{
something_busy_bits &= ~(1 << thing_id);
}
/* Otherwise, find the interface ID and push to the buffer */
iface_id = -1;
/* Find buffer first */
for (ii = 0u; ii < CAN_INTERFACE_QUANTITY; ii++)
{
if (can_metadata.socket[ii] == socket_desc)
{
iface_id = (int32_t) ii;
break;
}
}
if (-1 == iface_id)
{
goto unblock_signal;
}
/* Otherwise convert and buffer */
h_result = canFrameToMessage(&message, &frame);
if (CAN_RESULT_OK != h_result)
{
// ...
goto unblock_signal;
}
d_result = bufferMessageDirect((can_interface_id_t) iface_id, &message);
if (CAN_RESULT_OK != d_result)
{
// ...
assert(0);
}
// ........................................................................
unblock_signal:
/* Unblock this signal */
std_result = pthread_sigmask(SIG_UNBLOCK, &signal_set_can, NULL);
/* This result is asserted because the only failure is if the SIG_BLOCK
* action is invalid
*/
assert(0 == std_result);
}
// ============================================================================
cand_result_t
bufferMessageDirect(
can_interface_id_t const can_interface_id,
can_message_t const *const p_message)
{
canh_result_t h_result = CANH_RESULT_OK;
cand_result_t result = CAND_RESULT_OK;
h_result = validateInterfaceId(can_interface_id);
if (CANH_RESULT_OK != h_result)
{
result = CAND_RESULT_INTERNAL_ERROR;
assert(0); // This is tripped, because can_interface_id is -1
}
// ...
// Push into buffer (call to buffer utility)
return result;
}
老问题
我已经设置了一个自定义信号处理程序来缓冲从 CAN 接口中删除的帧。 在处理程序中,我首先阻塞信号,最后使用pthread_sigmask()
解除阻塞。
基本配方是:
void
myHandler(
int const signal_number,
siginfo_t *const p_signal_info,
void *const p_ucontext)
{
check_signal_info();
pthread_sigmask(SIG_BLOCK, set_of_this_signal_only);
read(socket, &can_frame, sizeof(can_frame));
derive_can_iface_id(&iface_id);
buffer_push((iface_enum_type) iface_id, can_frame);
pthread_sigmask(SIG_UNBLOCK, set_of_this_signal_only);
}
在buffer_push()
中,检查接口 ID iface_id
,如果出现错误,因为这通常只能是我的错,所以调用assert(0)
。
在我的程序运行了一段时间后,但确切时间有所不同,随着 stream 的 CAN 帧到达,此assert()
被触发并执行结束。 当我检查信号处理程序堆栈帧时, iface_id
是0
这是有效的,但传递给buffer_push()
的参数是-1
。
我添加了对pthread_sigmask()
的调用,因为我认为,也许信号在其处理程序已经在进行中时正在触发。
buffer_push()
的原型是:
result_enum_type
buffer_push(
iface_enum_type const can_interface_id,
can_frame_type const *const p_message)
iface_id
在 function 之外声明,如下所示:
static volatile uint32_t iface_id = 0;
为避免疑义,在对buffer_push()
的调用下面是我自己的所有代码 --- 没有外部调用。 另外,我有一个传入的 CAN 帧的转储,这个信号处理程序正确地解析了每个帧。
请您就可能导致此问题的原因提供建议?
从信号处理程序调用的 function 中的参数值如何与传入的值不同?
怎么会这样? 因为C 标准说它可以:
当抽象机的处理因收到信号而中断时,既不是无锁原子对象也不是
volatile sig_atomic_t
类型的对象的值是未指定的,浮点环境的 state 也是如此。 当处理程序退出时,处理程序修改的任何 object 的值既不是无锁原子 object 也不是volatile sig_atomic_t
类型的值变得不确定,如果它被处理程序修改且未恢复,浮点环境的 state 也是如此到原来的 state。
以下都不是“无锁原子对象”或“类型volatile sig_atomic_t
”:
STATIC volatile ssize_t bytes_read = 0;
STATIC volatile int socket_desc = 0;
STATIC volatile struct can_frame frame = {0};
STATIC volatile uint32_t receiver_id = 0u;
STATIC volatile uint32_t command_id = 0u;
STATIC volatile uint32_t function_id = 0u;
STATIC volatile int32_t iface_id = 0;
STATIC volatile cand_result_t d_result = CAND_RESULT_OK;
STATIC volatile canh_result_t h_result = CANH_RESULT_OK;
STATIC volatile internal_time_t *const heartbeats[2] =
{
otherthing_heartbeat,
something_heartbeat,
};
当您的信号处理程序被调用时,这些变量中的每一个都具有不确定的值,并且如果它们在信号处理程序中被修改,那么当您的信号处理程序返回时,这些变量中的每一个都将变得不确定。
另请注意(草案)C11 标准的脚注 188 :
因此,信号处理程序通常不能调用标准库函数。
基本上,您不能安全地在信号处理程序中做任何事情。 严格符合 C 的代码只能修改上面提到的对象。 POSIX 扩展了信号处理程序可以安全地执行的操作,以仅调用异步信号安全的函数——一个非常有限的集合。 Windows也有类似规定。 Linux signal-safety
手册页提供了特定于 Linux 的功能列表,这些功能在 Linux 上是异步信号安全的。
可能发生的情况是您的正常处理和信号处理之间存在竞争条件。 它在大多数情况下都有效,但时不时会出现其中一个竞争条件,并且您会得到损坏的值。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.