2011-11-21 13 views
6

Napisałem bardzo prosty program w C:W języku java instrukcja "5/0" nie uruchamia sygnału SIGFPE na moim komputerze z systemem Linux, dlaczego?

#include<stdio.h> 

int main(){ 
    int a=2; 
    int b=0; 
    printf("%d\n", a/b); 
} 

i uruchomić go z strace: strace ./a.out i dostać poniżej wyjścia (Tylko pasty część ogonowa)

... ... 
mprotect(0x600000, 4096, PROT_READ)  = 0 
mprotect(0x7f04c7fb8000, 4096, PROT_READ) = 0 
munmap(0x7f04c7f96000, 127640)   = 0 
--- SIGFPE (Floating point exception) @ 0 (0) --- 
+++ killed by SIGFPE +++ 
Floating point exception 

Wyjście pasuje do mojego oczekiwanie, ponieważ został zabity przez sygnał SIGFPE.

Jednak ten sam program napisany w Javie, nie otrzymuje sygnału SIGFPE, czy ktokolwiek wie, w jaki sposób wyjątki Javy dzielą się na "dzielenie przez zero"?

public class Main { 

    public static void main(String[] args) { 
    int a = 2; 
    int b = 0; 
    System.out.println(a/b); 
    } 
} 

strace java -Xcomp główna

... ... 
mprotect(0xf6949000, 8171520, PROT_READ|PROT_WRITE) = 0 
mprotect(0xf6949000, 8171520, PROT_READ|PROT_EXEC) = 0 
munmap(0xf774f000, 5727)    = 0 
mmap2(NULL, 331776, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_ANONYMOUS|MAP_STACK, -1, 0) = 0xfffffffff68d0000 
mprotect(0xf68d0000, 4096, PROT_NONE) = 0 
clone(child_stack=0xf6920494, flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID, parent_tidptr=0xf6920bd8, tls=0xf6920bd8, child_tidptr=0xff9c5520) = 958 
futex(0xf6920bd8, FUTEX_WAIT, 958, NULL) = 0 
exit_group(0) 
+0

nie ma sensu pływających tam. Dlaczego spodziewasz się sygnału zmiennoprzecinkowego? Czy twój kod "C" naprawdę używa ints? – EJP

+2

Masz wyjątek na poziomie Java? DivisionByZeroException lub coś takiego. Czy to nie wystarczy? Czy jesteś zainteresowany tym, jak JVM rozmawia z procesorem? – Thilo

+1

Być może brakuje mi czegoś, ale czy po prostu nie złapiesz wyjątku DivideByZeroException w Javie? – Gaff

Odpowiedz

7

Tutaj powstaje SIGFPE. Nie można zapomnieć powiedzieć strace o obserwowaniu dzieci Dodaj opcję -f do strace i powinieneś zobaczyć coś podobnego do:

[pid 2304] read(3, "\312\376\272\276\0\0\0001\0n\n\0\23\0I\t\0\3\0J\7\0K\n\0L\0M\n\0N\0"..., 2369) = 2369 
[pid 2304] --- SIGFPE (Floating point exception) @ 0 (0) --- 
[pid 2304] rt_sigreturn(0x1c50800)  = 5 
[pid 2304] write(2, "Exception in thread \"main\" ", 27Exception in thread "main") = 27 
[pid 2304] write(2, "java.lang.ArithmeticException: /"..., 40java.lang.ArithmeticException:/by zero) = 40 
[pid 2304] write(2, "\n", 1 
1

Java działa na maszynie wirtualnej (JVM), który Abstracts sprzęt z programu. Większość błędów w programie Java spowoduje wyjątki dla języka Java i nie spowoduje uruchomienia natywnych kodów błędów procesora lub os. Domyślam się, że ten kod rzuci wyjątek ArithmeticException (lub coś w tym stylu).

+1

Tak, rzuca wyjątek ArithmeticException, ale jak? – zhangfaen

5

W przeciwieństwie do (normalnego) programu kompilującego C, program Java działa w środowisku wykonawczym, a nie na procesorze, i nie zależy od platformy. Podzielenie przez zero w Java powoduje ArithmeticException tak:

Exception in thread "main" java.lang.ArithmeticException:/by zero 

Z JLS:

jest wyjątek dla jednej z trzech powodów:

Nienormalny wykonanie stan został synchronicznie wykrywane przez Maszyna wirtualna Java. Takie warunki powstają ponieważ:

ocena wyrazem narusza normalne semantyki języka, takiego jak podzielić całkowitej przez zero, jak podsumowano w §15.6

błąd występuje w załadunku lub łącząca część program (§12.2, §12.3)

pewne ograniczenia na zasobu przekroczeniu takich jak użycie zbyt dużej ilości pamięci

+1

Hmm .. Chociaż mówimy, że program Java działa w środowisku wykonawczym, w końcu działa na procesorze. Dodatkowo dodałem parametr "-Xcomp", co oznacza, że ​​JIT musi kompilować każdą metodę w instrukcje lokalnego procesora. Co więcej, oba programy działają na Linuksie, więc spodziewam się, że oba podniosą sygnał SIGFPE. – zhangfaen

+0

To prawda, ale jeśli się nie mylę, takie rzeczy są sprawdzane przez środowisko wykonawcze przed wykonaniem. – MByD

+0

sprawdzanie dzielnika dla 0 przed wykonaniem jest bardzo kosztowne, nie sądzę, że VM to robi. – zhangfaen

2

Możliwe, że maszyna wirtualna testuje dzielnik dla 0 ręcznie w emulowanym kodzie bajtowym (dla uproszczenia implementacji), ale dla wydajności nadal będzie się przełączać na wykrywanie sygnału SIGFPE w kodzie JIT. Spróbuj umieścić kod podziału we własnym podprogramie i wywołaj go w pętli tysiące razy, aby upewnić się, że zostanie skompilowany.

+0

próbowałem, ale nadal nie ma SIGFPE. IMHO, nie myśl, że VM testuje dzielnik dla 0 za każdym razem, to oczywiście marnuje się. – zhangfaen

+0

@zhangfaen: Spróbuj użyć trybu '-XX: + PrintOptoAssembly'. Zobacz: http://stackoverflow.com/questions/1551781/how-can--see-the-code-that-hotspot-generates- po -optymaniu – Boann

+0

To nie jest odpowiedź. –

0

w Java jest Termin wyjątkiem jest skrótem Wyrażenie „przypadku wyjątkowego”.
Wyjątkiem jest zdarzenie, które występuje podczas wykonywania programu, które zakłóca normalny przepływ instrukcji programu. Po wystąpieniu błędu w metodzie metoda tworzy obiekt i przekazuje go do systemu wykonawczego.Obiekt, zwany obiektem wyjątku, zawiera informacje o błędzie, w tym jego typ i stan programu po wystąpieniu błędu. Utworzenie obiektu wyjątku i przekazanie go do systemu wykonawczego nazywa się wyrzucaniem wyjątku.
jeśli u złapać ten wyjątek następnie u może robić, co chce zrobić u po połowu wyjątek w przeciwnym razie jest uchwyt domyślnie obsługi wykazujących informacje o błędzie i zakończyć program ...
dzielenie przez zero w java zrobić jak ArithmeticException

Exception in thread "main" java.lang.ArithmeticException:/by zero 
4

Oczywiście jest to ponieważ JVM ma coś takiego w swoim kodzie:

#include <stdio.h> 
#include <signal.h> 
#include <stdlib.h> 

void fpe_handler(int signum) { 
    printf("JVM throws an ArithmeticException here...\n"); 
    exit (1); 
} 

int main() { 
    int a = 5; 
    int b = 0; 
    signal(SIGFPE, fpe_handler); 
    printf("%d\n", a/b); 
    return 0; 
} 

również JVM działa wiele wątków (patrz clone() w dzienniku powyżej lub zrobić ps -eLf, gdy działa java), tak aby wyjście strace było po prostu niekompletne.

Jeśli trochę więcej szczegółów, nieobsługiwany SIGFPE wskazuje na błąd w programie, w którym wystąpił. A jeśli java byłaby zabijana przez SIGFPE, wskazywałaby, że błąd występuje w JVM, ale nie w twojej aplikacji, działającej wewnątrz JVM.

+0

fajne, wielkie dzięki! – zhangfaen

Powiązane problemy