signal()
সি স্ট্যান্ডার্ড থেকে ফাংশনটি বিবেচনা করুন :
extern void (*signal(int, void(*)(int)))(int);
পুরোপুরি অস্পষ্টভাবে সুস্পষ্ট - এটি এমন একটি ফাংশন যা দুটি আর্গুমেন্ট গ্রহণ করে, একটি পূর্ণসংখ্যা এবং একটি ফাংশনটিতে একটি ফাংশন যা একটি পূর্ণসংখ্যা হিসাবে যুক্তি হিসাবে গ্রহণ করে এবং কিছুই দেয় না, এবং এটি ( signal()
) একটি ফাংশনটিতে একটি পয়েন্টার দেয় যা একটি পূর্ণসংখ্যা হিসাবে যুক্তি হিসাবে গ্রহণ করে এবং প্রত্যাবর্তন করে কিছুই নেই।
আপনি যদি লিখেন:
typedef void (*SignalHandler)(int signum);
তারপরে আপনি এর পরিবর্তে ঘোষণা করতে পারেন signal()
:
extern SignalHandler signal(int signum, SignalHandler handler);
এর অর্থ একই জিনিস, তবে সাধারণত এটি পড়া সহজতর হিসাবে বিবেচিত হয়। এটি স্পষ্ট যে ফাংশনটি একটি int
এবং a নেয় এবং a 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);
না অন্যটি বার্তা প্রিন্ট করার পরে ফিরে আসে। সচেতন হন যে সি স্ট্যান্ডার্ড আপনাকে সিগন্যাল হ্যান্ডলারের ভিতরে খুব বেশি অনুমতি দেয় না; পজিক্স যা অনুমোদিত তা কিছুটা উদার, তবে আনুষ্ঠানিকভাবে কলিং অনুমোদন করে না fprintf()
। আমি প্রাপ্ত সিগন্যাল নম্বরটিও মুদ্রণ করি। ইন alarm_handler()
ফাংশন, মান সবসময় হতে হবে SIGALRM
যেমন যে শুধুমাত্র সংকেত যে এটির জন্য একটি হ্যান্ডলার, কিন্তু signal_handler()
পেতে পারে SIGINT
বা SIGQUIT
সংকেত নম্বর হিসাবে কারণ একই ফাংশন উভয়ের জন্য ব্যবহার করা হয়।
তারপরে আমি স্ট্রাকচারের একটি অ্যারে তৈরি করি, যেখানে প্রতিটি উপাদান একটি সিগন্যাল নম্বর এবং সেই সংকেতের জন্য ইনস্টল করার জন্য হ্যান্ডলার সনাক্ত করে। আমি 3 সিগন্যাল সম্পর্কে চিন্তা করতে বেছে নিয়েছি; আমি প্রায়ই চিন্তা চাই SIGHUP
, SIGPIPE
এবং SIGTERM
খুব এবং প্রায় কিনা তারা (সংজ্ঞায়িত করা হয় #ifdef
শর্তাধীন সংকলন), কিন্তু যে শুধু কিছু complicates। আমি সম্ভবত এর sigaction()
পরিবর্তে পসিক্স ব্যবহার করবsignal()
তবে এটি অন্য সমস্যা; আসুন আমরা যা শুরু করেছিলাম তার সাথে লেগে থাকি।
main()
হ্যান্ডেলার তালিকাটির ফাংশন iterates ইনস্টল করার জন্য। প্রতিটি হ্যান্ডলারের জন্য, signal()
প্রক্রিয়াটি বর্তমানে সংকেতটিকে অগ্রাহ্য করছে কিনা তা অনুসন্ধান করার জন্য প্রথমে এটি কল করে এবং এটি করার সময় SIG_IGN
হ্যান্ডলার হিসাবে ইনস্টল করে, যা সিগন্যালটিকে অবহেলা করে তা নিশ্চিত করে। যদি সিগন্যালটি আগে উপেক্ষা করা হত না, তবে এটি তখন signal()
পছন্দসই সংকেত হ্যান্ডলারটি ইনস্টল করার জন্য আবার কল করে । (অন্যান্য মান সম্ভবতঃ হয় SIG_DFL
, সিগন্যাল জন্য ডিফল্ট সংকেত হ্যান্ডলার।) এর প্রথম কল 'সংকেত ()' থেকে হ্যান্ডলার সেট কারণ SIG_IGN
এবং signal()
পূর্ববর্তী ত্রুটি হ্যান্ডলার, এর মান old
পরif
বিবৃতি হতে হবে SIG_IGN
অত: পর কথন -। (ভাল, এটা হতে পারে)SIG_ERR
যদি কিছু নাটকীয়ভাবে ভুল হয়ে যায় - তবে আমি দৃ firing়তার সাথে গুলি চালানো থেকে এটি শিখতে পারি))
প্রোগ্রামটি তখন তার স্টাফগুলি করে এবং স্বাভাবিকভাবে প্রস্থান করে।
নোট করুন যে কোনও ফাংশনের নামটি উপযুক্ত প্রকারের কোনও ফাংশনের পয়েন্টার হিসাবে বিবেচনা করা যেতে পারে। আপনি যখন ফাংশন-কল বন্ধনী প্রয়োগ না করেন - যেমন প্রাথমিক হিসাবে যেমন - ফাংশনটির নামটি একটি ফাংশন পয়েন্টার হয়ে যায়। এটি 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);
ব্যাখ্যা করা খুব জটিল - তাই আমি সম্ভবত এটি স্ক্রু আপ। এবার আমি প্যারামিটারের নাম দিয়েছি - যদিও নামগুলি সমালোচনামূলক নয়।
সাধারণভাবে, সি তে, ঘোষণা প্রক্রিয়াটি এমন যে আপনি যদি লিখেন:
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 বছর ধরে থাকেন তবে বা তাই, এটি আপনার দ্বিতীয় প্রকৃতির হয়ে উঠবে (এবং আপনি যদি চালাক হন তবে কিছুটা দ্রুতও হতে পারে)।