Android 筆記-Linux Kernel SMP (Symmetric Multi-Processors) 開機流程解析 Part(4) Linux 多核心啟動流程-kthreadd 與相關的

Android 筆記-Linux Kernel SMP (Symmetric Multi-Processors) 開機流程解析 Part(4) Linux 多核心啟動流程-kthreadd 與相關的核 Android 筆記-LinuxKernel SMP (Symmetric Multi-Processors) 開機流程解析 Part(4)Linux 多核心啟動流程-kthreadd與相關的核心模組 by  loda hlchou@mail2000.com.tw Loda's Blog App BizOrz Android/Linux Source Code T 阅读详情
Android  筆記 -LinuxKernel SMP (Symmetric Multi-Processors)  開機流程解析  Part(4)Linux  多核心啟動流程 -kthreadd 與相關的核心模組


by  loda

hlchou@mail2000.com.tw

Loda's Blog

App BizOrz

kthread第一次出現在LinuxKernel中是在Kernel版本2.6.4時,一開始的實作尚未有本文提到的kthreaddTask的具體架構,隨著版本的演進,除了這部份的設計完整外,需要產生KernelThread的實作也都已經改用kthread機制.


本文會先針對kthreaddTask行為加以說明,並會以在啟動後,屬於KernelMode產生的KernelThread的個別行為與功能做一個介紹,由於這部份涉及的範圍不少,筆者會以自己的角度選擇認為值得加以說明的項目,相信應該足以涵蓋大多數人對於LinuxKernel Mode Tasks所需的範圍.如果有所不足之處,也請透過LinuxKernel Hacking加以探索.


簡要來說,位於KernelMode的Tasks產生,除了直接透過kernel_thread函式外,還可以有兩個來源,一個是透過kthread_create(實作上,也是透過函式Kernel_Thread)產生的KernelThread,另一個則是透過WorkQueue機制產生的KernelThread,前者可以依據設計者自己對系統架構的掌握,去設計多工機制(例如,使用稍後提到的LinkedList Queue).而後者,則是由LinuxKernel提供延遲處理工作的機制,讓每個核心的Tasks可以透過WorkQueue機制把要延遲處理/指定處理器/指定延遲時間的工作,交派給WorkQueue.


在實際的應用上,WorkQueue還可以用以實現中斷的BottomHalf機制,讓中斷觸發時對Timing要求高的部分(TopHalf)可以在InterruptHandler中盡快執行完畢,透過WorkQueue把需要較長時間執行的部分(BottomHalf)交由WorkQueue延遲執行實現.(等同於RTOS下的LISR/HISR).


LinuxKernel的基礎Tasks.


在LinuxKernel中有三個最基礎的Tasks,分別為PID=0的IdleTask,PID=1負責初始化所有使用者環境與行程的init Task,與PID=2負責產生KernelMode行程的kthreaddTask.


其中,IdleTask主要用來在系統沒有其他工作執行時,可以執行省電機制(PMIdle)或透過IdleMigration把多核心其它處理器上的工作進行重分配(LoadBalance),讓處於Idle的處理器可以分擔Task工作,充分利用系統運算資源.


而initTask是所有UserMode Tasks的父行程,包含啟動時的ShellScript執行,或是載入必要的應用程式,都會基於initTask的執行來實現.


再來就是本文主要談的kthreaddTask,這是在LinuxKernel 2.6所引入的機制,在這之前要產生KernelThread需直接使用函式kernel_thread,而所產生的KernelThread父行程會是當下產生KernelThread的行程(或該父行程結束後,改為initTask(PID=1)).在kthreadd的機制下,UserMode與KernelMode Task的產生方式做了調整,並讓kthreaddTask成為使用kthread_create產生的KernelThread統一的父行程.也因此,在這機制實現下的LinuxKernel,屬於UserMode行程最上層的父行程為initTask (PID=1),而屬於KernelMode行程最上層的父行程為kthreaddTask (PID=2),而這兩個行程共同的父行程就是idle Task (PID=0).



kthreadd



kthreaddKernel Thread主要實作在檔案kernel/kthread.c中,入口函式為kthreadd(宣告為intkthreadd(void *unused) ),主要負責的工作是檢視目前LinkedList Queue "kthread_create_list"是否有要產生KernelThread的需求,若有,就呼叫函式create_kthread進行後續的產生工作.而要透過kthreadd產生KernelThread需求,就可以透過呼叫kthread_create(與其他衍生的kthread函式,ex:kthread_create_on_node....etc.)把需求加入到LinkedList Queue "kthread_create_list"中,並WakeUpkthreadd Task,就可以使用目前kthreadd新設計的機制.


有關函式kthreadd內部的運作流程,概述如下


1,執行set_task_comm(tsk,"kthreadd"),設定Task的執行檔名稱,會把"kthreadd"複製給task_struct中的comm (structtask_struct宣告在include/linux/sched.h).

2,執行ignore_signals(tsk),其中tsk= current,會設定讓Taskkthreadd忽略所有的Signals

3,設定kthreadd可以在所有處理器上執行.

4,進入kthreadd的for(;;)無窮迴圈,

4-1,透過函式list_empty確認kthread_create_list是否為空,若為空,就觸發Task排程

4-2,透過list_entry,取出要產生的KernelThread的”structkthread_create_info” Pointer

4-3,呼叫create_kthread產生KernelThread.

4-3-1,在create_kthread中會以"pid= kernel_thread(kthread, create, CLONE_FS | CLONE_FILES |SIGCHLD);"呼叫函式kernel_thread(inarch/arm/kernel/process.c),其中,入口函式為kthread,新產生的Task的第一個函式參數為create.

4-3-1-1,在函式kernel_thread中,會執行如下的程式碼,把最後新產生的KernelThread入口函式指給新Task的暫存器r5,要傳遞給該入口函式的變數只給暫存器r4,而該入口函式結束時要返回的函式kernel_thread_exit位址指給暫存器r7,並透過透過do_fork產生新的行程時,暫存器PC(Program Counter)指向函式kernel_thread_helper.也就是說每一個KernelThread的第一個函式統一都是kernel_thread_helper,而結束函式統一都為kernel_thread_exit.函式kernel_thread的參考程式碼如下所示

/*

*Create a kernel thread.

*/

pid_tkernel_thread(int (*fn)(void *), void *arg, unsigned long flags)

{

structpt_regs regs;

memset(&regs,0, sizeof(regs));

regs.ARM_r4= (unsigned long)arg;

regs.ARM_r5= (unsigned long)fn;

regs.ARM_r6= (unsigned long)kernel_thread_exit;

regs.ARM_r7= SVC_MODE | PSR_ENDSTATE | PSR_ISETSTATE;

regs.ARM_pc= (unsigned long)kernel_thread_helper;

regs.ARM_cpsr= regs.ARM_r7 | PSR_I_BIT;

returndo_fork(flags|CLONE_VM|CLONE_UNTRACED, 0, &regs, 0, NULL, NULL);

}


新產生的Task會執行函式kernel_thread_helper,並把結束函式由暫存器r6指給暫存器LR(LinkerRegister),把入口函式的第一個參數指給暫存器r0,新Task的入口函式由暫存器r5指給暫存器PC,開始新Task的執行.有關函式kernel_thread_helper的參考程式碼如下所示


externvoid kernel_thread_helper(void);

asm( ".pushsection .text\n"

" .align\n"

" .type kernel_thread_helper, #function\n"

"kernel_thread_helper:\n"

#ifdefCONFIG_TRACE_IRQFLAGS

" bl trace_hardirqs_on\n"

#endif

" msr cpsr_c, r7\n"

" mov r0, r4\n"

" mov lr, r6\n"

" mov pc, r5\n"

" .size kernel_thread_helper, . - kernel_thread_helper\n"

" .popsection");


由於新Task的入口函式統一為"kthread",第一個函式參數統一為”structkthread_create_info*create”,檢視函式kthread的實作,可以看到在新行程由kernel_thread_helper呼叫進入kthread後,就會執行函式參數create中的create->threadfn函式指標,執行其他應用透過kthread_create產生KernelThread時的最終函式入口,參考代碼如下所示


staticint kthread(void *_create)

{

/*Copy data: it's on kthread's stack */

structkthread_create_info *create = _create;

int(*threadfn)(void *data) = create->threadfn;

void*data = create->data;

….......................

ret= -EINTR;

if(!self.should_stop)

ret=threadfn(data);

/*we can't just return, we must preserve "self" on stack */

do_exit(ret);

}



有關kthreadd整體運作的概念,可參考下圖




kthread_createvs kernel_thread



kernel_thread函式,是kthreadd機制產生前,要使用KernelThread主要的方式,而根據前述的介紹,可以看到其實kthread_create也是透過函式kernel_thread實現.


如果我們選擇直接透過kernel_thread產生KernelThread,跟透過kthreadd機制相比,兩者的差別在於,一個是由當下呼叫的kernel_thread的Task行程所fork出來的,採用kthread_create機制則是由kthreaddTask行程所fork出來的.


執行指令ps -axjf可看到如下結果


PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND

0 2 0 0 ? -1 S 0 0:00 [kthreadd]

2 3 0 0 ? -1 S 0 0:00 \_ [ksoftirqd/0]

2 4 0 0 ? -1 S 0 0:01 \_ [kworker/0:0]

2 5 0 0 ? -1 S 0 0:00 \_ [kworker/u:0]

2 6 0 0 ? -1 S 0 0:00 \_ [migration/0]

2 7 0 0 ? -1 S 0 0:00 \_ [migration/1]

2 8 0 0 ? -1 S 0 0:00 \_ [kworker/1:0]

2 9 0 0 ? -1 S 0 0:00 \_ [ksoftirqd/1]

2 10 0 0 ? -1 S 0 0:01 \_ [kworker/0:1]

2 11 0 0 ? -1 S< 0 0:00 \_ [khelper]

2 12 0 0 ? -1 S< 0 0:00 \_ [netns]

2 13 0 0 ? -1 S 0 0:00 \_ [sync_supers]

2 14 0 0 ? -1 S 0 0:00 \_ [bdi-default]

2 15 0 0 ? -1 S< 0 0:00 \_ [kblockd]

2 16 0 0 ? -1 S< 0 0:00 \_ [kacpid]

2 17 0 0 ? -1 S< 0 0:00 \_[kacpi_notify]

2 18 0 0 ? -1 S< 0 0:00 \_[kacpi_hotplug]

2 19 0 0 ? -1 S 0 0:00 \_ [khubd]

2 20 0 0 ? -1 S< 0 0:00 \_ [md]

2 21 0 0 ? -1 S 0 0:00 \_ [khungtaskd]

2 22 0 0 ? -1 S 0 0:00 \_ [kswapd0]

2 23 0 0 ? -1 S 0 0:00 \_[fsnotify_mark]


我們可以知道,透過kthreadd所產生的Thread都會是以kthreadd為Parent的Task,跟原本透過kernel_thread所產生的Task是源自於各自的Tasks是有所不同的.


如下所示為透過kernel_thread所自行產生的KernelThread,在insmod指令結束後,這個KernelThread的ParentTask為PID=1(也就是initTask.).


PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND

…..

1 2122 2118 1987 pts/0 2126 R 0 0:21 insmod hello.ko



透過kthreadd的相關函式


函式名稱

說明

kthread_create_on_node

宣告為

structtask_struct *kthread_create_on_node(int (*threadfn)(void *data),

void*data,

intnode,

constchar namefmt[],

…)

用以產生與命名KernelThread,並可在支援NUMA(Non-UniformMemory Access Architecture)的核心下,透過node可以設定要在哪個處理器上執行所產生的KernelThread.(會透過設定task->pref_node_fork),產生後的行程會等待被函式wake_up_process喚醒或是被kthread_stop所終止.

kthread_create

參考檔案include/linux/kthread.h,

函式kthread_create的宣告如下


#definekthread_create(threadfn, data, namefmt, arg...) \

kthread_create_on_node(threadfn,data, -1, namefmt, ##arg)


可以看到,kthread_create也是透過kthread_create_on_node實現,差異在於node值為-1,也就是可以在所有處理器上運作,產生後的行程會等待被函式wake_up_process喚醒或是被kthread_stop所終止.

kthread_run

參考檔案include/linux/kthread.h,

函式kthread_run的宣告如下


#definekthread_run(threadfn, data, namefmt, ...) \

({ \

structtask_struct *__k \

=kthread_create(threadfn, data, namefmt, ## __VA_ARGS__); \

if(!IS_ERR(__k)) \

wake_up_process(__k); \

__k; \

})


主要用以產生並喚醒KernelThread.(開發者可以省去要呼叫wake_up_process的動作.),且這呼叫是基於kthread_create,所以產生的KernelThread也不限於在特定的處理器上執行.

kthread_bind

綁定KernelThread到指定的處理器,主要是透過設定CPUAllowed Bitmask,所以在多核心的架構下,就可以指定給一個以上的處理器執行.

kthread_stop

用來暫停透過kthread_create產生的KernelThread,會等待KernelThread結束,並傳回函式threadfn(=產生KernelThread的函式)的返回值.

透過"wait_for_completion(&kthread->exited);"等待KernelThread結束,並透過"int ret;....ret =k->exit_code;.....return ret; "取得該結束的KernelThread返回值,傳回給函式kthread_stop的呼叫者.

kthread_should_stop

用來在KernelThread中呼叫,如果該值反為True,就表示該KernelThread X有被透過呼叫函式kthread_stop指定結束,因此,KernelThread X就必須要準備進行KernelThread的結束流程.










基於kthreadd產生的KernelTasks


接下來,我們把在LinuxKernel下,基於kthread產生的KernelTasks做一個概要的介紹,藉此可以知道LinuxKernel有哪些模組基於這機制實現了哪些核心的能力.包括檔案系統Journaling機制,InetrruptBottomHalf,Kernel USB-Hub,Kernel Helper..等,都是基於這機制下,所衍生出來的核心實作.


在這段落會頻繁提及的WorkQueue可以有兩種實現方式,分別為


1,WorkQueue (實作在kernel/workqueue.c).

2,產生Kernel Thread,並搭配Linked List Queue (ininclude/linux/list.h)機制.


如下,逐一介紹筆者認為值得說明的KernelThread與內部機制.




Kernel Tasks

名稱

kworker

參考檔案kernel/workqueue.c,kworker主要提供系統非同步執行的共用WorkerPool方案(WorkQueue),一般而言會區分為每個處理器專屬的WorkerPool或是屬於整個系統使用的WorkerPool

“kworker/A:B”後方的數字意義分別是A為CPU ID而B為透過ida_get_new配置的ID(範圍從0-0x7fffffff).

以筆者雙核心的環境為例,以CPU#0來說,會透過kthread_create_on_node產生固定在CPU#0上執行的[kworker/0:0],[kworker/0:1],[kworker/1:0]與[kworker/1:1].

並透過kthread_create產生不固定在特定處理器上執行的[kworker/u:0]與[kworker/u:1].

總共呼叫create_worker執行六個執行gcwq(GlobalCPU Workqueue) worker thread function的KernelThread.


PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND

2 4 0 0 ? -1 S 0 0:01 \_[kworker/0:0]

2 5 0 0 ? -1 S 0 0:00 \_[kworker/u:0]

2 8 0 0 ? -1 S 0 0:00 \_[kworker/1:0]

2 10 0 0 ? -1 S 0 0:00 \_[kworker/0:1]

2 30 0 0 ? -1 S 0 0:00 \_[kworker/1:1]

2 46 0 0 ? -1 S 0 0:00 \_[kworker/u:1]


worker_thread為WorkQueue機制的核心,包括新的WorkQueue產生(alloc_workqueue),指派工作到WorkQueue(queue_work),把工作指派到特定的處理器(queue_work_on),指派工作並設定延遲執行(queue_delayed_work),在指定的處理器上指派工作並延遲執行(queue_delayed_work_on)..等.


限於本次預定的篇幅,有關WorkQueue的進一步討論,會在後續文章中介紹.


需要進一步資訊的開發者,可以自行參閱LinuxKernel文件Documentation/workqueue.txt.


ksoftirqd

參考檔案kernel/softirq.c,會在每個處理器進入CPU_UP_PREPARE或CPU_UP_PREPARE_FROZEN狀態時,在個別處理器產生ksoftirqdKernel Thread,如下所示,在雙核心環境中會有兩個KernelThread各自在兩個處理器上執行.

PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND

2 3 0 0 ? -1 S 0 0:00 \_[ksoftirqd/0]

2 9 0 0 ? -1 S 0 0:00 \_[ksoftirqd/1]


在ksoftirqdKernel Thread函式run_ksoftirqd中,會透過

"while(!kthread_should_stop()) { ...}"迴圈,確認目前是否有被執行終止動作,若無,就往後繼續執行.並透過local_softirq_pending確認是否有SoftIRQ被觸發,為了系統效能考量,目前LinuxKernel會把TopHalves放在對應的中斷Routine中執行,而把屬於BottomHalves的部份透過kirqsoftd執行.並且在ARM多核心的架構下,每個處理器都會有自己的LocalIRQ,因此,如果發現有LocalSoftIRQ待處理,就會由各自的處理器對應的ksoftirqdKernel Thread負責執行BottomHalves中的工作.

如果發現,待處理的CPU已經OffLine也會立刻結束ksoftirqd的執行.而在ksoftirqd中,會透過函式__do_softirq執行SoftIRQBottom Halves 的工作.


若有進一步興趣的開發者,也可以參考這篇文章http://book.chinaunix.net/special/ebook/oreilly/Understanding_Linux_Network_Internals/0596002556/understandlni-CHP-9-SECT-3.html.

khelper

參考檔案kernel/kmod.c,在KernelInit的過程中會呼叫函式usermodehelper_init,在這函式中就會執行"khelper_wq =create_singlethread_workqueue("khelper");"產生khelperKernel Thread.

在LinuxKernel執行的過程中,就可以透過函式call_usermodehelper_exec執行

"queue_work(khelper_wq,&sub_info->work);"把工作指派給khelperWorkQueue中.

例如,要載入一個LinuxKernelModule時,就會透過函式__request_module,之後呼叫call_usermodehelper_fns並帶入modprobe路徑與相關參數作為函式參數(函式call_usermodehelper_fns的inline實作宣告在include/linux/kmod.h中),最後會呼叫進入call_usermodehelper_exec,把工作放到khelperWorkQueue中,來透過khelperKernel Thread帶起UserMode的應用程式執行.


如下為khelper在筆者環境中產生的行程資訊


PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND

2 11 0 0 ? -1 S< 0 0:00 \_[khelper]


而每一個要放到khelperWorkQueue的工作,都會透過函式call_usermodehelper_setup執行"INIT_WORK(&sub_info->work,__call_usermodehelper); " 設定WorkQueue要執行的Work函式(khelperWork函數固定設為__call_usermodehelper),外部執行檔路徑與相關參數.

因此,一旦khelperKernelThread從WorkQueue中執行到新工作時,就會呼叫函式__call_usermodehelper,最後透過函式____call_usermodehelper,執行核心函式kernel_execve,來達成執行User-Mode應用程式的目的.(這也就是如何由核心去執行外部的LinuxDevice Driver Module工具的執行路徑,清楚khelper的行為與機制,對了解LinuxDrivers載入/運作原理會很有幫助.)

kblockd

參考檔案block/blk-core.c,如同khelper,這同樣是透過WorkQueue產生的KernelThread,在initCall中呼叫genhd_device_init函式時,會執行blk_dev_init並執行"kblockd_workqueue= alloc_workqueue("kblockd",WQ_MEM_RECLAIM | WQ_HIGHPRI,0);" 產生kblockdKernel Thread.


如下為kblockd在筆者環境中產生的行程資訊

PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND

2 15 0 0 ? -1 S< 0 0:00 \_[kblockd]


在系統運作過程中,可透過blk_delay_queue/blk_run_queue_async/kblockd_schedule_work/kblockd_schedule_delayed_work對kblockdWorkQueue進行工作的指派.


kseriod

參考檔案drivers/input/serio/serio.c,

參考KernelSource Code, Serio在LinuxKernel是對"SerialI/O "周邊的支援模組,只要有使用到SerialI/O的輸入裝置(InputDevice),例如:ATkeyboard, PS/2 mouse, joysticks搖桿..etc都屬此類.

也因此,屬於Serio的輸入裝置,底層就可以包括RS232(ComPort),使用i8042Controller晶片的AT與PS/2鍵盤/滑鼠,使用ct82c710Controller晶片的QuickPort滑鼠...etc.Serio底層可支援的Controller很豐富,在此只列舉部分底層控制晶片,有興趣的開發者請自行參考LinuxKernel Source.


啟動時,會在KernelInit執行到initCall時呼叫serio_init函式,並執行"serio_task= kthread_run(serio_thread, NULL, "kseriod");"產生kseriodKernel Thread,以函式serio_thread為KernelThread的執行函式


staticint serio_thread(void *nothing)

{

do{

serio_handle_event();

wait_event_interruptible(serio_wait,

kthread_should_stop()|| !list_empty(&serio_event_list));

}while (!kthread_should_stop());

return0;

}


只要serio_queue_event被呼叫到(像是EventSERIO_RESCAN_PORT/SERIO_RECONNECT_CHAIN/SERIO_REGISTER_PORT/SERIO_ATTACH_DRIVER/SERIO_RECONNECT_PORT)就會把Add新的Event到LinkedList Queue中,並喚醒kseriod KernelThread處理對應SerioEvent.


如下為kseriod在筆者環境中產生的行程資訊

USER PID PPID VSIZE RSS WCHAN PC NAME

root 9 2 0 0 c018179c 00000000 S kseriod


kmmcd

參考檔案drivers/mmc/core/core.c,如同khelper,這同樣是透過WorkQueue產生的KernelThread,是透過執行"workqueue= create_singlethread_workqueue("kmmcd");"產生的kmmcdKernel Thread.用以支援MMC/SD卡的行為.


如下為kseriod在筆者環境中產生的行程資訊


USER PID PPID VSIZE RSS WCHAN PC NAME

root 10 2 0 0 c004b2c4 00000000 S kmmcd


kswapd

參考檔案mm/vmscan.c,

啟動時,會在KernelInit執行到initCall時呼叫kswapd_init函式,並進入kswapd_run後,執行"pgdat->kswapd= kthread_run(kswapd, pgdat, "kswapd%d", nid);"產生kswapdKernel Thread,並以函式kswapd(宣告:staticint kswapd(void *p) )為KernelThread的執行函式.如下為函式kswapd_init的內容,在swap_setup(in mm/swap.c)之後,會透過for_each_node_state(in include/linux/nodemask.h),


staticint __init kswapd_init(void)

{

intnid;

swap_setup();

for_each_node_state(nid,N_HIGH_MEMORY)

kswapd_run(nid);

hotcpu_notifier(cpu_callback,0);

return0;

}


其中 for_each_node_state宣告如下,


#definefor_each_node_state(__node, __state) \

for_each_node_mask((__node),node_states[__state])


對Linux而言,在多核心的架構下,可以支援以Node方式去GroupCPU,也就是說每個Node可以擁有一個以上的處理器,而在這就會選擇Node的HIGH_MEMORYState有被設定的Node.也就是說,如果該CPUNode的HIGH_MEMORYState有被設定,就會執行kswapd_run,並把該NodeId帶入成為nid的值.(也就是最後kswapd後面的數字).


筆者對LinuxKernel HighMemory的理解是,當所配置的實體記憶體大於LinuxKernel虛擬記憶體範圍(通常在ARM上是0xc0000000-0xffffffff這1GB的範圍),而因為受限於Memory MappedI/O或實際上的Kernel需要,有部分所需的實體記憶體無法被Mapped到這1GB虛擬記憶體空間中,此時就會需要KernelEnable High Memory的能力.目前在ARMLinux Kernel中也有支援HighMemory,並可針對HighMemory需求配置2nd-levelPageTables.在沒開啟HighMemory的LinuxKernel, Node States中N_HIGH_MEMORY的值會等於N_NORMAL_MEMORY,也就是會讓NormalMemory跟HighMemory的設定一致.一般而言,在考慮系統效能時,並不建議開啟HighMemory能力.前提也是硬體設計時,也要避免遇到這樣的問題,如果選擇把KernelSpace加大(例如從1G/3G變成2G/2G),則會限制到需要大量記憶體應用程式的執行.


這有一篇關於LinuxKernel HighMemory的文章,有興趣的開發者也可以參考看看http://kerneltrap.org/node/2450.



如下為kswapd在筆者環境中產生的行程資訊


PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND

2 22 0 0 ? -1 S 0 0:00 \_ [kswapd0]


而在kswapdKernel Thread的函式kswapd中,主要進行縮減Slab Cache(例如檔案系統快取),並在FreePages數量過低時,把記憶體SwapOut到磁碟中,會透過函式balance_pgdat對每個處理器Node(這定義是針對多核心的內化設計.)的記憶體區域(Zone)進行記憶體釋放,直到所有記憶體區域的FreePages都達到HighWaterMark(透過呼叫high_wmark_pages確認),也就是"free_pages> high_wmark_pages(zone)".


參考include/linux/mmzone.h,有關MemoryZone Water Marker相關巨集宣告如下

#definemin_wmark_pages(z) (z->watermark[WMARK_MIN])

#definelow_wmark_pages(z) (z->watermark[WMARK_LOW])

#definehigh_wmark_pages(z) (z->watermark[WMARK_HIGH])


最後,附帶補充一下,LinuxKernel本身支援在每次透過PageAllocate配置記憶體時(SourceCode在mm/page_alloc.c),透過核心的OOM模組(SourceCode在mm/oom_kill.c),評估目前記憶體資源是不是快耗盡,如果是就會透過函式mem_cgroup_out_of_memory呼叫select_bad_process選擇一個透過badness計算後,要被終止的行程,然後呼叫函式oom_kill_process,最後在函式__oom_kill_task中發送SIGKILLSignal強制終止行程執行.


而Android本身也有在LinuxKernel中加入LowMemory Killer模組(並關閉LinuxKernel原本的OOM),啟動時會透過函式register_shrinker(inmm/vmscan.c)註冊自己的lowmem_shrinker服務.

kstriped

參考檔案drivers/md/dm-stripe.c,

會在執行dm_stripe_init時,透過"kstriped= create_singlethread_workqueue("kstriped");"產生執行WorkQueue的kernelThread,並在函式stripe_ctr中透過"INIT_WORK(&sc->kstriped_ws,trigger_event);"設定函式trigger_event為WorkQueue的Work函式,並設定相關Strip所需參數.在函式trigger_event中,會再呼叫dm_table_event(indrivers/md/dm-table.c)然後執行event_fn.


MD (MultipleDevice)主要用以支援把多個裝置虛擬為單一裝置,一般會應用在SoftwareRAID或LVM(Logical Volume Management)的功能上.同時,參考LinuxKernel文件Documentation/device-mapper/striped.txt,dm-stripe (inDevice-Mapper)主要用以支援像是RAID0的Striped儲存裝置,可以把要寫入的資料,分割為不同的Chunks循序且循環的寫入到多個底層的儲存裝置中,由於是把單筆資料分割為不同的Chunks個別對多個裝置寫入,因此可以大幅改善儲存媒體寫入的效率.(相對於只對單一裝置寫入,把一筆資料分割後同時對多個裝置寫入可以縮短寫入等待的時間).


目前LinuxKernel的SoftwareRAID可支援軟體把多個磁碟Partition整合為一個邏輯磁碟,並支援像是RAID1,4或5,藉此避免硬碟損壞時造成的資料損失.不過在重視效能的環境中,HardwareRAID還是比較有效率的.


如下為kstriped在筆者環境中產生的行程資訊


USER PID PPID VSIZE RSS WCHAN PC NAME

root 23 2 0 0 c004b2c4 00000000 S kstriped


Android本身也有支援對Device-Mapper的操作,細節就不在本文範圍中,各位可以參考2.3Source Code "gingerbread/system/vold/Devmapper.cpp".

kjournald

參考檔案fs/jbd/journal.c,會透過執行"t= kthread_run(kjournald, journal, "kjournald");",產生支援Ext3檔案系統Journaling的KernelThread.或參考檔案fs/jbd2/journal.c,會透過執行"t= kthread_run(kjournald2, journal,"jbd2/%s",journal->j_devname);",產生支援Ext4與OCFS2檔案系統,並可延伸支援更大空間64bits block numbers機制的JournalingKernel Thread.


以支援Ext3的kjournald來說,主要負責兩類工作,

1,Commit: 用以把JoirnalingFileSystem所產生的MetaData寫回對應的檔案系統位置.用以正式的把資料完成寫入動作,並可釋出MetaData的空間.

2,CheckPoint:會由這個Thread執行查核點的動作,把JournalingLog內容回寫,以便這些空間可以重複利用.


啟動流程為,在Ext3初始化時,執行函式ext3_create_journal(in fs/ext3/super.c),之後進入函式journal_create(in fs/jbd/journal.c)->journal_reset (in fs/jbd/journal.c)->journal_start_thread (in fs/jbd/journal.c),透過函式kthread_run產生kjournaldKernel Thread,之後進入"wait_event(journal->j_wait_done_commit,journal->j_task != NULL);"等待kjournaldKernel Thread產生完畢,呼叫"wake_up(&journal->j_wait_done_commit);"讓函式journal_start_thread結束執行.


有關kjournald會被TimerWakeUp起來執行Commit的流程,其中Interval的設定如下"journal->j_commit_interval= (HZ * JBD_DEFAULT_MAX_COMMI

T_AGE);" (infs/jbd/journal.c),而HZ為每秒觸發的Tick中斷次數(在筆者環境為100).同時參考檔案"include/linux/jbd.h"中有關JBD_DEFAULT_MAX_COMMI

T_AGE的設定為5.("#define JBD_DEFAULT_MAX_COMMIT_AGE 5"),也就是說kjournaldKernel Thread會被Timer喚醒的Interval值為5秒.


函式kjournald(原型為"staticint kjournald(void *arg)"),為KernelThread的主函式,會執行如下的動作,


1,設定Timer(呼叫"setup_timer(&journal->j_commit_timer,commit_timeout,(unsignedlong)current);"),固定週期透過Callback函式commit_timeout喚醒kjournaldKernel Thread,進行Journaling檔案系統Log回寫的動作.參考檔案"fs/jbd/transaction.c",在函式get_transaction中,會設定Expire的時間點為現在的Tick值jiffies加上journal->j_commit_int

erval 的值("transaction->t_expires= jiffies + journal->j_commit_interval;"),(在筆者環境Interval設定為5秒.).

2,進入Loop中,

2-1,確認journal->j_flags,若狀態JFS_UNMOUNT成立,就結束Loop

2-2,呼叫函式journal_commit_transaction(in fs/jbd/commit.c),執行Log回寫

2-2-1,......至於回寫機制....過於細節,就不在這討論了.

2-3,喚醒等待journal->j_wait_done_commit的行程(等同告知Log回寫結束.)

2-4,確認thread_info->flags的TIF_FREEZEbit是否為1(TIF_FREEZE=19). (例如可以透過函式freeze_task對Task發出freezerequest ).

2-4-1,若上述成立,就會進入函式refrigerator(inkernel/freezer.c),並進入for(;;)迴圈中,直到frozen(current)不成立,才會結束迴圈,把函式執行完畢.

2-5,反之,若2-4條件不成立,就會等待journal->j_wait_commitWait Event,或判斷是否可以進入等待,例如還有待回寫的Log沒有執行完畢("if(journal->j_commit_sequence != journal->j_commit_request)"),或有執行中的Transaction且現在的系統Tick值已經大於等於原本kjournald所設定的Timer要觸發TimeOut的時間值(也就是說已經發生過TimeOut了),若上述條件成立,就會讓系統繼續執行下去,而不進入等待.

2-6,kjournald處於執行狀態(有可能是因為TimeOut或是journal->j_wait_commitWait Event喚醒),透過比對現在的Tick值是否大於等於TimerTimeOut值(transaction->t_expires),確認是否為透過Timer喚醒.

2-7,重複回到2-1,繼續kjournaldKernel Thread的執行.



參考下圖,為整個kjournaldKernel Thread與Timer運作的示意圖.



events

參考檔案kernel/workqueue.c,

events KernelThread主要提供核心延後工作執行的方式,如前述的實作介紹,有的應用會透過自己建立的WorkQueue來實現這樣的設計,或是也可以透過GlobalWorkQueue來達成目的,在啟動過程中會透過函式init_workqueues來初始化keventd_wq (也就是keventdWorkQueue).參考如下程式碼,


void__init init_workqueues(void)

{

…..

keventd_wq= create_workqueue("events");

….

}


當有需要使用預設的WorkQueue時,可以透過函式schedule_work,把要處理的工作放到GlobalWork Queue,這函式參考實作如下


intschedule_work(struct work_struct *work)

{

returnqueue_work(keventd_wq, work);

}


如果希望放到GlobalWorkQueue並加入Delayed值,可以透過函式schedule_delayed_work,參考實作如下


intschedule_delayed_work(struct delayed_work *dwork,

unsignedlong delay)

{

returnqueue_delayed_work(keventd_wq, dwork, delay);

}


或透過函式schedule_work_on把工作指派到指定的處理器上,參考實作如下


intschedule_work_on(int cpu, struct work_struct *work)

{

returnqueue_work_on(cpu, keventd_wq, work);

}


也可以透過函式schedule_delayed_work_on指定在所要的處理器上,並附帶Delayed值,參考實作如下


intschedule_delayed_work_on(int cpu,

structdelayed_work *dwork, unsigned long delay)

{

returnqueue_delayed_work_on(cpu, keventd_wq, dwork, delay);

}



有興趣的開發者,也可以參考這篇LinuxKernel Korner文章"KernelKorner - The New Work Queue Interface in the 2.6 Kernel" (inhttp://www.linuxjournal.com/article/6916)



pdflush

參考檔案mm/pdflush.c,

pdflush主要的工作為把由檔案系統Mapping到記憶體的內容為Dirty的Pages,負責回寫到檔案系統中.可以透過設定/proc/sys/vm/dirty_background_ratio決定當DirtyPages超過多少比率時,便執行回寫到檔案系統的動作.(在筆者的環境中為10%).


在運作時,會透過函式start_one_pdflush_thread產生pdflush KrnelThread,參考如下程式碼


staticvoid start_one_pdflush_thread(void)

{

structtask_struct *k;

k= kthread_run(pdflush, NULL, "pdflush");

if(unlikely(IS_ERR(k))) {

spin_lock_irq(&pdflush_lock);

nr_pdflush_threads--;

spin_unlock_irq(&pdflush_lock);

}

}


並可視目前執行回寫動作忙碌的情況,再透過函式start_one_pdflush_thread產生新的pdflushKernel Thread. (總量不超過MAX_PDFLUSH_THREADS).



Migration

參考檔案kernel/stop_machine.c,

在多核心的架構下,Migration在意義上主要是讓運作在處理器#A的行程可以轉移到處理器#B或其它處理器上.但在目前筆者使用的LinuxKernel中,Migration主要為當處理器進行CPUDown流程時,負責執行Callback函式,讓屬於要停止的處理器上的Tasks可以轉移到還能持續運作的處理器上.



Migration KernelThread主要的任務為在多核心的架構下,支援StopCPU的行為,也因此可以視這個KernelThread為StopperKernel Thread,

當處理器進行CPUDown時,就會透過這個KernelThread呼叫所指定CallbackFunctions.



如之前介紹到的其它模組,在多核心的處理器上,會產生對應的Migration KernelThread,例如:[migration/0]與[migration/1].



1,當新執行的應用程式,被分配到的處理器跟目前所在處理器不同時(dest_cpu不等於smp_processor_id()時).

例如,當使用者執行一個新的程式,就會透過函式do_execve(in fs/exec.c)開啟該執行檔,並呼叫函式sched_exec(in kernel/sched.c)在新產生的Task行程中執行"p->sched_class->select_task_rq",用以確認這個新產生的Task要被排程的目標處理器,是否跟目前所在的處理器一致,那是那就返回繼續執行,若非,就會執行"stop_one_cpu(cpu_of(rq),migration_cpu_stop, &arg);"透過函式stop_one_cpu,讓migrationKernel Thread執行函式migration_cpu_stop來進行Task轉移的工作.

2,當CPU的AllowedBitmask被修改,且該Task正位於不被允許的處理器上

例如,當使用者透過set_cpus_allowed_ptr(inkernel/sched.c)修改Task的CPUAllowed Bitmask時,會執行"if(cpumask_test_cpu(task_cpu(p), new_mask))"確認目前所在的處理器是否符合新的Bitmask設定,若所在的處理器並非允許執行的處理器,就會執行"stop_one_cpu(cpu_of(rq),migration_cpu_stop, &arg);"透過函式stop_one_cpu,讓migrationKernel Thread執行函式migration_cpu_stop來進行Task轉移的工作.

3,當處理器沒有Task運作時(也就是處於Idle的狀態),由忙碌的處理器轉移Task來執行.

例如:在排程函式schedule中,會透過"if(unlikely(!rq->nr_running))"確認目前該處理器是否處於沒有Task執行的狀態,若是就會嘗試把其它處理器上的Tasks移到目前Idle的處理器上執行.首先,會呼叫函式idle_balance(in kernel/sched_fair.c),並確認sched_domain的SD_BALANCE_NEWIDLEflag是否成立,若成立就會執行"pulled_task= load_balance(this_cpu, this_rq,sd, CPU_NEWLY_IDLE,&balance);",在函式load_balance(in kernel/sched_fair.c)中,會執行"stop_one_cpu_nowait(cpu_of(busiest),active_load_balance_cpu_stop, busiest,&busiest->active_balance_work);"透過函式stop_one_cpu_nowait,讓migrationKernelThread執行函式active_load_balance_cpu_stop,把Task從目前最忙碌的處理器移到Idle的處理器上.有關處理器TaskBalance的動作,值得討論的事項很多,後續有機會再加以詳細說明.

4,當處理器被關閉時(在有支援HotPlug的環境下,進行CPUDown)

例如:進行CPUDown時會透過函式cpu_down(in kernel/cpu.c)進入 _cpu_down(in kernel/cpu.c)執行"err= __stop_machine(take_cpu_down, &tcd_param,cpumask_of(cpu));",透過函式__stop_machine (inkernel/stop_machine.c)呼叫stop_cpus(in kernel/stop_machine.c),進入__stop_cpus(inkernel/stop_machine.c)中,執行函式cpu_stop_queue_work,把工作透過list_add_tail(&work->list,&stopper->works);放到stopper->worksQueue中,讓migrationKernel Thread執行函式take_cpu_down(in kernel/cpu.c)完成CPUDown的流程.



上述四個條件,是在目前LinuxKernel下,會透過migration KernelThread執行的情況.



在啟動過程中,會呼叫函式cpu_stop_init,並以啟動的處理器作為BootCPU來呼叫函式cpu_stop_cpu_callback,執行CPU_UP_PREPARE的動作,會以函式cpu_stopper_thread做為migrationKernel Thread的執行起點,並以函式kthread_bind設定該Thread只能在目前產生該KernelThread的處理器上執行.可以參考如下程式碼.

staticint __cpuinit cpu_stop_cpu_callback(struct notifier_block *nfb,

unsignedlong action, void *hcpu)

{

unsignedint cpu = (unsigned long)hcpu;

structcpu_stopper *stopper = &per_cpu(cpu_stopper, cpu);

structtask_struct *p;

switch(action & ~CPU_TASKS_FROZEN) {

caseCPU_UP_PREPARE:

BUG_ON(stopper->thread|| stopper->enabled ||

!list_empty(&stopper->works));

p= kthread_create_on_node(cpu_stopper_thread,

stopper,

cpu_to_node(cpu),

"migration/%d",cpu);

if(IS_ERR(p))

returnnotifier_from_errno(PTR_ERR(p));

get_task_struct(p);

kthread_bind(p,cpu);

sched_set_stop_task(cpu,p);

stopper->thread= p;

break;

….…..

migration KernelThread會在CPU_UP_PREPARE事件中產生,並在CPU_ONLINE事件被WakeUp與設定"stopper->enabled= true;".

在有支援CPUHotPlug的環境中,會有額外的CPU_UP_CANCELED與CPU_POST_DEAD事件,會在透過函式kthread_stop把migrationKernel Thread結束.



而在函式cpu_stopper_thread中,就會進入一個goto的迴圈中,確認stopper->worksQueue中是否有被指派的工作,若有就會執行該工作對應的Callback函式.


其他常見的KernelThread還包括suspend,cqueue,aio,mtdblockd,hid_compat,rpciod,mmcqd....etc,就不在這多做介紹,有興趣的開發者請自行參閱LinuxKernel Source Code.





結語


本文基本上以kthreaddTask為起點,探討了一部分以此為基礎的核心模組,然而由於涉及的領域很廣泛,筆者主要以自己認為值得加以說明的模組進行介紹,對核心解析有熱情的開發者,建議可以自行進一步的Hacking.


有關LinuxKernel啟動流程的介紹,就以本文告結.

Linux kworker 占用CPU过高 先打开HTOP htop 如何按H K(大写) 我们看到Kworker/0:0+events,下面参考下人家的回答 什么是kworker?kworker表示进行“工作”(处理系统调用)的Linux内核进程。在进程列表中可以有多个:kworker/0:1在第一个CPU内核上kworker/1:1是一个,在第二个CPU内核上是一个,依此类推。 为什么kworker占用您的CPU?... 阅读详情

相关推荐

【存储原理】IO超时处理 块层block-scsi层之间

【存储原理】IO超时处理 块层block-scsi层之间

sinat_37817094的博客 1024

内核基础层提供的服务---软中断和tasklet 、 工作队列

工作队列和tasklet相似,也是一种延缓执行的机制。 不同之处是工作队列有自己的进程上下文,所有工作队列都可以睡眠,也可以被调度【tasklet不可睡眠】。 以下代码是工作队列的例子: 工作队列很简单,s

abcdefg的专栏 439

20231126-超详细Isaac Gym安装教程(基于双系统版本)

我一开始使用了WSL2虚拟环境试了集群服务器里面的交互式桌面,但是都失败了,WSL2因为没办法调用系统的GPU,应该有办法配置但我放弃了,感兴趣的可以自己探索(图片贴出来了)。服务器上出现的问题是Segmentation fault (core dumped),目前还没解决,有可能是驱动和cuda版本的问题(后面有时间再解决)等上图右边都达到了100%,就创建了一个名为rlgpu的conda环境,然后会出现success。注意安装版本要低于3.9!参考链接:非常详细,大家直接看这个博主的安装就行。

m0_37802038的博客 2万+

NVMe Linux驱动系列一:host端[multipath.c]<31>

这些函数的目的是在 NVMe 多路径子系统中实现队列的冻结和解冻,以控制请求的处理。总之,这个函数的主要目的是遍历命名空间列表,调度重新排队工作以处理故障恢复,同时在需要时发送磁盘事件通知,以确保系统能够及时地响应 NVMe 控制器和磁盘的状态变化。这些函数是用于 NVMe 多路径 (multipath) 支持的一部分,其中包括多个命令和操作,用于管理 NVMe 子系统的多路径特性。通过调用这个函数,将会记录请求的处理时间、传输的数据量等 I/O 统计信息,以便在多路径配置中了解命令请求的性能和吞吐量。

探索未知,成就非凡 676

三相电机控制入门:端电压、相电压、线电压到底怎么测?(附实测波形对比)

本文为三相电机控制初学者提供了端电压、相电压和线电压的完整测量指南。通过厘清三者的物理定义与测量方法,详细讲解了如何搭建虚拟星形点电路,并对比分析了方波、SPWM和SVPWM三种主流控制模式下关键电压波形的核心特征与差异,帮助工程师掌握正确的示波器测量与波形解读技巧。

oauth7security的博客 650

ESP32学习,驱动LED点阵屏,SD卡播放GIF动图

搬运就可以实现效果,如果有不足,请批评指正。下步该学一下如何文章上视频了,老是截图不方便展示效果。

u011625956的博客 1868

深入浅出linux内核,深入浅出Linux之内核基础层

前言将内核分成了内核基础层和内核应用层。记得我们的目标是“快速流畅的阅读内核代码”,为了达到这个目标,必须先了解内核的基础和数据结构。这些知识使用范围很广,如果不了解,在内核代码的理解上就容易出现障碍。1.内核对内存的使用简单说,内核提供了两个层次的内存分配接口。一个是从伙伴系统分配,一个是从slab分配。关于伙伴系统和slab机制,在后面的章节再分析。这里只需要了解,伙伴系统是最底层的内存管理机...

weixin_39863759的博客 242

Llama模型结构拆解:为什么MLP模块要这样设计?从参数配置到计算流程全解析

本文深入解析了Llama模型MLP模块的设计精髓。通过拆解其三线性层结构、门控机制与SwiGLU激活函数,揭示了并行计算如何提升效率,并详细阐述了intermediate_size参数配置背后的经验权衡与计算考量,帮助读者透彻理解这一核心组件的设计逻辑与工程实现。

mac99的博客 529

Linux块设备子系统浅析(2.6.26)

<br />Linux块设备子系统浅析(2.6.26)<br />作者:guolele                                                                                                                        2011.3.20.<br />Blog:http://blog.youkuaiyun.com/guolele2010<br />这里只是说明每个函数大概是做些什么工作,用于了解块设备子系统工作的原

guolele2010的专栏 2388

C语言实现TCP聊天室:Socket编程与并发模型实战解析

网络编程是构建分布式系统的核心技术,其核心在于理解进程间通信的底层机制。TCP/IP协议栈提供了可靠的字节流传输服务,通过Socket API这一抽象接口,开发者可以在应用层建立连接、收发数据。为了高效处理多个并发连接,服务器需要采用多线程或I/O复用模型,前者为每个连接创建独立线程,逻辑清晰但资源消耗大;后者通过select、poll或epoll等系统调用监控多个文件描述符,显著提升并发能力。这些技术是构建高性能网络服务(如即时通讯、在线游戏服务器)的基础。本文以C语言实现一个简洁的TCP聊天室为例,详细

weixin_32642109的博客 292

快充协议全兼容!HUSB390对比测试:iPhone/小米/华为哪个充电最快?

本文深入评测了HUSB390快充协议芯片的全兼容性能,展示了其在iPhone、小米和华为设备上的极速充电表现。通过智能功率分配算法和多协议并行处理引擎,HUSB390实现了高效的动态调压和温度控制,显著提升多设备充电效率。特别适合需要同时为不同品牌设备快充的用户。

dd012的博客 1万+

一次内核block层Multi queue报错IO QID timeout, reset controller案例分析

一次内核block层Multi queue报错IO QID timeout, reset controller案例分析

dongzhiyan_hjp 4460

SDR 和 DDR 的区别

SDR 和 DDR 的差别,最明显提现在传输速率和频率上。DDR SDRAM与SDRAM的基本结构是相似的,最根本的区别在于DDR 支持在一个时钟周期内传输两次数据,,SDR 只能传输一次数据。

串起点滴 1960

块设备I/O调度程序

1.5.5 块设备I/O调度程序 我们建立请求队列建的目录是,当向请求队列增加一条新的请求,即产生一个request数据结构时,通用块层会调用I/O调度程序来确定该新request将在请求队列中的确切位置。I/O调度程序试图通过扇区将请求队列排序。如果顺序地从链表中提取要处理的请求,那么就会明显减少磁头寻道的次数,因为磁头是按照直线的方式从内磁道移向外磁道(反之亦然),而不是随意地从一个磁道跳跃

#维多利亚的秘密# 758

YOLOv11 改进 - 主干网络| 清华大学CloFormer AttnConv :利用共享权重和上下文感知权重增强局部感知,注意力机制与卷积的完美融合

本文介绍了轻量级视觉变换器CloFormer及其核心模块AttnConv在YOLOv11中的结合应用。为解决视觉变换器缩小到移动设备友好尺寸时性能下降的问题,提出CloFormer,其引入AttnConv操作符。AttnConv融合共享权重和上下文感知权重,先通过深度卷积提取局部表示,再用上下文感知权重增强特征,还结合传统注意力减少FLOPs,使模型感知高频和低频信息。我们将AttnConv集成进YOLOv11,实验表明,CloFormer在多个视觉任务中表现优越,改进后的YOLOv11也取得了良好实验结果

魔改工程师的博客 989

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain's apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath('DeepLearnToolbox'));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand('state',0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W'); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand('state',0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = 'sigm';%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, 'Too big error');```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand('state',0)sae = saesetup([784 100]);sae.ae{1}.activation_function = 'sigm';sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)')% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = 'sigm';nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, 'Too big error');```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x',28,28,60000))/255;test_x = double(reshape(test_x',28,28,10000))/255;train_y = double(train_y');test_y = double(test_y');%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you'll get around 1.2% errorrand('state',0)cnn.layers = { struct('type', 'i') %input layer struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %sub sampling layer struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, 'Too big error');```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand('state',0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, 'Too big error');%% ex2 neural net with L2 weight decayrand('state',0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex3 neural net with dropoutrand('state',0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex4 neural net with sigmoid activation functionrand('state',0)nn = nnsetup([784 100 10]);nn.activation_function = 'sigm'; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex5 plotting functionalityrand('state',0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = 'softmax'; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand('state',0)nn = nnsetup([784 20 10]); nn.output = 'softmax'; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

Linux那些事儿之我是Block层(9)scsi命令的前世今生(三)

下一个更为重要的函数是scsi_dispatch_cmd,来自drivers/scsi/scsi.c:    459 /*    460  * Function:    scsi_dispatch_command    461  *    462  * Purpose:     Dispatch a command to the low-level driver.    4

7393

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

linux workqueue实现原理

工作队列的设计思想可以类比于现实中的生产流水线 :流水线相当于工作队列中的worklist链表,加工部件相当于中断发生 时所产生的工作序列,工人就是工作者线程。当中断发生时,内核将本次中断延后执行的工作序列,放到worklist工作链表中,唤醒 工作者线程执行工作。工作者线程在执行时可能阻塞;当 worklist 中的工作处理完毕后,工作者线程进入空闲状态。

tugouxp的专栏 872

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

SIMATIC S7-1200, S7-1500 PID 控制[手册]

SIMATIC S7-1200, S7-1500 PID 控制[手册]

谈一谈Linux让实时/高性能任务独占CPU的事

文章目录谈一谈Linux让实时/高性能任务独占CPU的事Part 1工程需求Part 2用户态隔离Part 3内核态隔离中断内核态线程Part 4最佳实践指南 谈一谈Linux让实时/高性能任务独占CPU的事 原版文章 https://mp.weixin.qq.com/s/_NwWD5CM1tda6lJXYFjnSQ 内容简介 本文主要讨论在高实时要求、高效能计算、DPDK等领域,Linux如何让某一个线程排他性独占CPU;独占CPU涉及的线程、中断隔离原理;以及如何在排他性独占的情况下,甚至让系统的tim

从善若水的博客 1058

linux kworker cpu,Kworker,它是什么,为什么它占用这么多 CPU?

问题:有时候它几乎使用一半的CPU。答案1:运行"kworker"是内核工作线程的占位符进程,它执行内核的大部分实际处理,尤其存在中断,计时器,I/O的情况下。答案2:在进程列表中可以有几个:kworker/0:1是第一个CPU内核,kworker/1:1是第二个。kworker为什么要占用你的CPU?echo l > /proc/sysrq-trigger 执行此操作几次,然后在dmesg...

weixin_30610943的博客 1万+

kworker(kworker/u2:1,kworker/0:13,kworker/0:1H) 工作队列的命名

kworker(kworker/u2:1,kworker/0:13,kworker/0:1H) 工作队列的命名

Amelio Ming 4179

kworker是什么,又什么用

名字的意思 什么时候有的 这么看 系统中查看 显示的内容怎么看 有什么用 参考名字的意思Kernel Worker什么时候有的kworker是3.x内核引入的这么看系统中查看Linux下使用 ps -ef|grep kowrker显示的内容怎么看显示的格式kworker/%u:%d%su:是unbound的缩写,代表没有绑定特定的CPU,kworker /u2:0中的 2 是 work_pool 的

lyblyblyblin的博客 6万+

linux内核 work stuct,Linux内核实现透视---kthread_work

内核线程工作队列内核线程工作队列和普通工作队列看着十分相似,很多抽象概念如work和worker等都很相同并且执行对象也都是内核想成,但是内核线程工作队列没有普通工作队列的线程池的概念一个worker(工作组)对应到一个实际的内核线程而内核线程则按顺序依次执行worker上的每一个work从数据结构上也能看出来如下:工作组struct kthread_worker {unsigned intfla...

weixin_39934063的博客 431

【Linux 应用】 kworker 进程

kworker” 是 Linux 内核的工作线程,用于异步处理工作队列中的任务。这些任务包括处理硬件中断、文件系统事件、管理系统内存等。你可能会看到多个 kworker 进程,每个进程的名称后面都有一个数字,如 “kworker/0:1”、“kworker/1:2” 等。这个数字表示了 kworker 是在哪个 CPU 核心上运行的以及任务的顺序。kworker是3.x内核引入的,用来执行工作队列中的work,一般由kthreadd建立。

平凡的世界 6315

记录一次解决kworkerds挖矿木马的过程

1.楔子 阿里云的服务器发生了SSH链接特别慢,上去后发现CPU占用100%,好像和进程【 kworkerds】有关。停止进程后,CPU正常,稍候又会自动重启,CPU又100%。这是中毒的症状,于是记录下这个过程。 病毒kworkerds又叫挖矿,中毒的服务器一般都是现实cpu 100%。 2. 定位问题 使用Top命令查看CPU的使用情况,如下所示: 阿里云上的监视器显示如下: 很明显一个叫...

借你一秒 5622
上一篇: Android筆記-Linux Kernel Ftrace (Function Trace)解析(非常强悍的性能分析方法)
下一篇: Installing Arch Linux and Win8 together on motherboard Z97A
pointfish
博客等级 码龄17年 58粉丝 11原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值