2013-05-30 3 views
10

पर एक नया शीघ्र हो जाओ करने के बाद, ReadLine का उपयोग कर:ReadLine: मैं निम्नलिखित के समान कोड मिला SIGINT

#include <errno.h> 
#include <error.h> 
#include <getopt.h> 
#include <stdio.h> 
#include <stdlib.h> 
#include <unistd.h> 
#include <signal.h> 
#include <readline/readline.h> 
#include <readline/history.h> 

void handle_signals(int signo) { 
    if (signo == SIGINT) { 
    printf("You pressed Ctrl+C\n"); 
    } 
} 

int main (int argc, char **argv) 
{ 
    //printf("path is: %s\n", path_string); 
    char * input; 
    char * shell_prompt = "i-shell> "; 
    if (signal(SIGINT, handle_signals) == SIG_ERR) { 
    printf("failed to register interrupts with kernel\n"); 
    } 

    //set up custom completer and associated data strucutres 
    setup_readline(); 

    while (1) 
    { 
    input = readline(shell_prompt); 
    if (!input) 
     break; 
    add_history(input); 

    //do something with the code 
    execute_command(input); 

    } 
    return 0; 
} 

मुझे मिल गया है यह SIGINT (यानी उपयोगकर्ता Ctrl+C दबाने) अवरोधन करने की स्थापना की है, इसलिए मैं बता सकते हैं कि सिग्नल हैंडलर handle_signals() काम कर रहा है। हालांकि, जब नियंत्रण readline() पर वापस आता है, तो यह इनपुट से पहले उपयोग की जाने वाली टेक्स्ट की उसी पंक्ति का उपयोग कर रहा है। मैं क्या करना चाहता हूं टेक्स्ट की वर्तमान पंक्ति को "रद्द" करने के लिए रीडलाइन के लिए है और मुझे एक नई लाइन दें, जो कि बाश खोल की तरह है। ऐसा कुछ:

i-shell> bad_command^C 
i-shell> _ 

इसे काम करने का कोई मौका? मैंने पढ़ने वाली मेलिंग सूची पर कुछ longjmp(2) का उपयोग करके उल्लेख किया है, लेकिन यह वास्तव में एक अच्छा विचार प्रतीत नहीं होता है।

उत्तर

5

आप longjmp का उपयोग करने के लिए अपनी सोच की रेखा में सही हैं। लेकिन क्योंकि longjmp सिग्नल हैंडलर में होगा, आपको sigsetjmp/siglongjmp का उपयोग करने की आवश्यकता है।

एक त्वरित उदाहरण एक आधार के रूप अपने कोड का उपयोग के रूप में:

#include <setjmp.h> 
#include <errno.h> 
#include <error.h> 
#include <getopt.h> 
#include <stdio.h> 
#include <stdlib.h> 
#include <unistd.h> 
#include <signal.h> 
#include <readline/readline.h> 
#include <readline/history.h> 

sigjmp_buf ctrlc_buf; 

void handle_signals(int signo) { 
    if (signo == SIGINT) { 
    printf("You pressed Ctrl+C\n"); 
    siglongjmp(ctrlc_buf, 1); 
    } 
} 

int main (int argc, char **argv) 
{ 
    //printf("path is: %s\n", path_string); 
    char * input; 
    char * shell_prompt = "i-shell> "; 
    if (signal(SIGINT, handle_signals) == SIG_ERR) { 
    printf("failed to register interrupts with kernel\n"); 
    } 

    //set up custom completer and associated data strucutres 
    setup_readline(); 

    while (sigsetjmp(ctrlc_buf, 1) != 0); 

    while (1) 
    { 
    input = readline(shell_prompt); 
    if (!input) 
     break; 
    add_history(input); 

    //do something with the code 
    execute_command(input); 

    } 
    return 0; 
} 

siglongjmp 0 के अलावा किसी अन्य मान देता है (इस मामले में एक 1 में) इसलिए जब पाश कॉल sigsetjmp फिर से (एक सफल वापसी मान sigsetjmp को sigsetjmp का 0 है) और फिर फिर से रीडलाइन कॉल करेगा।

यह भी rl_catch_signals = 1 सेट और फिर फोन rl_set_signals() ताकि ReadLine संकेत से निपटने के लिए किसी भी चर यह करने की जरूरत को साफ अपने कार्यक्रम है जहाँ आप तो ReadLine दूसरी बार कॉल करने के लिए वापस कूद जाएगा संकेत पार करने से पहले करने के लिए सहायक हो सकता है।

+1

आप सिग्नल हैंडलर से सुरक्षित रूप से 'printf' को कॉल नहीं कर सकते हैं। – pat

4

मुझे पहले जांचेता के जवाब से उलझन में था, जब तक मुझे पता चला कि siglongjmp का उद्देश्य सिग्नल मास्क में प्राप्त सिग्नल को अनब्लॉक करना है, कूदने से पहले। संकेत सिग्नल हैंडलर के प्रवेश पर अवरुद्ध है ताकि हैंडलर खुद को बाधित न करे। हम सामान्य निष्पादन को फिर से शुरू करते समय सिग्नल अवरुद्ध नहीं करना चाहते हैं, और यही कारण है कि हम longjmp के बजाय siglongjmp का उपयोग करते हैं। एआईयूआई, यह सिर्फ शॉर्टेंड है, हम sigprocmask पर भी longjmp पर कॉल कर सकते हैं, ऐसा लगता है कि siglongjmp में ग्लिब क्या कर रहा है।

मैंने सोचा कि यह कूदने के लिए असुरक्षित हो सकता है क्योंकि readline()malloc और free पर कॉल करता है। अगर सिग्नल प्राप्त होता है जबकि कुछ एसिंक-सिग्नल-असुरक्षित फ़ंक्शन malloc या free वैश्विक स्थिति को संशोधित कर रहा है, तो कुछ भ्रष्टाचार का परिणाम हो सकता है यदि हम सिग्नल हैंडलर से बाहर निकलते हैं। लेकिन रीडलाइन अपने सिग्नल हैंडलर स्थापित करती है जो इस बारे में सावधान हैं। वे सिर्फ एक झंडा सेट और बाहर निकलें; जब रीडलाइन लाइब्रेरी को फिर से नियंत्रण मिल जाता है (आमतौर पर बाधित 'पढ़ने()' कॉल के बाद) यह RL_CHECK_SIGNALS() पर कॉल करता है जो उसके बाद kill() का उपयोग कर क्लाइंट एप्लिकेशन को लंबित सिग्नल अग्रेषित करता है। इसलिए सिग्नल हैंडलर से बाहर निकलने के लिए siglongjmp() का उपयोग करना सुरक्षित है, जिसने readline() पर कॉल को बाधित कर दिया - संकेत को एसिंक-सिग्नल-असुरक्षित फ़ंक्शन के दौरान प्राप्त नहीं किया गया है।

वास्तव में, जो पूरी तरह सच नहीं है क्योंकि वहाँ rl_set_prompt() भीतर malloc() और free() करने के लिए कुछ कॉल है, जो readline() कॉल बस से पहले rl_set_signals() हैं। मुझे आश्चर्य है कि क्या यह कॉलिंग ऑर्डर बदला जाना चाहिए। किसी भी मामले में दौड़ की स्थिति की संभावना बहुत पतली है।

मैंने बैश स्रोत कोड को देखा और ऐसा लगता है कि यह अपने सिगिनट हैंडलर से बाहर निकलता है।

एक अन्य रीडलाइन इंटरफ़ेस जिसका आप उपयोग कर सकते हैं वह कॉलबैक इंटरफ़ेस है। इसका उपयोग पायथन या आर जैसे अनुप्रयोगों द्वारा किया जाता है, जिसे एक साथ कई फ़ाइल डिस्क्रिप्टरों पर सुनने की आवश्यकता होती है, उदाहरण के लिए यह बताएं कि कमांड लाइन इंटरफ़ेस सक्रिय होने पर प्लॉट विंडो का आकार बदल रहा है या नहीं। वे इसे select() लूप में करेंगे।

यहाँ जो क्या कॉलबैक इंटरफ़ेस में SIGINT प्राप्त होने पर बैश की तरह व्यवहार प्राप्त करने के लिए ऐसा करने के लिए की कुछ विचार देता है चेट रामे से एक संदेश है:

https://lists.gnu.org/archive/html/bug-readline/2016-04/msg00071.html

संदेशों पता चलता है कि आप की तरह कुछ करना इस:

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"); 

जब आपके SIGINT प्राप्त होता है, यदि आप एक ध्वज सेट कर सकते हैं, और बाद में अपने select() पाश में झंडा जाँच - के बाद से select() कॉल बाधित हो जाएगी errno==EINTR के साथ सिग्नल द्वारा। यदि आपको लगता है कि ध्वज सेट किया गया है, तो उपरोक्त कोड निष्पादित करें।

मेरी राय यह है कि रीडलाइन को अपने स्वयं के सिगिनट हैंडलिंग कोड में उपरोक्त खंड की तरह कुछ चलाना चाहिए। वर्तमान में यह केवल पहले दो पंक्तियों को कम या कम निष्पादित करता है, यही कारण है कि वृद्धिशील-खोज और कीबोर्ड मैक्रोज़ जैसी सामग्री^सी द्वारा रद्द कर दी जाती है, लेकिन रेखा को साफ़ नहीं किया जाता है।

एक और पोस्टर ने कहा "कॉल rl_clear_signals()", जो अभी भी मुझे भ्रमित करता है। मैंने कोशिश नहीं की है, लेकिन मुझे नहीं पता कि यह कुछ भी कैसे पूरा करेगा (1) रीडलाइन के सिग्नल हैंडलर आपको संकेत भेजते हैं, और (2) readline() प्रविष्टि पर सिग्नल हैंडलर इंस्टॉल करता है (और जब यह निकलता है तो उन्हें साफ़ करता है), इसलिए वे आमतौर पर रीडलाइन कोड के बाहर सक्रिय नहीं होंगे।

1

एक कूद बनाना मुझे हैकी और त्रुटि-प्रवण लगता है। खोल कार्यान्वयन मैं इस परिवर्तन को जोड़ने के लिए इस परिवर्तन को जोड़ने की अनुमति नहीं दे रहा था।

सौभाग्य से, readline में एक स्पष्ट, वैकल्पिक समाधान है। मेरे SIGINT हैंडलर इस तरह दिखता है:

static void 
int_handler(int status) { 
    printf("\n"); // Move to a new line 
    rl_on_new_line(); // Regenerate the prompt on a newline 
    rl_replace_line("", 0); // Clear the previous text 
    rl_redisplay(); 
} 

यह कहीं और कोई अन्य अतिरिक्त कोड लिया इस काम कर पाने के लिए - कोई वैश्विक चर, कोई सेटिंग कूदता है।

संबंधित मुद्दे