cronスケジューリングは実際にどのように実装され、スクリプトが時間内に実行されることを保証しますか?

cronスケジューリングは実際にどのように実装され、スクリプトが時間内に実行されることを保証しますか?

クローンの仕事について質問したいです。さて、スクリプトをcrontabに入れてから、cronデーモンがそれを実行します。

これを理解すると、cronは毎分各ユーザーのcrontabをチェックし、設定されたスクリプトを実行します。しかし、実際にはどうなりますか?サブプロセスなどをフォークしますか?

時間が無駄になるため、ジョブを順番に実行することはできません(たとえば、長期実行スクリプトが完了するのを待っている場合など)。それでは、これは実際にどのように達成されますか?

ただ助けるために、私は低レベルのコードを探していません。私にとっては、高レベルの説明(おそらくアルゴリズム?)やほとんどのディストリビューションでこれを実装する方法で十分です。

答え1

StackOverflowで次のタイトルのQ&Aを見つけました。クローンは内部的にどのようにタスクをスケジュールしますか?

この投稿から抜粋した内容とcronに関するWikipediaの記事

The algorithm used by this cron is as follows:

1. On start-up, look for a file named .crontab in the home directories of 
   all account holders.

2. For each crontab file found, determine the next time in the future that
   each command is to be run.

3. Place those commands on the Franta-Maly event list with their corresponding
   time and their "five field" time specifier.

4. Enter main loop:

   1. Examine the task entry at the head of the queue, compute how far in 
      the future it is to be run.

   2. Sleep for that period of time.

   3. On awakening and after verifying the correct time, execute the task 
      at the head of the queue (in background) with the privileges of the 
      user who created it.

   4. Determine the next time in the future to run this command and place 
      it back on the event list at that time

このスーパーユーザーQ&Aのタイトルは次のとおりです。クローナはどのように機能しますか?他の質問のいくつかを扱います。たとえば、cronが同時にスケジュールされたジョブを処理する方法について質問があります。このスレッドの答えの1つは、cronデーモンが各ジョブを処理するときに1つのジョブが時間が重なるジョブをブロックするのを防ぐために、スケジュールされた各ジョブを分岐することです。

答え2

私は書いたブログ投稿説明してみてください。
関連テキストを引用するには:

  • PriorityBlockingQueue優先順位(スレッドセーフヒープ)から選択して、すべてのジョブを実行する制限付きスレッドプールを持つことができますjob.nextExecutionTime()
  • これは、ヒープの最上位要素が常に最も速く実行されることを意味します。
  • 標準スレッドプールの生産者 - 消費者パターンに従います。
  • 無限ループで実行され、新しいジョブを消費した後、キューからスレッドプールに送信するスレッドがあります。私たちはそれを呼ぶキューコンシューマスレッド:
void goToSleep(job, jobQueue){
    jobQueue.push(job);
    sleep(job.nextExecutionTime() - getCurrentTime());
}

void executeJob(job, jobQueue){
    threadpool.submit(job); // async call
    job = job.copy();
    job.setNextExecutionTime(getCurrentTime() + job.getExecutionInterval());
    jobQueue.add(job);
}

@Override
void run(){
    while(true)
    {
        job = jobQueue.pop()
        if(job.nextExecutionTime() > getCurrentTime()){
            // Nothing to do
            goToSleep(job, jobQueue)
        }
        else{
            executeJob(job, jobQueue)
        }
    }
}
  • 新しく追加されたジョブのcrontabファイルを監視し、それをキューにプッシュするスレッドもあります。
  • 私たちはそれを呼ぶキューコンストラクタスレッド:
@Override
void run()
{
    while(true)
    {
        newJob = getNewJobFromCrontabFile() // blocking call
        jobQueue.push(newJob)
    }
}
  • しかし、ここに問題があります。
    • Thread1が眠っていて、1時間後に目が覚めたと想像してください。
    • 同時に毎分実行する必要がある新しいタスクが到着します。
    • この新しい操作は、1時間は開始されません。
  • この問題を解決するには、新しいジョブがキューのフロントエンドジョブの前に実行する必要があるときはいつでも、ProducerThreadにConsumerThreadをスリープモードで強制的に実行させることができます。
@Override
void run()
{
    while(true)
    {
        newJob = getNewJobFromCrontabFile() // blocking call
        jobQueue.push(newJob)
        if(newJob == jobQueue.peek())
        {
            // The new job is the one that will be scheduled next.
            // So wakeup consumer thread so that it does not oversleep.
            jobQueueConsumerThread.interrupt()
        }
    }
}

関連情報