91欧美超碰AV自拍|国产成年人性爱视频免费看|亚洲 日韩 欧美一厂二区入|人人看人人爽人人操aV|丝袜美腿视频一区二区在线看|人人操人人爽人人爱|婷婷五月天超碰|97色色欧美亚州A√|另类A√无码精品一级av|欧美特级日韩特级

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

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

3天內不再提示

深入淺析Linux內核之內核線程(上)

Linux閱碼場 ? 來源:Linux內核遠航者 ? 作者:Linux內核遠航者 ? 2021-04-28 16:26 ? 次閱讀
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

1.開場白

環(huán)境:

處理器架構:arm64

內核源碼:linux-5.11

ubuntu版本:20.04.1

代碼閱讀工具:vim+ctags+cscope

在linux系統(tǒng)中, 我們接觸最多的莫過于用戶空間的任務,像用戶線程或用戶進程,因為他們太活躍了,也太耀眼了以至于我們感受不到內核線程的存在,但是內核線程卻在背后默默地付出著,如內存回收,臟頁回寫,處理大量的軟中斷等,如果沒有內核線程那么linux世界是那么的可怕!本文力求與完整介紹完內核線程的整個生命周期,如內核線程的創(chuàng)建、調度等等,當然本文還是主要從內存管理和進程調度兩個維度來解析,且不會涉及到具體的內核線程如kswapd的實現(xiàn),最后我們會以一個簡單的內核模塊來說明如何在驅動代碼中來創(chuàng)建使用內核線程。

在進入我們真正的主題之前,我們需要知道一下事實:

1. 內核線程永遠運行于內核態(tài)絕不會跑到用戶態(tài)去執(zhí)行。

2.由于內核線程運行于內核態(tài),所有它的權限很高,請注意這里說的是權限很高并不意味著它的優(yōu)先級高,所有他可以直接做到操作頁表,維護cache, 讀寫系統(tǒng)寄存器等操作。

3.內核線性是沒有地址空間的概念,準確的來說是沒有用戶地址空間的概念,使用的是所有進程共享的內核地址空間,但是調度的時候會借用前一個進程的地址空間。

4.內核線程并沒有什么特別神秘的地方,他和普通的用戶任務一樣參與系統(tǒng)調度,也可以被遷移到任何cpu上運行。

5.每個cpu都有自己的idle進程,實質上也是內核線程,但是他們比較特殊,一來是被靜態(tài)創(chuàng)建,二來他們的優(yōu)先級最低,cpu上沒有其他進程運行的時候idle進程才運行。

6.除了初始化階段0號內核線程和kthreadd本身,其他所有的內核線程都是被kthreadd內核線程來間接創(chuàng)建。

2.kthreadd的誕生

盤古開天辟地,我們知道linux所有任務的祖先是0號進程,然后0號進程創(chuàng)建了天字第一號的1號init進程,init進程是所有用戶任務的祖先,而內核線程同樣也有自己的祖先那就是kthreadd內核線程他的pid是2,我們通過top命令可以觀察到:紅色方框都是父進程為2號進程的內核線程,綠色方框為kthreadd,他的父進程為0號進程。

ef285c5e-a7f7-11eb-9728-12bb97331649.png

下面我們來看內核線程的祖先線程kthreadd如何創(chuàng)建的:

start_kernel //init/main.c

-》arch_call_rest_init

-》rest_init

-》pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES)

可以看的在rest_init中調用kernel_thread來創(chuàng)建kthreadd內核線程,實際上初始化階段有兩個內核線程比較特殊一個是0號的idle(唯一一個沒有通過fork創(chuàng)建的任務),一個是被idle創(chuàng)建的kthreadd內核線程(內核初始化階段可以看成idle進程在做初始化)。

我們再來看看kernel_thread是如何實現(xiàn)的:

/*

* Create a kernel thread.

*/

pid_t kernel_thread(int (*fn)(void *), void *arg, unsigned long flags)

{

struct kernel_clone_args args = {

.flags = ((lower_32_bits(flags) | CLONE_VM |

| CLONE_UNTRACED) & ~CSIGNAL),

.exit_signal = (lower_32_bits(flags) & CSIGNAL),

.stack = (unsigned long)fn,

.stack_size = (unsigned long)arg,

};

return kernel_clone(&args);

}

這里需要注意兩點:1.fork時傳遞了CLONE_VM標志 2.如何標識要創(chuàng)建出來的是內核線程不是普通的用戶任務

我們先來看看CLONE_VM標志對fork的影響:

kernel_clone

-》copy_process

-》copy_mm

-》dup_mm

-》。..。

1394 tsk-》mm = NULL;

1395 tsk-》active_mm = NULL;

1396

1397 /*

1398 |* Are we cloning a kernel thread?

1399 |*

1400 |* We need to steal a active VM for that.。

1401 |*/

1402 oldmm = current-》mm;

1403 if (!oldmm)

1404 return 0;

1405

1406 /* initialize the new vmacache entries */

1407 vmacache_flush(tsk);

1408

1409 if (clone_flags & CLONE_VM) {

1410 mmget(oldmm);

1411 mm = oldmm;

1412 goto good_mm;

1413 }

1414

1415 retval = -ENOMEM;

1416 mm = dup_mm(tsk, current-》mm);

1417 if (!mm)

1418 goto fail_nomem;

1419

1420 good_mm:

1421 tsk-》mm = mm;

1422 tsk-》active_mm = mm;

1423 return 0;

可以看的當我們傳遞了CLONE_VM標志之后,本來應該走到1409 行進程處理的,但是我們需要知道的是1403 行可能判斷為空,因為這里父進程為idle為內核線程,憑直覺我們知道代碼應該從 1404 返回了,但是不能光憑直覺要拿出證據(jù),那就需要看看idle進程長啥樣了:

64 struct task_struct init_task //init/init_task.c

69 = {

。..

85 .mm = NULL,

86 .active_mm = &init_mm,

上面是靜態(tài)創(chuàng)建的idle進程,可以看的他的進程控制塊的 .mm 為空, .active_mm 為&init_mm,所有啊,我們的kthreadd內核線程的tsk-》mm = tsk-》active_mm =NULL;所以我們上面的猜想是對的代碼直接從 1404 返回了,這里也是他應該擁有的屬性,因為我們知道內核線程沒有用戶地址空間(使用tsk-》mm來描述),所以所有的內核線程的tsk-》mm都為空,這也是判斷任務是否為內核線程的一個條件,但是tsk-》active_mm 就不一定了,內核線程在每次進程切換的時候都會借用前一個進程的tsk-》active_mm 賦值到自己tsk-》active_mm 上,后面會講到。這里需要注意的是,有一個內核線程很特殊,特殊到他的tsk-》active_mm 不是在進程切換的時候被賦值而是靜態(tài)初始化號,他就是上面的idle線程 .active_mm = &init_mm。

我們來看下init_mm是什么內容,有什么貓膩:

mm/init-mm.c

struct mm_struct init_mm = {

.mm_rb = RB_ROOT,

.pgd = swapper_pg_dir,

。..

可以看到他的特殊之處在于它的tsk-》active_mm-》pgd為swapper_pg_dir,我們知道這是主內核頁表,我們知道系統(tǒng)初始化的時候,會出現(xiàn)3個特殊的任務0,1,2號,這幾個任務剛開始都是內核線程,他們之間進行切換的時候使用的都是swapper_pg_dir這個頁表,也很合理,因為都訪問內核空間,一旦有用戶進程介入?yún)⑴c調度了就不一樣了,就可以借用用戶的tsk-》active_mm-》pgd(這個時候不再是swapper_pg_dir,但是沒有關系,通過ttbr1_el1同樣可以訪問到swapper_pg_dir頁表來訪問內核空間)。

再來看看如何標識要創(chuàng)建的是內核線程的?

kernel_clone

-》copy_process

-》copy_thread //arch/arm64/kernel/process.c

-》 。..

if (likely(?。╬-》flags & PF_KTHREAD))) { //創(chuàng)建用戶任務的情況

。..

} else { //創(chuàng)建內核線程的情況

memset(childregs, 0, sizeof(struct pt_regs));

childregs-》pstate = PSR_MODE_EL1h | PSR_IL_BIT;

p-》thread.cpu_context.x19 = stack_start;

p-》thread.cpu_context.x20 = stk_sz;

}

以上路徑是為創(chuàng)建任務準備調度上下文和異常返回現(xiàn)場,調度上下文由 p-》thread.cpu_context來描述,異常返回現(xiàn)場由保存在內核棧的struct pt_regs來描述,在這里判斷p-》flags & PF_KTHREAD))是否成立,也就是如果p-》flags設置了PF_KTHREAD標志則是創(chuàng)建內核線程,但是我們找了一圈貌似沒有找到在哪個位置設置這個標志的,那究竟在哪設置的呢?我們還是首先回到它的父進程也就是idle進程:

struct task_struct init_task

= {

。..

.flags = PF_KTHREAD,

。..

}

憑直覺,應該是父進程設置了然后賦值給了子進程,那我們就要看看合適賦值的:

copy_process

-》dup_task_struct

-》arch_dup_task_struct

-》*dst = *src;

我們看的會把父進程的的task的內容賦值給子進程,然后后面在進程一些個性化設置,.flags = PF_KTHREAD也被設置給了子進程。

ok, 分析到這里idle就創(chuàng)建好了kthreadd內核線程,通過wake_up_new_task喚醒kthreadd運行:當它喚醒被調度后,就會恢復調度上下文,就是上面說的 p-》thread.cpu_context,具體如何執(zhí)行到內核線程指定的執(zhí)行函數(shù)后面我們會講解!

但是我們需要知道的是,kthreadd被調度執(zhí)行后執(zhí)行kthreadd這個函數(shù)?。?!這個函數(shù)實現(xiàn)在:kernel/kthread.c中。

3. kthreadd內核線程處理流程

上面我們介紹了kthreadd內核線程的創(chuàng)建過程,接下來看一下kthreadd做了哪些事情:

代碼路徑為:kernel/kthread.c

kthreadd函數(shù)中設置了線程名字和親和性屬性之后 進入下面給出的循環(huán)處理流程:

ef5c496a-a7f7-11eb-9728-12bb97331649.png

它首先將自己的狀態(tài)設置為TASK_INTERRUPTIBLE,然后判斷kthread_create_list鏈表是否為空,這個鏈表存放其他內核路徑的創(chuàng)建內核線程的請求結構struct kthread_create_info:

kernel/kthread.c

struct kthread_create_info

{

/* Information passed to kthread() from kthreadd. */

int (*threadfn)(void *data); //請求創(chuàng)建的內核線程處理函數(shù)

void *data; //傳遞給請求創(chuàng)建的內核線程的參數(shù)

int node;

/* Result passed back to kthread_create() from kthreadd. */

struct task_struct *result; //請求創(chuàng)建的內核線程的tsk結構

struct completion *done;

struct list_head list; //用于加 入kthread_create_list鏈表

};

有創(chuàng)建內核線程時,會封裝kthread_create_info結構然后加入到kthread_create_list鏈表中。

如果kthread_create_list鏈表為空,說明沒有創(chuàng)建內核線程的請求,則直接調用schedule進行睡眠。當某個內核路徑有kthread_create_info結構加入到kthread_create_list鏈表中并喚醒kthreadd后,kthreadd從__set_current_state(TASK_RUNNING)開始執(zhí)行,設置狀態(tài)為運行狀態(tài),然后進入一個循環(huán),不斷的從kthread_create_list.next取出kthread_create_info結構,并從鏈表中刪除,調用create_kthread創(chuàng)建一個內核線程來執(zhí)行剩余的工作。

create_kthread很簡單,就是創(chuàng)建內核線程,然后執(zhí)行kthread函數(shù),將取到的kthread_create_info結構傳遞給這個函數(shù):

kernel/kthread.c

create_kthread

-》 pid = kernel_thread(kthread, create, CLONE_FS | CLONE_FILES | SIGCHLD)

4.kthread處理流程

當kthreadd內核線程創(chuàng)建內核線程之后就完成了它的使命,開始處理kthread_create_list鏈表上的下一個內核線程創(chuàng)建請求,主要工作交給了kthread函數(shù)來處理。實際上,kthreadd創(chuàng)建的內核線程就是請求創(chuàng)建的內核線程的外殼,只不過創(chuàng)建完成之后并沒有馬上執(zhí)行線程的執(zhí)行函數(shù),這和用戶空間執(zhí)行程序很相似:一般在shell中執(zhí)行程序,首先shell進程通過fork創(chuàng)建一個子進程,然后子進程中調用exec來加載新的程序。而創(chuàng)建內核線程也必須首先要創(chuàng)建一個子進程,這是kthreadd通過kernel_thread來完成的,然后在kthread執(zhí)行函數(shù)中在合適的時機來執(zhí)行所請求的內核線程執(zhí)行函數(shù)。這說起來有點繞,因為這里涉及到了三個任務:kthreadd內核線程,kthreadd內核線程通過kernel_thread創(chuàng)建的內核線程,往kthread_create_list鏈表加入創(chuàng)建請求的那個任務

注:執(zhí)行kthread函數(shù)處于新創(chuàng)建的內核線程上下文!

下面我們來看下kthreadd內核線程創(chuàng)建的內核線程的執(zhí)行函數(shù)kthread:這里傳遞給kthread的參數(shù)就是從kthread_create_list鏈表摘取的創(chuàng)建結構kthread_create_info,函數(shù)中又出現(xiàn)了一個新的結構struct kthread:

kernel/kthread.c

struct kthread {

unsigned long flags;

unsigned int cpu;

int (*threadfn)(void *); //線程執(zhí)行函數(shù)

void *data; //線程執(zhí)行函數(shù)傳遞的參數(shù)

mm_segment_t oldfs;

struct completion parked;

struct completion exited;

#ifdef CONFIG_BLK_CGROUP

struct cgroup_subsys_state *blkcg_css;

#endif

};

其中比較重要的是threadfn和data。kthread函數(shù)并不長,我們把代碼都羅列如下:

244 static int kthread(void *_create)

245 {

246 /* Copy data: it‘s on kthread’s stack */

247 struct kthread_create_info *create = _create; //獲取傳遞過來的線程創(chuàng)建信息

248 int (*threadfn)(void *data) = create-》threadfn; //取出 線程執(zhí)行函數(shù)

249 void *data = create-》data; //取出 傳遞給 線程執(zhí)行函數(shù)的參數(shù)

250 struct completion *done;

251 struct kthread *self;

252 int ret;

253

254 self = kzalloc(sizeof(*self), GFP_KERNEL); //分配 kthread 結構

255 set_kthread_struct(self); //current-》set_child_tid = (__force void __user *)kthread

256

257 /* If user was SIGKILLed, I release the structure. */

258 done = xchg(&create-》done, NULL); //獲得 done完成量

259 if (!done) {

260 kfree(create);

261 do_exit(-EINTR);

262 }

263

264 if (!self) {

265 create-》result = ERR_PTR(-ENOMEM);

266 complete(done);

267 do_exit(-ENOMEM);

268 }

269

270 self-》threadfn = threadfn; // 賦值 self-》threadfn 為 線程執(zhí)行函數(shù)

271 self-》data = data; // 賦值 self-》data 線程執(zhí)行函數(shù)的參數(shù)

272 init_completion(&self-》exited);

273 init_completion(&self-》parked);

274 current-》vfork_done = &self-》exited;

276 /* OK, tell user we‘re spawned, wait for stop or wakeup */

277 __set_current_state(TASK_UNINTERRUPTIBLE); //設置內核線程狀態(tài)為 TASK_UNINTERRUPTIBLE 但是此時還沒又睡眠

278 create-》result = current; //用于返回 當前任務的tsk

279 /*

280 |* Thread is going to call schedule(), do not preempt it,

281 |* or the creator may spend more time in wait_task_inactive()。

282 |*/

283 preempt_disable();

284 complete(done); //喚醒等待done完成量的任務

285 schedule_preempt_disabled(); //睡眠

286 preempt_enable(); //喚醒的時候從此開始執(zhí)行

287

288 ret = -EINTR;

289 if (!test_bit(KTHREAD_SHOULD_STOP, &self-》flags)) { //判斷 self-》flags是否為 KTHREAD_SHOULD_STOP(kthread_stop會設置)

290 cgroup_kthread_ready();

291 __kthread_parkme(self);

292 ret = threadfn(data); //執(zhí)行 真正的線程執(zhí)行函數(shù)

293 }

294 do_exit(ret); //當前任務退出

295 }

可以看到,kthread函數(shù)用到了一些完成量和睡眠函數(shù),如果單獨看這個函數(shù)肯定會一頭霧水,要理解這個函數(shù)需要回答一下幾個問題:

1.284行的complete(done) 是喚醒哪個任務的?

2.當前內核線程在285 行睡眠后 誰來喚醒我?

5.kthread_run函數(shù)

這里我們以kthread_run為例來解答這兩個問題:

kthread_run這個內核api用來創(chuàng)建內核線程并喚醒執(zhí)行傳遞的執(zhí)行函數(shù)。調用路徑如下:

include/linux/kthread.h

#define kthread_run(threadfn, data, namefmt, 。..)

({

struct task_struct *__k

= kthread_create(threadfn, data, namefmt, ## __VA_ARGS__); //創(chuàng)建內核線程

if (!IS_ERR(__k))

wake_up_process(__k); //喚醒創(chuàng)建的內核線程

__k;

})

kthread_run這個宏傳遞三個參數(shù):執(zhí)行函數(shù),執(zhí)行函數(shù)傳遞的參數(shù),格式化線程名字

我們先來看下kthread_create函數(shù):

4.1 kthread_create函數(shù)

kthread_create

-》kthread_create_on_node

-》__kthread_create_on_node

__kthread_create_on_node函數(shù)并不長我們全部羅列:

330 struct task_struct *__kthread_create_on_node(int (*threadfn)(void *data),

331 | void *data, int node,

332 | const char namefmt[],

333 | va_list args)

334 {

335 DECLARE_COMPLETION_ONSTACK(done);

336 struct task_struct *task;

337 struct kthread_create_info *create = kmalloc(sizeof(*create),

338 | GFP_KERNEL); //分配 kthread_create_info結構

339

340 if (!create)

341 return ERR_PTR(-ENOMEM);

342 create-》threadfn = threadfn; //填充kthread_create_info結構 如執(zhí)行函數(shù)等

343 create-》data = data;

344 create-》node = node;

345 create-》done = &done;

346

347 spin_lock(&kthread_create_lock);

348 list_add_tail(&create-》list, &kthread_create_list); //kthread_create_info結構添加到 kthread_create_list 鏈表

349 spin_unlock(&kthread_create_lock);

350

351 wake_up_process(kthreadd_task); //喚醒 kthreadd來處理創(chuàng)建內核線程請求

352 /*

353 |* Wait for completion in killable state, for I might be chosen by

354 |* the OOM killer while kthreadd is trying to allocate memory for

355 |* new kernel thread.

356 |*/

357 if (unlikely(wait_for_completion_killable(&done))) { //等待請求的內核線程創(chuàng)建完成

358 /*

359 |* If I was SIGKILLed before kthreadd (or new kernel thread)

360 |* calls complete(), leave the cleanup of this structure to

361 |* that thread.

362 |*/

363 if (xchg(&create-》done, NULL))

364 return ERR_PTR(-EINTR);

365 /*

366 |* kthreadd (or new kernel thread) will call complete()

367 |* shortly.

368 |*/

369 wait_for_completion(&done);

370 }

371 task = create-》result; //獲得 創(chuàng)建完成的 內核線程的tsk

372 if (!IS_ERR(task)) { // 內核線程創(chuàng)建成功后 進行后續(xù)的處理

373 static const struct sched_param param = { .sched_priority = 0 };

374 char name[TASK_COMM_LEN];

375

376 /*

377 |* task is already visible to other tasks, so updating

378 |* COMM must be protected.

379 |*/

380 vsnprintf(name, sizeof(name), namefmt, args);

381 set_task_comm(task, name); //設置 內核線程的名字

382 /*

383 |* root may have changed our (kthreadd’s) priority or CPU mask.

384 |* The kernel thread should not inherit these properties.

385 |*/

386 sched_setscheduler_nocheck(task, SCHED_NORMAL, ?m); //設置 調度策略和優(yōu)先級

387 set_cpus_allowed_ptr(task,

388 | housekeeping_cpumask(HK_FLAG_KTHREAD)); //設置cpu親和性

389 }

390 kfree(create);

391 return task;

392 }

關于__kthread_create_on_node函數(shù)需要明白以下幾點:1.__kthread_create_on_node函數(shù)處于一個進程上下文如insmod進程2.__kthread_create_on_node函數(shù)需要與兩個任務交互,一個是kthreadd,一個是kthreadd的創(chuàng)建的內核線程(執(zhí)行函數(shù)為kthread)

函數(shù)中已經做了詳細的注釋,這里在說明一下:首先函數(shù)將需要在內核線程中執(zhí)行的函數(shù)等信息封裝在kthread_create_info結構中,然后加入到kthreadd的kthread_create_list鏈表,接著去喚醒kthreadd去處理創(chuàng)建內核線程請求,上面kthreadd函數(shù)我們分析過kthreadd函數(shù)會創(chuàng)建一個內核線程來執(zhí)行kthread函數(shù),并將kthread_create_info結構傳遞過去,在kthread函數(shù)中會通過complete(done)來喚醒357的完成等待(這就回答了第一個問題), 然后__kthread_create_on_node接著進行初始化,但是需要明白的是新創(chuàng)建的內核線程現(xiàn)在處于睡眠狀態(tài),等待被喚醒。

4.2 wake_up_process喚醒

上面通過kthread_create創(chuàng)建完成內核線程之后,內核線程處于TASK_UNINTERRUPTIBLE狀態(tài),等待被喚醒,這個時候kthread_run調用wake_up_process喚醒新創(chuàng)建的內核線程,內核線程愉快的執(zhí)行,走到了kthread函數(shù)的threadfn(data)處,執(zhí)行真正的線程處理,至此,新創(chuàng)建的內核線程開始完成實質性的工作。

6. kthread_stop函數(shù)

一般通過kthread_create創(chuàng)建的內核線程可以通過kthread_stop來停止:

609 int kthread_stop(struct task_struct *k)

610 {

611 struct kthread *kthread;

612 int ret;

613

614 trace_sched_kthread_stop(k);

615

616 get_task_struct(k);

617 kthread = to_kthread(k); //tsk中獲得kthread 結構

618 set_bit(KTHREAD_SHOULD_STOP, &kthread-》flags); //設置KTHREAD_SHOULD_STOP標志

619 kthread_unpark(k);

620 wake_up_process(k); //喚醒

621 wait_for_completion(&kthread-》exited); //等待退出完成

622 ret = k-》exit_code; //獲得退出碼

623 put_task_struct(k);

624

625 trace_sched_kthread_stop_ret(ret);

626 return ret;

627 }

一般內核線程會循環(huán)執(zhí)行一些事務,每次循環(huán)開始會調用kthread_should_stop來判斷線程是否應該停止:

bool kthread_should_stop(void)

{

return test_bit(KTHREAD_SHOULD_STOP, &to_kthread(current)-》flags); //判斷KTHREAD_SHOULD_STOP標志是否置位

}

在某個內核路徑調用kthread_stop,內核線程每次循環(huán)開始的時候,如果檢查到KTHREAD_SHOULD_STOP標志置位,就會退出,然后調用do_exit完成退出操作。

上面講解到很多函數(shù)也涉及到很多任務,下面總結一下:1.涉及到的函數(shù)有:kthreadd, kthread,kthread_run,kthread_create, wake_up_process, kthread_stop, kthread_should_stopkthreadd:為kthreadd內核線程執(zhí)行函數(shù),處理內核線程創(chuàng)建任務。kthread:每次kthreadd創(chuàng)建新的內核線程都會執(zhí)行kthread,里面會涉及到睡眠和喚醒后執(zhí)行線程執(zhí)行函數(shù)操作。kthread_run:創(chuàng)建并喚醒一個內核線程kthread_create:創(chuàng)建一個內核線程,創(chuàng)建之后處于TASK_UNINTERRUPTIBLE狀態(tài)wake_up_process:喚醒一個任務kthread_stop:停止一個內核線程kthread_should_stop:判斷一個內核線程是否應該停止2.涉及到的kthreadd內核線程,新創(chuàng)建的內核線程,發(fā)起創(chuàng)建內核線程請求的任務,他們直接通過完成量進行同步3.睡眠喚醒流程:先設置進程狀態(tài)為TASK_UNINTERRUPTIBLE這樣的狀態(tài),然后調度出去,喚醒的時候在調度回來

好了,下面給出精心制作的調用圖示:

ef8c80bc-a7f7-11eb-9728-12bb97331649.png

上面已經講解完了,內核線程是如何被創(chuàng)建的,又是如何執(zhí)行處理函數(shù)的,涉及到多個任務直接同步問題,看代碼的時候需要多個窗口配合之看才行。
編輯:lyn

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • 代碼
    +關注

    關注

    30

    文章

    4967

    瀏覽量

    73956
  • LINUX內核
    +關注

    關注

    1

    文章

    321

    瀏覽量

    23201

原文標題:深入理解Linux內核之內核線程(上)

文章出處:【微信號:LinuxDev,微信公眾號:Linux閱碼場】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

    評論

    相關推薦
    熱點推薦

    Linux內核伙伴系統(tǒng)內存申請函數(shù)詳解:從原理到實戰(zhàn)

    Linux 內核中,內存管理是整個系統(tǒng)穩(wěn)定運行的基石,而伙伴系統(tǒng)(Buddy System) 作為內核物理內存分配的核心機制,更是驅動開發(fā)、內核模塊開發(fā)的必備知識點。它通過 "2
    的頭像 發(fā)表于 02-10 16:58 ?3630次閱讀
    <b class='flag-5'>Linux</b><b class='flag-5'>內核</b>伙伴系統(tǒng)內存申請函數(shù)詳解:從原理到實戰(zhàn)

    深入理解設備樹chosen節(jié)點:固件與內核的“配置橋梁”

    在嵌入式 Linux 開發(fā)中,設備樹(Device Tree)是連接硬件與內核的關鍵紐帶。但有一個節(jié)點很特殊 —— 它不描述任何硬件模塊,卻直接決定內核能否正常啟動,這就是chosen節(jié)點。
    的頭像 發(fā)表于 02-09 16:36 ?135次閱讀
    <b class='flag-5'>深入</b>理解設備樹chosen節(jié)點:固件與<b class='flag-5'>內核</b>的“配置橋梁”

    Linux內核的“心跳”:jiffies如何為系統(tǒng)計時?

    Linux 內核的世界里,有一個默默工作的 "計時器"——jiffies。它不像我們手機上的時鐘那樣顯示年月日,卻掌控著內核中絕大多數(shù)時間相關的操作:從進程調度到設備驅動的定時檢查,都離不開它的身影。
    的頭像 發(fā)表于 02-04 16:27 ?815次閱讀
    <b class='flag-5'>Linux</b><b class='flag-5'>內核</b>的“心跳”:jiffies如何為系統(tǒng)計時?

    深入RK3588內核:rockchip_linux_defconfig的作用與調試價值

    在 RK3588 芯片的 Linux 開發(fā)中,有一個文件始終是開發(fā)者繞不開的核心 ——kernel/arch/arm64/configs/rockchip_linux_defconfig。無論是首次
    的頭像 發(fā)表于 02-03 15:56 ?1155次閱讀
    <b class='flag-5'>深入</b>RK3588<b class='flag-5'>內核</b>:rockchip_<b class='flag-5'>linux</b>_defconfig的作用與調試價值

    Linux系統(tǒng)內核參數(shù)調優(yōu)實戰(zhàn)指南

    Linux 內核參數(shù)調優(yōu)是系統(tǒng)性能優(yōu)化的核心環(huán)節(jié)。隨著云原生架構的普及和硬件性能的飛速提升,默認的內核參數(shù)配置往往無法充分發(fā)揮系統(tǒng)潛力。在高并發(fā) Web 服務、大數(shù)據(jù)處理、容器化部署等場景下,合理的
    的頭像 發(fā)表于 01-28 14:27 ?426次閱讀

    【「Linux 設備驅動開發(fā)(第 2 版)」閱讀體驗】+讀深入理解Linux內核內存分配

    每個內存地址是虛擬的,不是直接指向RAM中的任何地址。當用戶訪問內存中的存儲單元時,都會進行地址轉換以匹配相應的物理內存。書籍的第10章討論了五個主題,對Linux內核內存分配進行詳細講解。 接著
    發(fā)表于 01-16 20:05

    【「Linux 設備驅動開發(fā)(第 2 版)」閱讀體驗】Linux內核開發(fā)基礎

    感謝電子發(fā)燒友論壇提供的《Linux設備驅動開發(fā)(第2版)》閱讀機會,測評將從Linux內核開發(fā)基礎、Linux內核平臺抽象和設備驅動程序、
    發(fā)表于 01-12 22:45

    【「Linux 設備驅動開發(fā)(第 2 版)」閱讀體驗】+讀內核處理的核心輔助函數(shù)

    “處理內核的核心輔助函數(shù)”進行學習。 第3章又是以5個主題展開討論學習,①、Linux內核加鎖機制和共享資源;②、處理內核等待、睡眠和延遲機制;③、
    發(fā)表于 01-10 22:08

    深入Linux內核:進程調度的核心邏輯與實現(xiàn)細節(jié)

    ,背后都離不開內核調度算法的精準操控。今天,我們就從優(yōu)先級、調度算法、時間片分配到底層實現(xiàn),全方位拆解Linux內核進程調度的核心邏輯。 一、進程調度的“身份標識”:優(yōu)先級與分類 要理解調度邏輯,首先得搞懂:進程憑什么“插隊”?
    的頭像 發(fā)表于 12-24 07:05 ?4299次閱讀
    <b class='flag-5'>深入</b><b class='flag-5'>Linux</b><b class='flag-5'>內核</b>:進程調度的核心邏輯與實現(xiàn)細節(jié)

    Linux內核printk日志級別全解析:從參數(shù)解讀到實操配置

    一、開篇:一個命令引出的核心問題 在?Linux?終端執(zhí)行?cat /proc/sys/kernel/printk,你可能會看到這樣的輸出: 這串數(shù)字不是隨機的,而是內核日志系統(tǒng)的“核心配置開關
    的頭像 發(fā)表于 11-20 15:54 ?1696次閱讀
    <b class='flag-5'>Linux</b><b class='flag-5'>內核</b>printk日志級別全解析:從參數(shù)解讀到實操配置

    deepin亮相2025中國Linux內核開發(fā)者大會

    11 月 1 日,第二十屆中國 Linux 內核開發(fā)者大會(CLK)在深圳舉辦。CLK 作為國內 Linux 內核領域極具影響力的峰會,由清華大學、Intel、華為、阿里云、富士通南大
    的頭像 發(fā)表于 11-05 17:59 ?816次閱讀

    Linux內核參數(shù)調優(yōu)方案

    在高并發(fā)微服務環(huán)境中,網絡性能往往成為K8s集群的瓶頸。本文將深入探討如何通過精細化的Linux內核參數(shù)調優(yōu),讓你的K8s節(jié)點網絡性能提升30%以上。
    的頭像 發(fā)表于 08-06 17:50 ?947次閱讀

    如何配置和驗證Linux內核參數(shù)

    Linux系統(tǒng)運維和性能優(yōu)化中,內核參數(shù)(sysctl)的配置至關重要。合理的參數(shù)調整可以顯著提升網絡性能、系統(tǒng)穩(wěn)定性及資源利用率。然而,僅僅修改參數(shù)是不夠的,如何驗證這些參數(shù)是否生效同樣關鍵。
    的頭像 發(fā)表于 05-29 17:40 ?1150次閱讀

    Linux內核編譯失???移動硬盤和虛擬機的那些事兒

    Linux開發(fā)中,編譯內核是一項常見任務,但不少開發(fā)者在移動硬盤或虛擬機環(huán)境下嘗試時會遭遇失敗。本文將簡要探討這些問題的成因,并介紹一些虛擬機使用技巧,幫助大家更好地應對相關問題。在移動硬盤里編譯
    的頭像 發(fā)表于 04-11 11:36 ?991次閱讀
    <b class='flag-5'>Linux</b><b class='flag-5'>內核</b>編譯失?。恳苿佑脖P和虛擬機的那些事兒

    樹莓派4 性能大比拼:標準Linux與實時Linux 4.19內核的延遲測試

    引言本文是對我之前關于RaspberryPi3同一主題的帖子的更新。與之前的帖子一樣,我使用的是隨Raspbian鏡像提供的標準內核,以及應用了RT補丁的相似內核版本。對于實時版,我
    的頭像 發(fā)表于 03-25 09:39 ?809次閱讀
    樹莓派4 性能大比拼:標準<b class='flag-5'>Linux</b>與實時<b class='flag-5'>Linux</b> 4.19<b class='flag-5'>內核</b>的延遲測試