Mencegah penyebaran SIGINT ke Proses Induk

10

Mempertimbangkan skenario di mana program Induk (bisa berupa program C ++ atau Skrip Shell) mengeksekusi skrip shell Anak, ketika kami menekan Control + C (atau karakter apa pun yang dikonfigurasikan menjadi karakter INTR) ketika Skrip Shell Anak mengeksekusi, SIGINT dikirim ke semua proses dalam grup proses latar depan. Ini termasuk proses induk.

Sumber: POSIX.1-2008 XBD bagian 11.1.9

Apakah ada cara untuk mengesampingkan perilaku default ini? Bahwa Proses ANAK saja menangani SIGNAL tanpa itu menyebar ke induknya?

Referensi: Post Overflow Post - Proses Induk tidak Menyelesaikan ketika Anak Diinterupsi (TRAP INT)

Guddu
sumber

Jawaban:

11

(Terinspirasi oleh jawaban Gilles)

Dengan ISIGset flag, satu-satunya cara agar Childskrip bisa mendapatkan SIGINTtanpa induknya SIGINTadalah dengan skrip itu berada dalam grup prosesnya sendiri. Ini dapat dilakukan dengan set -mopsi.

Jika Anda mengaktifkan -mopsi di Childskrip shell, itu akan melakukan kontrol pekerjaan tanpa menjadi interaktif. Ini akan menyebabkannya menjalankan hal-hal dalam kelompok proses yang terpisah, mencegah orang tua menerima SIGINTketika INTRkarakter dibaca.

Berikut adalah deskripsi -mopsi POSIX :

-mOpsi ini harus didukung jika implementasi mendukung opsi Utilitas Portabilitas Pengguna. Semua pekerjaan harus dijalankan dalam kelompok proses mereka sendiri. Segera sebelum shell mengeluarkan prompt setelah penyelesaian pekerjaan latar belakang, sebuah pesan yang melaporkan status keluar dari pekerjaan latar belakang harus ditulis ke kesalahan standar. Jika pekerjaan latar depan berhenti, shell harus menulis pesan ke kesalahan standar untuk efek itu, diformat seperti yang dijelaskan oleh utilitas pekerjaan. Selain itu, jika pekerjaan berubah status selain keluar (misalnya, jika berhenti untuk input atau output atau dihentikan oleh sinyal SIGSTOP), shell harus menulis pesan yang sama segera sebelum menulis prompt berikutnya. Opsi ini diaktifkan secara default untuk shell interaktif.

The -mpilihan adalah mirip dengan -i, tetapi tidak mengubah perilaku shell hampir sebanyak -idilakukannya.

Contoh:

  • yang Parentnaskah:

    #!/bin/sh
    
    trap 'echo "PARENT: caught SIGINT; exiting"; exit 1' INT
    
    echo "PARENT: pid=$$"
    echo "PARENT: Spawning child..."
    ./Child
    echo "PARENT: child returned"
    echo "PARENT: exiting normally"
  • yang Childnaskah:

    #!/bin/sh -m
    #         ^^        
    # notice the -m option above!
    
    trap 'echo "CHILD: caught SIGINT; exiting"; exit 1' INT
    
    echo "CHILD: pid=$$"
    echo "CHILD: hit enter to exit"
    read foo
    echo "CHILD: exiting normally"

Inilah yang terjadi ketika Anda menekan Control+ Cketika Childsedang menunggu input:

$ ./Parent
PARENT: pid=12233
PARENT: Spawning child...
CHILD: pid=12234
CHILD: hit enter to exit
^CCHILD: caught SIGINT; exiting
PARENT: child returned
PARENT: exiting normally

Perhatikan bagaimana SIGINTpenangan orang tua tidak pernah dieksekusi.

Atau, jika Anda lebih suka memodifikasi Parentbukan Child, Anda dapat melakukan ini:

  • yang Parentnaskah:

    #!/bin/sh
    
    trap 'echo "PARENT: caught SIGINT; exiting"; exit 1' INT
    
    echo "PARENT: pid=$$"
    echo "PARENT: Spawning child..."
    sh -m ./Child  # or 'sh -m -c ./Child' if Child isn't a shell script
    echo "PARENT: child returned"
    echo "PARENT: exiting normally"
  • yang Childnaskah (normal, tidak perlu untuk -m):

    #!/bin/sh
    
    trap 'echo "CHILD: caught SIGINT; exiting"; exit 1' INT
    
    echo "CHILD: pid=$$"
    echo "CHILD: hit enter to exit"
    read foo
    echo "CHILD: exiting normally"

Ide alternatif

  1. Ubah proses lain dalam grup proses latar depan untuk mengabaikan SIGINTselama Child. Ini tidak menjawab pertanyaan Anda, tetapi mungkin memberi Anda apa yang Anda inginkan.
  2. Ubah Childke:
    1. Gunakan stty -guntuk membuat cadangan pengaturan terminal saat ini.
    2. Jalankan stty -isigtidak menghasilkan sinyal dengan INTR, QUIT, dan SUSPkarakter.
    3. Di latar belakang, baca input terminal dan kirim sendiri sinyal yang sesuai (mis. Jalankan kill -QUIT 0ketika Control+ \dibaca, kill -INT $$ketika Control+ Cdibaca). Ini bukan hal sepele, dan mungkin tidak mungkin untuk membuatnya bekerja dengan lancar jika Childskrip atau apa pun yang dijalankan dimaksudkan untuk bersifat interaktif.
    4. Kembalikan pengaturan terminal sebelum keluar (idealnya dari jebakan aktif EXIT).
  3. Sama seperti # 2 kecuali daripada menjalankan stty -isig, tunggu pengguna untuk menekan Enteratau kunci non-khusus lainnya sebelum membunuh Child.
  4. Tulis setpgidutilitas Anda sendiri dalam C, Python, Perl, dll. Yang dapat Anda gunakan untuk menelepon setpgid(). Berikut ini adalah implementasi C mentah:

    #define _XOPEN_SOURCE 700
    #include <unistd.h>
    #include <signal.h>
    
    int
    main(int argc, char *argv[])
    {
        // todo: add error checking
        void (*backup)(int);
        setpgid(0, 0);
        backup = signal(SIGTTOU, SIG_IGN);
        tcsetpgrp(0, getpid());
        signal(SIGTTOU, backup);
        execvp(argv[1], argv + 1);
        return 1;
    }

    Contoh penggunaan dari Child:

    #!/bin/sh
    
    [ "${DID_SETPGID}" = true ] || {
        # restart self after calling setpgid(0, 0)
        exec env DID_SETPGID=true setpgid "$0" "$@"
        # exec failed if control reached this point
        exit 1
    }
    unset DID_SETPGID
    
    # do stuff here
Richard Hansen
sumber
4

Seperti bab yang Anda kutip dari POSIX menjelaskan, SIGINT dikirim ke seluruh grup proses latar depan. Oleh karena itu, untuk menghindari pembunuhan induk program, atur agar program berjalan dalam grup prosesnya sendiri.

Shell tidak memberikan akses setpgrpmelalui konstruksi sintaksis atau builtin, tetapi ada cara tidak langsung untuk mencapainya, yaitu dengan menjalankan shell secara interaktif. (Terima kasih kepada Stéphane Gimenez untuk triknya.)

ksh -ic '
  … the part that needs to be interruptible without bothering the parent …
'
Gilles 'SANGAT berhenti menjadi jahat'
sumber
+1 untuk ide pintar, meskipun berhati-hatilah bahwa perilaku shell berubah ketika itu adalah shell interaktif (setidaknya shell POSIX tidak; saya tidak terbiasa dengan detailnya ksh). Contoh: ${ENV}bersumber, shell tidak akan segera keluar ketika menemukan kesalahan, SIGQUITdan SIGTERMdiabaikan.
Richard Hansen
1

Nah, dari pertanyaan Stack Overflow Anda direferensikan dengan jelas menyatakan bahwa orang tua perlu dikonfigurasi untuk menangani sinyal.

Kedua, referensi POSIX dengan jelas menyatakan bahwa "Jika ISIG diatur, karakter INTR akan dibuang ketika diproses".

Jadi itu dua opsi. Yang ketiga adalah menjalankan anak dalam kelompok prosesnya sendiri.

bahamat
sumber
Terimakasih atas tanggapan Anda. Saya juga perlu mencari cara agar pengguna keluar dari skrip. Apakah ada cara untuk mengatur ISIG dan pada saat yang sama memungkinkan untuk beberapa kombinasi Kunci CONTRL (CTRL-Q mungkin) untuk keluar dari skrip shell tanpa mengirim sinyal ke orang tua dan juga? Juga, bisakah Anda memberi tahu saya bagaimana saya bisa menjalankan anak dalam kelompok proses yang berbeda dari orang tuanya?
Guddu