linux內核ksoftirqd線程分析

系統現象

我們執行ps會發現一個叫ksoftirqd的線程

 ps -ef | grep ksoftirqd
root         6     2  0 8月19 ?       00:01:15 [ksoftirqd/0]
root        14     2  0 8月19 ?       00:01:13 [ksoftirqd/1]
root        20     2  0 8月19 ?       00:00:51 [ksoftirqd/2]
root        26     2  0 8月19 ?       00:00:56 [ksoftirqd/3]
root        31     2  0 8月19 ?       00:00:58 [ksoftirqd/4]
root        36     2  0 8月19 ?       00:00:56 [ksoftirqd/5]
root        41     2  0 8月19 ?       00:01:01 [ksoftirqd/6]
root        46     2  0 8月19 ?       00:01:00 [ksoftirqd/7]
root        51     2  0 8月19 ?       00:00:46 [ksoftirqd/8]
root        56     2  0 8月19 ?       00:00:31 [ksoftirqd/9]
root        61     2  0 8月19 ?       00:00:57 [ksoftirqd/10]
root        66     2  0 8月19 ?       00:01:20 [ksoftirqd/11]
root        71     2  0 8月19 ?       00:01:29 [ksoftirqd/12]
root        76     2  0 8月19 ?       00:00:58 [ksoftirqd/13]
root        81     2  0 8月19 ?       00:00:49 [ksoftirqd/14]
root        86     2  0 8月19 ?       00:00:48 [ksoftirqd/15]

軟中斷:

從上面的結果可以看出,每個處理器都有一個這樣的線程。所有的線程的名字都叫做ksoftirq/n,區別在於n,他對應的是處理器的編號。

這個線程就是專門用於處理軟中斷的程序。

軟中斷:

UN1X系統提供軟中斷機制作爲進程通信的一種手段。軟中斷是通過發送規定的信號到指定進程,對方進程定時地查詢有無外來信號,若有則按約定進行處理,處理完畢,返回斷點繼續執行原來的指令。可見,軟中斷是對硬中斷的一種模擬。軟中斷存在較大的時延,不象硬中斷能獲得及時響應。例如,對方進程若處在阻塞隊列,那麼只有等到該進程執行時才能查詢軟中斷信號。顯然,從軟中斷信號發出到對方響應,時間可能拖得很長。此外,軟中斷處理程序運行在用戶態,硬中斷處理程序則運行在覈心態

優先級:

中斷>軟中斷>用戶進行

對於軟中斷,內核會在幾個特殊的時機執行(注意執行和調度的區別,調度軟中斷只是對軟中斷打上待執行的標記,並沒有真正執行),而在中斷處理程序返回時處理是最常見的。

代碼分析

在<Softirq.c(kernel)>中

static int ksoftirqd(void * __bind_cpu)
{
    set_user_nice(current, 19);
    current->flags |= PF_NOFREEZE;

    set_current_state(TASK_INTERRUPTIBLE);  //處於等待隊伍中,等待資源有效時喚醒(比方等待鍵盤輸入、socket連接、信號等等)

    while (!kthread_should_stop()) {
        preempt_disable();
        if (!local_softirq_pending()) {
            preempt_enable_no_resched();
            schedule();
            preempt_disable();
        }

        set_current_state(TASK_RUNNING); //正在運行或處於就緒狀態

        while (local_softirq_pending()) {  //當有軟中斷未解決
            // cpu正常可用
            if (cpu_is_offline((long)__bind_cpu))
                goto wait_to_die;
            do_softirq();  //執行軟中斷  這裏一般執行的就是 註冊的回調函數,比如epoll
            preempt_enable_no_resched();  //搶佔cpu計數-1 但不立即搶佔式調度
            cond_resched();  //在內核態主動讓出cpu可以調用cond_resched,讓出cpu,此時就是等待調度
            preempt_disable();   //cpu搶佔計數+1  就是當cpu重新調度到此處時,提高計數
			/**
			當從內核態返回到用戶態的時候,要檢查是否進行調度,而調度要看兩個條件:
			1. preempt_count是否爲0
			2. rescheduled是否置位
			因此上方讓出cpu之前先將preempt_count-1(否則cond_resched可能失敗),調度到ksoftirq又加回來
         */
        }
        preempt_enable();
        set_current_state(TASK_INTERRUPTIBLE); //執行完一次   標記線程進入等待隊列
    }
    set_current_state(TASK_RUNNING); //這裏是爲了執行之後的處理 保證本線程正常終止
    return 0;

wait_to_die:
    preempt_enable();
    /* Wait for kthread_stop */
    set_current_state(TASK_INTERRUPTIBLE);
	//kthread_should_stop()返回should_stop標誌。它用於創建的線程檢查結束標誌,並決定是否退出
    while (!kthread_should_stop()) {  //只要不接受到結束信號,就繼續進行調度
        schedule();
        set_current_state(TASK_INTERRUPTIBLE);
    }
    set_current_state(TASK_RUNNING); //這裏是爲了執行之後的處理 保證本線程正常終止
    return 0;
}

小知識

cpu搶佔

在中斷或臨界區代碼中,線程需要關閉內核搶佔,因此,互斥機制(如:自旋鎖(spinlock)、RCU等)、中斷代碼、鏈表數據遍歷等需要關閉內核搶佔,臨界代碼運行完時,需要開啓內核搶佔。關閉/開啓內核搶佔需要使用內核搶佔API函數preempt_disable和 preempt_enable。

關於搶佔cpu的幾個函數認知

內核搶佔API函數說明如下(在include/linux/preempt.h中):
preempt_enable() //內核搶佔計數preempt_count減1
preempt_disable() //內核搶佔計數preempt_count加1
preempt_enable_no_resched()  //內核搶佔計數preempt_count減1,但不立即搶佔式調度
preempt_check_resched () //如果必要進行調度
preempt_count() //返回搶佔計數
preempt_schedule() //核搶佔時的調度程序的入口點

線程狀態

看完這段代碼,要了解一下linux線程的狀態:

  • TASK_RUNNING:正在運行或處於就緒狀態:就緒狀態是指進程申請到了CPU以外的其它全部資源。正所謂:萬事俱備,僅僅欠東風.提醒:一般的操作系統教科書將正在CPU上運行的進程定義爲RUNNING狀態、而將可運行可是尚未被調度運行的進程定義爲READY狀態。這兩種狀態在Linux下統一爲 TASK_RUNNING狀態.
  • TASK_INTERRUPTIBLE:處於等待隊伍中,等待資源有效時喚醒(比方等待鍵盤輸入、socket連接、信號等等),但能夠被中斷喚醒.普通情況下,進程列表中的絕大多數進程都處於TASK_INTERRUPTIBLE狀態.畢竟皇帝僅僅有一個(單個CPU時),後宮佳麗幾千;假設不是絕大多數進程都在睡眠,CPU又怎麼響應得過來.
  • TASK_UNINTERRUPTIBLE:處於等待隊伍中,等待資源有效時喚醒(比方等待鍵盤輸入、socket連接、信號等等),但不能夠被中斷喚醒.
  • TASK_ZOMBIE:僵死狀態。進程資源用戶空間被釋放,但內核中的進程PCB並沒有釋放。等待父進程回收.
  • TASK_STOPPED:進程被外部程序暫停(如收到SIGSTOP信號,進程會進入到TASK_STOPPED狀態),當再次同意時繼續運行(進程收到SIGCONT信號,進入TASK_RUNNING狀態)。

ksoftirqd創建過程

inux/init/main.c(do_pre_smp_initcalls)-> linux/kernel/softirq.c(spawn_ksoftirqd)-> linux/kernel/softirq.c(cpu_callback)

創建過程

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章