在线观看www成人影院-在线观看www日本免费网站-在线观看www视频-在线观看操-欧美18在线-欧美1级

電子發燒友App

硬聲App

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示
電子發燒友網>電子資料下載>嵌入式開發>Linux 2.4.x內核軟中斷機制

Linux 2.4.x內核軟中斷機制

2017-11-02 | rar | 0.3 MB | 次下載 | 1積分

資料介紹

本文從Linux內核幾種軟中斷機制相互關系和發展沿革入手,分析了這些機制的實現方法,給出了它們的基本用法。
  軟中斷概況
  軟中斷是利用硬件中斷的概念,用軟件方式進行模擬,實現宏觀上的異步執行效果。很多情況下,軟中斷和“信號”有些類似,同時,軟中斷又是和硬中斷相對應的,“硬中斷是外部設備對CPU的中斷”,“軟中斷通常是硬中斷服務程序對內核的中斷”,“信號則是由內核(或其他進程)對某個進程的中斷”(《Linux內核源代碼情景分析》第三章)。軟中斷的一種典型應用就是所謂的“下半部”(bottom half),它的得名來自于將硬件中斷處理分離成“上半部”和“下半部”兩個階段的機制:上半部在屏蔽中斷的上下文中運行,用于完成關鍵性的處理動作;而下半部則相對來說并不是非常緊急的,通常還是比較耗時的,因此由系統自行安排運行時機,不在中斷服務上下文中執行。bottom half的應用也是激勵內核發展出目前的軟中斷機制的原因,因此,我們先從bottom half的實現開始。
  bottom half
  在Linux內核中,bottom half通常用“bh”表示,最初用于在特權級較低的上下文中完成中斷服務的非關鍵耗時動作,現在也用于一切可在低優先級的上下文中執行的異步動作。最早的bottom half實現是借用中斷向量表的方式,在目前的2.4.x內核中仍然可以看到:
  static void (*bh_base[32])(void); /* kernel/softirq.c */
  系統如此定義了一個函數指針數組,共有32個函數指針,采用數組索引來訪問,與此相對應的是一套函數:
  void init_bh(int nr,void (*routine)(void));
  為第nr個函數指針賦值為routine。
  void remove_bh(int nr);
  動作與init_bh()相反,卸下nr函數指針。
  void mark_bh(int nr);
  標志第nr個bottom half可執行了。
  由于歷史的原因,bh_base各個函數指針位置大多有了預定義的意義,在v2.4.2內核里有這樣一個枚舉:
  enum {
  TIMER_BH = 0,
  TQUEUE_BH,
  DIGI_BH,
  SERIAL_BH,
  RISCOM8_BH,
  SPECIALIX_BH,
  AURORA_BH,
  ESP_BH,
  SCSI_BH,
  IMMEDIATE_BH,
  CYCLADES_BH,
  CM206_BH,
  JS_BH,
  MACSERIAL_BH,
  ISICOM_BH
  };
  并約定某個驅動使用某個bottom half位置,比如串口中斷就約定使用SERIAL_BH,現在我們用得多的主要是TIMER_BH、TQUEUE_BH和IMMEDIATE_BH,但語義已經很不一樣了,因為整個bottom half的使用方式已經很不一樣了,這三個函數僅僅是在接口上保持了向下兼容,在實現上一直都在隨著內核的軟中斷機制在變。現在,在2.4.x內核里,它用的是tasklet機制。
  task queue
  在介紹tasklet之前,有必要先看看出現得更早一些的task queue機制。顯而易見,原始的bottom half機制有幾個很大的局限,最重要的一個就是個數限制在32個以內,隨著系統硬件越來越多,軟中斷的應用范圍越來越大,這個數目顯然是不夠用的,而且,每個bottom half上只能掛接一個函數,也是不夠用的。因此,在2.0.x內核里,已經在用task queue(任務隊列)的辦法對其進行了擴充,這里使用的是2.4.2中的實現。
  task queue是在系統隊列數據結構的基礎上建成的,以下即為task queue的數據結構,定義在include/linux/tqueue.h中:
  struct tq_struct {
  struct list_head list; /* 鏈表結構 */
  unsigned long sync; /* 初識為0,入隊時原子的置1,以避免重復入隊 */
  void (*routine)(void *); /* 激活時調用的函數 */
  void *data; /* routine(data) */
  };
  typedef struct list_head task_queue;
  在使用時,按照下列步驟進行:
  DECLARE_TASK_QUEUE(my_tqueue); /* 定義一個my_tqueue,實際上就是一個以tq_struct為元素的list_head隊列 */
  說明并定義一個tq_struct變量my_task;
  queue_task(&my_task,&my_tqueue); /* 將my_task注冊到my_tqueue中 */
  run_task_queue(&my_tqueue); /* 在適當的時候手工啟動my_tqueue */
  大多數情況下,都沒有必要調用DECLARE_TASK_QUEUE()定義自己的task queue,因為系統已經預定義了三個task queue:
  tq_timer,由時鐘中斷服務程序啟動;
  tq_immediate,在中斷返回前以及schedule()函數中啟動;
  tq_disk,內存管理模塊內部使用。
  一般使用tq_immediate就可以完成大多數異步任務了。
  run_task_queue(task_queue *list)函數可用于啟動list中掛接的所有task,可以手動調用,也可以掛接在上面提到的bottom half向量表中啟動。以run_task_queue()作為bh_base[nr]的函數指針,實際上就是擴充了每個bottom half的函數句柄數,而對于系統預定義的tq_timer和tq_immediate的確是分別掛接在TQUEUE_BH和IMMEDIATE_BH上(注意,TIMER_BH沒有如此使用,但TQUEUE_BH也是在do_timer()中啟動的),從而可以用于擴充bottom half的個數。此時,不需要手工調用run_task_queue()(這原本就不合適),而只需調用mark_bh(IMMEDIATE_BH),讓bottom half機制在合適的時候調度它。
  tasklet
  由上看出,task queue以bottom half為基礎;而bottom half在v2.4.x中則以新引入的tasklet為實現基礎。
  之所以引入tasklet,最主要的考慮是為了更好的支持SMP,提高SMP多個CPU的利用率:不同的tasklet可以同時運行于不同的CPU上。在它的源碼注釋中還說明了幾點特性,歸結為一點,就是:同一個tasklet只會在一個CPU上運行。
  struct tasklet_struct
  {
  struct tasklet_struct *next; /* 隊列指針 */
  unsigned long state; /* tasklet的狀態,按位操作,目前定義了兩個位的含義:
  TASKLET_STATE_SCHED(第0位)或TASKLET_STATE_RUN(第1位) */
  atomic_t count; /* 引用計數,通常用1表示disabled */
  void (*func)(unsigned long); /* 函數指針 */
  unsigned long data; /* func(data) */
  };
  把上面的結構與tq_struct比較,可以看出,tasklet擴充了一點功能,主要是state屬性,用于CPU間的同步。
  tasklet的使用相當簡單:
  定義一個處理函數void my_tasklet_func(unsigned long);
  DECLARE_TASKLET(my_tasklet,my_tasklet_func,data); /* 定義一個tasklet結構my_tasklet,與my_tasklet_func(data)函數相關聯,相當于DECLARE_TASK_QUEUE() */
  tasklet_schedule(&my_tasklet); /* 登記my_tasklet,允許系統在適當的時候進行調度運行,相當于queue_task(&my_task,&tq_immediate)和mark_bh(IMMEDIATE_BH) */
  可見tasklet的使用比task queue更簡單,而且,tasklet還能更好的支持SMP結構,因此,在新的2.4.x內核中,tasklet是建議的異步任務執行機制。除了以上提到的使用步驟外,tasklet機制還提供了另外一些調用接口:
  DECLARE_TASKLET_DISABLED(name,function,data); /* 和DECLARE_TASKLET()類似,不過即使被調度到也不會馬上運行,必須等到enable */
  tasklet_enable(struct tasklet_struct *); /* tasklet使能 */
  tasklet_disble(struct tasklet_struct *); /* 禁用tasklet,只要tasklet還沒運行,則會推遲到它被enable */
  tasklet_init(struct tasklet_struct *,void (*func)(unsigned long),unsigned long); /* 類似DECLARE_TASKLET() */
  tasklet_kill(struct tasklet_struct *); /* 清除指定tasklet的可調度位,即不允許調度該tasklet,但不做tasklet本身的清除 */
  前面提到過,在2.4.x內核中,bottom half是利用tasklet機制實現的,它表現在所有的bottom half動作都以一類tasklet的形式運行,這類tasklet與我們一般使用的tasklet不同。
  在2.4.x中,系統定義了兩個tasklet隊列的向量表,每個向量對應一個CPU(向量表大小為系統能支持的CPU最大個數,SMP方式下目前2.4.2為32)組織成一個tasklet鏈表:
  struct tasklet_head tasklet_vec[NR_CPUS] __cacheline_aligned;
  struct tasklet_head tasklet_hi_vec[NR_CPUS] __cacheline_aligned;
  另外,對于32個bottom half,系統也定義了對應的32個tasklet結構:
  struct tasklet_struct bh_task_vec[32];
  在軟中斷子系統初始化時,這組tasklet的動作被初始化為bh_action(nr),而bh_action(nr)就會去調用bh_base[nr]的函數指針,從而與bottom half的語義掛鉤。mark_bh(nr)被實現為調用tasklet_hi_schedule(bh_tasklet_vec+nr),在這個函數中,bh_tasklet_vec[nr]將被掛接在tasklet_hi_vec[cpu]鏈上(其中cpu為當前cpu編號,也就是說哪個cpu提出了bottom half的請求,則在哪個cpu上執行該請求),然后激發HI_SOFTIRQ軟中斷信號,從而在HI_SOFTIRQ的中斷響應中啟動運行。
  tasklet_schedule(&my_tasklet)將把my_tasklet掛接到tasklet_vec[cpu]上,激發TASKLET_SOFTIRQ,在TASKLET_SOFTIRQ的中斷響應中執行。HI_SOFTIRQ和TASKLET_SOFTIRQ是softirq子系統中的術語,下一節將對它做介紹。
  softirq
  從前面的討論可以看出,task queue基于bottom half,bottom half基于tasklet,而tasklet則基于softirq。
  可以這么說,softirq沿用的是最早的bottom half思想,但在這個“bottom half”機制之上,已經實現了一個更加龐大和復雜的軟中斷子系統。
  struct softirq_action
  {
  void (*action)(struct softirq_action *);
  void *data;
  };
  static struct softirq_action softirq_vec[32] __cacheline_aligned;
  這個softirq_vec[]僅比bh_base[]增加了action()函數的參數,在執行上,softirq比bottom half的限制更少。
  和bottom half類似,系統也預定義了幾個softirq_vec[]結構的用途,通過以下枚舉表示:
  enum
  {
  HI_SOFTIRQ=0,
  NET_TX_SOFTIRQ,
  NET_RX_SOFTIRQ,
  TASKLET_SOFTIRQ
  };
  HI_SOFTIRQ被用于實現bottom half,TASKLET_SOFTIRQ用于公共的tasklet使用,NET_TX_SOFTIRQ和NET_RX_SOFTIRQ用于網絡子系統的報文收發。在軟中斷子系統初始化(softirq_init())時,調用了open_softirq()對HI_SOFTIRQ和TASKLET_SOFTIRQ做了初始化:
  void open_softirq(int nr, void (*action)(struct softirq_action*), void *data)
  open_softirq()會填充softirq_vec[nr],將action和data設為傳入的參數。TASKLET_SOFTIRQ填充為tasklet_action(NULL),HI_SOFTIRQ填充為tasklet_hi_action(NULL),在do_softirq()函數中,這兩個函數會被調用,分別啟動tasklet_vec[cpu]和tasklet_hi_vec[cpu]鏈上的tasklet運行。
  static inline void __cpu_raise_softirq(int cpu, int nr)
  這個函數用來激活軟中斷,實際上就是第cpu號CPU的第nr號軟中斷的active位置1。在do_softirq()中將判斷這個active位。tasklet_schedule()和tasklet_hi_schedule()都會調用這個函數。
  do_softirq()有4個執行時機,分別是:從系統調用中返回(arch/i386/kernel/entry.S::ENTRY(ret_from_sys_call))、從異常中返回(arch/i386/kernel/entry.S::ret_from_exception標號)、調度程序中(kernel/sched.c::schedule()),以及處理完硬件中斷之后(kernel/irq.c::do_IRQ())。它將遍歷所有的softirq_vec,依次啟動其中的action()。需要注意的是,軟中斷服務程序,不允許在硬中斷服務程序中執行,也不允許在軟中斷服務程序中嵌套執行,但允許多個軟中斷服務程序同時在多個CPU上并發。
  使用示例
  softirq作為一種底層機制,很少由內核程序員直接使用,因此,這里的使用范例僅對其余幾種軟中斷機制。
  1.bottom half
  原有的bottom half用法在drivers/char/serial.c中還能看到,包括三個步驟:
  init_bh(SERIAL_BH,do_serial_bh); //在串口設備的初始化函數rs_init()中,do_serial_bh()是處理函數
  mark_bh(SERIAL_BH); //在rs_sched_event()中,這個函數由中斷處理例程調用
  remove_bh(SERIAL_BH); //在串口設備的結束函數rs_fini()中調用
  盡管邏輯上還是這么三步,但在do_serial_bh()函數中的動作卻是啟動一個task queue:run_task_queue(&tq_serial),而在rs_sched_event()中,mark_bh()之前調用的則是queue_task(。..,&tq_serial),也就是說串口bottom half已經結合task queue使用了。而那些更通用一些的bottom half,比如IMMEDIATE_BH,更是必須要與task queue結合使用,而且一般情況下,task queue也很少獨立使用,而是與bottom half結合,這在下一節task queue使用示例中可以清楚地看到。
  2.task queue
  一般來說,程序員很少自己定義task queue,而是結合bottom half,直接使用系統預定義的tq_immediate等,尤以tq_immediate使用最頻繁。看以下代碼段,節選自drivers/block/floppy.c:
  static struct tq_struct floppy_tq; //定義一個tq_struct結構變量floppy_tq,不需要作其他初始化動作
  static void schedule_bh( void (*handler)(void*) )
  {
  floppy_tq.routine = (void *)(void *) handler;
  //指定floppy_tq的調用函數為handler,不需要考慮floppy_tq中的其他域
  queue_task(&floppy_tq, &tq_immediate);
  //將floppy_tq加入到tq_immediate中
  mark_bh(IMMEDIATE_BH);
  //激活IMMEDIATE_BH,由上所述可知,
  這實際上將引發一個軟中斷來執行tq_immediate中掛接的各個函數
  }
  當然,我們還是可以定義并使用自己的task queue,而不用tq_immediate,在drivers/char/serial.c中提到的tq_serial就是串口驅動自己定義的:
  static DECLARE_TASK_QUEUE(tq_serial);
  此時就需要自行調用run_task_queue(&tq_serial)來啟動其中的函數了,因此并不常用。
  3.tasklet
  這是比task queue和bottom half更加強大的一套軟中斷機制,使用上也相對簡單,見下面代碼段:
  1: void foo_tasklet_action(unsigned long t);
  2: unsigned long stop_tasklet;
  3: DECLARE_TASKLET(foo_tasklet, foo_tasklet_action, 0);
  4: void foo_tasklet_action(unsigned long t)
  5: {
  6: //do something
  7:
  8: //reschedule
  9: if(!stop_tasklet)
  10: tasklet_schedule(&foo_tasklet);
  11: }
  12: void foo_init(void)
  13: {
  14: stop_tasklet=0;
  15: tasklet_schedule(&foo_tasklet);
  16: }
  17: void foo_clean(void)
  18: {
  19: stop_tasklet=1;
  20: tasklet_kill(&foo_tasklet);
  21: }
  這個比較完整的代碼段利用一個反復執行的tasklet來完成一定的工作,首先在第3行定義foo_tasklet,與相應的動作函數foo_tasklet_action相關聯,并指定foo_tasklet_action()的參數為0。雖然此處以0為參數,但也同樣可以指定有意義的其他參數值,但需要注意的是,這個參數值在定義的時候必須是有固定值的變量或常數(如上例),也就是說可以定義一個全局變量,將其地址作為參數傳給foo_tasklet_action(),例如:
  int flags;
  DECLARE_TASKLET(foo_tasklet,foo_tasklet_action,&flags);
  void foo_tasklet_action(unsigned long t)
  {
  int flags=*(int *)t;
  。..
  }
  這樣就可以通過改變flags的值將信息帶入tasklet中。直接在DECLARE_TASKLET處填寫flags,gcc會報“initializer element is not constant”錯。
  第9、10行是一種RESCHEDULE的技術。我們知道,一個tasklet執行結束后,它就從執行隊列里刪除了,要想重新讓它轉入運行,必須重新調用tasklet_schedule(),調用的時機可以是某個事件發生的時候,也可以是像這樣在tasklet動作中。而這種reschedule技術將導致tasklet永遠運行,因此在子系統退出時,應該有辦法停止tasklet。stop_tasklet變量和tasklet_kill()就是干這個的。
?
下載該資料的人也在下載 下載該資料的人還在閱讀
更多 >

評論

查看更多

下載排行

本周

  1. 1TC358743XBG評估板參考手冊
  2. 1.36 MB  |  330次下載  |  免費
  3. 2開關電源基礎知識
  4. 5.73 MB  |  6次下載  |  免費
  5. 3100W短波放大電路圖
  6. 0.05 MB  |  4次下載  |  3 積分
  7. 4嵌入式linux-聊天程序設計
  8. 0.60 MB  |  3次下載  |  免費
  9. 5基于FPGA的光纖通信系統的設計與實現
  10. 0.61 MB  |  2次下載  |  免費
  11. 6基于FPGA的C8051F單片機開發板設計
  12. 0.70 MB  |  2次下載  |  免費
  13. 751單片機窗簾控制器仿真程序
  14. 1.93 MB  |  2次下載  |  免費
  15. 8基于51單片機的RGB調色燈程序仿真
  16. 0.86 MB  |  2次下載  |  免費

本月

  1. 1OrCAD10.5下載OrCAD10.5中文版軟件
  2. 0.00 MB  |  234315次下載  |  免費
  3. 2555集成電路應用800例(新編版)
  4. 0.00 MB  |  33564次下載  |  免費
  5. 3接口電路圖大全
  6. 未知  |  30323次下載  |  免費
  7. 4開關電源設計實例指南
  8. 未知  |  21548次下載  |  免費
  9. 5電氣工程師手冊免費下載(新編第二版pdf電子書)
  10. 0.00 MB  |  15349次下載  |  免費
  11. 6數字電路基礎pdf(下載)
  12. 未知  |  13750次下載  |  免費
  13. 7電子制作實例集錦 下載
  14. 未知  |  8113次下載  |  免費
  15. 8《LED驅動電路設計》 溫德爾著
  16. 0.00 MB  |  6653次下載  |  免費

總榜

  1. 1matlab軟件下載入口
  2. 未知  |  935054次下載  |  免費
  3. 2protel99se軟件下載(可英文版轉中文版)
  4. 78.1 MB  |  537796次下載  |  免費
  5. 3MATLAB 7.1 下載 (含軟件介紹)
  6. 未知  |  420026次下載  |  免費
  7. 4OrCAD10.5下載OrCAD10.5中文版軟件
  8. 0.00 MB  |  234315次下載  |  免費
  9. 5Altium DXP2002下載入口
  10. 未知  |  233046次下載  |  免費
  11. 6電路仿真軟件multisim 10.0免費下載
  12. 340992  |  191185次下載  |  免費
  13. 7十天學會AVR單片機與C語言視頻教程 下載
  14. 158M  |  183278次下載  |  免費
  15. 8proe5.0野火版下載(中文版免費下載)
  16. 未知  |  138040次下載  |  免費
主站蜘蛛池模板: 四虎永久在线精品| 免费看片aⅴ免费大片| 久久美女视频| 天天干天天色综合| 亚洲欧美经典| 四虎影院.com| 波多野结衣在线网址| 国产精品大全| 亚洲国产成人在线| 国产精品久久久精品视频| 国产午夜精品理论片免费观看| 老司机精品免费视频| 你懂的网站在线观看| 日本黄色小说视频| 最近2018中文字幕免费看手机 | 在线看片一区| 加勒比色综合| 国产视频久| 亚洲 欧美 另类 吹潮| 国产精品国产三级国产在线观看| 国产日日干| 五月婷婷丁香花| 永久视频免费| 中文字幕亚洲色图| 免费爱爱网| 国产精品护士| 四虎影视网站| 天天色天天草| 影音先锋ady69色资源网站| 国产女乱淫真高清免费视频| 999久久久国产精品| 四虎库| 天天黄色| 一级待一黄aaa大片在线还看| 国模网站| 在线免费看黄视频| 美女国产视频| 在线免费视频观看| 欧美啪啪精品| 91精品福利视频| 欧美色综合高清视频在线|