Ctrl-C को C में पकड़ें


158

कैसे पकड़ता है C में Ctrl+ C?


5
C में संकेतों को पकड़ने जैसी कोई चीज नहीं है .... या कम से कम इसलिए मैंने सोचा कि जब तक मैं C99 मानक नहीं पढ़ता। यह पता चला है कि सी में परिभाषित सिग्नल हैंडलिंग है, लेकिन किसी भी विशिष्ट सिग्नल या सिग्नल को उत्पन्न करने के लिए Ctrl-C अनिवार्य नहीं है। आपके प्लेटफॉर्म के आधार पर, यह असंभव हो सकता है।
जेरेमीपप

1
सिग्नल हैंडलिंग ज्यादातर कार्यान्वयन निर्भर है। * Nix प्लेटफार्मों पर, <signal.h> का उपयोग करें, और यदि आप OSX पर हैं, तो आप चीजों को और भी आसान बनाने के लिए GCD का लाभ उठा सकते हैं।
डायलन लुकेस

जवाबों:


205

एक सिग्नल हैंडलर के साथ।

यहां एक सरल उदाहरण का boolउपयोग किया गया है main():

#include <signal.h>

static volatile int keepRunning = 1;

void intHandler(int dummy) {
    keepRunning = 0;
}

// ...

int main(void) {

   signal(SIGINT, intHandler);

   while (keepRunning) { 
      // ...

जून 2017 में संपादित करें : जिनके लिए यह चिंता का विषय हो सकता है, विशेष रूप से इस जवाब को संपादित करने के लिए एक अतुलनीय आग्रह के साथ। देखिए, मैंने यह उत्तर सात साल पहले लिखा था। हां, भाषा के मानक बदल जाते हैं। यदि आपको वास्तव में दुनिया को बेहतर बनाना है, तो कृपया अपना नया उत्तर जोड़ें, लेकिन जैसा मेरा है उसे छोड़ दें। जैसा कि उत्तर में मेरा नाम है, मैं अपने शब्दों को भी शामिल करना पसंद करूंगा। धन्यवाद।


2
आइए उल्लेख करते हैं कि हमें इस कार्य के लिए #include <signal.h> की आवश्यकता है!
क्रिस्तिअल्म

13
स्थिर यह bool volatile keepRunning = true;100% सुरक्षित होना चाहिए । संकलक keepRunningएक रजिस्टर में कैश करने के लिए स्वतंत्र है और वाष्पशील इसे रोक देगा। जब लूप कम से कम एक गैर-इनलाइन फ़ंक्शन को कॉल करता है, तो व्यवहार में यह सबसे अधिक संभावना है कि यह अस्थिर कीवर्ड के बिना भी काम कर सकता है।
जोहान्स ओवरमैन

2
@DirkEddelbuettel मैं सही खड़ा हूं, मैंने सोचा कि मेरे सुधार आपके मूल इरादों को और अधिक प्रतिबिंबित करेंगे, क्षमा करें यदि ऐसा नहीं हुआ। वैसे भी, अधिक से अधिक मुद्दा यह है कि, चूंकि आपका उत्तर पर्याप्त रूप से सामान्य होने की कोशिश करता है, और क्योंकि IMO को एक स्निपेट प्रदान करना चाहिए जो अतुल्यकालिक अवरोधों के तहत भी काम कर रहा है: मैं या तो वहां उपयोग करेगा sig_atomic_tया atomic_boolटाइप करेगा । मुझे बस यही याद आया। अब, चूंकि हम बात कर रहे हैं: क्या आप मुझे अपना नवीनतम संपादन रोलबैक करना चाहेंगे? कोई कठिन भावना नहीं है, यह आपके दृष्टिकोण से पूरी तरह से समझा जा सकता है :)
पीटर वारो

2
यह बहुत बेहतर है!
डिर्क एडल्डबुलेटेल

2
@JohannesOvermann ऐसा नहीं है कि मैं नाइटपिक करना चाहता हूं, लेकिन कड़ाई से बोल रहा हूं, इससे कोई फर्क नहीं पड़ता कि कोड किसी गैर-इनलाइन फ़ंक्शन को कॉल करता है या नहीं, क्योंकि केवल एक मेमोरी बैरियर को पार करने पर कंपाइलर को कैश्ड मान पर भरोसा करने की अनुमति नहीं है। म्यूटेक्स को लॉक करना / अनलॉक करना ऐसी मेमोरी बैरियर होगा। चूंकि चर स्थिर है और इस प्रकार वर्तमान फ़ाइल के बाहर दिखाई नहीं देता है, संकलक यह मानने के लिए सुरक्षित है कि कोई फ़ंक्शन कभी भी इसका मान नहीं बदल सकता, जब तक कि आप फ़ंक्शन के लिए उस चर का संदर्भ न दें। इसलिए किसी भी मामले में अस्थिरता की दृढ़ता से सलाह दी जाती है।
मिकी जूल

47

यहा जांचिये:

नोट: जाहिर है, यह एक सरल उदाहरण है जिसमें बताया गया है कि कैसे एक CtrlCहैंडलर स्थापित किया जाए , लेकिन हमेशा की तरह ऐसे नियम हैं जिन्हें किसी और चीज को तोड़ने के लिए पालन करने की आवश्यकता है। कृपया नीचे टिप्पणी पढ़ें।

ऊपर से नमूना कोड:

#include  <stdio.h>
#include  <signal.h>
#include  <stdlib.h>

void     INThandler(int);

int  main(void)
{
     signal(SIGINT, INThandler);
     while (1)
          pause();
     return 0;
}

void  INThandler(int sig)
{
     char  c;

     signal(sig, SIG_IGN);
     printf("OUCH, did you hit Ctrl-C?\n"
            "Do you really want to quit? [y/n] ");
     c = getchar();
     if (c == 'y' || c == 'Y')
          exit(0);
     else
          signal(SIGINT, INThandler);
     getchar(); // Get new line character
}

2
@ डायरिक सहमत, int mainएक उचित बात है, लेकिन gccअन्य कंपाइलर्स 1990 के दशक से इसे सही ढंग से चलने वाले कार्यक्रमों के लिए संकलित करते हैं। यहाँ बहुत अच्छी तरह से समझाया गया है: eskimo.com/~scs/readings/voidmain.960823.html - यह मूल रूप से एक "सुविधा" है, मैं इसे इस तरह से लेता हूं।
icyrock.com

1
@ icyrock.com: सभी बहुत सच (सी में शून्य मुख्य () के बारे में), लेकिन सार्वजनिक रूप से पोस्ट करते समय यह संभवत: बहस से बचने के लिए पूरी तरह से इंट मेन का उपयोग करके होता है () ऐसा न हो कि यह मुख्य बिंदु से विचलित हो।
क्लिफोर्ड

21
इसमें बहुत बड़ा दोष है। आप सिग्नल हैंडलर की सामग्री के भीतर सुरक्षित रूप से प्रिंटफ का उपयोग नहीं कर सकते। यह अतुल्यकालिक सिग्नल सुरक्षा का उल्लंघन है। इसका कारण यह है कि प्रिंटफ रीवेंटेंट नहीं है। यदि Ctrl-C दबाए जाने पर प्रोग्राम प्रिंटफ का उपयोग करने के बीच में था तो क्या होता है, और आपका सिग्नल हैंडलर उसी समय उपयोग करना शुरू कर देता है? संकेत: यह संभवतः टूट जाएगा। इस संदर्भ में लिखने और लिखने के लिए उपयोग समान हैं।
डायलन लुकेस

2
@ icyrock.com: सिग्नल हैंडलर में कुछ भी जटिल करने से सिरदर्द होने लगता है। विशेषकर io प्रणाली का उपयोग करना।
मार्टिन यॉर्क

2
@stacker धन्यवाद - मुझे लगता है कि यह अंत में इसके लायक है। यदि कोई भविष्य में इस कोड को ठोकर मारता है, तो सवाल के विषय की परवाह किए बिना, जितना संभव हो उतना बेहतर होना चाहिए।
icyrock.com

30

यूएन * एक्स प्लेटफार्मों के बारे में परिशिष्ट।

signal(2)GNU / Linux पर मैन पेज के अनुसार , का व्यवहार signalउतना पोर्टेबल नहीं है sigaction:

संकेत का व्यवहार () UNIX संस्करणों में भिन्न होता है, और लिनक्स के विभिन्न संस्करणों में ऐतिहासिक रूप से भिन्न होता है। इसके उपयोग से बचें: इसके बजाय सिगनेशन (2) का उपयोग करें।

सिस्टम V पर, सिस्टम ने सिग्नल के आगे इंस्टेंसेस की डिलीवरी को ब्लॉक नहीं किया और सिग्नल की डिलीवरी हैंडलर को डिफ़ॉल्ट पर रीसेट कर देगी। बीएसडी में शब्दार्थ बदल गया।

इसके sigactionबजाय डिर्क एडल्डबुलेट द्वारा पिछले उत्तर की निम्नलिखित भिन्नता है signal:

#include <signal.h>
#include <stdlib.h>

static bool keepRunning = true;

void intHandler(int) {
    keepRunning = false;
}

int main(int argc, char *argv[]) {
    struct sigaction act;
    act.sa_handler = intHandler;
    sigaction(SIGINT, &act, NULL);

    while (keepRunning) {
        // main loop
    }
}


14

या आप टर्मिनल को कच्चे मोड में रख सकते हैं, जैसे:

struct termios term;

term.c_iflag |= IGNBRK;
term.c_iflag &= ~(INLCR | ICRNL | IXON | IXOFF);
term.c_lflag &= ~(ICANON | ECHO | ECHOK | ECHOE | ECHONL | ISIG | IEXTEN);
term.c_cc[VMIN] = 1;
term.c_cc[VTIME] = 0;
tcsetattr(fileno(stdin), TCSANOW, &term);

अब इसका उपयोग करके Ctrl+ Cकीस्ट्रोक्स को पढ़ना संभव होना चाहिए fgetc(stdin)। यह प्रयोग करने से सावधान रहें, क्योंकि आप Ctrl+ Z, Ctrl+ Q, Ctrl+ S, आदि नहीं कर सकते हैं , जैसे कि सामान्य रूप से या तो।


11

एक जाल स्थापित करें (आप एक हैंडलर के साथ कई संकेतों को फंसा सकते हैं):

संकेत (SIGQUIT, my_handler);
संकेत (SIGINT, my_handler);

हालांकि आप चाहते हैं कि सिग्नल को संभालें, लेकिन सीमाओं और वॉच के बारे में जानें:

शून्य my_handler (int sig)
{
  / * आपका कोड यहाँ। * /
}

8

@Peter Varo ने डिर्क के उत्तर को अपडेट किया, लेकिन डिर्क ने परिवर्तन को अस्वीकार कर दिया। यहाँ पीटर द्वारा नया उत्तर दिया गया है:

हालांकि उपरोक्त स्निपेट एक सही है उदाहरण के लिए, व्यक्ति को यदि संभव हो तो बाद के मानकों द्वारा प्रदान किए गए अधिक आधुनिक प्रकार और गारंटी का उपयोग करना चाहिए। इसलिए, यहां उन लोगों के लिए एक सुरक्षित और आधुनिक विकल्प है जो इसके लिए मांग कर रहे हैं तथा अनुरूपण कार्यान्वयन:

#include <signal.h>
#include <stdlib.h>
#include <stdio.h>

static volatile sig_atomic_t keep_running = 1;

static void sig_handler(int _)
{
    (void)_;
    keep_running = 0;
}

int main(void)
{
    signal(SIGINT, sig_handler);

    while (keep_running)
        puts("Still running...");

    puts("Stopped by signal `SIGINT'");
    return EXIT_SUCCESS;
}

C11 मानक: 7.14§2 शीर्ष लेख <signal.h>एक प्रकार की घोषणा करता है ... sig_atomic_tजो कि (संभवतः वाष्पशील-योग्य) पूर्णांक प्रकार की वस्तु है जिसे परमाणु इकाई के रूप में एक्सेस किया जा सकता है, यहां तक ​​कि अतुल्यकालिक अवरोधों की उपस्थिति में भी।

इसके अलावा:

C11 मानक: 7.14.1.1§5 यदि संकेत abortया raiseफ़ंक्शन को कॉल करने के परिणामस्वरूप अन्य के अलावा होता है , तो व्यवहार अनिर्धारित होता है यदि सिग्नल हैंडलर किसी भी ऑब्जेक्ट को संदर्भित करता है staticया थ्रेड स्टोरेज अवधि के साथ जो लॉक-फ्री परमाणु ऑब्जेक्ट नहीं है के रूप में घोषित एक वस्तु के लिए एक मूल्य प्रदान करके volatile sig_atomic_t...


मैं नहीं मानता (void)_;... इसका उद्देश्य क्या है? क्या ऐसा है कि संकलक एक अप्रयुक्त चर के बारे में चेतावनी नहीं देता है?
लुइस पाउलो


मुझे वह उत्तर मिल गया था और उस लिंक को यहाँ पेस्ट करना था! धन्यवाद :)
लुइस पाउलो

5

मौजूदा उत्तरों के बारे में, ध्यान दें कि सिग्नल हैंडलिंग प्लेटफॉर्म पर निर्भर है। Win32 उदाहरण के लिए POSIX ऑपरेटिंग सिस्टम की तुलना में बहुत कम संकेतों को संभालता है; यहाँ देखें । जबकि Win32 में संकेत में SIGINT घोषित है। प्रलेखन में नोट देखें, जो बताता है कि यह वह नहीं करेगा जो आप उम्मीद कर सकते हैं।


3
#include<stdio.h>
#include<signal.h>
#include<unistd.h>

void sig_handler(int signo)
{
  if (signo == SIGINT)
    printf("received SIGINT\n");
}

int main(void)
{
  if (signal(SIGINT, sig_handler) == SIG_ERR)
  printf("\ncan't catch SIGINT\n");
  // A long long wait so that we can easily issue a signal to this process
  while(1) 
    sleep(1);
  return 0;
}

फ़ंक्शन sig_handler जाँचता है कि पारित किए गए तर्क का मूल्य SIGINT के बराबर है, तो प्रिंटफ़ निष्पादित किया गया है।


सिग्नल हैंडलर में I / O रूटीन को कभी न बुलाएं।
jwdonahue

2

यह सिर्फ बाहर निकलने से पहले प्रिंट करें।

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

void sigint_handler(int);

int  main(void)
{
    signal(SIGINT, sigint_handler);

     while (1){
         pause();   
     }         
    return 0;
}

 void sigint_handler(int sig)
{
    /*do something*/
    printf("killing process %d\n",getpid());
    exit(0);
}

2
सिग्नल हैंडलर में I / O को कभी न बुलाएं।
jwdonahue
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.