写点什么

精通高并发与内核 | Linux 内核进程的切换方式

  • 2022 年 9 月 18 日
    上海
  • 本文字数:1890 字

    阅读完需:约 6 分钟

精通高并发与内核 | Linux内核进程的切换方式

前言

📫作者简介小明java问道之路,专注于研究计算机底层/Java/Liunx 内核,就职于大型金融公司后端高级工程师,擅长交易领域的高安全/可用/并发/性能的架构设计📫 

🏆 InfoQ 签约博主、CSDN 专家博主/Java 领域优质创作者/CSDN 内容合伙人、阿里云专家/签约博主、华为云专家、51CTO 专家/TOP 红人 🏆

🔥如果此文还不错的话,还请👍关注、点赞、收藏三连支持👍一下博主~


本文导读

Linux 中如何进行进程的切换。由于线程的表现形式也为进程,只不过共享了数据,所以我们研究进程即可,线程也是如此

一、Linux 中进程切换方式

我们先来推理一下:

1、进程为运行中的程序

2、程序的 1 进制数据:数据、运行代码(指令信息)存在于硬盘

3、CPU 控制硬盘将程序的这些信息装载到内存中,然后建立一个 PCB(进程控制块)来表示该程序在运行中的信息(进程信息:内存分布、PID、打开文件等等)

4、那么必然会有一个队列放置这些需要执行的进程,而我们只需要将 PCB 放入其中即可。这时我们称该队列为 CPU 的运行队列(runqueue)

5、在运行中的进程不可能无限期占用 CPU,比如现在有两个进程 A、B,如果 CPU 一直执行 A,那么 B 将永远无法执行。就像你在听音乐,然后 CPU 被音乐进程占用了,那么你将无法再继续其他工作

6、这时,我们可以用前文中介绍的:并行、并发,来解决该问题。我们可以引入多个 CPU 来并行执行 A 和 B 进程。当然我们也可以用一个 CPU 来完成对 A 和 B 的并发调用(分时复用 CPU,只要分时间隔足够小,用户几乎无法感知切换,那么这就成功啦)

二、Linux 中进程如何切换

1、CPU 正在执行 A 进程,那么有一种乐观的条件:A 进程说:我累了,换 B 吧。这时主动将 CPU 的执行权交给 B 进程。很乐观对吧?但是考虑一下:如果 A 进程来了个死循环,这会怎样?答案是 B 进程饿死

2、那么就需要一种被动切换的功能,该功能就是各位了解的中断。

也即通过一种方式,周期性的将正在执行进程指令的 CPU 打断,让它去执行某一段代码,在该代码中,去切换到进程 B。

于是,我们就可以根据该中断,来让 A、B 进程分时复用 CPU 资源我们看到核心就是:中断。那么如何中断呢?这就涉及到其他硬件设备了,我们可以引入一个时钟硬件,让它每隔一段时间就中断 CPU,让 CPU 执行中断代码。在以前 Intel 中该设备为:8259A 中断控制芯片(感兴趣可以研究下,不过不了解问题也不大,无外乎就是个硬件设备连接到 CPU 上,周期性产生信号,让 CPU 中断),通常该中断的周期性频率为:100HZ,也即 10ms(1/100HZ*1000ms),也即每隔 10ms,CPU 就会从执行任务代码的状态,切换到执行中断代码状态,这时就可以在其中执行中断代码了。当然有没有可能更小:1000HZ,也即 1ms。

三、主动切换进程

我们来看一个系统调用(还记得什么是系统调用吧?内核提供的 Controller 接口,由用户来按照约定协议调用)nanosleep,该系统调用为 C 语言中直接调用 sleep 睡眠函数调用。我们知道,一个进程要去睡眠,那么代表了一个阻塞状态,此时将为主动切换进程。我们看到流程如下:

1、设置进程状态为可中断阻塞状态

2、调用 schedule_timeout 函数完成阻塞,然后切换进程

3、在 schedule_timeout 函数中,初始化并设置了定时器 timer,然后将该定时器放入定时器链表,然后调用 schedule()函数完成进程的切换

4、在 schedule()函数中,获取到 runqueue,进程就绪队列,从中获取到进程,然后调用 context_switch 切换执行,这时 CPU 就会转而去执行其他进程的代码


好的,我们可以稍微推理一下:

1、CPU 只知道执行指令

2、当进程需要将 CPU 的资源释放时,将会进入 schedule()函数,该函数同样也是一堆代码

3、CPU 在执行该代码时,执行了 context_switch 函数,该函数将会把 CPU 的状态信息:EIP(用于指示 CPU 执行哪一段指令)等等修改为其他进程的代码地址,这时 CPU 将会跳转执行其他进程的 EIP 所指示的代码。这时便完成了进程的切换


小结:

主动释放时,需要进程 A 主动调用调度函数 schedule(),切换 CPU 状态信息到 B 进程即可。

四、被动切换进程

以下源码我们可以看到:

1、timer_interrupt 为时钟中断时调用的中断处理代码

2、最终我们看到调用了 update_process_times 函数

3、在 update_process_times 中调用了调度函数,scheduler_tick

4、在该函数中如果发现进程的时间片已经用完(如果不了解时间片,可以这么想:把一段时间分割为不同区间,A、B 进程互相分时使用 CPU 资源),那么将会调用 set_tsk_need_resched 函数设置重调度标记位我们可以看到,所谓的被动切换,其实是对进程设置一个标记位 TIF_NEED_RESCHED,由进程自己检测该标记为来切换进程。所以,实质上被动切换也是主动切换的一个表现形式。

总结

那么进程在哪里检测该标记位呢?从源码中我们可以看到,将会在返回用户空间时检测该标志位,最终,还是调用 schedule 函数完成切换。

发布于: 刚刚阅读数: 3
用户头像

InfoQ签约作者/技术专家/博客专家 2020.03.20 加入

🏆InfoQ签约作者、CSDN专家博主/Java领域优质创作者、阿里云专家/签约博主、华为云专家、51CTO专家/TOP红人 📫就职某大型金融互联网公司高级工程师 👍专注于研究Liunx内核、Java、源码、架构、设计模式、算法

评论

发布
暂无评论
精通高并发与内核 | Linux内核进程的切换方式_进程_小明Java问道之路_InfoQ写作社区