天天看点

Linux:上下文,进程上下文和中断上下文概念,上下文切换1. 上下文 context:(就是一个环境)2. 进程上下文3. 中断上下文4. 上下文切换(内核角度看)

Linux:上下文,进程上下文和中断上下文概念,上下文切换

  • 1. 上下文 context:(就是一个环境)
  • 2. 进程上下文
    • 2.1 进程上下文的三个部分:用户级上下文、寄存器上下文以及系统级上下文
    • 2.2 上下文切换
    • 2.3 用户态内陷内核态三种方式:系统调用,异常,中断
    • 2.4 cpu内核态的三种状态:
  • 3. 中断上下文
    • 3.1 中断上下文概念:
      • 3.1.1 定义
      • 3.1.2 中断上下文切换:
    • 3.2 中断上下文代码中注意事项
      • 3.2.1 不能-睡眠或者放弃CPU。
      • 3.2.2 不能-尝试获得信号量
      • 3.2.3 不能-执行耗时的任务
      • 3.2.4 不能-访问用户空间的虚拟地址
  • 4. 上下文切换(内核角度看)
    • 4.1 上下文切换定义
    • 4.2 上下文切换过程
      • 4.2.1 切换虚拟内存
      • 4.2.2 切换处理器
    • 4.3 抢占时机
    • 4.4 用户抢占
      • 4.4.1 用户抢占定义:
      • 4.4.2 用户抢占时机:
    • 4.5 内核抢占
      • 4.5.1 内核抢占前提--是调度安全的(进程没有锁)
      • 4.5.2 内核抢占发生时机

1. 上下文 context:(就是一个环境)

上下文简单说来就是一个环境,相对于进程而言,就是进程执行时的环境。

具体来说就是各个变量和数据,包括所有的寄存器变量、进程打开的文件、内存信息等。

2. 进程上下文

可以说:

当一个进程在执行时,CPU的所有寄存器中的值、进程的状态以及堆栈中的内容被称为该进程的上下文。

也可以说:

所谓的“进程上下文”,可以看作是用户进程传递给内核的这些参数以及内核要保存的那一整套的变量和寄存器值和当时的环境等。

进程切换:

当内核需要切换到另一个进程时,它需要保存当前进程的 所有状态,

即保存当前进程的上下文,以便在再次执行该进程时,能够必得到切换时的状态执行下去。

2.1 进程上下文的三个部分:用户级上下文、寄存器上下文以及系统级上下文

一个进程的上下文可以分为三个部分:用户级上下文、寄存器上下文以及系统级上下文。

  1. 用户级上下文: 正文、数据、用户堆栈以及共享存储区;
  2. 寄存器上下文: 通用寄存器、程序寄存器(IP)、处理器状态寄存器(EFLAGS)、栈指针(ESP);
  3. 系统级上下文: 进程控制块task_struct、内存管理信息(mm_struct、vm_area_struct、pgd、pte)、内核栈。

2.2 上下文切换

当发生进程调度时,进行进程切换就是上下文切换(context switch)。

操作系统必须对上面提到的全部信息进行切换,新调度的进程才能运行。

2.3 用户态内陷内核态三种方式:系统调用,异常,中断

系统调用:用户态进程主动要求切换到内核态的一种方式

本质:相当于执行了一个中断响应的过程,因为系统调用实际上最终是中断机制实现的,
而异常和中断的处理机制基本上也是一致的
           

异常:(软中断)比如缺页异常

异常: 进程发,内核收
           

中断:外围设备的中断 如 ctrl+c等

中断: 硬件发,内核收


扩展:
	中断: 硬件/进程发,内核收
	信号:内核发,进程收
           

2.4 cpu内核态的三种状态:

程序在执行过程中通常有用户态和内核态两种状态,

CPU对处于内核态根据上下文环境进一步细分,因此有了下面三种状态:

1、内核态,运行于进程上下文,内核代表进程运行于内核空间;

2、内核态,运行于中断上下文,内核代表硬件运行于内核空间;

3、用户态,运行于用户空间。

3. 中断上下文

3.1 中断上下文概念:

3.1.1 定义

硬件通过触发信号,导致内核调用中断处理程序,进入内核空间。

这个过程中,硬件的一些变量和参数也要传递给内核,内核通过这些参数进行中断处理。

“ 中断上下文”,就是硬件传递过来的这些参数和内核需要保存的一些其他环境(主要是当前被打断执行的进程环境)

特点:

中断上下文也称原子上下文,该上下文中执行的代码不可阻塞

3.1.2 中断上下文切换:

在发生中断时,内核就在被中断进程的上下文中,在内核态下执行中断服务例程。

但同时会保留所有需要用到的资源,以便中继服务结束时能恢复被中断进程 的执行。

3.2 中断上下文代码中注意事项

运行于进程上下文的内核代码是可抢占的,但中断上下文则会一直运行至结束,不会被抢占。

所以中断处理程序代码要受到一些限制,在中断代码中不能出现实现下面功能的代码:

3.2.1 不能-睡眠或者放弃CPU。

因为内核在进入中断之前会关闭进程调度,一旦睡眠或者放弃CPU,这时内核无法调度别的进程来执行,系统就会死掉。牢记:中断服务子程序一定不能睡眠(或者阻塞)。

3.2.2 不能-尝试获得信号量

如果获得不到信号量,代码就会睡眠,导致(1)中的结果。

3.2.3 不能-执行耗时的任务

中断处理应该尽可能快,因为如果一个处理程序是IRQF_DISABLED类型,他执行的时候会禁止所有本地中断线,而内核要响应大量服务和请求,中断上下文占用CPU时间太长会严重影响系统功能。中断处理程序的任务尽可能放在中断下半部执行。

3.2.4 不能-访问用户空间的虚拟地址

因为中断运行在内核空间。

4. 上下文切换(内核角度看)

4.1 上下文切换定义

上下文切换,即从一个可执行进程切换到另一个可执行进程。

由 context_switch()函数负责处理。

4.2 上下文切换过程

每当一个新的进程被选出来准备投入运行的时候, schedule()就会调用该函数,它完成两项基本的工作

4.2.1 切换虚拟内存

调用 switch_mm(),把虚拟内存从上一个进程映射切换到新进程中

4.2.2 切换处理器

调用 switch_to(),从上一个进程的处理器状态切换到新进程的处理器状态。

包括保存、恢复栈信息和寄存器信息,以及其他任何与体系结构相关的状态信息。

4.3 抢占时机

每个进程都包含一个 need_resched 标志,用来表明是否需要重新执行一次调度。

当某个进程应该被抢占时, scheduler_tick()会设置这个标志;
当一个优先级高的进程进入可执行状态时, try_to_wake_up()也会设置这个标志。
内核会检查该标志(如返回用户空间以及从中断返回的时候),确认其被设置,
然会调用 schedule()来切换到一个新的进程。
           

4.4 用户抢占

4.4.1 用户抢占定义:

在从系统调用或者中断处理程序返回用户空间时,如果 need_resched 标志被设置,会导致 schedule()被调用,内核会选择一个其他(更合适的)进程投入运行,即发生用户抢占。

4.4.2 用户抢占时机:

即用户抢占发生在: 从系统调用返回用户空间时; 从中断处理程序返回用户空间时

4.5 内核抢占

4.5.1 内核抢占前提–是调度安全的(进程没有锁)

Linux支持内核抢占,前提是重新调度是安全的。只要进程没有持有锁,就是安全的。

具体实现就是

在每个进程的 thread_info 中引入 preempt_count 计数器,初试为0。
每当使用锁的时候+1,释放锁的时候-1,当数值为0时,内核就可以抢占。
           

4.5.2 内核抢占发生时机

从中断返回内核空间时,内核会检查need_resched和preempt_count的值,以决定是调用调度程序(内核抢占)还是返回执行进程。如果内核中的进程被阻塞了,或者它显式地调用了schedule(),内核抢占也会显式地发生。

即内核抢占会发生在:

中断处理程序正在执行,且返回内核空间之前; 
内核代码再一次具有可抢占性的时候; 
内核中的任务显式调用schedule(); 
内核中的任务阻塞(这同样也会导致调用schedule());
           

参考:

https://www.cnblogs.com/hustcat/articles/1505618.html

https://blog.csdn.net/qq_38500662/article/details/80598486

继续阅读