सिग्नल हैंडलर में प्रिंटफ का उपयोग करने से कैसे बचें?


86

चूँकि printfयह रीक्रेंट नहीं है, इसलिए सिग्नल हैंडलर में इसका उपयोग करना सुरक्षित नहीं माना जाता है। लेकिन मैंने बहुत सारे उदाहरण कोड देखे हैं जो printfइस तरह से उपयोग करते हैं।

तो मेरा सवाल यह है: हमें printfसिग्नल हैंडलर के उपयोग से बचने की आवश्यकता कब है , और क्या कोई अनुशंसित प्रतिस्थापन है?


12
आपके शीर्षक में प्रश्न का एक सरल और बहुत उपयोगी उत्तर नहीं: printfउस सिग्नल हैंडर में उस कॉल को देखें ? इसे मिटाओ।
कीथ थॉम्पसन

6
हेलो यू हाओ! मुझे लगता है कि आपको पढ़ने के लिए लिंक बहुत दिलचस्प लगेगा। "सुरक्षित सिग्नल से निपटने के लिए रीन्ट्रेंट फ़ंक्शंस का उपयोग करें" मैंने इसे इतने लंबे समय के बाद पढ़ा है, मैं यहां आपके साथ कृत्रिम साझा करना चाहूंगा। उम्मीद करता हु आपको आनंद मिला हो।
ब्रिजेश चौहान

जवाबों:


58

आप कुछ ध्वज चर का उपयोग कर सकते हैं, उस ध्वज को संकेत हैंडलर के अंदर सेट कर सकते हैं, और printf()सामान्य ऑपरेशन के दौरान मुख्य () या कार्यक्रम के अन्य भाग में उस ध्वज कॉल फ़ंक्शन के आधार पर ।

सभी कार्यों को कॉल करना सुरक्षित नहीं है, जैसे कि printfसिग्नल हैंडलर के भीतर से। एक उपयोगी तकनीक एक सिग्नल हैंडलर का उपयोग करने के लिए एक सेट करने के लिए है flagऔर फिर flag मुख्य कार्यक्रम से जांच करें और यदि आवश्यक हो तो एक संदेश प्रिंट करें।

नीचे दिए गए उदाहरण में सूचना, सिग्नल हैंडलर डिंग () ने एक ध्वज alarm_firedको 1 पर सेट किया जैसा कि SIGALRM पकड़ा गया और मुख्य फ़ंक्शन alarm_firedमान में सशर्त रूप से प्रिंटफ को सही ढंग से कॉल करने के लिए जांच की गई।

static int alarm_fired = 0;
void ding(int sig) // can be called asynchronously
{
  alarm_fired = 1; // set flag
}
int main()
{
    pid_t pid;
    printf("alarm application starting\n");
    pid = fork();
    switch(pid) {
        case -1:
            /* Failure */
            perror("fork failed");
            exit(1);
        case 0:
            /* child */
            sleep(5);
            kill(getppid(), SIGALRM);
            exit(0);
    }
    /* if we get here we are the parent process */
    printf("waiting for alarm to go off\n");
    (void) signal(SIGALRM, ding);
    pause();
    if (alarm_fired)  // check flag to call printf
      printf("Ding!\n");
    printf("done\n");
    exit(0);
}

संदर्भ: लिनक्स प्रोग्रामिंग, 4 वें संस्करण की शुरुआत , इस पुस्तक में वास्तव में आपके कोड को समझाया गया है (आप क्या चाहते हैं), अध्याय 11: प्रक्रियाएं और संकेत, पृष्ठ 484

इसके अतिरिक्त, आपको हैंडलर फ़ंक्शन लिखने में विशेष ध्यान रखने की आवश्यकता है क्योंकि उन्हें अतुल्यकालिक रूप से कहा जा सकता है। यही है, अप्रत्याशित रूप से कार्यक्रम में किसी भी समय एक हैंडलर को बुलाया जा सकता है। यदि दो सिग्नल बहुत कम अंतराल के दौरान आते हैं, तो एक हैंडलर दूसरे के भीतर चल सकता है। और यह घोषित करने के लिए बेहतर अभ्यास माना जाता है volatile sigatomic_t, इस प्रकार को हमेशा एटोमिक रूप से एक्सेस किया जाता है, एक चर तक पहुंच को बाधित करने के बारे में अनिश्चितता से बचें। (पढ़ें: परमाणु डेटा एक्सेस और विस्तार के विस्तार के लिए सिग्नल हैंडलिंग )।

सिग्नल हैंडलर्स को परिभाषित करना पढ़ें : सिग्नल हैंडलर फ़ंक्शन को लिखना सीखना है जो signal()या sigaction()कार्यों के साथ स्थापित किया जा सकता है। मैन्युअल पेज
में अधिकृत कार्यों की सूची , सिग्नल हैंडलर के अंदर इस फ़ंक्शन को कॉल करना सुरक्षित है।


18
यह घोषित करने के लिए बेहतर अभ्यास माना जाता हैvolatile sigatomic_t alarm_fired;
बेसिल स्टारीनेविच 11


1
@GrijeshChauhan: यदि हम उत्पाद के एक कोड में काम कर रहे हैं, तो हम ठहराव फ़ंक्शन को नहीं कह सकते हैं, सिग्नल होने पर प्रवाह कहीं भी हो सकता है, इसलिए उस स्थिति में हम वास्तव में नहीं जानते हैं कि "" (अलार्म_फायर) प्रिंटफ ("डिंग" कहाँ रखना है)! \ n ")," कोड में।
पंकज कुशवाहा

@pankajkushwaha हाँ, आप सही हैं, यह दौड़ की स्थिति से पीड़ित है
ग्रजेश चौहान

@ बृजेशचौहान, दो ऐसी बातें हैं, जिन्हें मैं समझ नहीं पाया। 1. आपको कैसे पता चलेगा कि झंडे की जाँच कब होगी? तो प्रिंट करने के लिए लगभग हर बिंदु पर कोड में कई चेक पॉइंट होंगे। 2. निश्चित रूप से दौड़ की स्थितियां होंगी जहां सिग्नल में पंजीकरण से पहले कॉल किया जा सकता है या चेक प्वाइंट के बाद सिग्नल हो सकता है। मुझे लगता है कि यह केवल कुछ स्थितियों में प्रिंट में मदद करेगा लेकिन समस्या को पूरी तरह से हल नहीं करता है।
दर्शन बी

52

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

सी मानक एक बहुत रूढ़िवादी दृष्टिकोण लेता है कि आप सिग्नल हैंडलर में क्या कर सकते हैं:

आईएसओ / आईईसी 9899: 2011 §7.14.1.1 signalफ़ंक्शन

¶5 यदि सिग्नल abortया raiseफ़ंक्शन को कॉल करने के परिणामस्वरूप सिग्नल के अलावा अन्य व्यवहार होता है, तो सिग्नल हैंडलर स्थिर या थ्रेड स्टोरेज अवधि के साथ किसी भी ऑब्जेक्ट को संदर्भित करता है, जो एक मान असाइन करने के अलावा अन्य लॉक-फ्री परमाणु ऑब्जेक्ट नहीं है। किसी ऑब्जेक्ट के रूप में घोषित किया गया है volatile sig_atomic_t, या सिग्नल हैंडलर मानक लाइब्रेरी में किसी भी फ़ंक्शन को abortफ़ंक्शन, _Exitफ़ंक्शन, quick_exitफ़ंक्शन या फ़ंक्शन के अलावा किसी अन्य फ़ंक्शन signalको सिग्नल नंबर के बराबर सिग्नल के साथ कॉल करता है, जो सिग्नल के आह्वान के कारण होता है। हैंडलर। इसके अलावा, अगर इस तरह के कॉल से signalफंक्शन की SIG_ERRवापसी होती है, तो इसका मूल्य errnoअनिश्चित है। 252)

252) यदि कोई संकेत एक एसिंक्रोनस सिग्नल हैंडलर द्वारा उत्पन्न होता है, तो व्यवहार अपरिभाषित है।

POSIX एक बहुत अधिक उदार है कि आप सिग्नल हैंडलर में क्या कर सकते हैं।

POSIX 2008 संस्करण में सिग्नल कॉन्सेप्ट कहता है:

यदि प्रक्रिया बहु-थ्रेडेड है, या यदि प्रक्रिया एकल-थ्रेडेड है और एक सिग्नल हैंडलर को परिणाम के अलावा अन्य द्वारा निष्पादित किया जाता है:

  • प्रक्रिया बुला abort(), raise(), kill(), pthread_kill(), या sigqueue()एक संकेत है कि अवरुद्ध नहीं कर रहा है उत्पन्न करने के लिए

  • लंबित सिग्नल को अनब्लॉक किया जा रहा है और कॉल से पहले डिलीवर किया जा रहा है जो इसे वापस करता है

व्यवहार अनिर्धारित है यदि सिग्नल हैंडलर errnoस्थिर वस्तु की अवधि के अलावा किसी वस्तु को संदर्भित करता है volatile sig_atomic_t, जैसा कि घोषित वस्तु के मूल्य को निर्दिष्ट करने के अलावा , या यदि सिग्नल हैंडलर इस मानक में परिभाषित किसी भी फ़ंक्शन को सूचीबद्ध कार्यों के अलावा किसी अन्य फ़ंक्शन को कॉल करता है। निम्न तालिका।

निम्न तालिका उन कार्यों के एक समूह को परिभाषित करती है जो async-signal-safe होंगे। इसलिए, सिग्नल-कैचिंग फ़ंक्शंस से, एप्लिकेशन बिना प्रतिबंध के, उन्हें आमंत्रित कर सकते हैं:

_Exit()             fexecve()           posix_trace_event() sigprocmask()
_exit()             fork()              pselect()           sigqueue()
…
fcntl()             pipe()              sigpause()          write()
fdatasync()         poll()              sigpending()

संकेतों के संबंध में उपरोक्त तालिका में नहीं सभी कार्यों को असुरक्षित माना जाता है। संकेतों की उपस्थिति में, POSIX.1-2008 की इस मात्रा द्वारा परिभाषित सभी कार्य एक अपवाद के साथ सिग्नल-कैचिंग फ़ंक्शन द्वारा कॉल किए जाने या बाधित होने पर परिभाषित किए जाएंगे: जब कोई सिग्नल एक असुरक्षित फ़ंक्शन और सिग्नल को बाधित करता है- फंक्शन को पकड़ना एक असुरक्षित फंक्शन कहलाता है, व्यवहार अपरिभाषित होता है।

वे संचालन जो मूल्य प्राप्त करते हैं errnoऔर वे संचालन जो एक मान प्रदान करते हैं, errnoवह async-signal-safe होगा।

जब किसी सिग्नल को एक थ्रेड पर पहुंचाया जाता है, अगर उस सिग्नल की कार्रवाई समाप्ति को निर्दिष्ट करती है, रोकती है, या जारी रखती है, तो पूरी प्रक्रिया क्रमशः समाप्त, बंद या जारी रहेगी।

हालांकि, printf()फ़ंक्शन का परिवार उस सूची से काफी अनुपस्थित है और सिग्नल हैंडलर से सुरक्षित रूप से नहीं बुलाया जा सकता है।

POSIX 2016 अद्यतन शामिल करने के लिए सुरक्षित कार्यों की सूची फैली हुई है, विशेष रूप से, से कार्यों की एक बड़ी संख्या <string.h>है, जो एक विशेष रूप से महत्वपूर्ण है (या एक विशेष रूप से निराशाजनक निरीक्षण किया गया था)। सूची अब है:

_Exit()              getppid()            sendmsg()            tcgetpgrp()
_exit()              getsockname()        sendto()             tcsendbreak()
abort()              getsockopt()         setgid()             tcsetattr()
accept()             getuid()             setpgid()            tcsetpgrp()
access()             htonl()              setsid()             time()
aio_error()          htons()              setsockopt()         timer_getoverrun()
aio_return()         kill()               setuid()             timer_gettime()
aio_suspend()        link()               shutdown()           timer_settime()
alarm()              linkat()             sigaction()          times()
bind()               listen()             sigaddset()          umask()
cfgetispeed()        longjmp()            sigdelset()          uname()
cfgetospeed()        lseek()              sigemptyset()        unlink()
cfsetispeed()        lstat()              sigfillset()         unlinkat()
cfsetospeed()        memccpy()            sigismember()        utime()
chdir()              memchr()             siglongjmp()         utimensat()
chmod()              memcmp()             signal()             utimes()
chown()              memcpy()             sigpause()           wait()
clock_gettime()      memmove()            sigpending()         waitpid()
close()              memset()             sigprocmask()        wcpcpy()
connect()            mkdir()              sigqueue()           wcpncpy()
creat()              mkdirat()            sigset()             wcscat()
dup()                mkfifo()             sigsuspend()         wcschr()
dup2()               mkfifoat()           sleep()              wcscmp()
execl()              mknod()              sockatmark()         wcscpy()
execle()             mknodat()            socket()             wcscspn()
execv()              ntohl()              socketpair()         wcslen()
execve()             ntohs()              stat()               wcsncat()
faccessat()          open()               stpcpy()             wcsncmp()
fchdir()             openat()             stpncpy()            wcsncpy()
fchmod()             pause()              strcat()             wcsnlen()
fchmodat()           pipe()               strchr()             wcspbrk()
fchown()             poll()               strcmp()             wcsrchr()
fchownat()           posix_trace_event()  strcpy()             wcsspn()
fcntl()              pselect()            strcspn()            wcsstr()
fdatasync()          pthread_kill()       strlen()             wcstok()
fexecve()            pthread_self()       strncat()            wmemchr()
ffs()                pthread_sigmask()    strncmp()            wmemcmp()
fork()               raise()              strncpy()            wmemcpy()
fstat()              read()               strnlen()            wmemmove()
fstatat()            readlink()           strpbrk()            wmemset()
fsync()              readlinkat()         strrchr()            write()
ftruncate()          recv()               strspn()
futimens()           recvfrom()           strstr()
getegid()            recvmsg()            strtok_r()
geteuid()            rename()             symlink()
getgid()             renameat()           symlinkat()
getgroups()          rmdir()              tcdrain()
getpeername()        select()             tcflow()
getpgrp()            sem_post()           tcflush()
getpid()             send()               tcgetattr()

नतीजतन, आप या तो एट अल write()द्वारा प्रदान किए गए स्वरूपण समर्थन के बिना उपयोग करते हैं printf(), या आप अपने कोड में उपयुक्त स्थानों पर एक झंडा सेट करना शुरू करते हैं (जिसे आप समय-समय पर परीक्षण करते हैं)। इस तकनीक को बृजेश चौहान द्वारा उत्तर में प्रदर्शित किया गया है ।


मानक सी फ़ंक्शन और सिग्नल सुरक्षा

चकरली एक दिलचस्प सवाल पूछता है , जिसके लिए मेरे पास आंशिक उत्तर से अधिक नहीं है:

अधिकांश स्ट्रिंग फ़ंक्शंस <string.h>या कैरेक्टर क्लास फ़ंक्शंस से कैसे आते हैं <ctype.h>और कई और सी स्टैंडर्ड लाइब्रेरी फ़ंक्शंस उपरोक्त सूची में नहीं हैं? strlen()सिग्नल हैंडलर से असुरक्षित कॉल करने के लिए कार्यान्वयन को जानबूझकर बुराई करने की आवश्यकता होगी ।

में कार्यों के कई लोगों के लिए <string.h>, यह कारण है कि वे घोषित नहीं किए गए थे async संकेत सुरक्षित देखना कठिन है, और मैं इस बात से सहमत था strlen(), एक प्रमुख उदाहरण है के साथ strchr(), strstr()इस तरह के रूप, आदि दूसरी ओर, अन्य कार्यों strtok(), strcoll()और strxfrm()बल्कि जटिल हैं और async-signal सुरक्षित होने की संभावना नहीं है। क्योंकि strtok()कॉल के बीच स्थिति को बनाए रखता है, और सिग्नल हैंडलर आसानी से नहीं बता सकता है कि कोड का कुछ हिस्सा जो उपयोग कर रहा है, strtok()वह गड़बड़ हो जाएगा। strcoll()और strxfrm()कार्यों स्थान के प्रति संवेदनशील डेटा के साथ काम करते हैं, और स्थान लोड हो रहा है राज्य की स्थापना के सभी प्रकार के शामिल है।

से कार्य (मैक्रो) <ctype.h>सभी लोकेल के प्रति संवेदनशील हैं, और इसलिए रूप में एक ही समस्या में पड़ सकते strcoll()हैं और strxfrm()

मुझे यह देखने में मुश्किल है कि जब से गणितीय कार्य <math.h>सुरक्षित नहीं हैं, तब तक यह सुरक्षित नहीं है, जब तक कि यह एक SIGFPE (फ्लोटिंग पॉइंट अपवाद) से प्रभावित नहीं हो सकता है, हालांकि इन दिनों में से एक के बारे में मुझे केवल एक ही बार पूर्णांक के लिए है शून्य से विभाजन। इसी तरह की अनिश्चितता से उत्पन्न होती है <complex.h>, <fenv.h>और <tgmath.h>

उदाहरण के लिए , कुछ कार्यों को <stdlib.h>छूट दी जा सकती है abs()। अन्य विशेष रूप से समस्याग्रस्त हैं: malloc()और परिवार प्रमुख उदाहरण हैं।

POSIX वातावरण में उपयोग किए जाने वाले Standard C (2011) में अन्य हेडर के लिए एक समान मूल्यांकन किया जा सकता है। (मानक C इतना प्रतिबंधक है कि शुद्ध मानक C वातावरण में उनका विश्लेषण करने में कोई रुचि नहीं है।) चिह्नित 'लोकेल-डिपेंडेंट' असुरक्षित हैं क्योंकि हेरफेर करने वाले स्थानों को मेमोरी आवंटन की आवश्यकता हो सकती है, आदि।

  • <assert.h>- शायद सुरक्षित नहीं है
  • <complex.h>- संभवतः सुरक्षित
  • <ctype.h> - असुरक्षित
  • <errno.h> - सुरक्षित
  • <fenv.h>- शायद सुरक्षित नहीं है
  • <float.h> - नहीं भी
  • <inttypes.h> - स्थानीय-संवेदनशील कार्य (असुरक्षित)
  • <iso646.h> - नहीं भी
  • <limits.h> - नहीं भी
  • <locale.h> - स्थानीय-संवेदनशील कार्य (असुरक्षित)
  • <math.h>- संभवतः सुरक्षित
  • <setjmp.h> - असुरक्षित
  • <signal.h> - आवंटित किया गया
  • <stdalign.h> - नहीं भी
  • <stdarg.h> - नहीं भी
  • <stdatomic.h>- संभवतः सुरक्षित, संभवतः सुरक्षित नहीं है
  • <stdbool.h> - नहीं भी
  • <stddef.h> - नहीं भी
  • <stdint.h> - नहीं भी
  • <stdio.h> - असुरक्षित
  • <stdlib.h> - सभी सुरक्षित नहीं हैं (कुछ की अनुमति है; अन्य नहीं हैं)
  • <stdnoreturn.h> - नहीं भी
  • <string.h> - सभी सुरक्षित नहीं हैं
  • <tgmath.h>- संभवतः सुरक्षित
  • <threads.h>- शायद सुरक्षित नहीं है
  • <time.h>- स्थानीय-निर्भर (लेकिन time()स्पष्ट रूप से अनुमति है)
  • <uchar.h> - स्थान-निर्भर
  • <wchar.h> - स्थान-निर्भर
  • <wctype.h> - स्थान-निर्भर

POSIX हेडर का विश्लेषण करना ... इसमें कठिन होगा कि उनमें से बहुत सारे हैं, और कुछ फ़ंक्शन सुरक्षित हो सकते हैं लेकिन कई नहीं होंगे ... लेकिन यह भी सरल है क्योंकि POSIX का कहना है कि कौन से कार्य async-signal सुरक्षित हैं (उनमें से कई नहीं)। ध्यान दें कि शीर्ष लेख <pthread.h>में तीन सुरक्षित कार्य और कई असुरक्षित कार्य हैं।

NB: POSIX वातावरण में C फ़ंक्शंस और हेडर के मूल्यांकन के लगभग सभी अर्ध-शिक्षित अनुमान हैं। यह एक मानक शरीर से एक निश्चित बयान नहीं है।


अधिकांश स्ट्रिंग फ़ंक्शंस <string.h>या कैरेक्टर क्लास फ़ंक्शंस से कैसे आते हैं <ctype.h>और कई और सी स्टैंडर्ड लाइब्रेरी फ़ंक्शंस उपरोक्त सूची में नहीं हैं? strlen()सिग्नल हैंडलर से असुरक्षित कॉल करने के लिए कार्यान्वयन को जानबूझकर बुराई करने की आवश्यकता होगी ।
चकरली

@chqrlie: दिलचस्प सवाल - अपडेट देखें (समझदारी से टिप्पणियों में इसे फिट करने का कोई तरीका नहीं था)।
जोनाथन लेफ्लर

गहराई से विश्लेषण के लिए आपका धन्यवाद। <ctype.h>सामान के संबंध में , यह लोकेल विशिष्ट है और समस्याएं पैदा कर सकता है अगर सिग्नल एक लोकल सेटिंग फ़ंक्शन को बाधित करता है, लेकिन एक बार लोकेल लोड होने के बाद, उनका उपयोग सुरक्षित होना चाहिए। मुझे लगता है, कुछ जटिल स्थितियों में, स्थानीय डेटा को लोड करना वृद्धिशील रूप से किया जा सकता है, इस प्रकार यह <ctype.h>असुरक्षित से कार्य करता है । निष्कर्ष बना हुआ है: जब संदेह है, तो परहेज करें।
चकरली 19

@chqrlie: मैं सहमत हूं कि कहानी का नैतिक होना चाहिए जब संदेह में, संयम रखें । यह एक अच्छा सारांश है।
जोनाथन लेफ्लर 19

13

printfसिग्नल हैंडलर में उपयोग करने से कैसे बचें ?

  1. हमेशा इससे बचें, कहेंगे: बस printf()सिग्नल हैंडलर में उपयोग न करें ।

  2. कम से कम POSIX अनुरूप प्रणाली पर, आप write(STDOUT_FILENO, ...)इसके बजाय उपयोग कर सकते हैं printf()। फ़ॉर्मेट करना आसान नहीं हो सकता है: लिखने या async-safe फ़ंक्शंस का उपयोग करके सिग्नल हैंडलर से int प्रिंट करें


1
अलक का Always avoid it.अर्थ है? बचना है printf()?
ब्रिजेश चौहान

2
@GrijeshChauhan: हाँ, जैसा कि ओपी पूछ रहा था कि printf()सिग्नल हैंडलर में उपयोग करने से कब बचें ।
alk

2बिंदु के लिए Alk +1 , ओपी से पूछें कि सिग्नल हैंडलर में उपयोग करने से कैसे बचें printf()?
ब्रिजेश चौहान

7

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

स्रोत कोड GitHub पर है । यह ओवरलोडिंग द्वारा काम करता है signal/sigaction, फिर अस्थायी रूप PLTसे असुरक्षित कार्यों की प्रविष्टियों को अपहरण कर लेता है; यह असुरक्षित कार्यों को एक आवरण में पुनर्निर्देशित करने का कारण बनता है।


GCC सुविधा अनुरोध: gcc.gnu.org/ml/gcc-help/2012-03/msg00210.html
Ciro Santilli Sant g g g

1

अपने खुद के async-signal-safe snprintf("%dऔर उपयोग को लागू करेंwrite

यह उतना बुरा नहीं है जितना मैंने सोचा था, सी में एक इंट को स्ट्रिंग में कैसे परिवर्तित किया जाए? कई कार्यान्वयन हैं।

चूंकि केवल दो दिलचस्प प्रकार के डेटा हैं जो सिग्नल हैंडलर एक्सेस कर सकते हैं:

  • sig_atomic_t वैश्विक
  • int संकेत तर्क

यह मूल रूप से सभी दिलचस्प उपयोग मामलों को कवर करता है।

तथ्य यह strcpyहै कि सिग्नल सुरक्षित भी चीजों को और बेहतर बनाता है।

अब तक जितनी बार SIGINT मिली Ctrl + C, उसे और सिग्नल आईडी को ट्रिगर करने के लिए प्रिंट के नीचे POSIX प्रोग्राम ।

आप Ctrl + \(SIGQUIT) के साथ कार्यक्रम से बाहर निकल सकते हैं ।

main.c:

#define _XOPEN_SOURCE 700
#include <assert.h>
#include <limits.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>

/* Calculate the minimal buffer size for a given type.
 *
 * Here we overestimate and reserve 8 chars per byte.
 *
 * With this size we could even print a binary string.
 *
 * - +1 for NULL terminator
 * - +1 for '-' sign
 *
 * A tight limit for base 10 can be found at:
 * /programming/8257714/how-to-convert-an-int-to-string-in-c/32871108#32871108
 *
 * TODO: get tight limits for all bases, possibly by looking into
 * glibc's atoi: /programming/190229/where-is-the-itoa-function-in-linux/52127877#52127877
 */
#define ITOA_SAFE_STRLEN(type) sizeof(type) * CHAR_BIT + 2

/* async-signal-safe implementation of integer to string conversion.
 *
 * Null terminates the output string.
 *
 * The input buffer size must be large enough to contain the output,
 * the caller must calculate it properly.
 *
 * @param[out] value  Input integer value to convert.
 * @param[out] result Buffer to output to.
 * @param[in]  base   Base to convert to.
 * @return     Pointer to the end of the written string.
 */
char *itoa_safe(intmax_t value, char *result, int base) {
    intmax_t tmp_value;
    char *ptr, *ptr2, tmp_char;
    if (base < 2 || base > 36) {
        return NULL;
    }

    ptr = result;
    do {
        tmp_value = value;
        value /= base;
        *ptr++ = "ZYXWVUTSRQPONMLKJIHGFEDCBA9876543210123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"[35 + (tmp_value - value * base)];
    } while (value);
    if (tmp_value < 0)
        *ptr++ = '-';
    ptr2 = result;
    result = ptr;
    *ptr-- = '\0';
    while (ptr2 < ptr) {
        tmp_char = *ptr;
        *ptr--= *ptr2;
        *ptr2++ = tmp_char;
    }
    return result;
}

volatile sig_atomic_t global = 0;

void signal_handler(int sig) {
    char key_str[] = "count, sigid: ";
    /* This is exact:
     * - the null after the first int will contain the space
     * - the null after the second int will contain the newline
     */
    char buf[2 * ITOA_SAFE_STRLEN(sig_atomic_t) + sizeof(key_str)];
    enum { base = 10 };
    char *end;
    end = buf;
    strcpy(end, key_str);
    end += sizeof(key_str);
    end = itoa_safe(global, end, base);
    *end++ = ' ';
    end = itoa_safe(sig, end, base);
    *end++ = '\n';
    write(STDOUT_FILENO, buf, end - buf);
    global += 1;
    signal(sig, signal_handler);
}

int main(int argc, char **argv) {
    /* Unit test itoa_safe. */
    {
        typedef struct {
            intmax_t n;
            int base;
            char out[1024];
        } InOut;
        char result[1024];
        size_t i;
        InOut io;
        InOut ios[] = {
            /* Base 10. */
            {0, 10, "0"},
            {1, 10, "1"},
            {9, 10, "9"},
            {10, 10, "10"},
            {100, 10, "100"},
            {-1, 10, "-1"},
            {-9, 10, "-9"},
            {-10, 10, "-10"},
            {-100, 10, "-100"},

            /* Base 2. */
            {0, 2, "0"},
            {1, 2, "1"},
            {10, 2, "1010"},
            {100, 2, "1100100"},
            {-1, 2, "-1"},
            {-100, 2, "-1100100"},

            /* Base 35. */
            {0, 35, "0"},
            {1, 35, "1"},
            {34, 35, "Y"},
            {35, 35, "10"},
            {100, 35, "2U"},
            {-1, 35, "-1"},
            {-34, 35, "-Y"},
            {-35, 35, "-10"},
            {-100, 35, "-2U"},
        };
        for (i = 0; i < sizeof(ios)/sizeof(ios[0]); ++i) {
            io = ios[i];
            itoa_safe(io.n, result, io.base);
            if (strcmp(result, io.out)) {
                printf("%ju %d %s\n", io.n, io.base, io.out);
                assert(0);
            }
        }
    }

    /* Handle the signals. */
    if (argc > 1 && !strcmp(argv[1], "1")) {
        signal(SIGINT, signal_handler);
        while(1);
    }

    return EXIT_SUCCESS;
}

संकलित करें और चलाएं:

gcc -std=c99 -Wall -Wextra -o main main.c
./main 1

Ctrl + C पंद्रह बार दबाने के बाद, टर्मिनल दिखाता है:

^Ccount, sigid: 0 2
^Ccount, sigid: 1 2
^Ccount, sigid: 2 2
^Ccount, sigid: 3 2
^Ccount, sigid: 4 2
^Ccount, sigid: 5 2
^Ccount, sigid: 6 2
^Ccount, sigid: 7 2
^Ccount, sigid: 8 2
^Ccount, sigid: 9 2
^Ccount, sigid: 10 2
^Ccount, sigid: 11 2
^Ccount, sigid: 12 2
^Ccount, sigid: 13 2
^Ccount, sigid: 14 2

के 2लिए सिग्नल नंबर कहां है SIGINT

उबुन्टु 18.04 पर परीक्षण किया गया। गिटहब ऊपर


0

एक तकनीक जो कार्यक्रमों में विशेष रूप से उपयोगी होती है जिसमें एक चयन लूप होता है, सिग्नल की प्राप्ति पर एक पाइप को एक बाइट के नीचे लिखना होता है और फिर सिलेक्ट लूप में सिग्नल को संभालना होता है। इन रेखाओं के साथ कुछ (त्रुटि से निपटने और संक्षिप्तता के लिए छोड़े गए अन्य विवरण) :

static int sigPipe[2];

static void gotSig ( int num ) { write(sigPipe[1], "!", 1); }

int main ( void ) {
    pipe(sigPipe);
    /* use sigaction to point signal(s) at gotSig() */

    FD_SET(sigPipe[0], &readFDs);

    for (;;) {
        n = select(nFDs, &readFDs, ...);
        if (FD_ISSET(sigPipe[0], &readFDs)) {
            read(sigPipe[0], ch, 1);
            /* do something about the signal here */
        }
        /* ... the rest of your select loop */
    }
}

यदि आप परवाह करते हैं कि यह कौन सा सिग्नल था, तो पाइप के नीचे बाइट सिग्नल नंबर हो सकता है।


-1

यदि आप pthread लाइब्रेरी का उपयोग कर रहे हैं तो आप सिग्नल हैंडलर में प्रिंटफ का उपयोग कर सकते हैं। यूनिक्स / पॉज़िक्स निर्दिष्ट करता है कि प्रिंटफ थ्रेड्स के लिए परमाणु है डेव ब्यूटेनहोफ़ ने यहां उत्तर दिया: https://groups.google.com/forum/# .topic/comp.programming.threads/1-bU71nYgqb कृपया स्पष्ट चित्र प्राप्त करने के लिए ध्यान दें। प्रिंट आउटपुट के लिए, आपको GUI द्वारा निर्मित छद्म ट्टी के बजाय कंसोल में अपना एप्लिकेशन चलाना चाहिए (कंसोल 1 को शुरू करने के लिए ctl + alt + f1 का उपयोग करें)।


3
सिग्नल हैंडलर कुछ अलग थ्रेड में नहीं चलते हैं, वे उस थ्रेड के संदर्भ में चलते हैं जो सिग्नल इंटरप्ट होने पर चल रहा था। यह जवाब पूरी तरह से गलत है।
itaych
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.