Tôi đã nhầm lẫn lúc đầu bởi câu trả lời của jancheta, cho đến khi tôi phát hiện ra rằng mục đích của siglongjmp
là để bỏ chặn tín hiệu nhận được trong mặt nạ tín hiệu, trước khi thực hiện bước nhảy. Tín hiệu bị chặn ở đầu vào của trình xử lý tín hiệu để trình xử lý không tự ngắt. Chúng tôi không muốn để lại tín hiệu bị chặn khi chúng tôi tiếp tục thực hiện bình thường và đó là lý do chúng tôi sử dụng siglongjmp
thay vì longjmp
. AIUI, đây chỉ là viết tắt, chúng tôi cũng có thể gọi sigprocmask
theo sau là longjmp
, mà dường như là những gì glibc đang làm trong siglongjmp
.
Tôi nghĩ có thể không an toàn khi thực hiện một bước nhảy vì readline()
gọi malloc
và free
. Nếu tín hiệu được nhận trong khi một số chức năng không đồng bộ-tín hiệu-không an toàn như malloc
hoặc free
đang sửa đổi trạng thái toàn cầu, một số tham nhũng có thể xảy ra nếu chúng tôi sau đó nhảy ra khỏi trình xử lý tín hiệu. Nhưng Readline cài đặt các trình xử lý tín hiệu của riêng nó, điều này rất cẩn thận về điều này. Họ chỉ cần đặt cờ và xuất cảnh; khi thư viện Readline được kiểm soát một lần nữa (thường là sau khi một cuộc gọi bị gián đoạn 'read()' gọi nó là RL_CHECK_SIGNALS()
mà sau đó chuyển tiếp bất kỳ tín hiệu đang chờ xử lý nào đến ứng dụng khách bằng cách sử dụng kill()
. Vì vậy, an toàn khi sử dụng siglongjmp()
để thoát khỏi trình xử lý tín hiệu cho tín hiệu gián đoạn cuộc gọi đến readline()
- tín hiệu được đảm bảo không được nhận trong một chức năng không đồng bộ-tín hiệu-không an toàn.
Trên thực tế, đó không phải là hoàn toàn đúng bởi vì có một vài cuộc gọi đến malloc()
và free()
trong rl_set_prompt()
, mà readline()
cuộc gọi ngay trước khi rl_set_signals()
. Tôi tự hỏi nếu lệnh gọi này nên được thay đổi. Trong mọi trường hợp, xác suất của điều kiện chủng tộc là rất mỏng.
Tôi đã xem mã nguồn Bash và dường như nó nhảy ra khỏi bộ xử lý SIGINT của nó.
Một giao diện Readline khác mà bạn có thể sử dụng là giao diện gọi lại. Điều đó được sử dụng bởi các ứng dụng như Python hoặc R cần phải lắng nghe trên nhiều bộ mô tả tập tin cùng một lúc, ví dụ để cho biết nếu một cửa sổ lô đang được thay đổi kích cỡ trong khi giao diện dòng lệnh đang hoạt động. Họ sẽ làm điều này trong một vòng lặp select()
.
Dưới đây là một thông điệp từ Chet Ramey đó đưa ra một số ý tưởng về những gì phải làm để có được hành vi Bash giống như khi nhận SIGINT trong giao diện callback:
https://lists.gnu.org/archive/html/bug-readline/2016-04/msg00071.html
Các thông điệp gợi ý rằng bạn làm điều gì đó như này:
rl_free_line_state();
rl_cleanup_after_signal();
RL_UNSETSTATE(RL_STATE_ISEARCH|RL_STATE_NSEARCH|RL_STATE_VIMOTION|RL_STATE_NUMERICARG|RL_STATE_MULTIKEY);
rl_line_buffer[rl_point = rl_end = rl_mark = 0] = 0;
printf("\n");
Khi SIGINT của bạn được nhận, bạn có thể thiết lập một lá cờ, và sau đó kiểm tra các cờ trong select()
vòng lặp của bạn - kể từ khi cuộc gọi select()
sẽ bị gián đoạn bằng tín hiệu với errno==EINTR
. Nếu bạn thấy rằng cờ đã được đặt, hãy thực thi mã trên.
Ý kiến của tôi là Readline sẽ chạy một phần giống như đoạn trên trong mã xử lý SIGINT của riêng nó. Hiện nay nó ít nhiều thực hiện chỉ hai dòng đầu tiên, đó là lý do tại sao các công cụ như macro tìm kiếm gia tăng và bàn phím bị hủy bởi^C, nhưng dòng này không bị xóa.
Một áp phích khác cho biết "Gọi rl_clear_signals()", vẫn khiến tôi bối rối. Tôi đã không thử nó nhưng tôi không thấy làm thế nào nó sẽ thực hiện bất cứ điều gì cho rằng (1) xử lý tín hiệu Readline chuyển tiếp tín hiệu cho bạn anyway, và (2) readline()
cài đặt xử lý tín hiệu khi nhập cảnh (và xóa chúng khi nó thoát), vì vậy chúng sẽ không hoạt động bình thường bên ngoài mã Readline.
Bạn không thể gọi một cách an toàn 'printf' từ trình xử lý tín hiệu. – pat