进程上下文切换、线程上下文切换、中断上下文切换的区别

发布时间 2023-04-18 17:18:13作者: xzy186

概念

​ CPU 上下文切换指的是 CPU 从一个进程或线程切换到另一个进程或线程的次数。当 CPU 执行一个进程或线程时,会为其建立一个执行上下文(Context),当 CPU 切换到另一个进程或线程时,需要保存当前的上下文并建立新的上下文,这个过程就是上下文切换。

​ 上下文切换会消耗一定的 CPU 资源,因为它需要保存和刷新各个进程的内存映射、缓存等状态。所以,上下文切换次数越高,表示 CPU 花在切换进程和线程上的时间越多,CPU 用于执行实际任务的时间就越少,影响系统性能。

按照任务类型,CPU 上下文切换分为:

  • 进程上下文切换
  • 线程上下文切换
  • 中断上下文切换

下面我们通过实例分别来演示一下这三种类型的CPU上下文切换的应用场景,以更加直观的方式来理解这三种类型的含义。

进程上下文切换

​ “进程上下文切换”就是当 CPU 从当前进程切换到另一个进程时,需要保存和恢复两个进程的执行状态(上下文)的过程。频繁的上下文切换会导致资源浪费和性能下降。

​ 比如现在 CPU 正在执行进程 A,这个时候进程 A 的许多状态信息(控制流位置、堆栈指针、寄存器信息等)都保存着。当进程 A 的时间片用完或被其他高优先级的进程 B 打断时,CPU 就需要先保存进程 A 的所有状态,然后加载进程 B 的状态,接着执行进程 B。也就是说,当 CPU 从一个进程切换到另一个进程时,需要保存和恢复两个进程的“执行环境”(上下文),这个过程就叫上下文切换。

我们可以通过下面这段shell脚本来演示进程上下文切换的应用场景:

#!/bin/bash
# 获取当前进程pid
process_pid=$(echo $$)

# 计算两个时间差(单位:毫秒)
function time_diff() {
    local start_time end_time diff

    # 获取开始时间
    start_time=$1

    # do something...

    # 获取结束时间
    end_time=$2

    # 计算时间差
    diff=$((end_time-start_time))

    # 转换为毫秒
    echo $diff
}

# 子进程 A
function sub_process_A() {
  while true; do
    echo "I'm process A"
  done
}

# 子进程 B
function sub_process_B() {
  while true; do
    echo "I'm process B"
  done
}

# 开始时间
start_time=$(date +%s%3N)

# 启动子进程 A
sub_process_A &

# 启动子进程 B
sub_process_B &

# 等待程序运行60秒
sleep 60

# 查找子进程 ID,结束所有子进程
Subprocess_pid=$(pgrep -P ${process_pid}) && kill -9 ${Subprocess_pid}

# 等待子进程结束
wait ${Subprocess_pid}

# 结束时间
end_time=$(date +%s%3N)

# 执行时间
execute_time=$(time_diff ${start_time} ${end_time})

echo "Execute time is: $execute_time milliseconds."

# 主进程退出
exit

这个脚本首先定义了两个子进程 A 和 B,然后先启动了进程 A,接着启动进程 B,等待60秒后结束所有子进程。main 进程使用 wait 等待两个子进程结束。当我们运行这个脚本时,会看到输出交替出现 “I'm process A” 和 “I'm process B”,这表示 CPU 在两个进程之间切换执行,就会出现 CPU 在两个进程上下文之间切换的情况,需要保存和恢复两个进程的执行状态,这就是“上下文切换”。我们可以观察执行时间,会发现实际上总执行时间会大于 60 秒。这是因为上下文切换也需要一定的 CPU 时间,导致实际执行时间增加。所以,这个示例脚本演示了当两个进程同时运行,CPU 在它们之间切换时,需要进行上下文切换,这会消耗一定的 CPU 资源,影响实际执行效率。

[root@node116 test1]# sh process.sh
I'm process A
I'm process B
I'm process A
I'm process B
I'm process A
I'm process B
...
...
I'm process A
I'm process B
I'm process B
I'm process A
I'm process B
process.sh: line 54:  8937 Killed                  sub_process_A
process.sh: line 54:  8938 Killed                  sub_process_B
Execute time is: 60051 milliseconds.

程序运行过程中,可以使用pidstat命令来监控进程的上下文信息:

  • ​ cswch/s:表示进程每秒自愿进行上下文切换的次数(进程无法获取所需资源导致的上下文切换).
  • ​ nvcswch/s:表示进程每秒非自愿进行上下文切换的次数(时间片轮流等系统强制调度).
[root@node116 ~]# pidstat -w 1 20 -p 9080,9081
Linux 3.10.0-957.el7.x86_64 (node116) 	04/18/2023 	_x86_64_	(8 CPU)

01:51:05 PM   UID       PID   cswch/s nvcswch/s  Command
01:51:06 PM     0      9080    172.00     76.00  sh
01:51:06 PM     0      9081    173.00     84.00  sh

01:51:06 PM   UID       PID   cswch/s nvcswch/s  Command
01:51:07 PM     0      9080    187.00     71.00  sh
01:51:07 PM     0      9081    183.00     68.00  sh

01:51:07 PM   UID       PID   cswch/s nvcswch/s  Command
01:51:08 PM     0      9080    180.00     67.00  sh
01:51:08 PM     0      9081    186.00     77.00  sh

01:51:08 PM   UID       PID   cswch/s nvcswch/s  Command
01:51:09 PM     0      9080    157.00     70.00  sh
01:51:09 PM     0      9081    170.00     74.00  sh

01:51:09 PM   UID       PID   cswch/s nvcswch/s  Command
01:51:10 PM     0      9080    182.00     83.00  sh
01:51:10 PM     0      9081    164.00     79.00  sh

01:51:10 PM   UID       PID   cswch/s nvcswch/s  Command
01:51:11 PM     0      9080    171.00     82.00  sh
01:51:11 PM     0      9081    165.00     65.00  sh

01:51:11 PM   UID       PID   cswch/s nvcswch/s  Command
01:51:12 PM     0      9080    186.00     64.00  sh
01:51:12 PM     0      9081    167.00     87.00  sh

01:51:12 PM   UID       PID   cswch/s nvcswch/s  Command
01:51:13 PM     0      9080    152.00     88.00  sh
01:51:13 PM     0      9081    168.00     76.00  sh

01:51:13 PM   UID       PID   cswch/s nvcswch/s  Command
01:51:14 PM     0      9080    164.00     68.00  sh
01:51:14 PM     0      9081    165.00     69.00  sh

01:51:14 PM   UID       PID   cswch/s nvcswch/s  Command
01:51:15 PM     0      9080    154.00     64.00  sh
01:51:15 PM     0      9081    141.00     76.00  sh

查看进程的上下文统计信息:

[root@node116 ~]# cat /proc/9080/status | grep ctxt
voluntary_ctxt_switches:	1722
nonvoluntary_ctxt_switches:	253
[root@node116 ~]# cat /proc/9080/status | grep ctxt
voluntary_ctxt_switches:	1819
nonvoluntary_ctxt_switches:	276
[root@node116 ~]# cat /proc/9080/status | grep ctxt
voluntary_ctxt_switches:	1901
nonvoluntary_ctxt_switches:	284
[root@node116 ~]# cat /proc/9080/status | grep ctxt
voluntary_ctxt_switches:	1974
nonvoluntary_ctxt_switches:	300
[root@node116 ~]# cat /proc/9081/status | grep ctxt
voluntary_ctxt_switches:	2614
nonvoluntary_ctxt_switches:	484
[root@node116 ~]# cat /proc/9081/status | grep ctxt
voluntary_ctxt_switches:	2713
nonvoluntary_ctxt_switches:	500
[root@node116 ~]# cat /proc/9081/status | grep ctxt
voluntary_ctxt_switches:	2798
nonvoluntary_ctxt_switches:	515

通过vmstat命令可以查看CPU整体的上下文切换次数,cs这个值要越小越好,太大了,要考虑调低线程或者进程的数目,例如在apache和nginx这种web服务器中,我们一般做性能测试时会进行几千并发甚至几万并发的测试,选择web服务器的进程可以由进程或者线程的峰值一直下调,压测,直到cs到一个比较小的值,这个进程和线程数就是比较合适的值了。系统调用也是,每次调用系统函数,我们的代码就会进入内核空间,导致上下文切换,这个是很耗资源的,也要尽量避免频繁调用系统函数。上下文切换次数过多表示你的CPU大部分浪费在上下文切换,导致CPU干正经事的时间少了,CPU没有充分利用,是不可取的。

  • in:每秒CPU的中断次数,包括时间中断.

  • cs:每秒CPU上下文切换次数.

[root@node116 ~]# vmstat 1
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
 1  0      0 7312984   2104 555596    0    0     0     0    1   10  0  0 100  0  0
 0  0      0 7312984   2104 555596    0    0     0     0  119   56  0  0 100  0  0
 0  0      0 7312984   2104 555596    0    0     0     0  107   54  0  0 100  0  0
 0  0      0 7312984   2104 555596    0    0     0     0  147   80  0  0 100  0  0
 0  0      0 7312984   2104 555596    0    0     0     0  220   87  0  0 100  0  0
 0  0      0 7312984   2104 555596    0    0     0     0  142   62  0  0 100  0  0
 0  0      0 7312984   2104 555596    0    0     0     0  109   51  0  0 100  0  0
 0  0      0 7312984   2104 555596    0    0     0     0  214   96  0  0 100  0  0
 0  0      0 7312984   2104 555596    0    0     0     0  157   67  0  0 100  0  0
 0  0      0 7312984   2104 555596    0    0     0     0  216  105  0  0 100  0  0
 3  0      0 7312088   2104 555612    0    0     0     0  513  290  1  1 98  0  0
 3  0      0 7312072   2104 555612    0    0     0     0 3662 2181 17  7 76  0  0
 3  0      0 7311980   2104 555612    0    0     0     0 3876 2262 17  8 76  0  0
 3  0      0 7311980   2104 555612    0    0     0     0 3718 2170 16  8 75  0  0
 1  0      0 7311980   2104 555612    0    0     0     0 3772 2174 17  8 75  0  0
 3  0      0 7311980   2104 555612    0    0     0     0 3779 2228 16  8 75  0  0
 3  0      0 7311980   2104 555612    0    0     0     0 3754 2230 17  8 75  0  0
 3  0      0 7311980   2104 555612    0    0     0     0 3722 2176 17  8 75  0  0
 3  0      0 7311980   2104 555612    0    0     0     0 3714 2146 17  8 75  0  0
 3  0      0 7311980   2104 555612    0    0     0     0 3734 2244 17  8 75  0  0
 3  0      0 7311980   2104 555612    0    0     0     0 3718 2138 17  8 75  0  0
 3  0      0 7312012   2104 555612    0    0     0     0 3752 2174 17  8 75  0  0
 3  0      0 7311980   2104 555612    0    0     0     0 3778 2201 17  8 75  0  0
 3  0      0 7311980   2104 555612    0    0     0     0 3834 2347 17  8 75  0  0
 3  0      0 7311980   2104 555612    0    0     0     0 3755 2237 17  8 75  0  0
 3  0      0 7311980   2104 555612    0    0     0     2 3899 2272 17  8 75  0  0
 3  0      0 7311980   2104 555612    0    0     0     0 3792 2230 17  8 75  0  0
 3  0      0 7311980   2104 555612    0    0     0     0 3796 2233 17  8 75  0  0
 0  0      0 7311980   2104 555612    0    0     0     0 3909 2244 17  8 75  0  0

那么当一个进程的 ctxt 值为多少的时候可以认为该进程的上下文切换太多呢?这并没有一个绝对的标准,但一般来说:

  1. 如果一个进程的 ctxt 值远高于其他进程,例如超过其他进程的 2-3 倍,这可能表示该进程的上下文切换比较频繁,需要关注。
  2. 如果一个进程的 ctxt 值在短时间内突然大幅升高,例如在 1-2 分钟内增加了 50-100 以上,这也可能是一个信号,表明上下文切换出现异常,需要检查该进程。
  3. 一个常驻后台进程,如果 ctxt 值持续在 500 次/秒到 2000 次/秒之间,这可能会影响该进程的性能和稳定性。可以考虑如何优化程序和配置来减少上下文切换。
  4. 对于大多数常见进程来说,ctxt 值保持在每秒 200-500 次以下可以认为比较正常。但对于一些特殊的业务进程,这个参考值可能需要按实际情况判断。

除了观察 ctxt 的绝对值之外,我们还需要关注其变化趋势。如果一个进程的 ctxt 值在短期内呈现突增趋势,这也可能表示该进程出现了过度的上下文切换,需要排查原因。所以,总而言之,没有一个标准的 ctxt 阈值来判断上下文切换过多。我们需要结合进程类别、其他进程比较、ctxt 变化趋势等因素来综合判断一个进程的上下文切换是否比较频繁,并可能导致性能问题。当有明显迹象表明上下文切换过多时,则需要对程序和系统进行优化,尽量减少上下文切换次数。

线程上下文切换

​ 线程上下文切换和进程上下文切换都是计算机在执行过程中进行的一种切换,但是它们的切换粒度不同。

  • 进程上下文切换是在进程级别进行的切换,意味着CPU从执行一个进程切换到执行另一个进程。因为进程具有独立的地址空间,所以在切换时需要保存和恢复较多的上下文信息,成本较高。
  • 线程上下文切换是在线程级别进行的切换,意味着CPU从执行一个线程切换到执行同一进程中的另一个线程。因为线程共享进程的地址空间,在切换时只需要保存和恢复较少的上下文信息,所以成本较低。

举个简单的比喻,可以这样理解:线程上下文切换就像一个工厂里的工人从做产品A切换到做产品B,因为仍在同一个工厂,切换起来比较容易。进程上下文切换就像一个工人从工厂A切换到工厂B工作,因为环境和条件变了,所以切换难度较大。

下面编写一段c代码来演示线程上下文切换的应用场景:

#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/time.h>

// 线程 A 函数
void *thread_A(void *arg) {
  int sleep_time = 0;
  int i = 0;
  while (i < 600) {
    printf("I'm thread A\n");
    usleep(100 * 1000);
    sleep_time += 100;
    i++;
  }
  return (void*)sleep_time;
}

// 线程 B 函数
void *thread_B(void *arg) {
  int sleep_time = 0;
  int i = 0;
  while (i < 60) {
    printf("I'm thread B\n");
    sleep(1);
    sleep_time += 1000;
    i++;
  }
  return (void*)sleep_time;
}

int main() {
  // 记录开始时间
  struct timeval start;
  gettimeofday(&start, NULL);

  // 创建线程 A
  pthread_t tid_A;
  pthread_create(&tid_A, NULL, thread_A, NULL);

  // 立即获取 start 时间
  gettimeofday(&start, NULL);

  // 创建线程 B
  pthread_t tid_B;
  pthread_create(&tid_B, NULL, thread_B, NULL);

  // 等待线程结束
  void *status;
  pthread_join(tid_A, &status);
  int sleep_time_A = (int)status;
  pthread_join(tid_B, &status);
  int sleep_time_B = (int)status;

  // 获取 end 时间
  struct timeval end;
  gettimeofday(&end, NULL);

  // 总睡眠时间(毫秒)
  int total_sleep_time = sleep_time_A + sleep_time_B;

  // 计算运行时间(毫秒)
  long milliseconds = (end.tv_sec - start.tv_sec) * 1000 + (end.tv_usec - start.tv_usec) / 1000;

  printf("Total sleep time: %d milliseconds\n", total_sleep_time);
  printf("Run time: %ld milliseconds\n", milliseconds);

  return 0;
}

编译代码,注意因为代码中使用了 pthread 库函数,所以编译的时候要添加-lpthread 选项来链接 pthread 库。

[root@node116 test1]# gcc chid_process.c -o chid_process -lpthread
chid_process.c: In function ‘thread_A’:
chid_process.c:16:10: warning: cast to pointer from integer of different size [-Wint-to-pointer-cast]
   return (void*)sleep_time;
          ^
chid_process.c: In function ‘thread_B’:
chid_process.c:29:10: warning: cast to pointer from integer of different size [-Wint-to-pointer-cast]
   return (void*)sleep_time;
          ^
chid_process.c: In function ‘main’:
chid_process.c:51:22: warning: cast from pointer to integer of different size [-Wpointer-to-int-cast]
   int sleep_time_A = (int)status;
                      ^
chid_process.c:53:22: warning: cast from pointer to integer of different size [-Wpointer-to-int-cast]
   int sleep_time_B = (int)status;
                      ^
                      
[root@node116 test1]# ls
chid_process  chid_process.c  process.sh

这段代码中,我们在 thread_A 和 thread_B 中各添加了一个 sleep 命令来控制线程切换的时间间隔,thread_A睡眠100毫秒,然后释放 CPU,thread_B 睡眠 1 秒,然后释放 CPU。所以在运行时,会每 1秒看到10次 thread_A 打印,接着打印thread_B,共循环60次 。最后计算所有线程的睡眠时间之和以及程序的总运行时间,这个总运行时间里面就包含了线程切换的时间。

[root@node116 test1]# ./chid_process
I'm thread A
I'm thread B
I'm thread A
I'm thread A
I'm thread A
I'm thread A
I'm thread A
I'm thread A
I'm thread A
I'm thread A
I'm thread A
I'm thread B
I'm thread A
I'm thread A
I'm thread A
I'm thread A
I'm thread A
I'm thread A
I'm thread A
I'm thread A
I'm thread A
I'm thread A
I'm thread B
I'm thread A
I'm thread A
I'm thread A
I'm thread A
I'm thread A
I'm thread A
I'm thread A
I'm thread A
I'm thread A
I'm thread A
I'm thread B
...
...
I'm thread B
I'm thread A
I'm thread A
I'm thread A
I'm thread A
I'm thread A
I'm thread A
I'm thread A
I'm thread A
I'm thread A
I'm thread A
Total sleep time: 120000 milliseconds
Run time: 60112 milliseconds

chid_process程序运行过程中,使用pidstat命令监控程序的线程上下文信息,这里要添加-t参数,否则查看不到线程信息,通过监控数据可以看到ID为9546的进程下面有3个子线程,其中9546为主线程ID,9547和9548为子线程ID,其中子线程9547每秒钟的cswch为10,子线程9548每秒钟的cswch为1,与上面代码中设置的一致。

  • ​ cswch/s:表示进程每秒自愿进行上下文切换的次数.
  • ​ nvcswch/s:表示进程每秒非自愿进行上下文切换的次数.
[root@node116 ~]# pidstat -w 1 20 -t -p 9546
Linux 3.10.0-957.el7.x86_64 (node116) 	04/18/2023 	_x86_64_	(8 CPU)

03:36:15 PM   UID      TGID       TID   cswch/s nvcswch/s  Command
03:36:16 PM     0      9546         -      0.00      0.00  chid_process
03:36:16 PM     0         -      9546      0.00      0.00  |__chid_process
03:36:16 PM     0         -      9547     10.00      0.00  |__chid_process
03:36:16 PM     0         -      9548      1.00      0.00  |__chid_process

03:36:16 PM   UID      TGID       TID   cswch/s nvcswch/s  Command
03:36:17 PM     0      9546         -      0.00      0.00  chid_process
03:36:17 PM     0         -      9546      0.00      0.00  |__chid_process
03:36:17 PM     0         -      9547     10.00      0.00  |__chid_process
03:36:17 PM     0         -      9548      1.00      0.00  |__chid_process

03:36:17 PM   UID      TGID       TID   cswch/s nvcswch/s  Command
03:36:18 PM     0      9546         -      0.00      0.00  chid_process
03:36:18 PM     0         -      9546      0.00      0.00  |__chid_process
03:36:18 PM     0         -      9547     10.00      0.00  |__chid_process
03:36:18 PM     0         -      9548      1.00      0.00  |__chid_process

03:36:18 PM   UID      TGID       TID   cswch/s nvcswch/s  Command
03:36:19 PM     0      9546         -      0.00      0.00  chid_process
03:36:19 PM     0         -      9546      0.00      0.00  |__chid_process
03:36:19 PM     0         -      9547     10.00      0.00  |__chid_process
03:36:19 PM     0         -      9548      1.00      0.00  |__chid_process

03:36:19 PM   UID      TGID       TID   cswch/s nvcswch/s  Command
03:36:20 PM     0      9546         -      0.00      0.00  chid_process
03:36:20 PM     0         -      9546      0.00      0.00  |__chid_process
03:36:20 PM     0         -      9547     10.00      0.00  |__chid_process
03:36:20 PM     0         -      9548      1.00      0.00  |__chid_process
...
...

中断上下文切换

​ 中断上下文切换是指当 CPU 执行某个进程或线程的指令时,如果发生中断,CPU 会暂停当前正在执行的进程或线程,转而去执行中断处理程序。等中断处理程序执行完毕后,CPU 会返回到原来暂停的进程或线程,继续执行。这个从一个进程或线程转到中断处理程序,然后再返回到原进程或线程的过程,就是一个“中断上下文切换”。在中断发生时,CPU 必须保存当前进程或线程的上下文(如程序计数器、寄存器值等),然后载入中断处理程序的上下文去执行。中断处理完成后,CPU再将原进程或线程的上下文还原,继续执行。这个保存上下文、转而执行其他程序、再还原上下文的过程,就像在不同的执行环境之间切换,所以称为“上下文切换”。由于是在中断发生时触发的切换,所以叫做“中断上下文切换”。

通过下面这段c代码来演示一下中断上下文切换的应用场景:

#include <signal.h>
#include <stdio.h>
#include <unistd.h>

void handler(int signum)
{
    printf("Interrupt received!\n");
}

int main()
{
    // 注册 SIGINT 信号的中断处理函数
    signal(SIGINT, handler);

    // 程序主体
    int i = 0;
    while(1) {
        printf("Doing main program...\n");
        sleep(1);
        i++;
        if (i == 3) {
            raise(SIGINT); // 触发 SIGINT 信号,从而执行中断处理函数
        }
    }
}

编译代码:

[root@node116 test1]# gcc handler_process.c -o handler_process
[root@node116 test1]# ls
chid_process  chid_process.c  handler_process  handler_process.c  process.sh

这个程序主体是一个无限循环,每秒打印一句话“Doing main program...”。当 i 等于 3 时,程序会通过 raise() 函数触发 SIGINT(键盘中断)信号。当 SIGINT 信号被触发后,程序会暂停主体循环,转而执行 SIGINT 信号的中断处理函数 handler()。等 handler() 执行完毕后,程序会返回主体循环,继续执行。这个从主体循环转到中断处理函数,再返回主体循环的过程,就是一次“中断上下文切换”。

[root@node116 test1]# ./handler_process
Doing main program...
Doing main program...
Doing main program...
Interrupt received!
Doing main program...
Doing main program...
Doing main program...
Doing main program...
Doing main program...
Doing main program...
...

handler_process程序运行过程中,使用pidstat命令监控程序的线程上下文信息,可以看到该程序的进程每秒的cswch为1,与程序的设置一致。

[root@node116 ~]# pidstat -w 1 -t -p 9658
Linux 3.10.0-957.el7.x86_64 (node116) 	04/18/2023 	_x86_64_	(8 CPU)

04:30:33 PM   UID      TGID       TID   cswch/s nvcswch/s  Command
04:30:34 PM     0      9658         -      1.00      0.00  handler_process
04:30:34 PM     0         -      9658      1.00      0.00  |__handler_process

04:30:34 PM   UID      TGID       TID   cswch/s nvcswch/s  Command
04:30:35 PM     0      9658         -      1.00      0.00  handler_process
04:30:35 PM     0         -      9658      1.00      0.00  |__handler_process

04:30:35 PM   UID      TGID       TID   cswch/s nvcswch/s  Command
04:30:36 PM     0      9658         -      1.00      0.00  handler_process
04:30:36 PM     0         -      9658      1.00      0.00  |__handler_process

04:30:36 PM   UID      TGID       TID   cswch/s nvcswch/s  Command
04:30:37 PM     0      9658         -      1.00      0.00  handler_process
04:30:37 PM     0         -      9658      1.00      0.00  |__handler_process

04:30:37 PM   UID      TGID       TID   cswch/s nvcswch/s  Command
04:30:38 PM     0      9658         -      1.00      0.00  handler_process
04:30:38 PM     0         -      9658      1.00      0.00  |__handler_process

04:30:38 PM   UID      TGID       TID   cswch/s nvcswch/s  Command
04:30:39 PM     0      9658         -      1.00      0.00  handler_process
04:30:39 PM     0         -      9658      1.00      0.00  |__handler_process
...

在程序的运行终端窗口中,我们使用键盘来发送键盘中断信号(这里执行命令:kill -s SIGINT handler_process_pid也是同样的效果),可以看到当按下键盘的“Ctrl+C”键时,进程的cswch值变成了2,表示在这一秒内进行了2次上下文切换,新增的这1次就是键盘中断信号的上下文。

程序运行终端窗口

...
Doing main program...
Doing main program...
Doing main program...
Doing main program...
Doing main program...
Doing main program...
Doing main program...
Doing main program...
Doing main program...
Doing main program...
Doing main program...
^CInterrupt received!
Doing main program...
Doing main program...
Doing main program...
Doing main program...
Doing main program...
Doing main program...
Doing main program...
Doing main program...
Doing main program...
...

pidstat终端窗口

...
04:34:07 PM   UID      TGID       TID   cswch/s nvcswch/s  Command
04:34:08 PM     0      9658         -      1.00      0.00  handler_process
04:34:08 PM     0         -      9658      1.00      0.00  |__handler_process

04:34:08 PM   UID      TGID       TID   cswch/s nvcswch/s  Command
04:34:09 PM     0      9658         -      2.00      0.00  handler_process
04:34:09 PM     0         -      9658      2.00      0.00  |__handler_process

04:34:09 PM   UID      TGID       TID   cswch/s nvcswch/s  Command
04:34:10 PM     0      9658         -      1.00      0.00  handler_process
04:34:10 PM     0         -      9658      1.00      0.00  |__handler_process

04:34:10 PM   UID      TGID       TID   cswch/s nvcswch/s  Command
04:34:11 PM     0      9658         -      1.00      0.00  handler_process
04:34:11 PM     0         -      9658      1.00      0.00  |__handler_process
...

总结

以上就是这三种类型的CPU上下文的应用场景介绍,简单来说,这三种上下文的主要区别是:

  1. 进程上下文切换涉及进程级资源,线程上下文切换仅涉及线程级资源,中断上下文切换可以发生在进程或线程执行期间。
  2. 进程和线程上下文切换是由调度机制触发的,中断上下文切换是由中断触发的。