0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

深入浅析Linux内核之内核线程(上)

Linux阅码场 来源:Linux内核远航者 作者:Linux内核远航者 2021-04-28 16:26 次阅读

1.开场白

环境:

处理器架构:arm64

内核源码:linux-5.11

ubuntu版本:20.04.1

代码阅读工具:vim+ctags+cscope

在linux系统中, 我们接触最多的莫过于用户空间的任务,像用户线程或用户进程,因为他们太活跃了,也太耀眼了以至于我们感受不到内核线程的存在,但是内核线程却在背后默默地付出着,如内存回收,脏页回写,处理大量的软中断等,如果没有内核线程那么linux世界是那么的可怕!本文力求与完整介绍完内核线程的整个生命周期,如内核线程的创建、调度等等,当然本文还是主要从内存管理和进程调度两个维度来解析,且不会涉及到具体的内核线程如kswapd的实现,最后我们会以一个简单的内核模块来说明如何在驱动代码中来创建使用内核线程。

在进入我们真正的主题之前,我们需要知道一下事实:

1. 内核线程永远运行于内核态绝不会跑到用户态去执行。

2.由于内核线程运行于内核态,所有它的权限很高,请注意这里说的是权限很高并不意味着它的优先级高,所有他可以直接做到操作页表,维护cache, 读写系统寄存器等操作。

3.内核线性是没有地址空间的概念,准确的来说是没有用户地址空间的概念,使用的是所有进程共享的内核地址空间,但是调度的时候会借用前一个进程的地址空间。

4.内核线程并没有什么特别神秘的地方,他和普通的用户任务一样参与系统调度,也可以被迁移到任何cpu上运行。

5.每个cpu都有自己的idle进程,实质上也是内核线程,但是他们比较特殊,一来是被静态创建,二来他们的优先级最低,cpu上没有其他进程运行的时候idle进程才运行。

6.除了初始化阶段0号内核线程和kthreadd本身,其他所有的内核线程都是被kthreadd内核线程来间接创建。

2.kthreadd的诞生

盘古开天辟地,我们知道linux所有任务的祖先是0号进程,然后0号进程创建了天字第一号的1号init进程,init进程是所有用户任务的祖先,而内核线程同样也有自己的祖先那就是kthreadd内核线程他的pid是2,我们通过top命令可以观察到:红色方框都是父进程为2号进程的内核线程,绿色方框为kthreadd,他的父进程为0号进程。

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

下面我们来看内核线程的祖先线程kthreadd如何创建的:

start_kernel //init/main.c

-》arch_call_rest_init

-》rest_init

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

可以看的在rest_init中调用kernel_thread来创建kthreadd内核线程,实际上初始化阶段有两个内核线程比较特殊一个是0号的idle(唯一一个没有通过fork创建的任务),一个是被idle创建的kthreadd内核线程(内核初始化阶段可以看成idle进程在做初始化)。

我们再来看看kernel_thread是如何实现的:

/*

* 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.如何标识要创建出来的是内核线程不是普通的用户任务

我们先来看看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 返回了,但是不能光凭直觉要拿出证据,那就需要看看idle进程长啥样了:

64 struct task_struct init_task //init/init_task.c

69 = {

。..

85 .mm = NULL,

86 .active_mm = &init_mm,

上面是静态创建的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 不是在进程切换的时候被赋值而是静态初始化号,他就是上面的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,我们知道这是主内核页表,我们知道系统初始化的时候,会出现3个特殊的任务0,1,2号,这几个任务刚开始都是内核线程,他们之间进行切换的时候使用的都是swapper_pg_dir这个页表,也很合理,因为都访问内核空间,一旦有用户进程介入参与调度了就不一样了,就可以借用用户的tsk-》active_mm-》pgd(这个时候不再是swapper_pg_dir,但是没有关系,通过ttbr1_el1同样可以访问到swapper_pg_dir页表来访问内核空间)。

再来看看如何标识要创建的是内核线程的?

kernel_clone

-》copy_process

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

-》 。..

if (likely(!(p-》flags & PF_KTHREAD))) { //创建用户任务的情况

。..

} else { //创建内核线程的情况

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;

}

以上路径是为创建任务准备调度上下文和异常返回现场,调度上下文由 p-》thread.cpu_context来描述,异常返回现场由保存在内核栈的struct pt_regs来描述,在这里判断p-》flags & PF_KTHREAD))是否成立,也就是如果p-》flags设置了PF_KTHREAD标志则是创建内核线程,但是我们找了一圈貌似没有找到在哪个位置设置这个标志的,那究竟在哪设置的呢?我们还是首先回到它的父进程也就是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就创建好了kthreadd内核线程,通过wake_up_new_task唤醒kthreadd运行:当它唤醒被调度后,就会恢复调度上下文,就是上面说的 p-》thread.cpu_context,具体如何执行到内核线程指定的执行函数后面我们会讲解!

但是我们需要知道的是,kthreadd被调度执行后执行kthreadd这个函数!!!这个函数实现在:kernel/kthread.c中。

3. kthreadd内核线程处理流程

上面我们介绍了kthreadd内核线程的创建过程,接下来看一下kthreadd做了哪些事情:

代码路径为:kernel/kthread.c

kthreadd函数中设置了线程名字和亲和性属性之后 进入下面给出的循环处理流程:

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

它首先将自己的状态设置为TASK_INTERRUPTIBLE,然后判断kthread_create_list链表是否为空,这个链表存放其他内核路径的创建内核线程的请求结构struct kthread_create_info:

kernel/kthread.c

struct kthread_create_info

{

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

int (*threadfn)(void *data); //请求创建的内核线程处理函数

void *data; //传递给请求创建的内核线程的参数

int node;

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

struct task_struct *result; //请求创建的内核线程的tsk结构

struct completion *done;

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

};

有创建内核线程时,会封装kthread_create_info结构然后加入到kthread_create_list链表中。

如果kthread_create_list链表为空,说明没有创建内核线程的请求,则直接调用schedule进行睡眠。当某个内核路径有kthread_create_info结构加入到kthread_create_list链表中并唤醒kthreadd后,kthreadd从__set_current_state(TASK_RUNNING)开始执行,设置状态为运行状态,然后进入一个循环,不断的从kthread_create_list.next取出kthread_create_info结构,并从链表中删除,调用create_kthread创建一个内核线程来执行剩余的工作。

create_kthread很简单,就是创建内核线程,然后执行kthread函数,将取到的kthread_create_info结构传递给这个函数:

kernel/kthread.c

create_kthread

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

4.kthread处理流程

当kthreadd内核线程创建内核线程之后就完成了它的使命,开始处理kthread_create_list链表上的下一个内核线程创建请求,主要工作交给了kthread函数来处理。实际上,kthreadd创建的内核线程就是请求创建的内核线程的外壳,只不过创建完成之后并没有马上执行线程的执行函数,这和用户空间执行程序很相似:一般在shell中执行程序,首先shell进程通过fork创建一个子进程,然后子进程中调用exec来加载新的程序。而创建内核线程也必须首先要创建一个子进程,这是kthreadd通过kernel_thread来完成的,然后在kthread执行函数中在合适的时机来执行所请求的内核线程执行函数。这说起来有点绕,因为这里涉及到了三个任务:kthreadd内核线程,kthreadd内核线程通过kernel_thread创建的内核线程,往kthread_create_list链表加入创建请求的那个任务

注:执行kthread函数处于新创建的内核线程上下文!

下面我们来看下kthreadd内核线程创建的内核线程的执行函数kthread:这里传递给kthread的参数就是从kthread_create_list链表摘取的创建结构kthread_create_info,函数中又出现了一个新的结构struct kthread:

kernel/kthread.c

struct kthread {

unsigned long flags;

unsigned int cpu;

int (*threadfn)(void *); //线程执行函数

void *data; //线程执行函数传递的参数

mm_segment_t oldfs;

struct completion parked;

struct completion exited;

#ifdef CONFIG_BLK_CGROUP

struct cgroup_subsys_state *blkcg_css;

#endif

};

其中比较重要的是threadfn和data。kthread函数并不长,我们把代码都罗列如下:

244 static int kthread(void *_create)

245 {

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

247 struct kthread_create_info *create = _create; //获取传递过来的线程创建信息

248 int (*threadfn)(void *data) = create-》threadfn; //取出 线程执行函数

249 void *data = create-》data; //取出 传递给 线程执行函数的参数

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 为 线程执行函数

271 self-》data = data; // 赋值 self-》data 线程执行函数的参数

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); //设置内核线程状态为 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(); //唤醒的时候从此开始执行

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); //执行 真正的线程执行函数

293 }

294 do_exit(ret); //当前任务退出

295 }

可以看到,kthread函数用到了一些完成量和睡眠函数,如果单独看这个函数肯定会一头雾水,要理解这个函数需要回答一下几个问题:

1.284行的complete(done) 是唤醒哪个任务的?

2.当前内核线程在285 行睡眠后 谁来唤醒我?

5.kthread_run函数

这里我们以kthread_run为例来解答这两个问题:

kthread_run这个内核api用来创建内核线程并唤醒执行传递的执行函数。调用路径如下:

include/linux/kthread.h

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

({

struct task_struct *__k

= kthread_create(threadfn, data, namefmt, ## __VA_ARGS__); //创建内核线程

if (!IS_ERR(__k))

wake_up_process(__k); //唤醒创建的内核线程

__k;

})

kthread_run这个宏传递三个参数:执行函数,执行函数传递的参数,格式化线程名字

我们先来看下kthread_create函数:

4.1 kthread_create函数

kthread_create

-》kthread_create_on_node

-》__kthread_create_on_node

__kthread_create_on_node函数并不长我们全部罗列:

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结构 如执行函数等

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来处理创建内核线程请求

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))) { //等待请求的内核线程创建完成

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; //获得 创建完成的 内核线程的tsk

372 if (!IS_ERR(task)) { // 内核线程创建成功后 进行后续的处理

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); //设置 调度策略和优先级

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函数需要明白以下几点:1.__kthread_create_on_node函数处于一个进程上下文如insmod进程2.__kthread_create_on_node函数需要与两个任务交互,一个是kthreadd,一个是kthreadd的创建的内核线程(执行函数为kthread)

函数中已经做了详细的注释,这里在说明一下:首先函数将需要在内核线程中执行的函数等信息封装在kthread_create_info结构中,然后加入到kthreadd的kthread_create_list链表,接着去唤醒kthreadd去处理创建内核线程请求,上面kthreadd函数我们分析过kthreadd函数会创建一个内核线程来执行kthread函数,并将kthread_create_info结构传递过去,在kthread函数中会通过complete(done)来唤醒357的完成等待(这就回答了第一个问题), 然后__kthread_create_on_node接着进行初始化,但是需要明白的是新创建的内核线程现在处于睡眠状态,等待被唤醒。

4.2 wake_up_process唤醒

上面通过kthread_create创建完成内核线程之后,内核线程处于TASK_UNINTERRUPTIBLE状态,等待被唤醒,这个时候kthread_run调用wake_up_process唤醒新创建的内核线程,内核线程愉快的执行,走到了kthread函数的threadfn(data)处,执行真正的线程处理,至此,新创建的内核线程开始完成实质性的工作。

6. kthread_stop函数

一般通过kthread_create创建的内核线程可以通过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 }

一般内核线程会循环执行一些事务,每次循环开始会调用kthread_should_stop来判断线程是否应该停止:

bool kthread_should_stop(void)

{

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

}

在某个内核路径调用kthread_stop,内核线程每次循环开始的时候,如果检查到KTHREAD_SHOULD_STOP标志置位,就会退出,然后调用do_exit完成退出操作。

上面讲解到很多函数也涉及到很多任务,下面总结一下:1.涉及到的函数有:kthreadd, kthread,kthread_run,kthread_create, wake_up_process, kthread_stop, kthread_should_stopkthreadd:为kthreadd内核线程执行函数,处理内核线程创建任务。kthread:每次kthreadd创建新的内核线程都会执行kthread,里面会涉及到睡眠和唤醒后执行线程执行函数操作。kthread_run:创建并唤醒一个内核线程kthread_create:创建一个内核线程,创建之后处于TASK_UNINTERRUPTIBLE状态wake_up_process:唤醒一个任务kthread_stop:停止一个内核线程kthread_should_stop:判断一个内核线程是否应该停止2.涉及到的kthreadd内核线程,新创建的内核线程,发起创建内核线程请求的任务,他们直接通过完成量进行同步3.睡眠唤醒流程:先设置进程状态为TASK_UNINTERRUPTIBLE这样的状态,然后调度出去,唤醒的时候在调度回来

好了,下面给出精心制作的调用图示:

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

上面已经讲解完了,内核线程是如何被创建的,又是如何执行处理函数的,涉及到多个任务直接同步问题,看代码的时候需要多个窗口配合之看才行。
编辑:lyn

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 代码
    +关注

    关注

    30

    文章

    4786

    浏览量

    68568
  • LINUX内核
    +关注

    关注

    1

    文章

    316

    浏览量

    21646

原文标题:深入理解Linux内核之内核线程(上)

文章出处:【微信号:LinuxDev,微信公众号:Linux阅码场】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    嵌入式学习-飞凌嵌入式ElfBoard ELF 1板卡-Linux内核移植之内核简介

    所以每个模块都有对应的维护人员。维护人员的工作就是审核人们提交的代码是否正确,如果没有问题,就会合并到主分支。这样就会使linux内核不断完善和更新。接下来就是芯片原厂例如恩智浦,开发人员会不定期在
    发表于 12-16 13:08

    飞凌嵌入式ElfBoard ELF 1板卡-Linux内核移植之内核简介

    所以每个模块都有对应的维护人员。维护人员的工作就是审核人们提交的代码是否正确,如果没有问题,就会合并到主分支。这样就会使linux内核不断完善和更新。接下来就是芯片原厂例如恩智浦,开发人员会不定期在
    发表于 12-13 09:03

    嵌入式工程师都在找的【Linux内核调试技术】建议收藏!

    Linux内核调试器(KDB) KDB是Linux内核的一个补丁,提供了一种在系统运行时对内核内存和数据结构进行检查的方法。 它允
    发表于 11-28 15:37

    deepin社区亮相第19届中国Linux内核开发者大会

    中国 Linux 内核开发者大会,作为中国 Linux 内核领域最具影响力的峰会之一,一直以来都备受瞩目。
    的头像 发表于 10-29 16:35 508次阅读

    详解linux内核的uevent机制

    linux内核中,uevent机制是一种内核和用户空间通信的机制,用于通知用户空间应用程序各种硬件更改或其他事件,比如插入或移除硬件设备(如USB驱动器或网络接口)。uevent表示“用户空间
    的头像 发表于 09-29 17:01 678次阅读

    linux驱动程序如何加载进内核

    Linux系统中,驱动程序是内核与硬件设备之间的桥梁。它们允许内核与硬件设备进行通信,从而实现对硬件设备的控制和管理。 驱动程序的编写 驱动程序的编写是Linux驱动开发的基础。在编
    的头像 发表于 08-30 15:02 462次阅读

    Linux内核测试技术

    Linux 内核Linux操作系统的核心部分,负责管理硬件资源和提供系统调用接口。随着 Linux 内核的不断发展和更新,其复杂性和代码规
    的头像 发表于 08-13 13:42 492次阅读
    <b class='flag-5'>Linux</b><b class='flag-5'>内核</b>测试技术

    Linux内核中的页面分配机制

    Linux内核中是如何分配出页面的,如果我们站在CPU的角度去看这个问题,CPU能分配出来的页面是以物理页面为单位的。也就是我们计算机中常讲的分页机制。本文就看下Linux内核是如何管
    的头像 发表于 08-07 15:51 290次阅读
    <b class='flag-5'>Linux</b><b class='flag-5'>内核</b>中的页面分配机制

    欢创播报 华为宣布鸿蒙内核已超越Linux内核

    1 华为宣布鸿蒙内核已超越Linux内核   6月21日,在华为开发者大会上, HarmonyOS NEXT(鸿蒙NEXT)——真正独立于安卓和iOS的鸿蒙操作系统,正式登场。这是HarmonyOS
    的头像 发表于 06-27 11:30 837次阅读

    使用 PREEMPT_RT 在 Ubuntu 中构建实时 Linux 内核

    盟通技术干货构建实时Linux内核简介盟通技术干货Motrotech如果需要在Linux中实现实时计算性能,进而有效地将Linux转变为RTOS,那么大多数发行版都可以打上名为PREE
    的头像 发表于 04-12 08:36 2440次阅读
    使用 PREEMPT_RT 在 Ubuntu 中构建实时 <b class='flag-5'>Linux</b> <b class='flag-5'>内核</b>

    C++在Linux内核开发中从争议到成熟

    Linux 内核邮件列表中一篇已有六年历史的老帖近日再次引发激烈讨论 —— 主题是建议将 Linux 内核的开发语言从 C 转换为更现代的 C++。
    的头像 发表于 01-31 14:11 627次阅读
    C++在<b class='flag-5'>Linux</b><b class='flag-5'>内核</b>开发中从争议到成熟

    Ubuntu 24.04 LTS选用Linux 6.8为默认内核

    关于Ubuntu 24.04 LTS使用何种内核版本,一直备受关注。Canonical工程师Andrea Righi昨日宣布,Ubuntu 24.04将默认搭载Linux 6.8内核
    的头像 发表于 01-29 11:27 1110次阅读

    linux内核主要由哪几个部分组成,作用是什么

    Linux内核主要由以下几个部分组成: 进程管理:Linux内核负责管理和调度系统中的进程。它通过进程调度算法来决定哪个进程在什么时间运行以及如何分配系统资源。 内存管理:
    的头像 发表于 01-22 14:34 2685次阅读

    rk3399移植Linux内核

    RK3399是一款由中国厂商瑞芯微推出的高性能处理器芯片,被广泛用于嵌入式系统开发。在进行应用程序开发之前,我们需要将Linux内核移植到RK3399,以支持硬件的驱动和功能。本文将详细介绍如何将
    的头像 发表于 01-08 09:56 1145次阅读

    Linux内核中RCU的用法

    Linux内核中,RCU最常见的用途是替换读写锁。在20世纪90年代初期,Paul在实现通用RCU之前,实现了一种轻量级的读写锁。后来,为这个轻量级读写锁原型所设想的每个用途,最终都使用RCU来实现了。
    的头像 发表于 12-27 09:56 1765次阅读
    <b class='flag-5'>Linux</b><b class='flag-5'>内核</b>中RCU的用法