Sự khác biệt giữa exit () và abort () là gì?


130

Trong C và C ++, sự khác biệt giữa exit()và là abort()gì? Tôi đang cố gắng kết thúc chương trình của mình sau một lỗi (không phải là ngoại lệ).

Câu trả lời:


116

abort()thoát khỏi chương trình của bạn mà không gọi các chức năng đã đăng ký bằng cách sử dụng atexit()trước và không gọi hàm hủy của đối tượng trước. exit()làm cả hai trước khi thoát khỏi chương trình của bạn. Nó không gọi hàm hủy cho các đối tượng tự động mặc dù. Vì thế

A a;
void test() { 
    static A b;
    A c;
    exit(0);
}

Sẽ phá hủy abđúng cách, nhưng sẽ không gọi hàm hủy của c. abort()sẽ không gọi kẻ hủy diệt của cả hai đối tượng. Vì điều này là không may, Tiêu chuẩn C ++ mô tả một cơ chế thay thế đảm bảo chấm dứt hợp lý:

Các đối tượng có thời lượng lưu trữ tự động đều bị hủy trong một chương trình có chức năng main()không chứa đối tượng tự động và thực hiện cuộc gọi đến exit(). Kiểm soát có thể được chuyển trực tiếp đến như vậy main()bằng cách ném một ngoại lệ được bắt gặp main().

struct exit_exception { 
   int c; 
   exit_exception(int c):c(c) { } 
};

int main() {
    try {
        // put all code in here
    } catch(exit_exception& e) {
        exit(e.c);
    }
}

Thay vì gọi exit(), hãy sắp xếp mã throw exit_exception(exit_code);đó để thay thế.


2
+1 bởi vì, trong khi Brian R. Bondy vẫn tốt, bạn đã nêu ra vấn đề hủy bỏ / thoát (không phải là hàm hủy của các đối tượng ngăn xếp được gọi) và đưa ra giải pháp thay thế cho quy trình C ++ chuyên sâu RAII.
paercebal

Tôi đang tìm cách để thoát khỏi một chương trình mà không gọi cho dtor và câu trả lời của bạn chỉ là những gì tôi đang tìm kiếm! Cảm ơn
acemtp

Tất nhiên điều đó là hoàn toàn chính xác, nếu thực sự có vấn đề là các công cụ phá hủy đối tượng tự động của bạn không được gọi là :-)
Chris Huang-Leaver

Theo hiểu biết của tôi, một sự khác biệt nữa giữa thoát và hủy bỏ sẽ là, việc hủy bỏ đó có thể (tùy thuộc vào cấu hình hệ điều hành) dẫn đến việc tạo ra một bãi chứa lõi.
Dirk Herrmann

33

hủy bỏ gửi tín hiệu SIGABRT, thoát chỉ cần đóng ứng dụng thực hiện dọn dẹp bình thường.

Bạn có thể xử lý tín hiệu hủy bỏ theo bất kỳ cách nào bạn muốn, nhưng hành vi mặc định là đóng ứng dụng cũng bằng mã lỗi.

hủy bỏ sẽ không thực hiện phá hủy đối tượng của các thành viên tĩnh và toàn cầu của bạn, nhưng thoát sẽ .

Tất nhiên mặc dù khi ứng dụng hoàn toàn đóng, hệ điều hành sẽ giải phóng mọi bộ nhớ không tham gia và các tài nguyên khác.

Trong cả hai hủy bỏthoát chấm dứt chương trình (giả sử bạn không ghi đè lên hành vi mặc định), mã trả về sẽ được trả lại cho quá trình cha mẹ mà bắt đầu ứng dụng của bạn.

Xem ví dụ sau:

SomeClassType someobject;

void myProgramIsTerminating1(void)
{
  cout<<"exit function 1"<<endl;
}

void myProgramIsTerminating2(void)
{
  cout<<"exit function 2"<<endl;
}

int main(int argc, char**argv)
{
  atexit (myProgramIsTerminating1);
  atexit (myProgramIsTerminating2);
  //abort();
  return 0;
}

Bình luận:

  • Nếu hủy bỏ không được ghi chú: không có gì được in và hàm hủy của someobject sẽ không được gọi.

  • Nếu hủy bỏ được nhận xét như trên: một số hàm hủy sẽ được gọi, bạn sẽ nhận được kết quả đầu ra sau:

chức năng thoát 2
chức năng thoát 1


Ở đây, nó được gọi là chức năng thoát 2 Chức năng thoát THEN 1. gcc 4, Linux 2.6.
strager

1
Trang man cho atexit nói: "Các hàm [đã đăng ký sử dụng atexit] được gọi theo thứ tự ngược lại; không có đối số nào được thông qua."
strager

@strager là đúng, các hàm được đăng ký bởi atexit được cho là được gọi theo thứ tự ngược lại khi lối ra được gọi hoặc trả về chính.
Robert Gamble

Chạy thử nghiệm và nó xuất hiện các hàm hủy đối với các thể hiện chung được gọi sau tất cả các cuộc gọi lại không chính xác.
strager

+1 để nhắc nhở mọi người rằng HĐH cuối cùng sẽ giải phóng tất cả các tài nguyên được phân bổ ngay cả sau khi có lệnh gọi hủy bỏ ().
Fingerolfin

10

Những điều sau đây xảy ra khi một chương trình gọi exit():

  • Chức năng được đăng ký bởi atexit chức năng được thực thi
  • Tất cả các luồng mở được tuôn ra và đóng lại, các tệp được tạo bởi tmpfile sẽ bị xóa
  • Chương trình kết thúc với mã thoát được chỉ định đến máy chủ

Hàm abort() gửi SIGABRTtín hiệu đến quy trình hiện tại, nếu không bắt được, chương trình sẽ bị chấm dứt mà không đảm bảo rằng các luồng mở được tuôn ra / đóng hoặc các tệp tạm thời được tạo qua tmpfilebị xóa, các atexithàm đã đăng ký sẽ không được gọi và không trạng thái thoát không được trả lại cho máy chủ.


hmm tiêu chuẩn nói rằng chương trình chỉ không bị chấm dứt nếu bộ xử lý tín hiệu "không quay lại". bạn khá hợp với C. bạn có thể tưởng tượng bất kỳ kịch bản nào sẽ khiến nó cho phép tiếp tục thực hiện bình thường mà không quay lại không? Tôi tưởng tượng longjmp, nhưng tôi không chắc về cách nó hoạt động trong bộ xử lý tín hiệu.
Julian Schaub - litb

Nói chung, việc gọi longjmp từ bộ xử lý tín hiệu là không xác định nhưng có một trường hợp đặc biệt khi tín hiệu được tạo ra với tăng / hủy bỏ vì vậy tôi đoán điều này sẽ có thể về mặt lý thuyết mặc dù tôi không nghĩ rằng tôi đã từng thấy nó được thực hiện. Bây giờ tôi sẽ phải thử nó;)
Robert Gamble

1
Điều này dường như hoạt động (được chia thành nhiều bài đăng do giới hạn 300 char): #include <stdio.h> #include <stdlib.h> #include <signal.h> #include <setjmp.h> volility sig_atomic_t do_abort = 1; jmp_buf env; void abort_handler (int i) {do_abort = 0; longjmp (env, 1);}
Robert Gamble

int main (void) {setjmp (env); đặt ("Tại setjmp"); if (do_abort) {signal (SIGABRT, abort_handler); đặt ("Gọi hủy bỏ"); Huỷ bỏ(); } đặt ("Không hủy bỏ!"); trả về 0; }
Robert Gamble

Trên Ubuntu 7.04, bản in này: Tại setjmp Gọi hủy bỏ Tại setjmp Không hủy bỏ!
Robert Gamble

5

Từ trang hướng dẫn exit ():

Hàm exit () gây ra chấm dứt quá trình bình thường và giá trị của trạng thái & 0377 được trả về cho cha.

Từ trang hướng dẫn hủy bỏ ():

Đầu tiên hủy bỏ () bỏ chặn tín hiệu SIGABRT và sau đó tăng tín hiệu đó cho quá trình gọi. Điều này dẫn đến việc chấm dứt bất thường của quy trình trừ khi tín hiệu SIGABRT bị bắt và bộ xử lý tín hiệu không quay trở lại.


4

abortgửi SIGABRTtín hiệu. abortkhông trả lại cho người gọi Trình xử lý mặc định cho SIGABRTtín hiệu đóng ứng dụng. stdiodòng tập tin được tuôn ra, sau đó đóng cửa. Tuy nhiên, các cấu trúc hủy cho các thể hiện của lớp C ++ không (tuy nhiên không chắc chắn về điều này - có lẽ kết quả không được xác định?).

exitcó cuộc gọi lại riêng của nó, thiết lập với atexit. Nếu các cuộc gọi lại được chỉ định (hoặc chỉ một), chúng được gọi theo thứ tự ngược lại với thứ tự đăng ký của chúng (như ngăn xếp), sau đó chương trình thoát. Như với abort, exitkhông trở lại với người gọi. stdiodòng tập tin được tuôn ra, sau đó đóng cửa. Ngoài ra, các hàm hủy cho các thể hiện của lớp C ++ được gọi.


exit có thể có nhiều hàm gọi lại được đăng ký qua atexit, khi thoát được gọi, tất cả các hàm gọi lại sẽ được gọi theo thứ tự ngược lại mà chúng đã được đăng ký.
Robert Gamble

@Gamble, Tất nhiên, tôi đã đề cập đến bản thân mình vài phút trước trong một bình luận cho câu trả lời của @ Bondy. Tôi sẽ chỉnh sửa câu trả lời của riêng tôi để phản ánh điều đó.
strager
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.