2009-02-16 25 views
9

Tôi có một chương trình mà segfaults từ số học con trỏ đôi khi. Tôi biết điều này xảy ra, nhưng tôi không thể dễ dàng kiểm tra trước để xem liệu nó có phân đoạn hay không - hoặc tôi có thể "quét trước" dữ liệu đầu vào để xem liệu nó có gây ra sự phân đoạn (không thể xác định), hoặc tôi có thể tái trang bị nó để không sử dụng số học con trỏ, mà sẽ đòi hỏi một số lượng công việc lớn hơn đáng kể, hoặc tôi có thể cố gắng để bắt một segfault. Vì vậy, câu hỏi của tôi:Bắt segfaults trong C

1) Làm thế nào, trong C, tôi có thể bắt một segfault? Tôi biết một cái gì đó trong hệ điều hành gây ra một segfault, nhưng những gì có thể một chương trình C làm trong trường hợp nó segfaults chết một chút duyên dáng hơn chỉ Segmentation fault?

2) Di động như thế nào?

Tôi cho rằng đây là một hành vi không thể chuyển đổi cao, vì vậy nếu bạn đăng bất kỳ mã nào để bắt được đoạn mã, hãy cho tôi biết nó hoạt động như thế nào. Tôi đang sử dụng Mac OS X nhưng tôi muốn chương trình của mình hoạt động trên nhiều nền tảng nhất có thể và tôi muốn xem các tùy chọn của mình là gì.

Và đừng lo lắng - về cơ bản tất cả những gì tôi muốn làm là in thêm thông báo lỗi thân thiện với người dùng và giải phóng bộ nhớ ed malloc() và sau đó chết. Tôi không có ý định bỏ qua tất cả các segfaults tôi nhận được và cày trước.

+1

In thông báo lỗi; không giải phóng bộ nhớ, vì cơ hội là phong nha mà hệ thống bộ nhớ của bạn bị xáo trộn khi lỗi seg xảy ra. –

Trả lời

9

Bạn phải xác định trình xử lý tín hiệu. Điều này được thực hiện trên các hệ thống Unix sử dụng hàm sigaction. Tôi đã làm điều này với cùng một mã trên Fedora 64- và 32-bit, và trên Sun Solaris.

21

Vâng, SIGSEGV bị trói buộc, và đây là POSIX, vì vậy nó có thể di động theo nghĩa đó.

Bu Tôi lo ngại rằng bạn dường như muốn xử lý segfault thay vì khắc phục sự cố gây ra sự phân đoạn. Nếu tôi phải chọn xem đó là hệ điều hành có lỗi hay mã của riêng tôi, tôi biết tôi sẽ chọn cái nào. Tôi đề nghị bạn truy tìm lỗi đó, sửa lỗi, sau đó viết một trường hợp kiểm tra để chắc chắn rằng nó không bao giờ cắn bạn một lần nữa.

+0

Nếu không đào sâu vào các chi tiết gớm ghiếc, chế độ mặc định sẽ an toàn và bảo vệ người dùng không bao giờ hết bộ nhớ (nó sử dụng danh sách được liên kết).Họ phải chọn sử dụng phiên bản "kém an toàn" một cách rõ ràng, và nên nhận thức được hậu quả của việc làm như vậy. –

+0

Phiên bản "kém an toàn" được cung cấp để tương thích với các chương trình khác KHÔNG cung cấp danh sách liên kết vô hạn như của tôi. Thêm vào đó, nếu không có gì khác, tôi sẽ học một cái gì đó mới. –

+1

Đã hiểu - bạn có lý do chính đáng. Tôi đã chỉ ra nó mặc dù ... –

1

Tôi nghĩ bạn đang cố gắng giải quyết vấn đề không tồn tại. Ít nhất bạn đang làm việc vào cuối sai. Bạn sẽ không thể bắt lỗi do lỗi này do hệ điều hành (do đó là gây ra bởi chương trình của bạn, hệ điều hành chỉ bắt được nó).

Tôi khuyên bạn nên suy nghĩ lại chiến lược của mình về đầu vào: Tại sao không thể khử trùng nó? Điều quan trọng nhất cần làm là kiểm tra kích thước, cho điều này stdlib C có chức năng thích hợp. Sau đó, tất nhiên bạn sẽ phải kiểm tra đầu vào hợp lệ liên quan đến nội dung. Có, điều này có thể sẽ dẫn đến rất nhiều công việc, nhưng đó là cách duy nhất để viết một chương trình mạnh mẽ.

EDIT: Tôi không phải là một chuyên gia về C, không biết rằng ngay cả lỗi phân đoạn có thể được xử lý bởi trình xử lý tín hiệu. Tuy nhiên, tôi nghĩ rằng đó không phải là cách đi đúng đắn vì những lý do nêu trên.

+0

Kiểm tra kích thước không phải là vấn đề - người dùng nhập một chương trình (trong não * ck) và chương trình của tôi chạy nó. Kiểm tra đầu vào hợp lệ chạy lên chống lại sự cố dừng. Ngoài ra, một số chương trình sẽ chỉ đôi khi segfault (tức là với nhiều dữ liệu đầu vào), và có hay không chúng sẽ phụ thuộc vào đầu vào của người dùng. –

+0

@Chris Lutz: Ahhh ... trong ngữ cảnh này, câu hỏi của bạn trông hợp lý hơn nhiều. Bây giờ tôi có thể thấy lý do tại sao bạn muốn - thực tế, tại sao đó là lựa chọn khả thi duy nhất của bạn - để bắt SIGSEGV. – paprika

+0

Nó không phải là lựa chọn "khả thi" duy nhất. Tôi có thể viết lại nó để sử dụng một mảng và một biến trỏ đến một phần tử của mảng đó, và sau đó có kiểm tra dễ dàng xem con trỏ có nằm ngoài giới hạn hay không (ví dụ: < 0 or > MAX). từ nơi tôi đang đứng). –

11

Bạn có thể sử dụng chức năng tín hiệu để cài đặt một handler tín hiệu mới cho các tín hiệu:

#include <signal.h> 
    void (*signal(int signum, void (*sighandler)(int)))(int); 

Something như đoạn mã sau:

signal(SIGINT , clean_exit_on_sig); 
signal(SIGABRT , clean_exit_on_sig); 
signal(SIGILL , clean_exit_on_sig); 
signal(SIGFPE , clean_exit_on_sig); 
signal(SIGSEGV, clean_exit_on_sig); // <-- this one is for segmentation fault 
signal(SIGTERM , clean_exit_on_sig); 

void 
clean_exit_on_sig(int sig_num) 
{ 
     printf ("\n Signal %d received",sig_num); 
} 
+0

... lưu ý rằng vào thời điểm bạn đã có SEGV - nơi con trỏ đọc/ghi chỉ * xảy ra * để đạt được bộ nhớ không thể truy cập - rất có khả năng nó đã được quản lý để phân bổ, bộ nhớ có thể truy cập, chứa dữ liệu của bạn và danh sách chặn miễn phí. Vì vậy, không mong đợi phân bổ để làm việc, và không mong đợi bất kỳ dữ liệu trong bộ nhớ được sane. – ijw

1

Có một ví dụ về cách bắt SIGSEGV và in dấu vết ngăn xếp bằng cách sử dụng backtrace của glibc() tại đây:

how to generate a stacktrace when my C++ app crashes

Bạn có thể sử dụng điều này để bắt segfault của bạn và dọn dẹp, nhưng được cảnh báo: bạn không nên làm quá nhiều thứ trong một xử lý tín hiệu, đặc biệt là những thứ có liên quan đến thực hiện cuộc gọi như malloc(). Có rất nhiều cuộc gọi mà không phải là tín hiệu an toàn, và bạn có thể kết thúc tự bắn vào chân nếu bạn thực hiện, nói, một cuộc gọi đến malloc từ trong malloc.

+0

Liên kết đã chết, hãy cân nhắc việc cập nhật hoặc xóa câu trả lời này – slayton

1

xử lý tín hiệu (tương đối) di động trên các máy unix (bao gồm mac và linux). Sự khác biệt lớn là trong chi tiết ngoại lệ, được chuyển làm đối số cho thủ tục xử lý tín hiệu. Buồn cười, nhưng có thể bạn sẽ cần một loạt #ifdefs cho điều đó, nếu bạn muốn in các thông báo lỗi hợp lý hơn (chẳng hạn như ở đâu và do địa chỉ lỗi nào xảy ra) ...

ok, đây là mã đoạn để bạn có thể bắt đầu với:

#include <signal.h> 

/* reached when a segv occurrs */ 
void 
SEGVFunction(SIGARGS) 
{ 
    ... 
} 

... 
main(...) { 
    signal(SIGSEGV, SEGVFunction); /* tell the OS, where to go in case... */ 
    ... 
    ... do your work ... 
} 

nhiệm vụ của bạn là để:

  • kiểm tra những gì SIGARGS là (OS phụ thuộc, vì vậy sử dụng một ifdef)
  • xem làm thế nào để giải nén lỗi địa chỉ và pc từ thông tin ngoại lệ trong sigArgs
  • in thông điệp hợp lý
  • thoát

về mặt lý thuyết, bạn thậm chí có thể vá các pc trong xử lý tín hiệu (để sau khi hướng dẫn đứt gãy), và tiến hành. Tuy nhiên, các trình xử lý tín hiệu điển hình hoặc là exit() hoặc một longjmp() trở lại vào một vị trí lưu trong chính.

liên quan

5

Những hành động an toàn trong một xử lý tín hiệu là rất hạn chế. Không an toàn khi gọi bất kỳ chức năng thư viện nào không được gọi lại, điều này sẽ loại trừ, ví dụ: free()printf(). Thực hành tốt nhất là đặt biến và trả về, nhưng điều này không giúp bạn nhiều. Cũng an toàn khi sử dụng các cuộc gọi hệ thống như write(). Lưu ý rằng trong hai ví dụ backtrace được đưa ra ở đây, chức năng backtrace_symbols_fd() sẽ an toàn vì nó sử dụng trực tiếp fd thô, nhưng cuộc gọi đến fprintf() là không chính xác và phải được thay thế bằng cách sử dụng write().

+0

Ồ, thật là thô. Tôi sẽ giữ những quy tắc của Draconia trong tâm trí. Có lẽ tôi thực sự KHÔNG nên cố gắng để bắt segfault ... –

+1

Tôi chắc chắn đồng ý với các ý kiến ​​khác cho thấy rằng điều phải làm là sửa chữa các lỗi gây ra segv. –

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