2011-07-20 21 views
9

Trên x86 (hoặc 64-bit hoặc 32-bit) Linux - ví dụ:Trong trình xử lý tín hiệu, làm cách nào để biết chương trình bị gián đoạn ở đâu?

void signal_handler(int) { 
    // want to know where the program is interrupted ... 
} 

int main() { 
    ... 
    signal(SIGALRM, signal_handler); 
    alarm(5); 
    ... 
    printf(...); <------- at this point, we trigger signal_handler 
    ... 
} 

Trong signal_handler, làm thế nào chúng ta có thể biết chúng ta đang bị gián đoạn tại printf trong main()?

Trả lời

11

Sử dụng sigaction với SA_SIGINFO được đặt trong sa_flags.

đang Prototype:

#define _GNU_SOURCE 1 /* To pick up REG_RIP */ 
#include <stdio.h> 
#include <signal.h> 
#include <assert.h> 

static void 
handler(int signo, siginfo_t *info, void *context) 
{ 
    const ucontext_t *con = (ucontext_t *)context; 

    /* I know, never call printf from a signal handler. Meh. */ 
    printf("IP: %lx\n", con->uc_mcontext.gregs[REG_RIP]); 
} 

int 
main(int argc, char *argv[]) 
{ 
    struct sigaction sa = { }; 
    sa.sa_flags = SA_SIGINFO; 
    sa.sa_sigaction = handler; 
    assert(sigaction(SIGINT, &sa, NULL) == 0); 
    for (;;); 
    return 0; 
} 

Chạy nó và nhấn Ctrl-C . (Sử dụng Ctrl- \ để chấm dứt ...)

Điều này là dành cho x86_64. Đối với x86 32 bit, hãy sử dụng REG_EIP thay vì REG_RIP.

[sửa]

Tất nhiên, nếu bạn đang thực sự trong một hàm thư viện (như printf) hoặc một cuộc gọi hệ thống (như write), thanh ghi RIP/EIP có thể chỉ ở đâu đó buồn cười ...

Bạn có thể muốn sử dụng libunwind để thu thập dữ liệu ngăn xếp.

+0

Cảm ơn rất nhiều! Đây là những gì tôi cần! – flyingbin

+0

printf nói chung là không reentrant, vì vậy nếu bạn xảy ra được printf khi bạn nhận được tín hiệu, điều này có thể sẽ in rác hoặc tai nạn ... –

+2

@ Chris: Vâng, tôi biết. Chương trình mẫu này hoạt động tốt trên chính nó (có thể), và đó là cách dễ nhất để chứng minh nguyên tắc.Nhưng đó là lý do tại sao tôi bao gồm các bình luận và gọi nó là "nguyên mẫu mã" :-) – Nemo

1

Tùy thuộc vào hệ điều hành của bạn/Platform, nó có thể là trong một loạt các lĩnh vực:

  • Trên chồng hiện tại một số tập các thanh ghi sâu
  • Trên/ngắt chồng
  • Trong một số loại gọi lại liên quan đến thói quen tín hiệu của bạn ...

Không có thông tin bổ sung, tôi không nghĩ rằng chúng tôi có thể theo dõi điều này nhiều hơn nữa. Việc thêm thẻ C/C++ vào thẻ của bạn có thể tạo ra nhiều phản hồi và chế độ xem hơn.

+0

Cảm ơn. Tôi cố gắng sử dụng sigaction. Dường như ucontext_t có thể giúp một chút. Nhưng không có bất kỳ tiến bộ nào bây giờ .. – flyingbin

0

Ý tưởng giải quyết: nếu chỉ có một số ít địa điểm có thể kích hoạt trình xử lý tín hiệu hoặc bạn chỉ quan tâm đến khối lớn hơn xảy ra, bạn có thể duy trì điều đó trong một biến.

entered = 1; // or entered = ENTER_PRINTF1; 
printf(....); 
1

Trong signal_handler, làm thế nào chúng ta có thể biết chúng ta đang bị gián đoạn tại printf trong main()? Bạn không thể, ít nhất không phải từ góc độ C, C++ hoặc POSIX. Hệ điều hành của bạn có thể cung cấp các cuộc gọi cụ thể cho hệ điều hành cho phép bạn ping vào ngăn xếp. Đó là nhiều hơn một chút không rõ ràng, mặc dù.

Với tín hiệu dựa trên bộ hẹn giờ, lệnh nào kích hoạt tín hiệu là tung đồng xu.

Các vấn đề liên quan