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

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

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

      【linux kernel】 中斷處理-中斷下半部

      歡迎轉載,轉載時需保留作者信息,謝謝。

      郵箱:tangzhongp@163.com

      博客園地址:http://www.rzrgm.cn/embedded-tzp

      Csdn博客地址:http://blog.csdn.net/xiayulewa

       

       

      1.   概述

      Linux內核中斷機制:為了在中斷執行時間盡可能短和中斷處理需要完成大量工作之間找到一個平衡點,Linux將中斷處理程序分解為兩個半部,頂半部和底半部。

            頂半部完成盡可能少的比較緊急的任務,它往往只是簡單地讀取寄存器中的中斷狀態并清除中斷標志位就進行登記工作,將底半部處理程序掛到該設備的底半部執行隊列中去。

            那上半部和下半部是分界線是什么? request_irq注冊的中斷函數為分界線。

            上半部:

              當執行完request_irq注冊的中斷函數后,上半部結束。在注冊的中斷函數中,登記下半部要做的工作。

             上半部已經討論了:http://www.rzrgm.cn/embedded-tzp/p/4451354.html

            底半部實現方式有:

                  軟中斷

                  tasklet

                  工作隊列

           

      2.   軟中斷

      http://www.rzrgm.cn/embedded-tzp/p/4452041.html中已討論。

      3.   Tasklet

      3.1. 數據結構

      struct tasklet_struct

      {

          struct tasklet_struct *next;

          unsigned long state;

          atomic_t count;

          void (*func)(unsigned long);

          unsigned long data;

      };

      該結構體由tasklet_init初始化。

       

      Softirq.c (src\kernel)中:

      struct tasklet_head

      {

          struct tasklet_struct *head;

          struct tasklet_struct **tail;

      };

      struct tasklet_head  tasklet_vec;

      3.2. 注冊

      softirq_init中有:

      open_softirq(TASKLET_SOFTIRQ, tasklet_action);

      可見其實現方式為軟件中斷。其處理函數為tasklet_action

       

      tasklet_schedule__tasklet_schedule

          t->next = NULL;

          *__this_cpu_read(tasklet_vec.tail) = t;

          __this_cpu_write(tasklet_vec.tail, &(t->next));

      tasklet對象組織成一張鏈表。

       

      3.3. 執行流程

             tasklet_schedule__tasklet_scheduleraise_softirq_irqoff(TASKLET_SOFTIRQ);

          觸發軟中斷執行,軟中斷執行流程見http://www.rzrgm.cn/embedded-tzp/p/4452041.html

          最終軟中斷會執行到tasklet的處理函數tasklet_action

      tasklet_action中是個while,循環處理tasklet_vec鏈表中的struct   tasklet_struct

       

       

      3.4. 其它:

      * 不允許訪問用戶空間;?????
      *
      不允許訪問current指針;
      *
      不能執行休眠或調度。

      特征:

      * 一個tasklet可被禁用或啟用;只用啟用的次數和禁用的次數相同時,tasklet才會被執行。
      *
      和定時器類似,tasklet可以注冊自己;
      * tasklet
      可被調度在一般優先級或者高優先級上執行,高優先級總會首先執行;
      *
      如果系統負荷不重,則tasklet會立即得到執行,且始終不會晚于下一個定時器滴答;
      *
      一個tasklet可以和其他tasklet并發,但對自身來講必須嚴格串行處理,即一個tasklet不會在多個處理器上執行。

       

      3.5. 函數集合

       

      DECLARE_TASKLET(name,func,data); /*定義及初始化tasklet*/

      DECLARE_TASKLET_DISABLED(name,func,data);      /*定義及初始化后禁止該tasklet*/

       

      void tasklet_init(struct tasklet_struct *t, void (*func)(unsigned long), unsigned long data); /* 初始化tasklet,func指向要執行的函數,data為傳遞給函數func的參數 */

       

      void tasklet_disable(struct tasklet_struct *t) /*禁用指定tasklet */

      void tasklet_disable_nosync(struct tasklet_struct *t) /*禁用指定tasklet,但不會等待任何正在運行的tasklet退出*/

      void tasklet_enable(struct tasklet_struct *t)      /*啟用先前被禁用的tasklet*/

       

      void tasklet_schedule(struct tasklet_struct *t)    /*注冊并調度執行指定的tasklet*/

      void tasklet_hi_schedule(struct tasklet_struct *t) /*調度指定的tasklet以高優先級執行*/

       

      void tasklet_kill(struct tasklet_struct *t)   /*移除指定tasklet*/

       

      3.6.    實例

      #include <linux/init.h>

      #include <linux/module.h>

      #include <linux/irq.h>

      #include <linux/interrupt.h>

      #include <asm/gpio.h>

      #include <plat/gpio-cfg.h>

       

      /*硬件相關的數據結構*/

      struct btn_resource {

          int irq;  //中斷號

          char *name; //中斷名稱

      };

       

      //初始化板卡按鍵信息

      static struct btn_resource btn_info[] = {

          [0] = {

              .irq = IRQ_EINT(0),

              .name = "KEY_UP"

          },

          [1] = {

              .irq = IRQ_EINT(1),

              .name = "KEY_DOWN"

          }

      };

       

      //tasklet的處理函數,在tasklet_action函數中被處理

      static void btn_tasklet_func(unsigned long data)

      {

          struct btn_resource *pdata = (struct btn_resource *)data;

          printk("%s: irq = %d, name = %s\n",

                  __func__, pdata->irq, pdata->name);

      }

       

      //定義tasklet對象

      static DECLARE_TASKLET(btn_tasklet,

                      btn_tasklet_func, (unsigned long)&btn_info[0]);

       

      //中斷處理函數

      //irq:中斷號,dev_id:保存注冊中斷時傳遞的參數信息

      static irqreturn_t button_isr(int irq, void *dev_id)

      {

          //登記底半部信息

          tasklet_schedule(&btn_tasklet); //注冊并調度taskletCPU會在空閑時執行tasklet的處理函數

          printk("%s\n", __func__);

          return IRQ_HANDLED; //成功,失敗:IRQ_NONE

      }

       

      static int btn_init(void)

      {

          //申請中斷和注冊中斷處理程序

          /*

           * IRQ_EINT(0):中斷號

           * button_isr:中斷處理程序,中斷發生以后,內核執行此函數

           * IRQF*:外部中斷的觸發方式,內部中斷此參數為0

           * KEY_UP:中斷名稱,出現在 cat /proc/interrupts

           * &mydata:給中斷處理程序傳遞的參數信息,不傳遞參數指定為NULL

           */

          int i;

       

          for (i = 0; i < ARRAY_SIZE(btn_info); i++)

              request_irq(btn_info[i].irq, button_isr,

                      IRQF_TRIGGER_RISING|IRQF_TRIGGER_FALLING,

                      btn_info[i].name, &btn_info[i]);

          printk("%s\n", __func__);

          return 0;

      }

       

      static void btn_exit(void)

      {

          int i;

       

          for (i = 0; i < ARRAY_SIZE(btn_info); i++)

              free_irq(btn_info[i].irq, &btn_info[i]);

          printk("%s\n", __func__);

      }

      module_init(btn_init);

      module_exit(btn_exit);

      MODULE_LICENSE("GPL");

       

       

      4.   工作隊列

             工作隊列涉及到linux內核進程調度策略,但調度策略不是此處應該討論的。

      4.1. 內核默認隊列

      4.1.1.   數據結構

      struct work_struct

      struct delayed_work

       

      初始化隊列對象

          INIT_WORK(&mywork, my_work_func);

          INIT_DELAYED_WORK(&mydwork, my_dwork_func);

       

      4.1.2.   函數集合

      schedule_work(&mywork)

      schedule_delayed_work(&mydwork, 5*HZ)

       

      4.1.3.   為什么是內核默認隊列

      Workqueue.c (src\kernel) 中:

          init_workqueues

             system_wq = alloc_workqueue("events", 0, 0);

       

      schedule_work

          queue_work(system_wq, work);

       

      可見struct work_struct對象是默認放在缺省的內核線程線程events中。當缺省工作隊列負載太重,執行效率會很低,需要我們自建隊列

       

      4.1.4.   實例

      #include <linux/init.h>

      #include <linux/module.h>

      #include <linux/irq.h>

      #include <linux/interrupt.h>

      #include <asm/gpio.h>

      #include <plat/gpio-cfg.h>

       

      /*硬件相關的數據結構*/

      struct btn_resource {

          int irq;  //中斷號

          char *name; //中斷名稱

      };

       

      //初始化板卡按鍵信息

      static struct btn_resource btn_info[] = {

          [0] = {

              .irq = IRQ_EINT(0),

              .name = "KEY_UP"

          },

          [1] = {

              .irq = IRQ_EINT(1),

              .name = "KEY_DOWN"

          }

      };

       

      //分配工作和延時工作

      static struct work_struct mywork;

       

      //工作隊列處理函數

      static void my_work_func(struct work_struct *work)

      {

          printk("%s\n", __func__);

      }

       

      //中斷處理函數

      //irq:中斷號,dev_id:保存注冊中斷時傳遞的參數信息

      static irqreturn_t button_isr(int irq, void *dev_id)

      {

          //登記工作底半部信息

          /*

           * schedule_work:登記工作,將工作交給內核默認的工作隊列和

           * 內核線程去管理和調度執行,cpu會在適當的時候會執行工作的處理函數

           * */

          schedule_work(&mywork);

       

          printk("%s\n", __func__);

          return IRQ_HANDLED; //成功,失敗:IRQ_NONE

      }

       

      static int btn_init(void)

      {

          //申請中斷和注冊中斷處理程序

          /*

           * IRQ_EINT(0):中斷號

           * button_isr:中斷處理程序,中斷發生以后,內核執行此函數

           * IRQF*:外部中斷的觸發方式,內部中斷此參數為0

           * KEY_UP:中斷名稱,出現在 cat /proc/interrupts

           * &mydata:給中斷處理程序傳遞的參數信息,不傳遞參數指定為NULL

           */

          int i;

       

          for (i = 0; i < ARRAY_SIZE(btn_info); i++)

              request_irq(btn_info[i].irq, button_isr,

                      IRQF_TRIGGER_RISING|IRQF_TRIGGER_FALLING,

                      btn_info[i].name, &btn_info[i]);

         

          //初始化工作和延時工作

          INIT_WORK(&mywork, my_work_func);

       

          printk("%s\n", __func__);

          return 0;

      }

       

      static void btn_exit(void)

      {

          int i;

       

          for (i = 0; i < ARRAY_SIZE(btn_info); i++)

              free_irq(btn_info[i].irq, &btn_info[i]);

          printk("%s\n", __func__);

      }

      module_init(btn_init);

      module_exit(btn_exit);

      MODULE_LICENSE("GPL");

       

      4.2. 自建隊列

      如上討論,當使用work_struct,默認放在缺省的內核線程線程events中。當缺省工作隊列負載太重,執行效率會很低,需要我們自建隊列

      4.2.1.   數據結構

      struct workqueue_struct

       

      4.2.2.   函數集合

      wq = create_workqueue("tzp");

       

      queue_work(wq, &mywork);

      queue_delayed_work(wq, &mydwork, 3*HZ); // 3秒后執行

       

      destroy_workqueue

       

       

      4.2.3.   實例

      #include <linux/init.h>

      #include <linux/module.h>

      #include <linux/irq.h>

      #include <linux/interrupt.h>

      #include <asm/gpio.h>

      #include <plat/gpio-cfg.h>

       

      /*硬件相關的數據結構*/

      struct btn_resource {

          int irq;  //中斷號

          char *name; //中斷名稱

      };

       

      //初始化板卡按鍵信息

      static struct btn_resource btn_info[] = {

          [0] = {

              .irq = IRQ_EINT(0),

              .name = "KEY_UP"

          },

          [1] = {

              .irq = IRQ_EINT(1),

              .name = "KEY_DOWN"

          }

      };

      //定義工作隊列的指針

      static struct workqueue_struct *wq;

       

      //分配延時工作

      static struct delayed_work mydwork;

       

      //延時工作的處理函數

      static void my_dwork_func(struct work_struct *work)

      {

          printk("%s\n", __func__);

      }

       

       

      //中斷處理函數

      //irq:中斷號,dev_id:保存注冊中斷時傳遞的參數信息

      static irqreturn_t button_isr(int irq, void *dev_id)

      {

          //登記關聯自己的工作隊列和工作

          queue_delayed_work(wq, &mydwork, 3*HZ);

         

          printk("%s\n", __func__);

          return IRQ_HANDLED; //成功,失敗:IRQ_NONE

      }

       

      static int btn_init(void)

      {

          //申請中斷和注冊中斷處理程序

          /*

           * IRQ_EINT(0):中斷號

           * button_isr:中斷處理程序,中斷發生以后,內核執行此函數

           * IRQF*:外部中斷的觸發方式,內部中斷此參數為0

           * KEY_UP:中斷名稱,出現在 cat /proc/interrupts

           * &mydata:給中斷處理程序傳遞的參數信息,不傳遞參數指定為NULL

           */

          int i;

       

          for (i = 0; i < ARRAY_SIZE(btn_info); i++)

              request_irq(btn_info[i].irq, button_isr,

                      IRQF_TRIGGER_RISING|IRQF_TRIGGER_FALLING,

                      btn_info[i].name, &btn_info[i]);

         

          //初始化延時工作

          INIT_DELAYED_WORK(&mydwork, my_dwork_func);

       

          //創建自己的工作隊列和內核線程

          wq = create_workqueue("tzp"); //線程名叫tzp

       

          printk("%s\n", __func__);

          return 0;

      }

       

      static void btn_exit(void)

      {

          int i;

       

          //銷毀自己的工作隊列和線程

          destroy_workqueue(wq);

         

          for (i = 0; i < ARRAY_SIZE(btn_info); i++)

              free_irq(btn_info[i].irq, &btn_info[i]);

          printk("%s\n", __func__);

      }

      module_init(btn_init);

      module_exit(btn_exit);

      MODULE_LICENSE("GPL");

       

       

       

      posted on 2015-04-24 17:08  embedded_linux  閱讀(543)  評論(0)    收藏  舉報

      主站蜘蛛池模板: 亚洲欧美日韩综合久久久| 国产不卡精品视频男人的天堂| 杨浦区| 日韩国产成人精品视频| 亚洲精品成人福利网站| 无码人妻aⅴ一区二区三区69岛 | 国内精品久久人妻无码妲| AV最新高清无码专区| 国产精品一区二区三区91| 人妻少妇精品中文字幕| 欧美巨大极度另类| 国产一区二区视频在线看| 伊人色综合一区二区三区影院视频| 国内精品久久人妻无码不卡| 久久精品国产99麻豆蜜月| 国产精品自拍午夜福利| 高级会所人妻互换94部分| 久久精品人人槡人妻人人玩av| 亚洲精品一区二区三区小| 91亚洲国产三上悠亚在线播放| 成全影视大全在线观看| 国产乱码精品一区二区三| 丰满少妇呻吟高潮经历| 无码AV无码免费一区二区| 国产一区二三区日韩精品| 亚洲精品97久久中文字幕无码| 亚洲精品无码成人A片九色播放| 91精品国产色综合久久| 亚洲熟妇色xxxxx亚洲| 亚洲色最新高清AV网站| 日本久久精品一区二区三区 | 日韩人妻无码精品久久| 国产精品综合在线免费看| 国产久免费热视频在线观看| 祥云县| 国产亚洲人成网站在线观看| 国产美女被遭强高潮免费一视频| 温宿县| 91久久偷偷做嫩草影院免费看| 无码国产偷倩在线播放| 诏安县|