[英]Handling SIGINT in a C program that uses syslog() on ARM causes the program execution to jump to backwards a few lines
考慮以下程序。 它在while
循環內進入一個忙等待狀態while
等待SIGINT
信號處理程序取消設置循環條件,從而使其離開並允許main()
正常return
而不是僅僅終止進程:
#include <unistd.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
#include <inttypes.h>
#include <stdbool.h>
#include <signal.h>
#include <syslog.h>
#define RES_ERROR -1
#define RES_OK 1
#define ARG_MAX_SIZE 30
#define MAX_BUFFER 64
static bool module_running = true;
static void SigHandlerIMU(int signal_number);
static int ProcessSignalConfig(void);
static void SigHandlerIMU(int signal_number)
{
if(signal_number == SIGINT){
module_running = false;
}
return;
}/*SigHandlerIMU*/
static int ProcessSignalConfig(void)
{
int ret_value = RES_ERROR;
struct sigaction signal_handler;
syslog(LOG_USER | LOG_NOTICE, "Catching SIGINT...\n");
signal_handler.sa_handler = SigHandlerIMU;
if(sigaction(SIGINT, &signal_handler, NULL) == -1){
syslog(LOG_USER | LOG_ERR, "can't catch SIGINT\n");
}
else{
ret_value = RES_OK;
}
return ret_value;
}/*ProcessSignalConfig*/
int main(int argcount, char const *argvalue[])
{
int main_return_val = RES_ERROR;
struct sigaction signal_handler;
(void)setlogmask (LOG_UPTO (LOG_DEBUG));
openlog(NULL, LOG_PERROR | LOG_PID, LOG_USER);
syslog(LOG_USER | LOG_NOTICE, "Starting program...\n");
if(ProcessSignalConfig() < 0){
syslog(LOG_USER | LOG_ERR, "Failed catching process signals\n");
module_running = false;
}
syslog(LOG_USER | LOG_DEBUG, "Entering loop...\n");
while(module_running == true){
}
syslog(LOG_USER | LOG_DEBUG, "Exiting...\n");
closelog();
return main_return_val;
} /*main*/
根據目標體系結構,我得到不同的行為。
使用gcc signal_test.c -o signal_test
進行編譯時,程序會在最后一次調用syslog()
return
s。
signal_test[4620]: Starting program...
signal_test[4620]: Catching SIGINT...
signal_test[4620]: Entering loop...
^Csignal_test[4620]: Exiting...
但是,使用arm-linux-gnueabihf-gcc signal_test.c -o signal_test
,似乎會跳回對ProcessSignalConfig()
的調用,然后從那里恢復(觀察重復的跟蹤):
signal_test[395]: Starting program...
signal_test[395]: Catching SIGINT...
signal_test[395]: Entering loop...
^Csignal_test[395]: Catching SIGINT...
signal_test[395]: Entering loop...
signal_test[395]: Exiting...
編輯:我一直在做進一步的測試,如果我使用了所有的printf()而不是syslog(),該程序也可以在ARM上正常運行。 我將問題標題更新為當前情況
您告訴您的程序是在信號處理程序返回之后“恢復”,但實際上該程序永遠不會停止運行,因為它正在執行“繁忙等待”。 如果你想等待一個信號到達你應該使用功能sigsuspend
實際上至極阻塞,直到一個信號,它它傳遞的過程中看到在這里幫助 。 無論如何,意外的行為可能是由while循環中檢查的標志引起的,請注意,它與信號處理程序共享,因此該變量應該是原子的,並聲明如下: static volatile sig_atomic_t module_running;
。
信號處理程序不是魔術。 在用戶模式下調用它們,因此必須在用戶代碼中調用它們。 但是只能在內核模式下檢測到它們,因此內核會在調用信號處理程序時將信號傳遞給進程,並以某種方式標記進程在用戶模式下運行。
僅當進程正在執行系統調用時才會發生這種情況,或者通常是內核因進程運行時間過長而搶占進程時才會發生這種情況。 由於代碼必須在用戶模式下運行,因此UNIX的設計人員(恐怕到目前為止一直如此),信號處理程序的調用僅在內核即將返回用戶進程時發生(該機制包括在處理用戶堆棧以從系統調用返回時跳到信號處理程序,並讓被堆棧處理的堆棧返回被中斷的代碼,就好像該中斷是真正的硬件中斷一樣,這允許一切在用戶代碼中發生並且不會妥協在內核模式下運行的可能性。
當進程在系統調用中停止時,該機制很簡單,因為用戶進程未執行用戶代碼,並且在syscall返回之后,信號處理程序將在一個非常特定的點被調用(因此該位置實際上位於該點)系統調用之后的代碼集---返回-1
並將errno
設置為EINTR
但是實際上只有在調用信號處理程序之后 ,您才能夠檢查此代碼),但是當進程被搶占時,存在該進程的問題可以在其代碼中的任何位置。 上面提到的堆棧處理必須處理這個問題,並准備恢復完整的cpu狀態(例如發生從硬件中斷返回的情況),以便能夠在用戶代碼中的任何時候執行信號處理程序並離開沒事。 這沒有問題,因為內核在中斷進程時將其保存。 唯一的不同是,在返回用戶模式后,完整的cpu狀態還原將推遲到執行信號處理程序之前。
信號處理程序管理的代碼通常由內核安裝在用戶模式內存映射中(在BSD系統中,這發生在主進程線程堆棧的頂部,然后推送環境以及exec(2)
args和argv
, argc
參數。)但是它可以在程序虛擬空間中的任何位置。
這里的情況是純在用戶代碼中執行,因此直到內核確實搶占了該進程,它才得到信號。 這可能發生在循環中的任何地方,當計時器中斷停止進程並重新調度該進程時,就在返回用戶模式之前,堆棧被重整以強制跳轉到信號處理程序管理器,從而切換到用戶模式,這使程序跳轉到信號處理程序管理器,它調用您的信號處理程序,然后信號處理程序管理器恢復完整的cpu狀態並返回到內核中斷進程的位置,就好像硬件中斷已引起中斷一樣。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.