Kode keluar default saat proses dihentikan?

54

Ketika sebuah proses terbunuh dengan sinyal yang dapat ditangani seperti SIGINTatau SIGTERMtetapi tidak menangani sinyal, apa yang akan menjadi kode keluar dari proses?

Bagaimana dengan sinyal yang tidak dapat ditangani seperti apa SIGKILL?

Dari apa yang dapat saya katakan, mematikan proses dengan SIGINTkemungkinan hasil dalam kode keluar 130, tetapi apakah itu berbeda dengan implementasi kernel atau shell?

$ cat myScript
#!/bin/bash
sleep 5
$ ./myScript
<ctrl-c here>
$ echo $?
130

Saya tidak yakin bagaimana saya akan menguji sinyal lain ...

$ ./myScript &
$ killall myScript
$ echo $?
0  # duh, that's the exit code of killall
$ killall -9 myScript
$ echo $?
0  # same problem
Cory Klein
sumber
1
killall myScriptkarya Anda , maka kembalinya killall (dan bukan skrip!) adalah 0. Anda bisa menempatkan kill -x $$[x menjadi nomor sinyal, dan $$ biasanya diperluas oleh shell ke PID skrip itu (berfungsi di sh, bash, ...)] di dalam skrip dan kemudian tes apa inti keluarnya.
Olivier Dulac
Superset: stackoverflow.com/questions/1101957/…
Ciro Santilli 新疆 改造 中心 法轮功 六四 事件
komentar tentang semi-pertanyaan: Jangan letakkan myScript di latar belakang. (hilangkan &). Kirim sinyal dari proses shell lain (di terminal lain), maka Anda dapat menggunakan $?setelah myScript telah berakhir.
MattBianco

Jawaban:

61

Proses dapat memanggil panggilan _exit()sistem (di Linux, lihat juga exit_group()) dengan argumen integer untuk melaporkan kode keluar ke induknya. Meskipun ini adalah bilangan bulat, hanya 8 bit paling tidak signifikan yang tersedia untuk induknya (kecuali saat menggunakan waitid()atau menangani SIGCHLD di induk untuk mengambil kode itu , meskipun tidak di Linux).

Orang tua biasanya akan melakukan wait()atau waitpid()untuk mendapatkan status anak mereka sebagai bilangan bulat (meskipun waitid()dengan semantik yang agak berbeda dapat digunakan juga).

Di Linux dan sebagian besar Unices, jika proses dihentikan secara normal, bit 8 hingga 15 dari nomor status itu akan berisi kode keluar sebagaimana diteruskan ke exit(). Jika tidak, maka 7 bit paling tidak signifikan (0 hingga 6) akan berisi nomor sinyal dan bit 7 akan ditetapkan jika sebuah inti dibuang.

perl's $?misalnya mengandung nomor sebagai set oleh waitpid():

$ perl -e 'system q(kill $$); printf "%04x\n", $?'
000f # killed by signal 15
$ perl -e 'system q(kill -ILL $$); printf "%04x\n", $?'
0084 # killed by signal 4 and core dumped
$ perl -e 'system q(exit $((0xabc))); printf "%04x\n", $?'
bc00 # terminated normally, 0xbc the lowest 8 bits of the status

Shell seperti Bourne juga membuat status keluar dari perintah run terakhir dalam $?variabel mereka sendiri . Namun, itu tidak berisi secara langsung nomor yang dikembalikan oleh waitpid(), tetapi suatu transformasi di atasnya, dan itu berbeda antara shell.

Apa yang umum di antara semua shell adalah yang $?berisi 8 bit terendah dari kode keluar (nomor yang diteruskan exit()) jika proses diakhiri secara normal.

Perbedaannya adalah ketika proses diakhiri oleh sinyal. Dalam semua kasus, dan itu diperlukan oleh POSIX, angkanya akan lebih besar dari 128. POSIX tidak menentukan berapa nilainya. Namun dalam praktiknya, di semua cangkang mirip Bourne yang saya tahu, 7 bit terendah $?akan berisi nomor sinyal. Tapi, di mana nnomor sinyalnya,

  • dalam abu, zsh, pdksh, bash, shell Bourne, $?adalah 128 + n. Apa artinya itu dalam shell itu, jika Anda mendapatkan $?dari 129, Anda tidak tahu apakah itu karena proses keluar dengan exit(129)atau apakah itu dibunuh oleh sinyal 1( HUPpada kebanyakan sistem). Tetapi alasannya adalah bahwa shell, ketika mereka keluar sendiri, secara default mengembalikan status keluar dari perintah yang terakhir keluar. Dengan memastikan $?tidak pernah lebih besar dari 255, yang memungkinkan untuk memiliki status keluar yang konsisten:

    $ bash -c 'sh -c "kill \$\$"; printf "%x\n" "$?"'
    bash: line 1: 16720 Terminated              sh -c "kill \$\$"
    8f # 128 + 15
    $ bash -c 'sh -c "kill \$\$"; exit'; printf '%x\n' "$?"
    bash: line 1: 16726 Terminated              sh -c "kill \$\$"
    8f # here that 0x8f is from a exit(143) done by bash. Though it's
       # not from a killed process, that does tell us that probably
       # something was killed by a SIGTERM
    
  • ksh93, $?adalah 256 + n. Itu berarti bahwa dari nilai $?Anda dapat membedakan antara proses terbunuh dan tidak terbunuh. Versi yang lebih baru ksh, saat keluar, jika $?lebih besar dari 255, membunuh dirinya sendiri dengan sinyal yang sama agar dapat melaporkan status keluar yang sama ke induknya. Sementara itu terdengar seperti ide yang bagus, itu berarti bahwa kshakan menghasilkan dump core tambahan (berpotensi menimpa yang lain) jika proses itu mati oleh sinyal pembangkit core:

    $ ksh -c 'sh -c "kill \$\$"; printf "%x\n" "$?"'
    ksh: 16828: Terminated
    10f # 256 + 15
    $ ksh -c 'sh -c "kill -ILL \$\$"; exit'; printf '%x\n' "$?"
    ksh: 16816: Illegal instruction(coredump)
    Illegal instruction(coredump)
    104 # 256 + 15, ksh did indeed kill itself so as to report the same
        # exit status as sh. Older versions of `ksh93` would have returned
        # 4 instead.
    

    Di mana Anda bahkan bisa mengatakan ada bug adalah yang ksh93membunuh dirinya sendiri meskipun $?berasal dari fungsi yang return 257dilakukan:

    $ ksh -c 'f() { return "$1"; }; f 257; exit'
    zsh: hangup     ksh -c 'f() { return "$1"; }; f 257; exit'
    # ksh kills itself with a SIGHUP so as to report a 257 exit status
    # to its parent
    
  • yash. yashmenawarkan kompromi. Ia kembali 256 + 128 + n. Itu berarti kita juga dapat membedakan antara proses yang terbunuh dan proses yang diakhiri dengan benar. Dan setelah keluar, ia akan melaporkan 128 + ntanpa harus bunuh diri dan efek samping yang ditimbulkannya.

    $ yash -c 'sh -c "kill \$\$"; printf "%x\n" "$?"'
    18f # 256 + 128 + 15
    $ yash -c 'sh -c "kill \$\$"; exit'; printf '%x\n' "$?"
    8f  # that's from a exit(143), yash was not killed
    

Untuk mendapatkan sinyal dari nilai $?, cara portabel adalah dengan menggunakan kill -l:

$ /bin/kill 0
Terminated
$ kill -l "$?"
TERM

(untuk portabilitas, Anda tidak boleh menggunakan nomor sinyal, hanya nama sinyal)

Di front non-Bourne:

  • csh/ tcshdan fishsama dengan shell Bourne kecuali bahwa statusnya $statusbukan $?(catatan yang zshjuga mengatur $statuskompatibilitas dengan csh(selain $?)).
  • rc: status keluar $statusjuga ada, tetapi ketika terbunuh oleh suatu sinyal, variabel itu berisi nama sinyal (seperti sigtermatau sigill+corejika suatu inti dihasilkan) alih-alih sebuah angka, yang merupakan bukti lain dari desain shell yang bagus. .
  • es. status keluar bukan variabel. Jika Anda peduli, Anda menjalankan perintah sebagai:

    status = <={cmd}
    

    yang akan mengembalikan nomor atau sigtermatau sigsegv+coresuka di rc.

Mungkin untuk kelengkapan, kita harus menyebutkan array zsh's $pipestatusdan bash' $PIPESTATUSyang berisi status keluar dari komponen pipa terakhir.

Dan juga untuk kelengkapan, ketika datang ke fungsi shell dan file sumber, secara default fungsi kembali dengan status keluar dari menjalankan perintah terakhir, tetapi juga dapat mengatur status pengembalian secara eksplisit dengan returnbuiltin. Dan kita melihat beberapa perbedaan di sini:

  • bashdan mksh(sejak R41, regresi ^ Wchange tampaknya diperkenalkan secara sengaja ) akan memotong angka (positif atau negatif) menjadi 8 bit. Jadi misalnya return 1234akan diatur $?ke 210, return -- -1akan diatur $?ke 255.
  • zshdan pdksh(dan turunan selain mksh) memungkinkan bilangan bulat desimal 32 bit yang ditandatangani (-2 31 hingga 2 31 -1) (dan memotong jumlahnya menjadi 32 bit ).
  • ashdan yashizinkan bilangan bulat positif dari 0 hingga 2 31 -1 dan kembalikan kesalahan untuk nomor apa pun di luar itu.
  • ksh93untuk return 0ke return 320set $?seperti, tapi untuk hal lain, truncate untuk 8 bit. Hati-hati seperti yang telah disebutkan bahwa mengembalikan nomor antara 256 dan 320 dapat menyebabkan kshbunuh diri saat keluar.
  • rcdan esmemungkinkan mengembalikan apa pun daftar genap.

Perhatikan juga bahwa beberapa shell juga menggunakan nilai khusus dari $?/ $statusuntuk melaporkan beberapa kondisi kesalahan yang bukan status keluar dari suatu proses, seperti 127atau 126untuk perintah yang tidak ditemukan atau tidak dapat dieksekusi (atau kesalahan sintaksis dalam file bersumber) ...

Stéphane Chazelas
sumber
1
an exit code to their parentdan to get the *status* of their child. Anda telah menambahkan penekanan pada "status". Apakah exit codedan *status*sama? Kasus ya, apa asal memiliki dua nama? Kasus tidak sama, dapatkah Anda memberikan definisi / referensi status?
n611x007
2
Ada 3 angka di sini. The kode keluar : nomor diteruskan ke exit(). The status keluar : jumlah yang diperoleh waitpid()yang meliputi kode keluar, nomor sinyal dan apakah ada inti dibuang. Dan nomor yang disediakan beberapa shell di salah satu variabel khusus mereka ( $?, $status) yang merupakan transformasi dari status keluar sedemikian rupa sehingga tidak mengandung kode keluar jika ada pemutusan normal, tetapi juga membawa informasi sinyal jika prosesnya terbunuh (yang itu juga umumnya disebut status keluar ). Itu semua dijelaskan dalam jawaban saya.
Stéphane Chazelas
1
Saya mengerti terima kasih! Saya sangat menghargai catatan eksplisit tentang perbedaan di sini. Ungkapan-ungkapan tentang pintu keluar ini digunakan secara bergantian di beberapa tempat, layak untuk dilakukan. Apakah varian variabel shell bahkan memiliki nama (umum)? Jadi saya sarankan untuk membersihkannya secara eksplisit sebelum membahas detail pada shell. Saya sarankan memasukkan penjelasan (dari komentar Anda) setelah paragraf pertama atau kedua Anda.
n611x007
1
Bisakah Anda menunjukkan kutipan POSIX yang mengatakan tentang 7 bit pertama yang menjadi sinyal? Yang dapat saya temukan hanyalah > 128bagian: "Status keluar dari perintah yang diakhiri karena menerima sinyal harus dilaporkan lebih besar dari 128." pubs.opengroup.org/onlinepubs/9699919799/utilities/...
Ciro Santilli 新疆 改造 中心 法轮功 六四 事件
1
@cuonglm, saya tidak berpikir itu tersedia untuk umum di mana saja melalui HTTP, Anda masih bisa mendapatkannya dari gmane melalui NNTP. Cari id pesan [email protected](mulai 2015-05-06) atauXref: news.gmane.org gmane.comp.standards.posix.austin.general:10726
Stéphane Chazelas
23

Ketika suatu proses keluar, ia mengembalikan nilai integer ke sistem operasi. Pada sebagian besar varian unix, nilai ini diambil modulo 256: semuanya kecuali bit orde rendah diabaikan. Status proses anak dikembalikan ke induknya melalui bilangan bulat 16-bit

  • bit 0–6 (7 bit orde rendah) adalah nomor sinyal yang digunakan untuk mematikan proses, atau 0 jika proses keluar secara normal;
  • bit 7 diatur jika proses itu dibunuh oleh sinyal dan inti dibuang;
  • bit 8–15 adalah kode keluar proses jika proses keluar secara normal, atau 0 jika proses terbunuh oleh suatu sinyal.

Status dikembalikan oleh waitsystem call atau salah satu saudara kandungnya. POSIX tidak menentukan pengkodean yang tepat dari status keluar dan nomor sinyal; hanya menyediakan

  • cara untuk mengetahui apakah status keluar sesuai dengan sinyal atau dengan keluar normal;
  • cara untuk mengakses kode keluar, jika proses keluar secara normal;
  • cara untuk mengakses nomor sinyal, jika proses itu mati oleh sinyal.

Sebenarnya, tidak ada kode keluar ketika suatu proses terbunuh oleh suatu sinyal: yang ada adalah status keluar .

Dalam skrip shell, status keluar dari suatu perintah dilaporkan melalui variabel khusus $?. Variabel ini menyandikan status keluar dengan cara yang ambigu:

  • Jika proses keluar secara normal maka $?status keluarnya.
  • Jika proses itu mati oleh sinyal maka $?128 ditambah nomor sinyal pada kebanyakan sistem. POSIX hanya mandat yang $?lebih besar dari 128 dalam hal ini; ksh93 menambahkan 256, bukan 128. Saya belum pernah melihat varian unix yang melakukan apa pun selain menambahkan konstanta ke nomor sinyal.

Dengan demikian dalam skrip shell Anda tidak dapat mengatakan secara meyakinkan apakah suatu perintah dibunuh oleh sinyal atau keluar dengan kode status lebih besar dari 128, kecuali dengan ksh93. Sangat jarang bagi program untuk keluar dengan kode status lebih besar dari 128, sebagian karena programmer menghindarinya karena $?ambiguitas.

SIGINT adalah sinyal 2 pada sebagian besar varian unix, sehingga $?128 + 2 = 130 untuk proses yang dibunuh oleh SIGINT. Anda akan melihat 129 untuk SIGHUP, 137 untuk SIGKILL, dll.

Gilles 'SANGAT berhenti menjadi jahat'
sumber
Banyak kata yang lebih baik dan lebih tepat daripada saya bahkan jika itu pada dasarnya mengatakan hal yang sama. Anda mungkin ingin mengklarifikasi itu $?hanya untuk cangkang mirip Bourne. Lihat juga yashuntuk perilaku yang berbeda (tapi masih POSIX). Juga sesuai POSIX + XSI (Unix), a kill -2 "$pid"akan mengirim SIGINT ke proses, tetapi nomor sinyal yang sebenarnya mungkin bukan 2, jadi $? belum tentu 128 + 2 (atau 256 + 2 atau 384 + 2), meskipun kill -l "$?"akan kembali INT, itulah sebabnya saya menyarankan portabilitas untuk tidak merujuk ke nomor itu sendiri.
Stéphane Chazelas
8

Itu tergantung pada cangkang Anda. Dari bash(1)halaman manual, bagian SHELL GRAMMAR , subbagian Perintah Sederhana :

Nilai kembali dari perintah sederhana adalah [...] 128+ n jika perintah diakhiri oleh sinyal n .

Karena SIGINTpada sistem Anda adalah sinyal nomor 2, nilai kembali adalah 130 ketika dijalankan di bawah Bash.

Ignacio Vazquez-Abrams
sumber
1
Bagaimana di dunia ini Anda menemukan ini, atau bahkan tahu ke mana harus mencari? Aku tunduk pada kejeniusanmu.
Cory Klein
1
@CoryKlein: Pengalaman, kebanyakan. Oh, dan Anda mungkin menginginkan signal(7)halaman manual juga.
Ignacio Vazquez-Abrams
hal-hal keren; Anda tahu apakah saya sudah memasukkan file dalam C dengan konstanta itu secara kebetulan? +1
Rui F Ribeiro
@CoryKlein Mengapa Anda tidak memilih ini sebagai jawaban yang benar?
Rui F Ribeiro
3

Tampaknya menjadi tempat yang tepat untuk menyebutkan bahwa SVr4 memperkenalkan waitid () pada tahun 1989, tetapi tidak ada program penting yang tampaknya menggunakannya sejauh ini. waitid () memungkinkan untuk mengambil 32 bit penuh dari kode exit ().

Sekitar 2 bulan yang lalu, saya menulis ulang bagian tunggu / tugas kontrol dari Bourne Shell untuk menggunakan waitid () alih-alih waitpid (). Ini dilakukan untuk menghapus batasan yang menutupi kode keluar dengan 0xFF.

Antarmuka waitid () jauh lebih bersih daripada implementasi wait () sebelumnya kecuali panggilan cwait () dari UNOS dari 1980.

Anda mungkin tertarik untuk membaca halaman manual di:

http://schillix.sourceforge.net/man/man1/bosh.1.html

dan periksa bagian "Substitusi Parameter" yang saat ini menatap halaman 8.

Variabel baru .sh. * Telah diperkenalkan untuk antarmuka waitid (). Antarmuka ini tidak lagi memiliki arti ambigu untuk angka yang dikenal dengan $? dan membuat antarmuka lebih mudah.

Perhatikan bahwa Anda perlu memiliki waitid () yang sesuai dengan POSIX untuk dapat menggunakan fitur ini, jadi Mac OS X dan Linux saat ini tidak menawarkan ini, tetapi waitid () ditiru pada panggilan waitpid (), jadi pada platform non-POSIX Anda hanya akan mendapatkan 8 bit dari kode keluar.

Singkatnya: .sh.status adalah kode keluar numerik, .sh.code adalah alasan keluar numerik.

Untuk portabilitas yang lebih baik, ada: .sh.codename untuk versi tekstual dari alasan keluar, misalnya "DUMPED" dan .sh.termsig, nama tunggal untuk sinyal yang menghentikan proses.

Untuk penggunaan yang lebih baik, ada dua nilai .sh.codename yang tidak terkait keluar: "NOEXEC" dan "NOTFOUND" yang digunakan ketika suatu program tidak dapat diluncurkan sama sekali.

FreeBSD memperbaiki bug waitid () mereka dalam waktu 20 jam setelah laporan saya, Linux belum memulai dengan perbaikan mereka. Saya harap 26 tahun setelah memperkenalkan fitur ini yang ada di POSIX sekarang, semua OS akan segera mendukungnya.

schily
sumber
Jawaban terkait adalah unix.stackexchange.com/a/453432/5132 .
JdeBP