Linuxドライバ_LDD3メモ_時間関連処理

來源:互聯網
上載者:User

時間関連処理            
1)、時間経過の計測            
    一般には「jiffies」の使用をお勧めします。        
    ①、「jiffies」カウンタを使う        
        キャッシュしたjiffiesと現在の値の比較は、    
            int time_after(unsigned long a, unsigned long b);
            int time_before(unsigned long a, unsigned long b);
            int time_after_eq(unsigned long a, unsigned long b);
            int time_before_eq(unsigned long a, unsigned long b);
        jiffiesとstruct timeval / struct timespecの変換は、    
            unsigned long timespec_to_jiffies(struct timespec *value);
            void jiffies_to_timespec(unsigned long jiffies, struct timespec *value);
            unsigned long timeval_to_jiffies(struct timeval *value);
            void jiffies_to_timeval(unsigned long jiffies, struct timeval *value);
        64ビットのjiffiesカウンタにアクセスするには、    
            u64 get_jiffies_64(void);    
2)、現在時刻を知る方法                
    方法一、「jiffies」を調べて現在時刻を表したものを得ることができます。
            通常、この値は最後に起動されてからの経過時間ですが、ドライバにとっては十分です。            
    方法二、「struct timespec current_kernel_time(void)」を使って、
            現在時刻を(jiffy粒度、即ち、クロックティック粒度)変數xtimeから得ることもできます。            

    実時刻を「jiffies」に変換するには、            
        unsigned long mktime (unsigned int year,
                              unsigned int mon,
                              unsigned int day,
                              unsigned int hour,
                              unsigned int min,
                              unsigned int sec);         
3)、実行を遅らせる                
    考える必要な重要なポイントの1つは、どうやってクロックティックから必要な遅延を作り出すかという點です。            
    ・1クロックティックより十分に長い遅延であり、粗い粒度でも困らない場合は、システムクロックを使えます。            
    ・非常に短い遅延には、一般にソフトウェアループを実裝します。            
    ①長い遅延 (1クロックティック以上)            
        ・ビジーな待ち        
            それほどの精度が求められず、クロックティックの整數倍の時間だけ遅延する場合、
            最も簡単な実裝はjiffyカウンタを監視するループになります。    
            実裝例:    
                while (time_before(jiffies, j1)) {
                    cpu_relax();                            
                }                                            
            この類の実裝は可能であれば絶対に避けるべきです。
        ・プロセッサの譲渡                                    
            必要ないときにCPUを明示的に開放します。            
            実裝例:                                        
                while (time_before(jiffies, j1)) {            
                    schedule();                                
                }                                            
            「schedule」でプロセスがプロセッサを解放すると、プロセスがプロセッサをいつでもすぐに取り戻せる保証がありません。
            ドライバのニーズに対する安全な解決策ではありません。
        ・タイムアウト                                                                                                                
            別のイベントを待つためにドライバが待ち列を使っており、一定の時間內に確実にそれを実行したいなら、
                long wait_event_timeout(wait_queue_head_t q, condition, long timeout);
                long wait_event_interruptible_timeout(wait_queue_head_t q,
                                                      condition,
                                                      long timeout);            // 「/proc/jitqueue」
            特定のイベントを待たずに遅延するなら、                    
                signed long schedule_timeout(signed long timeout);    
    ②、短い遅延        
        數十マイクロ秒見たいな遅延は    
            void msleep(unsigned int millisecs);
            unsigned long msleep_interruptible(unsigned int millisecs);
            void ssleep(unsigned int seconds)
4)、カーネルタイマ            
    後で起こるアクションをスケジュールする必要があり、その時が來るまでカレントプロセスをブロックしたくないなら、カーネルタイマを使います。        
    (アクションを起こすまでの間、現在の処理を続行したい)        
    カーネルタイマは、ユーザが指定した時間に、ユーザが指定した引數を使って、ユーザ定義の関數をカーネルに実行させるデータ構造體です。        
    カーネルタイマは「ソフトウェア割り込み」の結果です。        
        その1、タイマ関數はアトミックでなければなりません ( 並行&競爭の考えが必要 )。    
        その2、プロセスコンテキストがないことによって下記制限があります。    
            ・ユーザ空間へのアクセスは許されません。
            ・アトミックモードでは、Currentポインタは意味を持ちませんし、使うこともできません。
            ・スリープ、スケジュール、セマフォも使えません。 ( 例: kmallocはスリープの可能性があるので、使えません )
    SMPシステムでは、タイマを登録したものと、タイマ自體は、必ず同じCPU上で実行されます。        
    ①、タイマのAPI            
        struct timer_list    
        {                    
                /* ... */    
                unsigned long expires;                // タイマハンドラを実行したい時點のjiffies値                            
                void (*function)(unsigned long);    
                unsigned long data;                    
        };                                            
        void init_timer(struct timer_list *timer);    
        struct timer_list TIMER_INITIALIZER(_function, _expires, _data);                                                                    

        void add_timer(struct timer_list * timer);                                                                    
        int del_timer(struct timer_list * timer);                                                                    

        // タイマの満了時間を変更します
        int mod_timer(struct timer_list *timer, unsigned long expires);        
        //SMP版の「del_timer」、ロックを保持している間は、同じロックを獲得しないように要注意。
        int del_timer_sync(struct timer_list *timer);                        
        //タイマがスケジュールされているか否かを論理値で返します。
        int timer_pending(const struct timer_list * timer);                    
    ②、カーネルタイマの実裝                                                                        
        略。                                                                    
5)、タスクレット                                            
    タスクレットはたいていは割り込み管理のために使われます。
    カーネルタイマとは異なり、特定の時間に関數を実行させることはできませんが、スケジュールすることによって、
    後で、カーネルが選択した時間に実行するようにできるだけです。
    カーネルタイマと同じように、「ソフトウェア割り込み」のコンテキストの中で(アトミックモードで)実行されます。
    タスクレットはデータ構造體として存在します。                                                                                    
        struct tasklet_struct {                                                                                
          /* ... */                                                                                

          void (*func)(unsigned long);                                                                                
          unsigned long data;                                                                                
        };                                                                                

        void tasklet_init(struct tasklet_struct *t, void (*func)(unsigned long), unsigned long data);
        DECLARE_TASKLET(name, func, data);                                                                
        DECLARE_TASKLET_DISABLED(name, func, data);                                                                

    タスクレットの特徴:                                                                    
    ・タスクレットをいったん無効にして、後で再び有効にすることができます。                
    ・タイマとまったく同じように、タスクレットも自分自身を再登録できます。                
    ・標準の優先度か 高い優先度で実行するようにスケジュールできます。                    
    ・システムに非常に負荷がかかった狀態でなければ、タスクレットは直ちに実行されます。    
    ・同じタスクレットは複數のプロセッサ上で、同時には実行されません。                    

    関連関數:                                                                    
        void tasklet_disable(struct tasklet_struct *t);            
        void tasklet_disable_nosync(struct tasklet_struct *t);    
        void tasklet_enable(struct tasklet_struct *t);            
        void tasklet_schedule(struct tasklet_struct *t);        
        // 高い優先度でタスクレットの実行をスケジュールします。
        void tasklet_hi_schedule(struct tasklet_struct *t);        
        void tasklet_kill(struct tasklet_struct *t);                                                                                                
6)、作業待ち列                                                                        
    目ためはタスクレットに似ていますが、大きな違いがいくつかあります。                    
    ・作業待ち列の関數は特殊なカーネルプロセスのコンテキスト內で実行されますから、アトミックである必要がありません。
    ・カーネルコードは作業待ち列関數の実行を、指定した時間遅延をさせることができます。
    ・作業待ち列はスリープできます。                                                
    ・タスクレットが短い期間に素早く、アトミックモードで実行されるのに対して、
     作業待ち列の関數は待ち時間がばらつきますが、アトミックでなくでもよいです。

    関連関數:
        struct workqueue_struct *create_workqueue(const char *name);
        struct workqueue_struct *create_singlethread_workqueue(const char *name);

        DECLARE_WORK(name, void (*function)(void *), void *data);    //タスクを作業待ち列に登録
        int queue_work(struct workqueue_struct *queue, struct work_struct *work);
        int queue_delayed_work(struct workqueue_struct *queue, struct work_struct *work, unsigned long delay);

        INIT_WORK(struct work_struct *work,
                  void (*function)(void *),
                  void *data);                                         //実行時にwork_struct構造體を設定、完全初期化
        PREPARE_WORK(struct work_struct *work,
                     void (*function)(void *),
                     void *data);                                     //作業待ち列に登録した構造體を変更

        int cancel_delayed_work(struct work_struct *work);             //作業待ち列エントリをキャンセル                        
        void flush_workqueue(struct workqueue_struct *queue);      //ワーク関數が実行されていないことを保証するには、この関數                        
        void destroy_workqueue(struct workqueue_struct *queue);                                                                             

    共有待ち列は、カーネルが提供する共有の作業待ち列であり、ドライバ自身の作業待ち列ではありません。
    ・待ち列を誰かと共有することに注意を払う
    ・待ち列を長時間獨佔してはいけない
    ・プロセッサの中でタスクの順番が來るのにも時間がかかります

        int schedule_work(struct work_struct *work);
        int schedule_delayed_work(struct work_struct *work, unsigned long delay);
        void flush_scheduled_work(void);
        int cancel_delayed_work(struct work_struct *work);

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.