2010-03-29 36 views
84

Làm thế nào để bạn xử lý sạch khi chương trình nhận được tín hiệu giết? Ví dụ:Làm thế nào để xử lý một cách duyên dáng tín hiệu SIGKILL trong Java

Ví dụ: có một ứng dụng tôi kết nối với ứng dụng đó muốn bất kỳ ứng dụng của bên thứ ba nào (ứng dụng của tôi) gửi lệnh finish khi đăng xuất. Lời khuyên tốt nhất để gửi câu hỏi finish là gì khi ứng dụng của tôi bị phá hủy với kill -9?

chỉnh sửa 1: giết -9 không thể bị bắt. Cảm ơn các bạn đã sửa tôi.

chỉnh sửa 2: Tôi đoán trường hợp này sẽ là khi một trong những gọi chỉ giết mà là giống như ctrl-c

+30

'giết -9' có nghĩa là với tôi:" Bắt đầu, quá trình phạm lỗi, đi với ngươi! ", Khi mà quá trình sẽ chấm dứt. Ngay. – ZoogieZork

+7

Trên hầu hết các * nixes mà tôi biết, kill -9 không thể bị chặn và xử lý một cách duyên dáng bởi bất kỳ chương trình nào dù nó được viết bằng ngôn ngữ nào. –

+1

@Bui: ngoài những gì người khác đã nhận xét và trả lời, * NẾU * Un * x hệ điều hành của bạn không ngay lập tức giết * VÀ ĐĂNG KÝ TẤT CẢ TÀI NGUYÊN * được sử dụng bởi chương trình được * kill -9'ed *, tốt ... Hệ điều hành bị hỏng. – SyntaxT3rr0r

Trả lời

94

Cách xử lý này cho bất cứ điều gì khác hơn kill -9 sẽ phải đăng ký một cái móc shutdown . Nếu bạn có thể sử dụng (SIGTERM) kill -15 móc tắt sẽ hoạt động. (SIGINT) kill -2DO làm cho chương trình thoát ra một cách duyên dáng và chạy các móc tắt máy.

Đăng ký máy ảo mới móc tắt máy.

Các máy ảo Java tắt trong đáp ứng với hai loại sự kiện:

* The program exits normally, when the last non-daemon thread exits or 

khi exit (tương đương, System.exit) phương pháp được gọi, hoặc

* The virtual machine is terminated in response to a user 

ngắt , chẳng hạn như nhập^C hoặc sự kiện toàn hệ thống, chẳng hạn như đăng xuất người dùng hoặc tắt hệ thống.

Tôi đã thử các chương trình thử nghiệm sau đây trên OSX 10.6.3 và trên kill -9 nó đã làm KHÔNG chạy móc tắt máy, đã không nghĩ rằng nó sẽ. Trên kill -15DO chạy móc tắt máy mỗi lần.

public class TestShutdownHook 
{ 
    public static void main(final String[] args) throws InterruptedException 
    { 
     Runtime.getRuntime().addShutdownHook(new Thread() 
     { 
      @Override 
      public void run() 
      { 
       System.out.println("Shutdown hook ran!"); 
      } 
     }); 

     while (true) 
     { 
      Thread.sleep(1000); 
     } 
    } 
} 

Không có cách nào thực sự xử lý một cách duyên dáng kill -9 trong bất kỳ chương trình nào.

Trong trường hợp hiếm hoi, máy ảo có thể bị hủy, nghĩa là, dừng chạy mà không tắt máy sạch. Điều này xảy ra khi máy ảo bị chấm dứt bên ngoài, ví dụ với tín hiệu SIGKILL trên Unix hoặc cuộc gọi TerminateProcess trên Microsoft Windows.

Tùy chọn thực sự duy nhất để xử lý kill -9 là phải có chương trình xem đồng hồ khác để chương trình chính của bạn biến mất hoặc sử dụng tập lệnh trình bao bọc. Bạn có thể làm điều này với một kịch bản lệnh shell đã thăm dò lệnh ps tìm kiếm chương trình của bạn trong danh sách và hành động phù hợp khi nó biến mất.

#!/bin/bash 

java TestShutdownHook 
wait 
# notify your other app that you quit 
echo "TestShutdownHook quit" 
7

Bạn có thể sử dụng Runtime.getRuntime().addShutdownHook(...), nhưng bạn không thể đảm bảo rằng nó sẽ được gọi là trong bất kỳ trường hợp.

+5

Nhưng trong trường hợp giết -9 nó gần như chắc chắn sẽ không chạy. –

12

cách xử lý tín hiệu của riêng bạn trong một số JVM nhất định - xem this article about the HotSpot JVM ví dụ.

Bằng cách sử dụng phương thức CN sun.misc.Signal.handle(Signal, SignalHandler) gọi điện, bạn cũng có thể đăng ký bộ xử lý tín hiệu, nhưng có thể không cho các tín hiệu như INT hoặc TERM khi chúng được JVM sử dụng.

Để có thể xử lý mọi dấu hiệu, bạn sẽ phải nhảy ra khỏi JVM và vào lãnh thổ Hệ điều hành.

Những gì tôi thường làm để (ví dụ) phát hiện chấm dứt bất thường là khởi chạy JVM của tôi bên trong một tập lệnh Perl, nhưng có kịch bản chờ cho JVM bằng cách sử dụng cuộc gọi hệ thống waitpid.

Tôi sau đó được thông báo bất cứ khi nào JVM thoát, và lý do thoát khỏi JVM và có thể thực hiện hành động cần thiết.

+0

Lưu ý bạn * có thể * chụp 'INT' và' TERM' bằng 'sun.misc.Signal', nhưng bạn không thể xử lý' QUIT' vì JVM giữ nó để gỡ lỗi, cũng không phải 'KILL' vì hệ điều hành sẽ chấm dứt JVM ngay lập tức. Cố gắng xử lý hoặc sẽ tăng một 'IllegalArgumentException'. – dimo414

4

Tôi mong rằng JVM duyên dáng ngắt (thread.interrupt()) tất cả các chủ đề chạy tạo ra bởi các ứng dụng, ít nhất là cho tín hiệu SIGINT (kill -2)SIGTERM (kill -15).

Bằng cách này, tín hiệu sẽ được chuyển tiếp tới chúng, cho phép hủy bỏ chuỗi và xóa tài nguyên một cách duyên dáng trong standard ways.

Nhưng đây không phải là trường hợp (ít nhất là trong việc thực hiện JVM của tôi:.. Java(TM) SE Runtime Environment (build 1.8.0_25-b17), Java HotSpot(TM) 64-Bit Server VM (build 25.25-b02, mixed mode)

Khi người dùng khác nhận xét, việc sử dụng móc shutdown dường như bắt buộc

Vì vậy, làm thế nào tôi

Trước tiên, tôi không quan tâm đến nó trong tất cả các chương trình, chỉ trong những chương trình mà tôi muốn theo dõi việc hủy người dùng và kết thúc không mong muốn. Ví dụ, hãy tưởng tượng rằng chương trình java của bạn là một người xử lý tuổi khác. Bạn có thể muốn phân biệt xem nó đã được chấm dứt một cách duyên dáng (SIGTERM từ quá trình quản lý) hay tắt máy đã xảy ra (để khởi động lại tự động công việc khi khởi động).

Là cơ sở, tôi luôn làm cho các chuỗi dài chạy của mình nhận biết được trạng thái bị gián đoạn và ném InterruptedException nếu chúng bị gián đoạn. Điều này cho phép thực hiện quyết toán theo cách được kiểm soát bởi nhà phát triển (cũng tạo ra kết quả tương tự như các hoạt động chặn chuẩn). Sau đó, ở cấp cao nhất của ngăn xếp luồng, InterruptedException được chụp và thực hiện dọn sạch thích hợp. Các luồng này được mã hóa để biết cách trả lời yêu cầu gián đoạn. Thiết kế cao cohesion.

Vì vậy, trong những trường hợp này, tôi thêm một cái móc tắt máy, mà những gì tôi nghĩ JVM nên làm theo mặc định: ngắt tất cả các chủ đề phi daemon được tạo ra bởi ứng dụng của tôi vẫn đang chạy:

Runtime.getRuntime().addShutdownHook(new Thread() { 
    @Override 
    public void run() { 
     System.out.println("Interrupting threads"); 
     Set<Thread> runningThreads = Thread.getAllStackTraces().keySet(); 
     for (Thread th : runningThreads) { 
      if (th != Thread.currentThread() 
       && !th.isDaemon() 
       && th.getClass().getName().startsWith("org.brutusin")) { 
       System.out.println("Interrupting '" + th.getClass() + "' termination"); 
       th.interrupt(); 
      } 
     } 
     for (Thread th : runningThreads) { 
      try { 
       if (th != Thread.currentThread() 
       && !th.isDaemon() 
       && th.isInterrupted()) { 
        System.out.println("Waiting '" + th.getName() + "' termination"); 
        th.join(); 
       } 
      } catch (InterruptedException ex) { 
       System.out.println("Shutdown interrupted"); 
      } 
     } 
     System.out.println("Shutdown finished"); 
    } 
}); 

Hoàn thành ứng dụng thử nghiệm tại github: https://github.com/idelvall/kill-test

0

Có một cách để phản ứng với lệnh giết -9: có quy trình riêng biệt giám sát quá trình bị giết và dọn dẹp sau khi cần thiết. Điều này có lẽ sẽ liên quan đến IPC và sẽ có khá nhiều công việc, và bạn vẫn có thể ghi đè nó bằng cách giết chết cả hai quá trình cùng một lúc. Tôi cho rằng nó sẽ không đáng để gặp rắc rối trong hầu hết các trường hợp.

Bất cứ ai giết một quy trình với -9 lý thuyết nên biết những gì anh ta/cô ấy đang làm và nó có thể để lại mọi thứ trong trạng thái không nhất quán.

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