GNU/Linux >> Belajar Linux >  >> Linux

Mengizinkan Ctrl-C untuk menginterupsi ekstensi-C python

Ada cara alternatif untuk mengatasi masalah ini jika Anda tidak ingin Ekstensi C Anda (atau ctypes DLL) diikat ke Python, seperti kasus di mana Anda ingin membuat pustaka C dengan binding dalam berbagai bahasa, Anda harus mengizinkan Ekstensi C untuk berjalan dalam waktu lama, dan Anda dapat memodifikasi Ekstensi C:

Sertakan header sinyal di Ekstensi C.

#include <signal.h>

Buat typedef penangan sinyal di Ekstensi C.

typedef void (*sighandler_t)(int);

Tambahkan penangan sinyal di ekstensi C yang akan melakukan tindakan yang diperlukan untuk menghentikan kode yang berjalan lama (setel tanda stop, dll.), dan simpan penangan sinyal Python yang ada.

sighandler_t old_sig_int_handler = signal(SIGINT, your_sig_handler);
sighandler_t old_sig_term_handler = signal(SIGTERM, your_sig_handler);

Pulihkan penangan sinyal yang ada setiap kali ekstensi C kembali. Langkah ini memastikan penangan sinyal Python diterapkan kembali.

signal(SIGINT, old_sig_int_handler);
signal(SIGTERM, old_sig_term_handler);

Jika kode yang berjalan lama terputus (bendera, dll.), kembalikan kontrol ke Python dengan kode kembali yang menunjukkan nomor sinyal.

return SIGINT;

Dengan Python, kirim sinyal yang diterima di ekstensi C.

import os
import signal

status = c_extension.run()

if status in [signal.SIGINT, signal.SIGTERM]:
    os.kill(os.getpid(), status)

Python akan melakukan tindakan yang Anda harapkan, seperti memunculkan KeyboardInterrupt untuk SIGINT.


Namun, Ctrl-C tampaknya tidak berpengaruh apa pun

Ctrl-C di shell mengirim SIGINT ke grup proses latar depan. python saat menerima sinyal menetapkan bendera dalam kode C. Jika ekstensi C Anda berjalan di utas utama, maka tidak ada penangan sinyal Python yang akan dijalankan (dan karena itu Anda tidak akan melihat KeyboardInterrupt pengecualian pada Ctrl-C ) kecuali Anda memanggil PyErr_CheckSignals() yang memeriksa flag (artinya:seharusnya tidak memperlambat Anda) dan menjalankan penangan sinyal Python jika perlu atau jika simulasi Anda mengizinkan kode Python untuk dieksekusi (mis., jika simulasi menggunakan callback Python). Berikut adalah contoh kode modul ekstensi untuk CPython yang dibuat menggunakan pybind11 yang disarankan oleh @Matt:

PYBIND11_MODULE(example, m)
{
    m.def("long running_func", []()
    {
        for (;;) {
            if (PyErr_CheckSignals() != 0)
                throw py::error_already_set();
            // Long running iteration
        }
    });
}

Jika ekstensi berjalan di utas latar maka itu cukup untuk melepaskan GIL (untuk memungkinkan kode Python berjalan di utas utama yang memungkinkan penangan sinyal untuk berjalan). PyErr_CheckSignals() selalu mengembalikan 0 di utas latar belakang.

Terkait:Cython, Python, dan KeybordInterrupt terhapus


Python memiliki penangan sinyal yang terpasang di SIGINT yang hanya menetapkan bendera yang diperiksa oleh loop penerjemah utama. Agar penangan ini berfungsi dengan baik, juru bahasa Python harus menjalankan kode Python.

Anda memiliki beberapa opsi yang tersedia untuk Anda:

  1. Gunakan Py_BEGIN_ALLOW_THREADS /Py_END_ALLOW_THREADS untuk melepaskan GIL di sekitar kode ekstensi C Anda. Anda tidak dapat menggunakan fungsi Python apa pun saat tidak memegang GIL, tetapi kode Python (dan kode C lainnya) dapat berjalan bersamaan dengan utas C Anda (multithreading sebenarnya). Utas Python terpisah dapat dijalankan bersama ekstensi C dan menangkap sinyal Ctrl+C.
  2. Siapkan SIGINT Anda sendiri handler dan panggil handler sinyal asli (Python). SIGINT Anda handler kemudian dapat melakukan apa pun yang diperlukan untuk membatalkan kode ekstensi C dan mengembalikan kontrol ke juru bahasa Python.

Tidak elegan, tetapi satu-satunya pendekatan yang saya temukan juga mengganggu panggilan pustaka eksternal di C++ dan mematikan semua proses anak yang sedang berjalan.

#include <csignal>
#include <pybind11/pybind11.h>

void catch_signals() {
  auto handler = [](int code) { throw std::runtime_error("SIGNAL " + std::to_string(code)); };
  signal(SIGINT, handler);
  signal(SIGTERM, handler);
  signal(SIGKILL, handler);
}

PYBIND11_MODULE(example, m)
{
    m.def("some_func", []()
    {
        catch_signals();
        // ...
    });
}
import sys

from example import some_func

try:
    some_func()
except RuntimeError as e:
    if "SIGNAL" in str(e):
        code = int(str(e).rsplit(" ", 1)[1])
        sys.exit(128 + code)
    raise

Linux
  1. Mengonversi kode Python 2 Anda ke Python 3

  2. Cara:Pemrograman Socket dengan Python

  3. Instal python-novaclient di Windows

  1. Siapkan Python di IIS 7.5

  2. Pertanyaan virtualenv python

  3. Apa cara yang benar untuk membuat aplikasi PyQt saya berhenti saat dimatikan dari konsol (Ctrl-C)?

  1. Saat Mengetik Ctrl-c Di Terminal, Mengapa Pekerjaan Latar Depan Tidak Dihentikan Sampai Selesai??

  2. Python - Jebak semua sinyal

  3. Tidak dapat mematikan skrip Python dengan Ctrl-C