亚洲av成人无遮挡网站在线观看,少妇性bbb搡bbb爽爽爽,亚洲av日韩精品久久久久久,兔费看少妇性l交大片免费,无码少妇一区二区三区

  免費注冊 查看新帖 |

Chinaunix

  平臺 論壇 博客 文庫
最近訪問板塊 發(fā)新帖
查看: 774 | 回復(fù): 0
打印 上一主題 下一主題

linux中斷處理之IRQ中斷 [復(fù)制鏈接]

論壇徽章:
0
跳轉(zhuǎn)到指定樓層
1 [收藏(0)] [報告]
發(fā)表于 2008-11-11 17:03 |只看該作者 |倒序瀏覽

一:前言
在前一個專題里曾分析過所有IRQ中斷處理流程,經(jīng)過SAVE_ALL保存硬件環(huán)境后,都會進入do_IRQ()進行處理,今天接著分析do_IRQ()處理的相關(guān)東西.分為兩部中斷處理程序與軟中斷兩個大的部份進行介紹.
二:中斷處理程序
在驅(qū)動程序中,通常使用request_irq()來注冊中斷處理程序.我們先從注冊中斷處理程序的實現(xiàn)說起.
/*
      irq:可斷號
      handler:中斷處理程序
      irqflags:中斷處理標志.SA_SHIRQ:共享中斷線 SA_INTERRUPT:快速處理中斷
                 必須在關(guān)中斷的情況下運行.SA_SAMPLE_RANDOM:該中斷可能用于產(chǎn)生一個隨機數(shù)
      devname dev_id:設(shè)備名稱與ID      
*/
int request_irq(unsigned int irq,
           irqreturn_t (*handler)(int, void *, struct pt_regs *),
           unsigned long irqflags,
           const char * devname,
           void *dev_id)
{
      int retval;
      struct irqaction * action;

#if 1
      if (irqflags & SA_SHIRQ) {
           if (!dev_id)
                 printk("Bad boy: %s (at 0x%x) called us without a dev_id!\n", devname, (&irq)[-1]);
      }
#endif

      //參數(shù)有效性判斷
      if (irq >= NR_IRQS)
           return -EINVAL;
      if (!handler)
           return -EINVAL;

      // 分配一個irqaction
      action = (struct irqaction *)
                 kmalloc(sizeof(struct irqaction), GFP_ATOMIC);
      if (!action)
           return -ENOMEM;

      action->handler = handler;
      action->flags = irqflags;
      cpus_clear(action->mask);
      action->name = devname;
      action->next = NULL;
      action->dev_id = dev_id;

      //將創(chuàng)建并初始化完在的action加入irq_desc[NR_IRQS]
      retval = setup_irq(irq, action);
      if (retval)
           kfree(action);
      return retval;
}
上面涉及到的irqaction結(jié)構(gòu)與irq_desc[]的關(guān)系我們在上一節(jié)我們已經(jīng)詳細分析過了,這里不再贅述.
轉(zhuǎn)進setup_irq():
int setup_irq(unsigned int irq, struct irqaction * new)
{
      int shared = 0;
      unsigned long flags;
      struct irqaction *old, **p;
      irq_desc_t *desc = irq_desc + irq;

      //如果hander == no_irq_type:說明中斷控制器不支持該IRQ線
      if (desc->handler == &no_irq_type)
           return -ENOSYS;
      sif (new->flags & SA_SAMPLE_RANDOM) {
           rand_initialize_irq(irq);
      }

      /*
       * The following block of code has to be executed atomically
       */
      spin_lock_irqsave(&desc->lock,flags);
      p = &desc->action;
      if ((old = *p) != NULL) {

           //判斷這條中斷線上的中斷處理程序是否允許SHARE
           /* Can't share interrupts unless both agree to */
           if (!(old->flags & new->flags & SA_SHIRQ)) {
                 spin_unlock_irqrestore(&desc->lock,flags);
                 return -EBUSY;
           }

           /* add new interrupt at end of irq queue */
           do {
                 p = &old->next;
                 old = *p;
           } while (old);
           shared = 1;
      }

      //將其添加到中斷處理函數(shù)鏈的末尾
      *p = new;

      //如果這一條線還沒有被占用,初始化這條中斷線
      //包含清標志,在8259A上啟用這條中斷線
      if (!shared) {
           desc->depth = 0;
           desc->status &= ~(IRQ_DISABLED | IRQ_AUTODETECT | IRQ_WAITING | IRQ_INPROGRESS);
           desc->handler->startup(irq);
      }
      spin_unlock_irqrestore(&desc->lock,flags);

      //在proc下建立相關(guān)的文件
      register_irq_proc(irq);
      return 0;
}
現(xiàn)在知道怎么打一個中斷處理程序掛到irq_desc[NR_IRQS]數(shù)組上了,繼續(xù)分析中斷處理中如何調(diào)用中斷處理函數(shù).從我們開篇時說到的do_IRQ()說起.
asmlinkage unsigned int do_IRQ(struct pt_regs regs)
{     
       //屏蔽高位,取得中斷號
      int irq = regs.orig_eax & 0xff; /* high bits used in ret_from_ code  */
      //取得中斷號對應(yīng)的desc結(jié)構(gòu)
      irq_desc_t *desc = irq_desc + irq;
      struct irqaction * action;
      unsigned int status;

      irq_enter();
// 調(diào)試用,忽略
#ifdef CONFIG_DEBUG_STACKOVERFLOW
      /* Debugging check for stack overflow: is there less than 1KB free? */
      {
           long esp;

           __asm__ __volatile__("andl %%esp,%0" :
                            "=r" (esp) : "0" (THREAD_SIZE - 1));
           if (unlikely(esp
                 printk("do_IRQ: stack overflow: %ld\n",
                      esp - sizeof(struct thread_info));
                 dump_stack();
           }
      }
#endif
      //更新統(tǒng)計計數(shù)
      kstat_this_cpu.irqs[irq]++;
      spin_lock(&desc->lock);
      //給8259 回一個ack.回ack之后,通常中斷控制會屏蔽掉此條IRQ線
      desc->handler->ack(irq);
      //清除IRQ_REPLAY IRQ_WAITING標志
      status = desc->status & ~(IRQ_REPLAY | IRQ_WAITING);
      //設(shè)置IRQ_PENDING:表示中斷被應(yīng)答,但沒有真正被處理
      status |= IRQ_PENDING; /* we _want_ to handle it */

      /*
       * If the IRQ is disabled for whatever reason, we cannot
       * use the action we have.
       */
      action = NULL;
      //中斷被屏蔽或者正在處理
      //IRQ_DIASBLED:中斷被禁用
      //IRQ_INPROGRESS:這個類型的中斷已經(jīng)在被另一個CPU處理了
      if (likely(!(status & (IRQ_DISABLED | IRQ_INPROGRESS)))) {
           action = desc->action;
           status &= ~IRQ_PENDING; /* we commit to handling */
           //置位,表示正在處理中...
           status |= c; /* we are handling it */
      }
      desc->status = status;
      //沒有掛上相應(yīng)的中斷處理例程或者不滿足條件
      if (unlikely(!action))
           goto out;

      for (;;) {
           irqreturn_t action_ret;
           u32 *isp;
           union irq_ctx * curctx;
           union irq_ctx * irqctx;

           curctx = (union irq_ctx *) current_thread_info();
           irqctx = hardirq_ctx[smp_processor_id()];

           spin_unlock(&desc->lock);
           //通常curctx == irqctx.除非中斷程序使用獨立的4K堆棧.
           if (curctx == irqctx)
                 action_ret = handle_IRQ_event(irq, &regs, action);
           else {
                 /* build the stack frame on the IRQ stack */
                 isp = (u32*) ((char*)irqctx + sizeof(*irqctx));
                 irqctx->tinfo.task = curctx->tinfo.task;
                 irqctx->tinfo.real_stack = curctx->tinfo.real_stack;
                 irqctx->tinfo.virtual_stack = curctx->tinfo.virtual_stack;
                 irqctx->tinfo.previous_esp = current_stack_pointer();

                 *--isp = (u32) action;
                 *--isp = (u32) &regs;
                 *--isp = (u32) irq;

                 asm volatile(
                      "       xchgl   %%ebx,%%esp     \n"
                      "       call    handle_IRQ_event \n"
                      "       xchgl   %%ebx,%%esp     \n"
                      : "=a"(action_ret)
                      : "b"(isp)
                      : "memory", "cc", "edx", "ecx"
                 );


           }
           spin_lock(&desc->lock);
           //調(diào)試用,忽略
           if (!noirqdebug)
                 note_interrupt(irq, desc, action_ret, &regs);
           if (curctx != irqctx)
                 irqctx->tinfo.task = NULL;
           //如果沒有要處理的中斷了,退出
           if (likely(!(desc->status & IRQ_c)))
                 break;
           //又有中斷到來了,繼續(xù)處理
           desc->status &= ~c;
      }
      //處理完了,清除IRQ_INPROGRESS標志
      desc->status &= ~IRQ_INPROGRESS;

out:
      /*
       * The ->end() handler has to deal with interrupts which got
       * disabled while the handler was running.
       */
      //處理完了,調(diào)用中斷控制器的end.通常此函數(shù)會使中斷控制器恢復(fù)IRQ線中斷
      desc->handler->end(irq);
      spin_unlock(&desc->lock);
      //irq_exit():理論上中斷處理完了,可以處理它的下半部了
      irq_exit();

      return 1;
}
這段代碼比較簡單,但里面幾個標志讓人覺的很迷糊,列舉如下:
IRQ_DISABLED:相應(yīng)的IRQ被禁用.既然中斷線被禁用了,也就不會產(chǎn)生中斷,進入do_IRQ()了?因為電子器件的各種原因可能會產(chǎn)生 “偽中斷”上報給CPU.
IRQ_PENDING:CPU收到這個中斷信號了,已經(jīng)給出了應(yīng)答,但并末對其進行處理.回顧上面的代碼,進入do_IRQ后,發(fā)送ack,再設(shè)置此標志.
IRQ_ INPROGRESS:表示這條IRQ線的中斷正在被處理.為了不弄臟CPU的高速緩存.把相同IRQ線的中斷放在一起處理可以提高效率,且使中斷處理程序不必重入
舉例說明:如果CPU A接收到一個中斷信號.回一個ACK,設(shè)置c,假設(shè)此時末有這個中斷線的中斷處理程序在處理,繼而會將標志位設(shè)為IRQ_ INPROGRESS.轉(zhuǎn)去中斷處理函數(shù)執(zhí)行.如果此時,CPU B檢測到了這條IRQ線的中斷信號.它會回一個ACK.設(shè)置
IRQ_PENDING.但時此時這條IRQ線的標志為IRQ_ INPROGRESS.所以,它會進經(jīng)過goto out退出.如果cpu A執(zhí)行完了中斷處理程序,判斷它的標志線是否為IRQ_PENDING.因為CPU B已將其設(shè)為了IRQ_PENDING.所以繼續(xù)循環(huán)一次.直到循環(huán)完后,清除IRQ_INPROGRESS標志
注意上述讀寫標志都是加鎖的.linux采用的這個方法,不能不贊一個 *^_^*
繼續(xù)看代碼:
asmlinkage int handle_IRQ_event(unsigned int irq,
           struct pt_regs *regs, struct irqaction *action)
{
      int status = 1;  /* Force the "do bottom halves" bit */
      int ret, retval = 0;

      //如果沒有設(shè)置SA_INTERRUPT.將CPU 中斷打開
      //應(yīng)該盡量的避免CPU關(guān)中斷的情況,因為CPU屏弊本地中斷,會使
      //中斷丟失
      if (!(action->flags & SA_INTERRUPT))
           local_irq_enable();

      //遍歷運行中斷處理程序
      do {
           ret = action->handler(irq, action->dev_id, regs);
           if (ret == IRQ_HANDLED)
                 status |= action->flags;
           retval |= ret;
           action = action->next;
      } while (action);
      if (status & SA_SAMPLE_RANDOM)
           add_interrupt_randomness(irq);
      //關(guān)中斷
      local_irq_disable();
      return retval;
}
可能會有這樣的疑問.如果在一根中斷線上掛上了很多個中斷處理程序,會不會使這一段程序的效率變得很低下呢?事實上,我們在寫驅(qū)動程序的過程中,都會首先在中斷處理程序里判斷設(shè)備名字與設(shè)備ID,只有條件符合的設(shè)備中斷才會變處理.
三:軟中斷
為了提高中斷的響應(yīng)速度,很多操作系統(tǒng)都把中斷分成了兩個部份,上半部份與下半部份.上半部份通常是響應(yīng)中斷,并把中斷所得到的數(shù)據(jù)保存進下半部.耗時的操作一般都會留到下半部去處理.
接下來,我們看一下軟中斷的處理模型:
Start_kernel() à softirq_init();
在softirq_init()中會注冊兩個常用類型的軟中斷,看具體代碼:
void __init softirq_init(void)
{
      open_softirq(TASKLET_SOFTIRQ, tasklet_action, NULL);
      open_softirq(HI_SOFTIRQ, tasklet_hi_action, NULL);
}
//參數(shù)含義:nr:軟中斷類型 action:軟中斷處理函數(shù)    data:軟中斷處理函數(shù)參數(shù)
void open_softirq(int nr, void (*action)(struct softirq_action*), void *data)
{
      softirq_vec[nr].data = data;
      softirq_vec[nr].action = action;
}
static struct softirq_action softirq_vec[32] __cacheline_aligned_in_smp;
struct softirq_action
{
      void  (*action)(struct softirq_action *);
      void  *data;
};
在上面的代碼中,我們可以看到:open_softirq()中.其實就是對softirq_vec數(shù)組的nr項賦值.softirq_vec是一個32元素的數(shù)組,實際上linux內(nèi)核只使用了六項. 如下示:
enum
{
      HI_SOFTIRQ=0,
      TIMER_SOFTIRQ,
      NET_TX_SOFTIRQ,
      NET_RX_SOFTIRQ,
      SCSI_SOFTIRQ,
      TASKLET_SOFTIRQ
}
另外.如果使軟中斷能被CPU調(diào)度,還得要讓它激活才可以.激活所使用的函數(shù)為__raise_softirq_irqoff()
代碼如下:
#define __raise_softirq_irqoff(nr) do { local_softirq_pending() |= 1UL
這個宏使local_softirq_pending的nr位置1
好了,經(jīng)過open_softirq()à local_softirq_pending()后,我們來看下軟中斷怎么被CPU調(diào)度.
繼續(xù)上面中斷處理的代碼.在處理完硬件中斷后,會調(diào)用irq_exit().這就是軟中斷的入口點了,我們來看下
#define irq_exit()                                      \
do {                                              \
           preempt_count() -= IRQ_EXIT_OFFSET;               \
           //注意了,軟中斷不可以在硬件中斷上下文或者是在軟中斷環(huán)境中使用哦 ^_^
           //softirq_pending()的判斷,注意我們上面分析過的_raise_softirqoff().它判斷當前cpu有沒有激活軟中斷
           if (!in_interrupt() && softirq_pending(smp_processor_id())) \
                 do_softirq();                          \
           preempt_enable_no_resched();                      \
} while (0)
跟蹤進do_softirq()
asmlinkage void do_softirq(void)
{
      __u32 pending;
      unsigned long flags;
      //在硬件中斷環(huán)境中,退出
      if (in_interrupt())
           return;
      //禁止本地中斷,不要讓其受中斷的影響
      local_irq_save(flags);

      pending = local_softirq_pending();
      //是否有軟中斷要處理?
      if (pending)
           __do_softirq();
      //恢復(fù)CPU中斷
      local_irq_restore(flags);
}
轉(zhuǎn)入__do_softirq()
asmlinkage void __do_softirq(void)
{
      struct softirq_action *h;
      __u32 pending;
      int max_restart = MAX_SOFTIRQ_RESTART;
      int cpu;

      pending = local_softirq_pending();
      //禁止軟中斷,不允許軟中斷嵌套
      local_bh_disable();
      cpu = smp_processor_id();
restart:
      /* Reset the pending bitmask before enabling irqs */
      //把掛上去的軟中斷清除掉,因為我們在這里會全部處理完
      local_softirq_pending() = 0;
      //開CPU中斷
      local_irq_enable();

      //softirq_vec:32元素數(shù)組
      h = softirq_vec;

      //依次處理掛上去的軟中斷
      do {
           if (pending & 1) {
                 //調(diào)用軟中斷函數(shù)
                 h->action(h);
                 rcu_bh_qsctr_inc(cpu);
           }
           h++;
           pending >>= 1;
      } while (pending);

      //關(guān)CPU 中斷
      local_irq_disable();

      pending = local_softirq_pending();
      //在規(guī)定次數(shù)內(nèi),如果有新的軟中斷了,可以繼續(xù)在這里處理完
      if (pending && --max_restart)
           goto restart;
      //依然有沒有處理完的軟中斷,為了提高系統(tǒng)響應(yīng)效率,喚醒softirqd進行處理
      if (pending)
           wakeup_softirqd();
      //恢復(fù)軟中斷
      __local_bh_enable();
}
從上面的處理流程可以看到,軟中斷處理就是調(diào)用open_ softirq()的action參數(shù).這個函數(shù)對應(yīng)的參數(shù)是軟中斷本身(h->action(h)),采用這樣的形式,可以在改變softirq_action結(jié)構(gòu)的時候,不會重寫軟中斷處理函數(shù)
在進入了軟中斷的時候,使用了in_interrupt()來防止軟中斷嵌套,和搶占硬中斷環(huán)境。然后軟中斷以開中斷的形式運行,軟中斷的處理隨時都會被硬件中斷搶占,由于在軟中斷運行之前調(diào)用了local_bh_disable(),所以in_interrupt()為真,不會執(zhí)行軟中斷.
來看下in_interrupt() local_bh_disable() __local_bh_enable()的具體代碼:
#define in_interrupt()      (irq_count())
#define irq_count()   (preempt_count() & (HARDIRQ_MASK | SOFTIRQ_MASK))
#define local_bh_disable() \
           do { preempt_count() += SOFTIRQ_OFFSET; barrier(); } while (0)
#define __local_bh_enable() \
           do { barrier(); preempt_count() -= SOFTIRQ_OFFSET; } while (0)
相當于local_bh_disable設(shè)置了preempt_count的SOFTIRQ_OFFSET。In_interrupt判斷就會返回一個真值
相應(yīng)的__local_bh_enable()清除了SOFTIRQ_OFFSET標志
還有幾個常用的判斷,列舉如下:
in_softirq():判斷是否在一個軟中斷環(huán)境
hardirq_count():判斷是否在一個硬中斷環(huán)境
local_bh_enable()與__local_bh_enable()作用是不相同的:前者不僅會清除SOFTIRQ_OFFSET,還會調(diào)用do_softirq(),進行軟中斷的處理
上述幾個判斷的代碼都很簡單,可自行對照分析
四:幾種常用的軟中斷分析
經(jīng)過上面的分析,看到了linux的軟中斷處理模式,我們具體分析一下2.6kernel中常用的幾種軟中斷
1:tasklet分析
Tasklet也是俗稱的小任務(wù)機制,它使用比較方法,另外,還分為了高優(yōu)先級tasklet與一般tasklet。還記得我們剛開始分析過的softirq_init()這個函數(shù)嗎
void __init softirq_init(void)
{
      //普通優(yōu)先級
      open_softirq(TASKLET_SOFTIRQ, tasklet_action, NULL);
      //高優(yōu)先級
      open_softirq(HI_SOFTIRQ, tasklet_hi_action, NULL);
}
它們的軟中斷處理函數(shù)其實是tasklet_action與tasklet_hi_action.
static void tasklet_action(struct softirq_action *a)
{
      struct tasklet_struct *list;
      //禁止本地中斷
      local_irq_disable();
      //per_cpu變量
      list = __get_cpu_var(tasklet_vec).list;
      //鏈表置空
      __get_cpu_var(tasklet_vec).list = NULL;
      //恢復(fù)本地中斷
      local_irq_enable();
      //接下來要遍歷鏈表了
      while (list) {
           struct tasklet_struct *t = list;
           list = list->next;
           //為了避免競爭,下列操作都是在加鎖情況下進行的
           if (tasklet_trylock(t)) {
                 //t->count為零才會調(diào)用task_struct里的函數(shù)
                 if (!atomic_read(&t->count)) {
                      //t->count 為1。但又沒有置調(diào)度標志。系統(tǒng)BUG
                      if (!test_and_clear_bit(TASKLET_STATE_SCHED, &t->state))
                            BUG();
                      //調(diào)用tasklet函數(shù)
t->func(t->data);
                      tasklet_unlock(t);
                      continue;
                 }
                 tasklet_unlock(t);
           }
           //注意 :所有運行過的tasklet全被continue過去了,只有沒有運行的tasklet才會重新加入到鏈表里面
           //禁本地中斷
           local_irq_disable();
           //把t放入隊列頭,準備下一次接收調(diào)度
           t->next = __get_cpu_var(tasklet_vec).list;
           __get_cpu_var(tasklet_vec).list = t;
           //置軟中斷調(diào)用標志。下次運行到do_softirq的時候,可以繼續(xù)被調(diào)用
           __raise_softirq_irqoff(TASKLET_SOFTIRQ);
           //啟用本地中斷
           local_irq_enable();
      }
}
高優(yōu)先級tasklet的處理其實與上面分析的函數(shù)是一樣的,只是per_cpu變量不同而已。
另外,有幾個問題值得考慮:
1)       cpu怎么計算軟中斷優(yōu)先級的
在do_softirq()à__do_softirq()有:
{
      pending = local_softirq_pending();
      ......
      do {
      if (pending & 1) {
            h->action(h);
            rcu_bh_qsctr_inc(cpu);
      }
      h++;
      pending >>= 1;
} while (pending);
......
}
從上面看到,從softirq_vec[]中取項是由pending右移位計算的。
另外,在激活軟中斷的操作中:
#define __raise_softirq_irqoff(nr) do { local_softirq_pending() |= 1UL
可以看到 nr越小的就會越早被do_softirq遍歷到
2)       在什么條件下才會運行tasklet 鏈表上的任務(wù)
我們在上面的代碼里看到只有在t->count為零,且設(shè)置了TASKLET_STATE_SCHED標志才會被遍歷到鏈表上對應(yīng)的函數(shù)
那在我們自己的代碼里該如何使用tasklet呢?舉個例子:
#include
#include
#include
#include

static void tasklet_test_handle(unsigned long arg)
{
      printk("in tasklet test\n");
}
//聲明一個tasklet
DECLARE_TASKLET(tasklet_test,tasklet_test_handle,0);
MODULE_LICENSE("GPL xgr178@163.com");
int kernel_test_init()
{
      printk("test_init\n");
      //調(diào)度這個tasklet
      tasklet_schedule(&tasklet_test);
}

int kernel_test_exit()
{
      printk("test_exit\n");
      //禁用這個tasklet
      tasklet_kill(&tasklet_test);
      return 0;
}
module_init(kernel_test_init);
module_exit(kernel_test_exit);
示例模塊里涉及到tasklet通用的三個API.分別是DECLARE_TASKLET(), tasklet_schedule(),tasklet_kill()
跟蹤一下內(nèi)核代碼:
#define DECLARE_TASKLET(name, func, data) \
struct tasklet_struct name = { NULL, 0, ATOMIC_INIT(0), func, data }
struct tasklet_struct
{
      struct tasklet_struct *next;
      unsigned long state;
      atomic_t count;
      void (*func)(unsigned long);
      unsigned long data;
};
實際上,DECLARE_TASKLET就是定義了一個tasklet_struct的變量.相應(yīng)的tasklet調(diào)用函數(shù)為func().函數(shù)參數(shù)為data
static inline void tasklet_schedule(struct tasklet_struct *t)
{
      //如果tasklet沒有置調(diào)度標置,也就是說該tasklet沒有被調(diào)度
      if (!test_and_set_bit(TASKLET_STATE_SCHED, &t->state))
           __tasklet_schedule(t);
}
void fastcall __tasklet_schedule(struct tasklet_struct *t)
{
      unsigned long flags;

      //把tasklet加到__get_cpu_var(tasklet_vec).list鏈表頭
      local_irq_save(flags);
      t->next = __get_cpu_var(tasklet_vec).list;
      __get_cpu_var(tasklet_vec).list = t;
      //激活相應(yīng)的軟中斷
      raise_softirq_irqoff(TASKLET_SOFTIRQ);
      local_irq_restore(flags);
}
這個函數(shù)比較簡單,不詳細分析了
void tasklet_kill(struct tasklet_struct *t)
{
      //不允許在中斷環(huán)境中進行此操作
      if (in_interrupt())
           printk("Attempt to kill tasklet from interrupt\n");

      //一直等待tasklet被調(diào)度完
      while (test_and_set_bit(TASKLET_STATE_SCHED, &t->state)) {
           do
                 yield();
           while (test_bit(TASKLET_STATE_SCHED, &t->state));
      }
      //一直等待tasklet被運行完
      tasklet_unlock_wait(t);
      //清除調(diào)度標志
      clear_bit(TASKLET_STATE_SCHED, &t->state);
}
該函數(shù)會一直等待該tasklet調(diào)度并運行完,可能會睡眠,所以不能在中斷環(huán)境中使用它
2:網(wǎng)絡(luò)協(xié)議棧里專用軟中斷
在前面分析網(wǎng)絡(luò)協(xié)議協(xié)的時候分析過,網(wǎng)卡有兩種模式,一種是中斷,即數(shù)據(jù)到來時給CPU上傳中斷,等到CPU處理中斷.第二種是輪詢,即在接收到第一個數(shù)據(jù)包之后,關(guān)閉中斷,CPU每隔一定時間就去網(wǎng)卡DMA緩沖區(qū)取數(shù)據(jù).其實,所謂的輪詢就是軟中斷.接下來就來研究一下網(wǎng)絡(luò)協(xié)議棧的軟中斷
static int __init net_dev_init(void)
{
      ……
      open_softirq(NET_TX_SOFTIRQ, net_tx_action, NULL);
      open_softirq(NET_RX_SOFTIRQ, net_rx_action, NULL);
      ……
}
在這里注冊了兩個軟中斷,一個用于接收一個用于發(fā)送,函數(shù)大體差不多,我們以接收為例.從前面的分析可以知道,軟中斷的處理函數(shù)時就是它調(diào)用open_softirq的action參數(shù).在這里即是net_rx_action.代碼如下:
static void net_rx_action(struct softirq_action *h)
{
      //per_cpu鏈表.所有網(wǎng)卡的輪詢處理函數(shù)都通過napi_struct結(jié)構(gòu)存放在這鏈表里面
      struct list_head *list = &__get_cpu_var(softnet_data).poll_list;
      unsigned long start_time = jiffies;
      int budget = netdev_budget;
      void *have;
      //關(guān)中斷
      local_irq_disable();
      //遍歷鏈表
      while (!list_empty(list)) {
           struct napi_struct *n;
           int work, weight;
            if (unlikely(budget
                 goto softnet_break;
           local_irq_enable();
           // 取鏈表里的相應(yīng)數(shù)據(jù)
           n = list_entry(list->next, struct napi_struct, poll_list);
           have = netpoll_poll_lock(n);
           weight = n->weight;
           work = 0;
           //如果允許調(diào)度,則運行接口的poll函數(shù)
           if (test_bit(NAPI_STATE_SCHED, &n->state))
                 work = n->poll(n, weight);

           WARN_ON_ONCE(work > weight);

           budget -= work;
           //關(guān)中斷
           local_irq_disable();
           if (unlikely(work == weight)) {
                 //如果被禁用了,就從鏈表中刪除
                 if (unlikely(napi_disable_pending(n)))
                      __napi_complete(n);
                 Else
                      //否則加入鏈表尾,等待下一次調(diào)度
                      list_move_tail(&n->poll_list, list);
           }

           netpoll_poll_unlock(have);
      }
out:
      //啟用中斷
      local_irq_enable();
//選擇編譯部份,忽略
#ifdef CONFIG_NET_DMA
      /*
       * There may not be any more sk_buffs coming right now, so push
       * any pending DMA copies to hardware
       */
      if (!cpus_empty(net_dma.channel_mask)) {
           int chan_idx;
           for_each_cpu_mask(chan_idx, net_dma.channel_mask) {
                 struct dma_chan *chan = net_dma.channels[chan_idx];
                 if (chan)
                      dma_async_memcpy_issue_pending(chan);
           }
      }
#endif

      return;

softnet_break:
      __get_cpu_var(netdev_rx_stat).time_squeeze++;
      __raise_softirq_irqoff(NET_RX_SOFTIRQ);
      goto out;
}
一般在接口驅(qū)動中,會調(diào)用__napi_schedule.將其添加進遍歷鏈表.代碼如下示:
void fastcall __napi_schedule(struct napi_struct *n)
{
      unsigned long flags;

      local_irq_save(flags);
      //加至鏈表末尾
      list_add_tail(&n->poll_list, &__get_cpu_var(softnet_data).poll_list);
      //激活軟中斷
      __raise_softirq_irqoff(NET_RX_SOFTIRQ);
      local_irq_restore(flags);
}
關(guān)于網(wǎng)卡選擇哪一種模式最為合適,我們在前面已經(jīng)講述過,這里不再贅述.
五:小結(jié)
本節(jié)主要分析了中斷程序的處理過程與軟中斷的實現(xiàn).雖然軟中斷實現(xiàn)有很多種類,究其模型都是一樣的,就是把中斷的一些費時操作在響應(yīng)完中斷之后再進行.另外,中斷與軟中斷處理中有很多臨界區(qū),需要關(guān)閉CPU中斷和打開CPU中斷.其中的奧妙還需要慢慢的體會

本文來自ChinaUnix博客,如果查看原文請點:http://blog.chinaunix.net/u/26185/showart_1405394.html
您需要登錄后才可以回帖 登錄 | 注冊

本版積分規(guī)則 發(fā)表回復(fù)

  

北京盛拓優(yōu)訊信息技術(shù)有限公司. 版權(quán)所有 京ICP備16024965號-6 北京市公安局海淀分局網(wǎng)監(jiān)中心備案編號:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年舉報專區(qū)
中國互聯(lián)網(wǎng)協(xié)會會員  聯(lián)系我們:huangweiwei@itpub.net
感謝所有關(guān)心和支持過ChinaUnix的朋友們 轉(zhuǎn)載本站內(nèi)容請注明原作者名及出處

清除 Cookies - ChinaUnix - Archiver - WAP - TOP