<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      《Linux內(nèi)核設(shè)計與實(shí)現(xiàn)》讀書筆記(八)- 中斷下半部的處理

      在前一章也提到過,之所以中斷會分成上下兩部分,是由于中斷對時限的要求非常高,需要盡快的響應(yīng)硬件。

      主要內(nèi)容:

      • 中斷下半部處理
      • 實(shí)現(xiàn)中斷下半部的機(jī)制
      • 總結(jié)中斷下半部的實(shí)現(xiàn)
      • 中斷實(shí)現(xiàn)示例

       

      1. 中斷下半部處理

      那么對于一個中斷,如何劃分上下兩部分呢?哪些處理放在上半部,哪些處理放在下半部?

      這里有一些經(jīng)驗可供借鑒:

      1. 如果一個任務(wù)對時間十分敏感,將其放在上半部
      2. 如果一個任務(wù)和硬件有關(guān),將其放在上半部
      3. 如果一個任務(wù)要保證不被其他中斷打斷,將其放在上半部
      4. 其他所有任務(wù),考慮放在下半部

       

      2. 實(shí)現(xiàn)中斷下半部的機(jī)制

      實(shí)現(xiàn)下半部的方法很多,隨著內(nèi)核的發(fā)展,產(chǎn)生了一些新的方法,也淘汰了一些舊方法。

      目前使用最多的是以下3中方法

      • 2.1 軟中斷
      • 2.2 tasklet
      • 2.3 工作隊列

      2.1 軟中斷

      軟中斷的代碼在:kernel/softirq.c

       

      軟中斷的流程如下:

      softirq

      流程圖中幾個步驟的說明:

      ① 注冊軟中斷的函數(shù) open_softirq參見 kernel/softirq.c文件)

      /* 
       * 將軟中斷類型和軟中斷處理函數(shù)加入到軟中斷序列中
       * @nr                                 - 軟中斷類型
       * @(*action)(struct softirq_action *) - 軟中斷處理的函數(shù)指針
       */
      void open_softirq(int nr, void (*action)(struct softirq_action *))
      {
          /* softirq_vec是個struct softirq_action類型的數(shù)組 */
          softirq_vec[nr].action = action;
      }

      軟中斷類型目前有10個,其定義在 include/linux/interrupt.h 文件中:

      enum
      {
          HI_SOFTIRQ=0,
          TIMER_SOFTIRQ,
          NET_TX_SOFTIRQ,
          NET_RX_SOFTIRQ,
          BLOCK_SOFTIRQ,
          BLOCK_IOPOLL_SOFTIRQ,
          TASKLET_SOFTIRQ,
          SCHED_SOFTIRQ,
          HRTIMER_SOFTIRQ,
          RCU_SOFTIRQ,    /* Preferable RCU should always be the last softirq */
      
          NR_SOFTIRQS
      };

      struct softirq_action 的定義也在 include/linux/interrupt.h 文件中

      /*
       * 這個結(jié)構(gòu)體的字段是個函數(shù)指針,字段名稱是action
       * 函數(shù)指針的返回指是void型
       * 函數(shù)指針的參數(shù)是 struct softirq_action 的地址,其實(shí)就是指向 softirq_vec 中的某一項
       *     如果 open_softirq 是這樣調(diào)用的: open_softirq(NET_TX_SOFTIRQ, my_tx_action);
       *     那么 my_tx_action 的參數(shù)就是 softirq_vec[NET_TX_SOFTIRQ]的地址
       */
      struct softirq_action
      {
          void    (*action)(struct softirq_action *);
      };

       

      ② 觸發(fā)軟中斷的函數(shù) raise_softirq 參見 kernel/softirq.c文件

      /*
       * 觸發(fā)某個中斷類型的軟中斷
       * @nr - 被觸發(fā)的中斷類型
       * 從函數(shù)中可以看出,在處理軟中斷前后有保存和恢復(fù)寄存器的操作
       */
      void raise_softirq(unsigned int nr)
      {
          unsigned long flags;
      
          local_irq_save(flags);
          raise_softirq_irqoff(nr);
          local_irq_restore(flags);
      }

       

      ③ 執(zhí)行軟中斷 do_softirq 參見 kernel/softirq.c文件

      asmlinkage void do_softirq(void)
      {
          __u32 pending;
          unsigned long flags;
      
          /* 判斷是否在中斷處理中,如果正在中斷處理,就直接返回 */
          if (in_interrupt())
              return;
      
          /* 保存當(dāng)前寄存器的值 */
          local_irq_save(flags);
      
          /* 取得當(dāng)前已注冊軟中斷的位圖 */
          pending = local_softirq_pending();
      
          /* 循環(huán)處理所有已注冊的軟中斷 */
          if (pending)
              __do_softirq();
      
          /* 恢復(fù)寄存器的值到中斷處理前 */
          local_irq_restore(flags);
      }

       

      ④ 執(zhí)行相應(yīng)的軟中斷 - 執(zhí)行自己寫的中斷處理

      linux中,執(zhí)行軟中斷有專門的內(nèi)核線程,每個處理器對應(yīng)一個線程,名稱ksoftirqd/n (n對應(yīng)處理器號)

      通過top命令查看我的單核虛擬機(jī),CentOS系統(tǒng)中的ksoftirqd線程如下:

      [root@vbox ~]# top | grep ksoftirq
          4 root      20   0     0    0    0 S  0.0  0.0   0:00.02 ksoftirqd/0

       

      2.2 tasklet

      tasklet也是利用軟中斷來實(shí)現(xiàn)的,但是它提供了比軟中斷更好用的接口(其實(shí)就是基于軟中斷又封裝了一下),

      所以除了對性能要求特別高的情況,一般建議使用tasklet來實(shí)現(xiàn)自己的中斷。

       

      tasklet對應(yīng)的結(jié)構(gòu)體在 <linux/interrupt.h> 中

      struct tasklet_struct
      {
          struct tasklet_struct *next; /* 鏈表中的下一個tasklet */
          unsigned long state;         /* tasklet狀態(tài) */
          atomic_t count;              /* 引用計數(shù)器 */
          void (*func)(unsigned long); /* tasklet處理函數(shù) */
          unsigned long data;          /* tasklet處理函數(shù)的參數(shù) */
      };

      tasklet狀態(tài)只有3種值:

      1. 值 0 表示該tasklet沒有被調(diào)度
      2. 值 TASKLET_STATE_SCHED 表示該tasklet已經(jīng)被調(diào)度
      3. 值 TASKLET_STATE_RUN 表示該tasklet已經(jīng)運(yùn)行

      引用計數(shù)器count 的值不為0,表示該tasklet被禁止。


      tasklet使用流程如下:

      1. 聲明tasklet (參見<linux/interrupt.h>)

      /* 靜態(tài)聲明一個tasklet */
      #define DECLARE_TASKLET(name, func, data) \
      struct tasklet_struct name = { NULL, 0, ATOMIC_INIT(0), func, data }
      
      #define DECLARE_TASKLET_DISABLED(name, func, data) \
      struct tasklet_struct name = { NULL, 0, ATOMIC_INIT(1), func, data }
      
      /* 動態(tài)聲明一個tasklet 傳遞一個tasklet_struct指針給初始化函數(shù) */
      extern void tasklet_init(struct tasklet_struct *t,
                   void (*func)(unsigned long), unsigned long data);

      2. 編寫處理程序

      參照tasklet處理函數(shù)的原型來寫自己的處理邏輯

      void tasklet_handler(unsigned long date)

      3. 調(diào)度tasklet

      中斷的上半部處理完后調(diào)度tasklet,在適當(dāng)時候進(jìn)行下半部的處理

      tasklet_schedule(&my_tasklet)  /* my_tasklet就是之前聲明的tasklet_struct */

       

      2.3 工作隊列

      工作隊列子系統(tǒng)是一個用于創(chuàng)建內(nèi)核線程的接口,通過它可以創(chuàng)建一個工作者線程來專門處理中斷的下半部工作。

      工作隊列和tasklet不一樣,不是基于軟中斷來實(shí)現(xiàn)的。

       

      缺省的工作者線程名稱是 events/n (n對應(yīng)處理器號)。

      通過top命令查看我的單核虛擬機(jī),CentOS系統(tǒng)中的events線程如下:

      [root@vbox ~]# top | grep event
          7 root      20   0     0    0    0 S  0.0  0.0   0:03.71 events/0

       

      工作隊列主要用到下面3個結(jié)構(gòu)體,弄懂了這3個結(jié)構(gòu)體的關(guān)系,也就知道工作隊列的處理流程了。

      /* 在 include/linux/workqueue.h 文件中定義 */
      struct work_struct {
          atomic_long_t data;             /* 這個并不是處理函數(shù)的參數(shù),而是表示此work是否pending等狀態(tài)的flag */
      #define WORK_STRUCT_PENDING 0        /* T if work item pending execution */
      #define WORK_STRUCT_FLAG_MASK (3UL)
      #define WORK_STRUCT_WQ_DATA_MASK (~WORK_STRUCT_FLAG_MASK)
          struct list_head entry;         /* 中斷下半部處理函數(shù)的鏈表 */
          work_func_t func;               /* 處理中斷下半部工作的函數(shù) */
      #ifdef CONFIG_LOCKDEP
          struct lockdep_map lockdep_map;
      #endif
      };
      
      /* 在 kernel/workqueue.c文件中定義
       * 每個工作者線程對應(yīng)一個 cpu_workqueue_struct ,其中包含要處理的工作的鏈表
       * (即 work_struct 的鏈表,當(dāng)此鏈表不空時,喚醒工作者線程來進(jìn)行處理)
       */
      /*
       * The per-CPU workqueue (if single thread, we always use the first
       * possible cpu).
       */
      struct cpu_workqueue_struct {
      
          spinlock_t lock;                   /* 鎖保護(hù)這種結(jié)構(gòu) */
      
          struct list_head worklist;         /* 工作隊列頭節(jié)點(diǎn) */
          wait_queue_head_t more_work;
          struct work_struct *current_work;
      
          struct workqueue_struct *wq;       /* 關(guān)聯(lián)工作隊列結(jié)構(gòu) */
          struct task_struct *thread;        /* 關(guān)聯(lián)線程 */
      } ____cacheline_aligned;
      
      /* 也是在 kernel/workqueue.c 文件中定義的
       * 每個 workqueue_struct 表示一種工作者類型,系統(tǒng)默認(rèn)的就是 events 工作者類型
       * 每個工作者類型一般對應(yīng)n個工作者線程,n就是處理器的個數(shù)
       */
      /*
       * The externally visible workqueue abstraction is an array of
       * per-CPU workqueues:
       */
      struct workqueue_struct {
          struct cpu_workqueue_struct *cpu_wq;  /* 工作者線程 */
          struct list_head list;
          const char *name;
          int singlethread;
          int freezeable;        /* Freeze threads during suspend */
          int rt;
      #ifdef CONFIG_LOCKDEP
          struct lockdep_map lockdep_map;
      #endif
      };

       

      使用工作者隊列的方法見下圖:

      workqueue

       

      ① 創(chuàng)建推后執(zhí)行的工作 - 有靜態(tài)創(chuàng)建和動態(tài)創(chuàng)建2種方法

      /* 靜態(tài)創(chuàng)建一個work_struct 
       * @n - work_struct結(jié)構(gòu)體,不用事先定義
       * @f - 下半部處理函數(shù)
       */
      #define DECLARE_WORK(n, f)                    \
          struct work_struct n = __WORK_INITIALIZER(n, f)
      
      /* 動態(tài)創(chuàng)建一個 work_struct
       * @_work - 已經(jīng)定義好的一個 work_struct
       * @_func - 下半部處理函數(shù)
       */
      #ifdef CONFIG_LOCKDEP
      #define INIT_WORK(_work, _func)                        \
          do {                                \
              static struct lock_class_key __key;            \
                                          \
              (_work)->data = (atomic_long_t) WORK_DATA_INIT();    \
              lockdep_init_map(&(_work)->lockdep_map, #_work, &__key, 0);\
              INIT_LIST_HEAD(&(_work)->entry);            \
              PREPARE_WORK((_work), (_func));                \
          } while (0)
      #else
      #define INIT_WORK(_work, _func)                        \
          do {                                \
              (_work)->data = (atomic_long_t) WORK_DATA_INIT();    \
              INIT_LIST_HEAD(&(_work)->entry);            \
              PREPARE_WORK((_work), (_func));                \
          } while (0)
      #endif

      工作隊列處理函數(shù)的原型:

      typedef void (*work_func_t)(struct work_struct *work);

       

      ② 刷新現(xiàn)有的工作,這個步驟不是必須的,可以直接從第①步直接進(jìn)入第③步

         刷新現(xiàn)有工作的意思就是在追加新的工作之前,保證隊列中的已有工作已經(jīng)執(zhí)行完了。

      /* 刷新系統(tǒng)默認(rèn)的隊列,即 events 隊列 */
      void flush_scheduled_work(void);
      
      /* 刷新用戶自定義的隊列
       * @wq - 用戶自定義的隊列
       */
      void flush_workqueue(struct workqueue_struct *wq);

       

      ③ 調(diào)度工作 - 調(diào)度新定義的工作,使之處于等待處理器執(zhí)行的狀態(tài)

      /* 調(diào)度第一步中新定義的工作,在系統(tǒng)默認(rèn)的工作者線程中執(zhí)行此工作
       * @work - 第一步中定義的工作
       */
      schedule_work(struct work_struct *work);
      
      /* 調(diào)度第一步中新定義的工作,在系統(tǒng)默認(rèn)的工作者線程中執(zhí)行此工作
       * @work  - 第一步中定義的工作
       * @delay - 延遲的時鐘節(jié)拍
       */
      int schedule_delayed_work(struct delayed_work *work, unsigned long delay);
      
      /* 調(diào)度第一步中新定義的工作,在用戶自定義的工作者線程中執(zhí)行此工作
       * @wq   - 用戶自定義的工作隊列類型
       * @work - 第一步中定義的工作
       */
      int queue_work(struct workqueue_struct *wq, struct work_struct *work);
      
      /* 調(diào)度第一步中新定義的工作,在用戶自定義的工作者線程中執(zhí)行此工作
       * @wq    - 用戶自定義的工作隊列類型
       * @work  - 第一步中定義的工作
       * @delay - 延遲的時鐘節(jié)拍
       */
      int queue_delayed_work(struct workqueue_struct *wq,
                  struct delayed_work *work, unsigned long delay);

       

      3. 總結(jié)中斷下半部的實(shí)現(xiàn)

      下面對實(shí)現(xiàn)中斷下半部工作的3種機(jī)制進(jìn)行總結(jié),便于在實(shí)際使用中決定使用哪種機(jī)制

      下半部機(jī)制

      上下文

      復(fù)雜度

      執(zhí)行性能

      順序執(zhí)行保障

      軟中斷 中斷
      (需要自己確保軟中斷的執(zhí)行順序及鎖機(jī)制)

      (全部自己實(shí)現(xiàn),便于調(diào)優(yōu))
      沒有
      tasklet 中斷
      (提供了簡單的接口來使用軟中斷)
      同類型不能同時執(zhí)行
      工作隊列 進(jìn)程
      (在進(jìn)程上下文中運(yùn)行,與寫用戶程序差不多)
      沒有
      (和進(jìn)程上下文一樣被調(diào)度)

       

      4. 中斷實(shí)現(xiàn)示例

      4.1 軟中斷的實(shí)現(xiàn)

      本來想用內(nèi)核模塊的方法來測試一下軟中斷的流程,但是編譯時發(fā)現(xiàn)軟中斷注冊函數(shù)(open_softirq)和觸發(fā)函數(shù)(raise_softirq)

      并沒有用EXPORT_SYMBOL導(dǎo)出,所以自定義的內(nèi)核模塊中無法使用。

      測試的代碼如下:

      #include <linux/interrupt.h>
      #include "kn_common.h"
      
      MODULE_LICENSE("Dual BSD/GPL");
      
      static void my_softirq_func(struct softirq_action*);
      
      static int testsoftirq_init(void)
      {
          // 注冊softirq,這里注冊的是定時器的下半部
          open_softirq(TIMER_SOFTIRQ, my_softirq_func);
          
          // 觸發(fā)softirq
          raise_softirq(TIMER_SOFTIRQ);
      
          return 0;
          
      }
      
      static void testsoftirq_exit(void)
      {
          printk(KERN_ALERT "*************************\n");
          print_current_time(0);
          printk(KERN_ALERT "testrbtree is exited!\n");
          printk(KERN_ALERT "*************************\n");
              
      }
      
      static void my_softirq_func(struct softirq_action* act)
      {
          printk(KERN_ALERT "=========================\n");
          print_current_time(0);
          printk(KERN_ALERT "my softirq function is been called!....\n");
          printk(KERN_ALERT "=========================\n");
      }
      
      
      module_init(testsoftirq_init);
      module_exit(testsoftirq_exit);

      其中頭文件 kn_common.h 的相關(guān)內(nèi)容參見之前的博客《Linux內(nèi)核設(shè)計與實(shí)現(xiàn)》讀書筆記(六)- 內(nèi)核數(shù)據(jù)結(jié)構(gòu)

       

      由于內(nèi)核沒有用EXPORT_SYMBOL導(dǎo)出open_softirqraise_softirq函數(shù),所以編譯時有如下警告:

      WARNING: "open_softirq" [/root/chap08/mysoftirq.ko] undefined!
      WARNING: "raise_softirq" [/root/chap08/mysoftirq.ko] undefined!

      注:編譯用的系統(tǒng)時centos6.3 (uname -r結(jié)果 - 2.6.32-279.el6.x86_64)

       

      沒辦法,只能嘗試修改內(nèi)核代碼(將open_softirq和raise_softirq用EXPORT_SYMBOL導(dǎo)出),再重新編譯內(nèi)核,然后再嘗試能否測試軟中斷。

      主要修改2個文件,(既然要修改代碼,干脆加了一種軟中斷類型):

      /* 修改 kernel/softirq.c */
      // ... 略 ...
      char *softirq_to_name[NR_SOFTIRQS] = {
          "HI", "TIMER", "NET_TX", "NET_RX", "BLOCK", "BLOCK_IOPOLL",
          "TASKLET", "SCHED", "HRTIMER",  "RCU", "WYB"
      };  /* 追加了一種新的softirq,即 "WYB",我名字的縮寫 ^_^ */
      
      // ... 略 ...
      
      void raise_softirq(unsigned int nr)
      {
          unsigned long flags;
      
          local_irq_save(flags);
          raise_softirq_irqoff(nr);
          local_irq_restore(flags);
      }
      EXPORT_SYMBOL(raise_softirq);   /* 追加的代碼 */
      
      void open_softirq(int nr, void (*action)(struct softirq_action *))
      {
          softirq_vec[nr].action = action;
      }
      EXPORT_SYMBOL(open_softirq);    /* 追加的代碼 */
      
      // ... 略 ...
      
      
      /* 還修改了 include/linux/interrupt.h */
      enum
      {
          HI_SOFTIRQ=0,
          TIMER_SOFTIRQ,
          NET_TX_SOFTIRQ,
          NET_RX_SOFTIRQ,
          BLOCK_SOFTIRQ,
          BLOCK_IOPOLL_SOFTIRQ,
          TASKLET_SOFTIRQ,
          SCHED_SOFTIRQ,
          HRTIMER_SOFTIRQ,
          RCU_SOFTIRQ,    /* Preferable RCU should always be the last softirq */
      
          WYB_SOFTIRQS,   /* 追加的一種中斷類型 */
          NR_SOFTIRQS
      };

      重新編譯內(nèi)核后,在新的內(nèi)核上再次實(shí)驗軟中斷代碼:

      (編譯內(nèi)核方法參見:《Linux內(nèi)核設(shè)計與實(shí)現(xiàn)》讀書筆記(五)- 系統(tǒng)調(diào)用 3.3節(jié))

      測試軟中斷的代碼:testsoftirq.c

      #include <linux/interrupt.h>
      #include "kn_common.h"
      
      MODULE_LICENSE("Dual BSD/GPL");
      
      static void my_softirq_func(struct softirq_action*);
      
      static int testsoftirq_init(void)
      {
          printk(KERN_ALERT "interrupt's top half!\n");
          
          // 注冊softirq,這里注冊的是自定義的軟中斷類型
          open_softirq(WYB_SOFTIRQS, my_softirq_func);
          
          // 觸發(fā)softirq
          raise_softirq(WYB_SOFTIRQS);
      
          return 0;
          
      }
      
      static void testsoftirq_exit(void)
      {
          printk(KERN_ALERT "*************************\n");
          print_current_time(0);
          printk(KERN_ALERT "testsoftirq is exited!\n");
          printk(KERN_ALERT "*************************\n");
              
      }
      
      static void my_softirq_func(struct softirq_action* act)
      {
          printk(KERN_ALERT "=========================\n");
          print_current_time(0);
          printk(KERN_ALERT "my softirq function is been called!....\n");
          printk(KERN_ALERT "=========================\n");
      }
      
      
      module_init(testsoftirq_init);
      module_exit(testsoftirq_exit);

       

      Makefile:

      obj-m += mysoftirq.o
      mysoftirq-objs := testsoftirq.o kn_common.o
      
      #generate the path
      CURRENT_PATH:=$(shell pwd)
      #the current kernel version number
      LINUX_KERNEL:=$(shell uname -r)
      #the absolute path
      LINUX_KERNEL_PATH:=/usr/src/kernels/$(LINUX_KERNEL)
      #complie object
      all:
          make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) modules
          rm -rf modules.order Module.symvers .*.cmd *.o *.mod.c .tmp_versions *.unsigned
      #clean
      clean:
          rm -rf modules.order Module.symvers .*.cmd *.o *.mod.c *.ko .tmp_versions *.unsigned

       

      測試軟中斷的方法如下:

      make
      insmod mysoftirq.ko
      rmmod mysoftirq
      dmesg | tail -9
      
      # 運(yùn)行結(jié)果
      interrupt's top half!
      =========================
      2013-4-22 14:4:57
      my softirq function is been called!....
      =========================
      *************************
      2013-4-22 14:5:2
      testsoftirq is exited!
      *************************

       

      4.2 tasklet的實(shí)現(xiàn)

      tasklet的實(shí)驗用默認(rèn)的內(nèi)核即可,我們切換到centos6.3的默認(rèn)內(nèi)核(uname -r: 2.6.32-279.el6.x86_64)

      從中我們也可以看出,內(nèi)核之所以沒有導(dǎo)出open_softirq和raise_softirq函數(shù),可能還是因為提倡我們盡量用tasklet來實(shí)現(xiàn)中斷的下半部工作。

       

      tasklet測試代碼:testtasklet.c

      #include <linux/interrupt.h>
      #include "kn_common.h"
      
      MODULE_LICENSE("Dual BSD/GPL");
      
      static void my_tasklet_func(unsigned long);
      
      /* mytasklet 必須定義在testtasklet_init函數(shù)的外面,否則會出錯 */
      DECLARE_TASKLET(mytasklet, my_tasklet_func, 1000);
      
      static int testtasklet_init(void)
      {
          printk(KERN_ALERT "interrupt's top half!\n");
      
          // 如果在這里定義的話,那么 mytasklet是函數(shù)的局部變量,
          // 后面調(diào)度的時候會找不到 mytasklet
          // DECLARE_TASKLET(mytasklet, my_tasklet_func, 1000);
      
          // 調(diào)度tasklet, 處理器會在適當(dāng)時候執(zhí)行這個tasklet
          tasklet_schedule(&mytasklet);
          
          return 0;
          
      }
      
      static void testtasklet_exit(void)
      {
          printk(KERN_ALERT "*************************\n");
          print_current_time(0);
          printk(KERN_ALERT "testtasklet is exited!\n");
          printk(KERN_ALERT "*************************\n");
              
      }
      
      static void my_tasklet_func(unsigned long data)
      {
          printk(KERN_ALERT "=========================\n");
          print_current_time(0);
          printk(KERN_ALERT "my tasklet function is been called!....\n");
          printk(KERN_ALERT "parameter data is %ld\n", data);
          printk(KERN_ALERT "=========================\n");
      }
      
      
      module_init(testtasklet_init);
      module_exit(testtasklet_exit);

       

      Makefile:

      obj-m += mytasklet.o
      mytasklet-objs := testtasklet.o kn_common.o
      
      #generate the path
      CURRENT_PATH:=$(shell pwd)
      #the current kernel version number
      LINUX_KERNEL:=$(shell uname -r)
      #the absolute path
      LINUX_KERNEL_PATH:=/usr/src/kernels/$(LINUX_KERNEL)
      #complie object
      all:
          make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) modules
          rm -rf modules.order Module.symvers .*.cmd *.o *.mod.c .tmp_versions *.unsigned
      #clean
      clean:
          rm -rf modules.order Module.symvers .*.cmd *.o *.mod.c *.ko .tmp_versions *.unsigned

       

      測試tasklet的方法如下:

      make
      insmod mytasklet.ko
      rmmod mytasklet
      dmesg | tail -10
      
      # 運(yùn)行結(jié)果
      interrupt's top half!
      =========================
      2013-4-22 14:53:14
      my tasklet function is been called!....
      parameter data is 1000
      =========================
      *************************
      2013-4-22 14:53:20
      testtasklet is exited!
      *************************

       

      4.3 工作隊列的實(shí)現(xiàn)

      workqueue的例子的中靜態(tài)定義了一個工作,動態(tài)定義了一個工作。

      靜態(tài)定義的工作由系統(tǒng)工作隊列(events/n)調(diào)度,

      動態(tài)定義的工作由自定義的工作隊列(myworkqueue)調(diào)度。

       

      測試工作隊列的代碼:testworkqueue.c

      #include <linux/workqueue.h>
      #include "kn_common.h"
      
      MODULE_LICENSE("Dual BSD/GPL");
      
      static void my_work_func(struct work_struct *);
      static void my_custom_workqueue_func(struct work_struct *);
      
      /* 靜態(tài)創(chuàng)建一個工作,使用系統(tǒng)默認(rèn)的工作者線程,即 events/n */
      DECLARE_WORK(mywork, my_work_func);
      
      static int testworkqueue_init(void)
      {
          /*自定義的workqueue */
          struct workqueue_struct *myworkqueue = create_workqueue("myworkqueue");
      
          /* 動態(tài)創(chuàng)建一個工作 */
          struct work_struct *mywork2;
          mywork2 = kmalloc(sizeof(struct work_struct), GFP_KERNEL);
          INIT_WORK(mywork2, my_custom_workqueue_func);
                        
          printk(KERN_ALERT "interrupt's top half!\n");
      
          /* 刷新系統(tǒng)默認(rèn)的隊列 */
          flush_scheduled_work();
          /* 調(diào)度工作 */
          schedule_work(&mywork);
      
          /* 刷新自定義的工作隊列 */
          flush_workqueue(myworkqueue);
          /* 調(diào)度自定義工作隊列上的工作 */
          queue_work(myworkqueue, mywork2);
      
          return 0;
      }
      
      static void testworkqueue_exit(void)
      {
          printk(KERN_ALERT "*************************\n");
          print_current_time(0);
          printk(KERN_ALERT "my workqueue test is exited!\n");
          printk(KERN_ALERT "*************************\n");
              
      }
      
      static void my_work_func(struct work_struct *work)
      {
          printk(KERN_ALERT "=========================\n");
          print_current_time(0);
          printk(KERN_ALERT "my workqueue function is been called!....\n");
          printk(KERN_ALERT "=========================\n");
      }
      
      static void my_custom_workqueue_func(struct work_struct *work)
      {
          printk(KERN_ALERT "=========================\n");
          print_current_time(0);
          printk(KERN_ALERT "my cutomize workqueue function is been called!....\n");
          printk(KERN_ALERT "=========================\n");
          kfree(work);
      }
      
      module_init(testworkqueue_init);
      module_exit(testworkqueue_exit);

       

      Makefile:

      obj-m += myworkqueue.o
      myworkqueue-objs := testworkqueue.o kn_common.o
      
      #generate the path
      CURRENT_PATH:=$(shell pwd)
      #the current kernel version number
      LINUX_KERNEL:=$(shell uname -r)
      #the absolute path
      LINUX_KERNEL_PATH:=/usr/src/kernels/$(LINUX_KERNEL)
      #complie object
      all:
          make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) modules
          rm -rf modules.order Module.symvers .*.cmd *.o *.mod.c .tmp_versions *.unsigned
      #clean
      clean:
          rm -rf modules.order Module.symvers .*.cmd *.o *.mod.c *.ko .tmp_versions *.unsigned

       

      測試workqueue的方法如下:

      make
      insmod myworkqueue.ko
      rmmod myworkqueue
      dmesg | tail -13
      
      # 運(yùn)行結(jié)果
      interrupt's top half!
      =========================
      2013-4-23 9:55:29
      my workqueue function is been called!....
      =========================
      =========================
      2013-4-23 9:55:29
      my cutomize workqueue function is been called!....
      =========================
      *************************
      2013-4-23 9:55:29
      my workqueue is exited!
      *************************
      posted @ 2013-04-23 11:18  wang_yb  閱讀(13933)  評論(3)    收藏  舉報
      主站蜘蛛池模板: 日本免费人成视频在线观看| 国产99久60在线视频 | 传媒| 亚洲av无码片在线播放| 国产午夜一区二区在线观看| 国内精品伊人久久久久影院对白| 欧美精品人人做人人爱视频| 狼人大伊人久久一区二区| 国产精品v片在线观看不卡| 国产成人啪精品视频免费软件| 精品久久亚洲中文无码| 精品国产亚洲午夜精品a| 伊人久久大香线蕉av一区二区| 天天躁夜夜躁狠狠喷水| 视频一区二区三区四区五区| 亚洲人成网站在线播放2019 | 精品国产自线午夜福利| 国产精品一区二区中文| 武装少女在线观看高清完整版免费 | 国产老熟女无套内射不卡| 水蜜桃精品综合视频在线| 亚洲欧美另类久久久精品播放的| 口爆少妇在线视频免费观看| 中日韩中文字幕一区二区| 强奷乱码中文字幕| 4480yy亚洲午夜私人影院剧情| 亚洲av免费看一区二区| 亚洲av午夜福利精品一区二区| 2019国产精品青青草原| 日本不卡不二三区在线看| 国产高清一区二区三区视频 | 国产成人啪精品午夜网站 | 国产激情一区二区三区午夜| 欧美老少配性行为| 好紧好爽好湿别拔出来视频男男 | 2019亚洲午夜无码天堂| 丰满人妻一区二区三区色| 波多野结系列18部无码观看AV| 读书| 国产微拍一区二区三区四区| 国产成人午夜精品影院| 国产第一页浮力影院入口|