English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية

نقاش بسيط حول استخدام وظائف定时 في Linux

في عملية تطوير البرامج، نستخدم من وقت لآخر بعض أجهزة تقديم الزمن، عادةً إذا لم تكن دقة الوقت مرتفعة، يمكن استخدام وظيفة sleep،usleep لتسريع عملية النوم لفترة من الزمن لتحقيق وظيفة التوقيت المحدد،

الواحدة الأولى وحدةها ثانية (s)، والثانية وحدةها ميكرو ثانية (us);لكن في بعض الأحيان لا نريد أن تنتظر عملية النوم في مكان، نحتاج إلى أن تنفذ العملية بشكل طبيعي، وعند وصول الوقت المحدد، تنفذ العملية المطلوبة،

في نظام تشغيل Linux، نستخدم عادة وظيفة الرادار وفوق ذلك وظيفة setitimer لتحقيق وظيفة التوقيت المحدد؛

فيما يلي تحليل مفصل لهاتين الوظيفتين:

(1) وظيفة الرادار

الرادار أيضًا يُسمى وظيفة闹ق، يمكنه إعداد جهاز تقديم الزمن في عملية، عند وصول وقت الجهاز المحدد، يرسل إشارة SIGALRM إلى العملية؛

يكون نموذج function لـ alarm() كالتالي:}

unsigned int alarm(unsigned int seconds);
//seconds هو عدد الثواني المحدد

ملفات الرأس المطلوبة
  #include<unistd.h>

نموذج function
  unsigned int alarm(unsigned int seconds)

معلمات function
  seconds: يحدد عدد الثواني

قيمة العودة للfunction
  نجاح: إذا تم تعيين وقت الجرس للعملية قبل إجراء هذا call إلى alarm()، فإنه يعود بوقت الباقي من وقت الجرس السابق، وإلا يعود بـ 0.
  خطأ: -1

إليك مثال بسيط على function alarm():

void sigalrm_fn(int sig)  
{ 
  printf("alarm!\n"); 
  alarm(2); 
  return; 
{} 
int main(void) 
{ 
  signal(SIGALRM, sigalrm_fn); // يجب أن يكون function التالي يحتوي على argument int
  alarm(1); 
  while(1)  
  pause(); 
{}

(2) function setitimer()

في Linux، إذا كانت متطلبات الموقت غير دقيقة، يمكن استخدام alarm() و signal()، ولكن إذا كنت ترغب في تحقيق وظيفة موقت دقيقة، فعليك استخدام function setitimer().

setitimer() هي واجهة برمجة تطبيقات Linux، وليست مكتبة معيارية لـ C، ولديها وظيفتان، الأولى هي تحديد وقت معين بعد تنفيذ function معين، والثانية هي تنفيذ function كل فترة زمنية معينة;

يُنظم لكل مهمة في Linux ثلاثة مواقت داخليين:

ITIMER_REAL: موقته حقيقي، لا يهم أي وضعة يعمل فيه عملية (حتى عندما يتم تعليق العملية)، يتم حساب الوقت دائمًا. عند الوصول إلى المدة المحددة، يتم إرسال إشارة SIGALRM إلى العملية.

ITIMER_VIRTUAL: هذا ليس موقته حقيقي، عندما يتم حساب وقت تنفيذ عملية في الوضع المستخدم (أي أثناء تنفيذ البرنامج). يتم إرسال إشارة SIGVTALRM إلى هذه العملية عند الوصول إلى المدة المحددة.

ITIMER_PROF: يتم حساب الوقت في كل من الوضع المستخدم (أي أثناء تنفيذ البرنامج) والوضع النووي (أي أثناء تشغيل عملية التوجيه). يتم إطلاق إشارة SIGPROF عند الوصول إلى المدة المحددة. الوقت المسجل بواسطة ITIMER_PROF يزيد من الوقت المستغرق في عملية التوجيه عن ITIMER_VIRTUAL.

الموقت في عملية التشغيل، يتم منحه قيمة بدائية، تنخفض مع مرور الوقت، وعندما تنخفض إلى 0 يتم إطلاق الإشارة، ويتم استعادة القيمة البدائية. في المهمة، يمكن استخدام نوع واحد أو جميع الأنواع الثلاثة من الموقتات، ولكن يمكن استخدام موقرة واحدة فقط من نفس النوع في نفس الوقت.

تصريح وظيفة setitimer:

#include <sys/time.h>
    int setitimer(int which, const struct itimerval *new_value,
           struct itimerval *old_value);
 تم تعريف قيم التimers بواسطة الهياكل التالية:
      struct itimerval {
        struct timeval it_interval; /* next value */
        struct timeval it_value;  /* current value */
      };
      struct timeval {
        time_t   tv_sec;     /* seconds */
        suseconds_t tv_usec;    /* microseconds */
      };

يستخدم it_interval لتعيين فترة زمنية بين تنفيذ المهام، ويستخدم it_value لتخزين الوقت الحالي الذي يبقى حتى تنفيذ المهمة. على سبيل المثال، إذا قمت بتعيين it_interval إلى 2 ثانية (الميكروثانية 0)، فإننا نضبط it_value إلى 2 ثانية (الميكروثانية 0) في البداية، وعند مرور ثانية واحدة، يقل it_value بمقدار واحد إلى 1، وعند مرور ثانية أخرى، يقل it_value بمقدار واحد ويصبح 0، في هذه اللحظة يتم إرسال الإشارة (يخبر المستخدم أن الوقت قد حان لتنفيذ المهمة)، ويقوم النظام تلقائيًا بإعادة تعيين it_value إلى قيمة it_interval، أي 2 ثانية، ويبدأ في العد مرة أخرى

هذا مثال بسيط لـ setitimer:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/time.h>
void test_func();
{
  static count = 0;
  printf("عدد المرات هو %d\n", count++);
{}
void init_sigaction();
{
  struct sigaction act;
  act.sa_handler = test_func; // تعيين وظيفة معالجة الإشارة
  act.sa_flags = 0;
  sigemptyset(&act.sa_mask);
  sigaction(SIGPROF, &act, NULL); // الزمن إلى إرسال إشارة SIGPROF
{}
void init_time()}
{
  struct itimerval val;
  val.it_value.tv_sec = 1; // يتم تشغيل المدرب بعد 1 ثانية
  val.it_value.tv_usec = 0;
  val.it_interval = val.it_value; // فترات التباعد للمدارب 1 ثانية
  setitimer(ITIMER_PROF, &val, NULL);
{}
int main(int argc, char **argv)
{
  init_sigaction();
  init_time();
  while(1);
  عد 0;
{}

يمكن ملاحظة أن كل ثانية يتم إخراج قيمة count واحدة:

إليك النتائج بعد التنفيذ:

[root@localhost 5th]# ./test
عدد هو 0
عدد هو 1
عدد هو 2
عدد هو 3
عدد هو 4
عدد هو 5
عدد هو 6
عدد هو 7
عدد هو 8
عدد هو 9

المرفق:

signal

1. ملف الرأس
#include <signal.h>

2. الوظيفة
إعداد العملية المحددة لإشارة معينة

3. نموذج الدالة
void (*signal(int signum,void(* handler)(int)))(int);

نقوم بتفكيكها:

typedef void (*sig_t) (int);
sig_t signal(int sig, sig_t func);

المسار الأول هو إشارة الهدف. هو func يشير إلى دالة معالجة الإشارة. يجب أن تكون هذه الدالة تحتوي على دالة int كمعامل، ويجب أن تعود void.
يمكن أيضًا تعيين func إلى بعض القيم التالية:
SIG_IGN: إذا تم تعيين func إلى SIG_IGN، سيتم تجاهل الإشارة.
SIG_DFL: إذا تم تعيين func إلى SIG_DFL، سيتم التعامل مع الإشارة وفقًا للسلوك المحدد.

4. أنواع إشارات sig المحتملة

1) #define SIGHUP 1 /* hangup */

SIGHUP هو إشارة يستخدمها مديرو النظام في Unix بشكل شائع. ستقوم العديد من عمليات الخدمة الخلفية بقراءة ملفات تكوينها مرة أخرى بعد استقبال هذه الإشارة. ومع ذلك، وظيفة الإشارة الفعلية هي إبلاغ عملية أن وحدة التحكم الخاصة بها قد انقطعت. السلوك الافتراضي هو إنهاء العملية.

2) #define SIGINT 2 /* التوقف */

بالنسبة لمستخدمي Unix، إشارة SIGINT هي إشارة أخرى مستخدمة بشكل شائع. جعلت العديد من مفاتيح CTRL-C في الشاشات المعروفة هذه الإشارة. الاسم الرسمي لهذه الإشارة هو إشارة التوقف. السلوك الافتراضي هو إنهاء العملية.

3) #define SIGQUIT 3 /* الخروج */

يستخدم إشارة SIGQUIT لقبول مفتاح CTRL-/ للجملة. بالإضافة إلى ذلك، يتم استخدامها لتخبر عملية الخروج. هذا هو إشارة مستخدمة بشكل شائع، ويُستخدم لتخبر التطبيق بإغلاق سلسة (ملاحظة الترجمة: في نهاية التشغيل، إجراء بعض إجراءات الخروج). السلوك الافتراضي هو إنهاء العملية، وإنشاء ملف تراكم النواة.

4) #define SIGILL 4 /* تعليمات غير قانونية (لا يتم إعادة تعيين عند القبض عليه) */

إذا كان يحتوي عملية التنفيذ الحالية على تعليمات غير قانونية، فإن النظام التشغيل سيقوم بإرسال إشارة SIGILL إلى هذه العملية. إذا كان برنامجك يستخدم نواة أو functions pointer، فإنه يمكن محاولة التقاط هذه الإشارة لمساعدة في التتبع. ([color=Red]ملاحظة: جملة الأصل هذه هي: "If your program makes use of use of threads, or pointer functions, try to catch this signal if possible for aid in debugging.". العبارتين use of use of، لا أعرف إذا كانت عيوب في الطباعة الأصلية أو أنني لم أفهم معناها؛ بالإضافة إلى ذلك، سمعت مرارًا وتكرارًا عن functions pointer، بالنسبة لـ pointer functions، بحثت في google، يجب أن يكون هذا شيئًا في Fortran، على أي حال، لا أعرف بالضبط ما يعنيه، من فضلك، شكرًا للإصلاح الصحيح. [/color] السلوك الافتراضي هو إنهاء العملية، وإنشاء ملف تراكم النواة.

5) #define SIGTRAP 5 /* خطأ تتبع (لا يتم إعادة تعيين عند القبض عليه) */

SIGTRAP هذا الإشارة هو الذي تعريفه معيار POSIX، ويستخدم لغرض التتبع. عند استقبال عملية التتبع لهذه الإشارة، يعني ذلك أن وصلت إلى نقطة توقف تتبع معينة. بمجرد تسليم هذه الإشارة، سيتوقف عملية التتبع، وسيتم إعلام عملية الأب. السلوك الافتراضي هو إنهاء العملية، وإنشاء ملف تراكم النواة.

6) #define SIGABRT 6 /* abort() */

SIGABRT توفر طريقة لإنهاء عملية غير عادية (abort) وإجراء عملية إنشاء كومة النواة في نفس الوقت. ومع ذلك، إذا تم التقاطع هذه الإشارة، ولم يرجع معالج الإشارة، فإن العملية لن تنتهي. يحدث السلوك الافتراضي هو إنهاء العملية وإجراء عملية إنشاء كومة النواة.

7) #define SIGFPE 8 /* floating point exception */

عند حدوث خطأ في حسابات الثنائية، يتم إرسال إشارة SIGFPE إلى العملية. يُنصح عادةً بالتقاطع هذه الإشارة للبرامج التي تعالج العمليات الرياضية المعقدة. يحدث السلوك الافتراضي هو إنهاء العملية وإجراء عملية إنشاء كومة النواة.

8) #define SIGKILL 9 /* kill (cannot be caught or ignored) */

SIGKILL هي أكثر هذه الإشارات تضاربًا. كما ترى في التعليق بجانبها، لا يمكن إلتقاط هذه الإشارة أو تجاهلها. بمجرد تسليم هذه الإشارة إلى عملية، تنتهي العملية. ومع ذلك، هناك حالات نادرة لا تنتهي فيها عملية SIGKILL. تحدث هذه الحالات النادرة عند التعامل مع عملية "غير منقصة" (مثل I/O للقرص). حتى إذا كانت هذه الحالات نادرة، إلا أن وقوعها يمكن أن يؤدي إلى حبس العملية. الطريقة الوحيدة لإنهاء العملية هي إعادة التشغيل. يحدث السلوك الافتراضي هو إنهاء العملية.

9) #define SIGBUS 10 /* bus error */

كما يشير الاسم إلى ذلك، يتم إطلاق إشارة SIGBUS عندما يكتشف CPU خطأ في حاسوب البيانات. يتم إطلاق هذه الإشارة عندما يحاول البرنامج الوصول إلى عنوان ذاكرة لم يتم ضبطه بشكل صحيح. يحدث السلوك الافتراضي هو إنهاء العملية وإجراء عملية إنشاء كومة النواة.

10) #define SIGSEGV 11 /* segmentation violation */

SIGSEGV هو إشارة أخرى تعرفها برمجيون C/C++ جيدًا. يتم إطلاق هذه الإشارة عندما لا يملك البرنامج الحق في الوصول إلى عنوان ذاكرة محمي أو عندما يحاول الوصول إلى عنوان ذاكرة افتراضي غير صالح (نقاط إشارة قذرة، dirty pointers، شرح: بسبب عدم التزامن مع المحفوظات في المخزن المؤقت. حول نقاط الإشارة العشوائية، يمكنك الرجوع إلى http://en.wikipedia.org/wiki/Wild_pointer للحصول على التفسير.) يتم إطلاق هذه الإشارة. يحدث السلوك الافتراضي هو إنهاء العملية وإجراء عملية إنشاء كومة النواة.

11) #define SIGSYS 12 /* non-existent system call invoked */

تُسلم إشارة SIGSYS عندما يقوم العملية بتنفيذ دالة نظام غير موجودة. سيقوم النظام بتسليم هذه الإشارة ويُنهى العملية. سلوك افتراضي هو إنهاء العملية وإنتاج كومة النواة.

12) #define SIGPIPE 13 /* write on a pipe with no one to read it */

يشتغل أنبوب مثل الهاتف، مما يسمح بالتواصل بين العمليات. إذا حاولت العملية تنفيذ كتابة على الأنبوب، ولكن لم يكن هناك من يقرأه في الجانب الآخر، فإن النظام سيقوم بتسليم إشارة SIGPIPE إلى هذه العملية المزعجة (هذه هي العملية التي كانت تنوي الكتابة). سلوك افتراضي هو إنهاء العملية.

13) #define SIGALRM 14 /* alarm clock */

عند انتهاء وقت معين للعملياتة، سيتم تسليم إشارة SIGALRM إلى العملية. هذه المعاملات هي التي سيتم ذكرها في هذا الفصل لاحقًا.
باستخدام setitimer و alarm. سلوك افتراضي هو إنهاء العملية.

14) #define SIGTERM 15 /* software termination signal from kill */

تُرسل إشارة SIGTERM إلى العملية لت通知 العملية أنه الوقت للانتهاء، وأن تقوم بالتنظيف قبل الانتهاء. إشارة SIGTERM هي الإشارة الافتراضية التي يرسلها الأمر Unix kill، وهي أيضًا الإشارة الافتراضية التي يرسلها النظام عند إغلاقه إلى العملية. سلوك افتراضي هو إنهاء العملية.

15) #define SIGURG 16 /* urgent condition on IO channel */

عند حدوث بعض الحالات على جهاز التوصيل المفتوح للعمليات، سيتم إرسال إشارة SIGURG إلى هذه العملية. إذا لم يلتقط العملية هذه الإشارة، فإنها سيتم التخلص منها. سلوك افتراضي هو التخلص من الإشارة.

16) #define SIGSTOP 17 /* sendable stop signal not from tty */

لا يمكن إلتقاط هذه الإشارة أو تجاهلها. بمجرد استقبال عملية إشارة SIGSTOP، ستعمل على الفور (توقف)، حتى استقبال إشارة SIGCONT أخرى
الإشارة إلى التوقف. سلوك افتراضي هو إيقاف عملية حتى استقبال إشارة SIGCONT.

17) #define SIGTSTP 18 /* stop signal from tty */

SIGSTP يشبه SIGSTOP، ولكن الفرق بينهما هو أن إشارة SIGSTP يمكن تحقيقها أو تجاهلها. عندما يتلقى المزود من الشل إشارة CTRL-Z من لوحة المفاتيح، يتم تسليم هذه الإشارة إلى العملية. سلوك الافتراض هو إيقاف العملية حتى يتم استقبال إشارة SIGCONT.

18) #define SIGCONT 19 /* continue a stopped process */

SIGCONT هو إشارة مثيرة للاهتمام أيضًا. كما ذكرنا سابقًا، عندما يتوقف العملية، تستخدم هذه الإشارة لتخبر العملية بالاستئناف. ما هو مثير للاهتمام في هذه الإشارة: لا يمكن تجاهلها أو منعها، ولكن يمكن تحقيقها. هذا له معنى: لأن العملية قد لا تريد تجاهل أو منع إشارة SIGCONT، وإلا ماذا سيحدث إذا تلقت العملية إشارات SIGSTOP أو SIGSTP؟ سلوك الافتراض هو التخلص من الإشارة.

19) #define SIGCHLD 20 /* to parent on child stop or exit */

SIGCHLD تم إدخاله من قبل Unix Berkeley، وهو أفضل من تنفيذ SRV 4 Unix في الواجهة. (إذا كان الإشارة عملية لا تحتوي على قدرة استرجاعية، فإن تنفيذ إشارة SIGCHID في BSD سيكون أفضل. في تنفيذ Unix system V، إذا كان هناك طلب على تحقيق الإشارة، سيقوم النظام بتقديم التحقق من وجود أي من الأبناء غير المنتهية (هذه الأبناء قد انتهت بالفعل exit) الأبناء، والتي تنتظر أن يجمع الأبوية في انتظار التحقق من حالةهم). إذا كانت هناك بعض معلومات الانهيار (معلومات التوقف) مع انهيار الأبناء، سيتم استدعاء معالج الإشارة. لذا، فإن مطالبة بالتحقيق في الإشارة فقط ستعني استدعاء معالج الإشارة (ملاحظة الترجمة: هذا هو ما يُقال عن قدرة الإشارة على الاسترجاع)، وهو حالة مقلوبة نسبياً.). عند حدوث تغيير في حالة الأبناء لعملية، سيتم إرسال إشارة SIGCHLD إلى العملية. كما ذكرت في الفصل السابق، يمكن للآباء أن يفجر الأبناء، ولكن ليس من الضروري أن ينتظروا انتهاء الأبناء. في العادة هذا ليس جيدًا، لأنه إذا تم انتهاء العملية، قد يصبح هناك عملية زائفة. ولكن إذا قام الآباء بتحقيق إشارة SIGCHLD، فإنهم يمكنهم استخدام أي من الاستدعاءات في سلسلة wait لجمع حالة الأبناء، أو تحديد ما حدث. عند إرسال إشارات SIGSTOP، SIGSTP أو SIGCONF إلى الأبناء، سيتم إرسال إشارة SIGCHLD إلى الآباء. سلوك الافتراض هو التخلص من الإشارة.

20) #define SIGTTIN 21 /* إلى مجموعة العملاء pgrp عند قراءة tty خلفية */

عندما يحاول عملية خلفية تنفيذ عملية قراءة، يتم إرسال إشارة SIGTTIN إلى هذه العملية. ستكون العملية معطلة حتى استقبال إشارة SIGCONT. السلوك الافتراضي هو إيقاف العملية حتى استقبال إشارة SIGCONT.

21) #define SIGTTOU 22 /* مثل TTIN إذا (tp->t_local<OSTOP) */

إشارة SIGTTOU تشبه إشارة SIGTTIN، ولكن الفرق هو أن إشارة SIGTTOU تنتج فقط عندما يحاول عملية خلفية تنفيذ عملية كتابة على tty تم تعيين خاصية TOSTOP. ومع ذلك، إذا لم يتم تعيين هذه الخاصية على tty، لن يتم إرسال إشارة SIGTTOU. السلوك الافتراضي هو إيقاف العملية حتى استقبال إشارة SIGCONT.

22) #define SIGIO 23 /* إشارة ممكنة للإدخال والخروج */

إذا كانت عملية تجري عمليات I/O على ملفوصف، يتم إرسال إشارة SIGIO إلى هذه العملية. يمكن تعديل السلوك باستخدام دالة fcntl. السلوك الافتراضي هو التخلص من الإشارة.

23) #define SIGXCPU 24 /* تجاوزت حد وقت CPU */

إذا كان عملية تجاوزت الحد الذي يمكن استخدام CPU (حد CPU) الذي يمكن استخدامها، يتم إرسال إشارة SIGXCPU إليها. يمكن تعديل هذا الحد باستخدام إعداد setrlimit الذي سيتم مناقشته لاحقاً. السلوك الافتراضي هو إنهاء العملية.

24) #define SIGXFSZ 25 /* تجاوز الحد الأقصى لحجم الملف */

إذا تجاوزت عملية الحجم المسموح باستخدامه للملفات، يتم إرسال إشارة SIGXFSZ إليها. سنتحدث عن هذه الإشارة لاحقاً. السلوك الافتراضي هو إنهاء العملية.

25) #define SIGVTALRM 26 /* إنذار الوقت الافتراضي */

إذا تجاوزت عملية العداد الخاص بالوقت الافتراضي الذي قمت بضبطه، يتم إرسال إشارة SIGVTALRM إليها. السلوك الافتراضي هو إنهاء العملية.

26) #define SIGPROF 27 /* إنذار وقت التتبع */

عند ضبط جهاز الكمبيوتر المخصص للوقت، يتم إرسال إشارة SIGPROF إلى العملية. السلوك الافتراضي هو إنهاء العملية.

27) #define SIGWINCH 28 /* تغيير حجم النافذة */

عندما يعدل عملية السطر أو الصف في الترنيمال (مثل زيادة حجم xterm الخاص بك)، يتم إرسال إشارة SIGWINCH إلى هذه العملية. السلوك الافتراضي هو التخلص من الإشارة.

28) #define SIGUSR1 29 /* إشارة مخصصة للمستخدم 1 */

29) #define SIGUSR2 30 /* إشارة مخصصة للمستخدم 2 */

SIGUSR1 وSIGUSR2 هما إشارات تم تصميمها لتكون مخصصة للمستخدم. يمكن تكوينها للقيام بأي عملية تحتاجها. بمعنى آخر، لا يوجد أي سلوك في نظام التشغيل مرتبط بهذه الإشارات. السلوك الافتراضي هو إنهاء العملية. (ملاحظة الترجمة: يبدو أن هذين الجملين متعارضان عن الأصل.)

5. مثال

5.1. كيفية تنفيذ Ctrl+C تحت لينكس في ويندوزأ

الطريقة الشائعة تحت لينكس:

signal(SIGINT, sigfunc); // ضبط الإشارة
void sigfunc(int signo)
  {
   ... // معالجة العمليات المتعلقة بالإشارات
  {}

إليك كيفية تنفيذ Ctrl+C تحت لينكس في ويندوز

#include <stdio.h>
  #include <windows.h>
  static is_loop = 1;
  // دالة لالتقاط حدث Ctrl+C للكونترول تابل
  BOOL CtrlHandler( DWORD fdwCtrlType )
  {
   switch (fdwCtrlType)
   {
   /* معالجة إشارة CTRL-C. */
   case CTRL_C_EVENT:
     حالة CTRL_C_EVENT:
     break;
   حالة CTRL_C_EVENT:
     حالة CTRL_CLOSE_EVENT:
     break;
   حالة CTRL_CLOSE_EVENT:
     حالة CTRL_BREAK_EVENT:
     break;
   حالة CTRL_LOGOFF_EVENT:
     printf("إحداثية تسجيل الخروج\n");
     break;
   حالة CTRL_SHUTDOWN_EVENT:
     printf("إحداثية إغلاق التحكم\n");
     break;
   default:
     عد FALSE;
   {}
   is_loop = 0;
   عد (TRUE);
  {}
  int main(int argc, char *argv[])
  {
   printf("تعيين معالج لوحة التحكم\n");
   SetConsoleCtrlHandler((PHANDLER_ROUTINE)CtrlHandler, TRUE);
   while (is_loop);
   عد 0;
  {}

5.2.تحقيق Ctrl+C تحت نظام Linux في Windows

#include <stdio.h>
  #include <windows.h>
  #define CONTRL_C_HANDLE() signal(3, exit)
  int main(int argc, char *argv[])
  {
   printf("تعيين معالج لوحة التحكم\n");
   CONTRL_C_HANDLE();
   while (1);
   نظام("PAUSE");
   عد 0;
  {}

إليكم محتوى الحديث القصير الذي قدمه المحرر عن استخدام وظائف定时 Linux المختلفة، نأمل أن تحصلوا على الدعم والمزيد من التشجيع لتعليمات النظام~

أنت قد تحب