signal()
सी मानक से कार्य पर विचार करें :
extern void (*signal(int, void(*)(int)))(int);
पूरी तरह से अस्पष्ट रूप से स्पष्ट है - यह एक ऐसा फ़ंक्शन है जो दो तर्कों को लेता है, एक पूर्णांक और एक फ़ंक्शन के लिए एक सूचक जो एक तर्क के रूप में एक पूर्णांक लेता है और कुछ भी नहीं देता है, और यह ( signal()
) एक फ़ंक्शन को एक सूचक देता है जो पूर्णांक को एक तर्क के रूप में लेता है और वापस लौटता है कुछ भी तो नहीं।
यदि आप लिखते हैं:
typedef void (*SignalHandler)(int signum);
तो आप इसके signal()
रूप में घोषित कर सकते हैं :
extern SignalHandler signal(int signum, SignalHandler handler);
इसका मतलब वही है, लेकिन आमतौर पर इसे पढ़ने में कुछ आसान माना जाता है। यह स्पष्ट है कि फ़ंक्शन int
ए SignalHandler
और ए लेता है SignalHandler
।
हालाँकि, इसका उपयोग करने में थोड़ा समय लगता है। एक चीज़ जो आप नहीं कर सकते हैं, हालांकि SignalHandler
typedef
फ़ंक्शन परिभाषा में एक सिग्नल हैंडलर फ़ंक्शन का उपयोग करके लिखा गया है।
मैं अभी भी पुराने स्कूल का हूँ जो एक फंक्शन पॉइंटर को इनवाइट करना पसंद करता है:
(*functionpointer)(arg1, arg2, ...);
आधुनिक वाक्यविन्यास बस का उपयोग करता है:
functionpointer(arg1, arg2, ...);
मैं यह देख सकता हूं कि वह काम क्यों करता है - मैं सिर्फ यह जानना पसंद करता हूं कि मुझे यह देखने की जरूरत है कि किसी फ़ंक्शन के लिए वेरिएबल को इनिशियलाइज़ करने की बजाय कहा जाता है functionpointer
।
सैम ने टिप्पणी की:
इसका स्पष्टीकरण मैंने पहले भी देखा है। और फिर, जैसा कि अभी मामला है, मुझे लगता है कि मुझे जो नहीं मिला वह दो बयानों के बीच का संबंध था:
extern void (*signal(int, void()(int)))(int); /*and*/
typedef void (*SignalHandler)(int signum);
extern SignalHandler signal(int signum, SignalHandler handler);
या, मैं जो पूछना चाहता हूं वह यह है कि अंतर्निहित अवधारणा क्या है जिसका उपयोग आप दूसरे संस्करण के साथ कर सकते हैं? मूलभूत क्या है जो "सिग्नलहैंडलर" और पहला टाइपडेफ़ को जोड़ता है? मुझे लगता है कि यहां जो समझा जाना चाहिए, वह वही है जो वास्तव में टाइप्डिफ है।
चलो फिर से कोशिश करें। इनमें से पहला सी मानक से सीधे उठा हुआ है - मैंने इसे हटा दिया, और जांच की कि मेरे पास कोष्ठक सही थे (जब तक मैंने इसे सही नहीं किया - यह याद रखने के लिए एक कठिन कुकी है)।
सबसे पहले, याद रखें कि typedef
एक प्रकार के लिए एक उपनाम का परिचय देता है। तो, उपनाम है SignalHandler
और इसका प्रकार है:
किसी फ़ंक्शन के लिए एक संकेतक जो एक तर्क के रूप में पूर्णांक लेता है और कुछ भी नहीं देता है।
'रिटर्न नथिंग' भाग वर्तनी है void
; एक पूर्णांक है कि तर्क (मुझे विश्वास है) आत्म व्याख्यात्मक है। निम्नलिखित संकेतन बस (या नहीं) है कि कैसे सी सूचक इंगित करने के लिए तर्क देने के लिए कार्य करता है और दिए गए प्रकार को वापस करता है:
type (*function)(argtypes);
सिग्नल हैंडलर प्रकार बनाने के बाद, मैं इसका उपयोग चर और अन्य को घोषित करने के लिए कर सकता हूं। उदाहरण के लिए:
static void alarm_catcher(int signum)
{
fprintf(stderr, "%s() called (%d)\n", __func__, signum);
}
static void signal_catcher(int signum)
{
fprintf(stderr, "%s() called (%d) - exiting\n", __func__, signum);
exit(1);
}
static struct Handlers
{
int signum;
SignalHandler handler;
} handler[] =
{
{ SIGALRM, alarm_catcher },
{ SIGINT, signal_catcher },
{ SIGQUIT, signal_catcher },
};
int main(void)
{
size_t num_handlers = sizeof(handler) / sizeof(handler[0]);
size_t i;
for (i = 0; i < num_handlers; i++)
{
SignalHandler old_handler = signal(handler[i].signum, SIG_IGN);
if (old_handler != SIG_IGN)
old_handler = signal(handler[i].signum, handler[i].handler);
assert(old_handler == SIG_IGN);
}
...continue with ordinary processing...
return(EXIT_SUCCESS);
}
कृपया ध्यान दें कि सिग्नल हैंडलर में उपयोग करने से कैसे बचें printf()
?
तो, हमने यहाँ क्या किया है - 4 मानक हेडर के अलावा जो कोड को साफ-सुथरा बनाने के लिए आवश्यक होगा?
पहले दो कार्य ऐसे कार्य हैं जो एक पूर्णांक लेते हैं और कुछ भी नहीं लौटाते हैं। उनमें से एक वास्तव में सभी धन्यवाद पर वापस नहीं आता है, exit(1);
लेकिन दूसरा एक संदेश को प्रिंट करने के बाद वापस करता है। ध्यान रखें कि सी मानक आपको सिग्नल हैंडलर के अंदर बहुत कुछ करने की अनुमति नहीं देता है; POSIX इजाज़त में थोड़ा और उदार है, लेकिन आधिकारिक तौर पर कॉलिंग को मंजूरी नहीं देता है fprintf()
। मुझे जो सिग्नल नंबर मिला था, उसका प्रिंट आउट भी ले लेता हूं। में alarm_handler()
समारोह, मूल्य हमेशा रहेंगे SIGALRM
रूप में है कि केवल संकेत है कि इसके लिए एक हैंडलर है, लेकिन signal_handler()
मिल सकता है SIGINT
या SIGQUIT
संकेत संख्या के रूप में, क्योंकि एक ही समारोह दोनों के लिए प्रयोग किया जाता है।
फिर मैं संरचनाओं की एक सरणी बनाता हूं, जहां प्रत्येक तत्व एक सिग्नल संख्या की पहचान करता है और उस सिग्नल के लिए हैंडलर स्थापित किया जाता है। मैंने 3 संकेतों के बारे में चिंता करने के लिए चुना है; मैं अक्सर के बारे में चिंता है SIGHUP
, SIGPIPE
और SIGTERM
भी और के बारे में कि क्या वे ( #ifdef
सशर्त संकलन) परिभाषित कर रहे हैं , लेकिन यह सिर्फ चीजों को जटिल करता है। मैं शायद sigaction()
इसके बजाय POSIX का उपयोग करूँगा signal()
, लेकिन यह एक और मुद्दा है; चलो जो हमने शुरू किया था, उसके साथ चलो।
main()
संचालकों की सूची पर समारोह दोहराता स्थापित होने के लिए। प्रत्येक हैंडलर के लिए, यह पहले यह signal()
पता लगाने के लिए कॉल करता है कि क्या प्रक्रिया वर्तमान में सिग्नल की अनदेखी कर रही है, और ऐसा करते समय, SIG_IGN
हैंडलर के रूप में स्थापित होता है , जो यह सुनिश्चित करता है कि सिग्नल को नजरअंदाज किया जाता है। यदि सिग्नल को पहले नजरअंदाज नहीं किया गया था, तो यह फिर से कॉल करता है signal()
, इस बार पसंदीदा सिग्नल हैंडलर को स्थापित करने के लिए। (दूसरा मान निश्चित SIG_DFL
रूप से, सिग्नल के लिए डिफ़ॉल्ट सिग्नल हैंडलर है।) क्योंकि 'सिग्नल' () के लिए पहला कॉल हैंडलर को सेट करता है SIG_IGN
और signal()
पिछले त्रुटि हैंडलर को लौटाता है, बयान के old
बाद का मान if
होना चाहिए SIG_IGN
- इसलिए जोर। (खैर, यह हो सकता हैSIG_ERR
अगर कुछ नाटकीय रूप से गलत हो गया - लेकिन तब मैं उस बारे में जानूंगा जो मुखर गोलीबारी से हुई है।]
कार्यक्रम फिर अपना सामान करता है और सामान्य रूप से बाहर निकलता है।
ध्यान दें कि किसी फ़ंक्शन का नाम उपयुक्त प्रकार के फ़ंक्शन के लिए एक संकेतक के रूप में माना जा सकता है। जब आप फ़ंक्शन-कॉल कोष्ठकों को लागू नहीं करते हैं - जैसे कि शुरुआती में, उदाहरण के लिए - फ़ंक्शन का नाम फ़ंक्शन पॉइंटर बन जाता है। यह भी है कि pointertofunction(arg1, arg2)
संकेतन के माध्यम से कार्यों को लागू करना उचित है; जब आप देखते हैं alarm_handler(1)
, तो आप विचार कर सकते हैं कि alarm_handler
फ़ंक्शन के लिए एक पॉइंटर है और इसलिए alarm_handler(1)
फ़ंक्शन पॉइंटर के माध्यम से फ़ंक्शन का एक आह्वान है।
इसलिए, इस प्रकार, मैंने दिखाया है कि एक SignalHandler
चर का उपयोग करने के लिए अपेक्षाकृत सीधा-आगे है, जब तक कि आपके पास इसे असाइन करने के लिए कुछ सही प्रकार का मूल्य है - जो कि दो सिग्नल हैंडलर फ़ंक्शन प्रदान करता है।
अब हम इस प्रश्न पर वापस आते हैं - signal()
एक दूसरे से संबंधित दो घोषणाएँ कैसे होती हैं ।
आइए दूसरी घोषणा की समीक्षा करें:
extern SignalHandler signal(int signum, SignalHandler handler);
यदि हमने फ़ंक्शन का नाम और प्रकार इस प्रकार बदला है:
extern double function(int num1, double num2);
आपको किसी ऐसे फ़ंक्शन के रूप में व्याख्या करने में कोई समस्या नहीं होगी जो int
एक double
तर्क और एक तर्क के रूप में लेता है और एक double
मान लौटाता है (हो सकता है); यदि आप समस्याग्रस्त हैं तो बेहतर नहीं होगा '- लेकिन शायद आपको कठिन सवाल पूछने के बारे में सतर्क रहना चाहिए यह एक के रूप में अगर यह एक समस्या है)।
अब, एक होने के बजाय double
, signal()
फ़ंक्शन SignalHandler
अपने दूसरे तर्क के रूप में लेता है , और यह एक परिणाम के रूप में वापस आता है।
यांत्रिकी जिसके द्वारा भी इलाज किया जा सकता है:
extern void (*signal(int signum, void(*handler)(int signum)))(int signum);
समझाने के लिए मुश्किल कर रहे हैं - तो मैं शायद इसे खराब कर दूँगा। इस बार मैंने पैरामीटर नाम दिए हैं - हालांकि नाम महत्वपूर्ण नहीं हैं।
सामान्य तौर पर, C में, घोषणा तंत्र ऐसा होता है कि यदि आप लिखते हैं:
type var;
तब जब आप लिखते हैं तो var
यह दिए गए मान का प्रतिनिधित्व करता है type
। उदाहरण के लिए:
int i; // i is an int
int *ip; // *ip is an int, so ip is a pointer to an integer
int abs(int val); // abs(-1) is an int, so abs is a (pointer to a)
// function returning an int and taking an int argument
मानक में, typedef
व्याकरण में एक भंडारण वर्ग के रूप में व्यवहार किया जाता है, न कि तरह static
और extern
भंडारण वर्ग हैं।
typedef void (*SignalHandler)(int signum);
इसका मतलब यह है कि जब आप एक प्रकार का चर देखते हैं SignalHandler
(अलार्म_हैंडलर कहते हैं):
(*alarm_handler)(-1);
परिणाम type void
का कोई परिणाम नहीं है। और तर्क (*alarm_handler)(-1);
के alarm_handler()
साथ एक आह्वान है -1
।
इसलिए, अगर हमने घोषित किया:
extern SignalHandler alt_signal(void);
इसका मतलब है कि:
(*alt_signal)();
एक शून्य मान का प्रतिनिधित्व करता है। और इसीलिए:
extern void (*alt_signal(void))(int signum);
समतुल्य है। अब, signal()
अधिक जटिल है क्योंकि यह न केवल एक रिटर्न देता है SignalHandler
, यह एक अंतर और SignalHandler
तर्क के रूप में भी स्वीकार करता है:
extern void (*signal(int signum, SignalHandler handler))(int signum);
extern void (*signal(int signum, void (*handler)(int signum)))(int signum);
यदि वह अभी भी आपको भ्रमित करता है, तो मुझे यकीन नहीं है कि मैं कैसे मदद कर सकता हूं - यह अभी भी मेरे लिए कुछ स्तरों पर रहस्यमय है, लेकिन मैं इसे कैसे काम करता है, इसके लिए उपयोग किया गया है और इसलिए आपको बता सकता है कि यदि आप इसके साथ एक और 25 साल तक चिपके रहते हैं या तो, यह आपके लिए दूसरा स्वभाव बन जाएगा (और शायद थोड़ा तेज भी अगर आप चतुर हैं)।