落雪无迹飞叶谷 - Preemption Model
type: channel
link: https://www.batsom.net/index.php/tag/Preemption-Model/
items: linux 抢占模型

linux 抢占模型
type: item
date: 2024-07-13T15:31:00+09:00
description: Linux中用户态程序总是preemptible的，内核使用clock tick中断用户态程序切换到别的线程，不用等待用户态程序主动放弃CPU。但在Kernel Preemption被引入之前，一个线程进入内核态，不放弃CPU也不返回用户态就能一直占用CPU。直到linux 2.6才引入Kernel Preemption。本文的主要目的是介绍Linux的三种Kernel Preemption模式。但介绍Voluntary Preemption的时候，也需要把might_sleep搞清楚。三种Preemption模式 (1)编译linux kernel 3.19.8，配置的时候（make menuconfig），关于preemption模式（Processor type and features —> Preemption Model），有3个选项：No Forced Preemption (Server)Voluntary Kernel Preemption (Desktop)Preemptible Kernel (Low-Latency Desktop)选择”No Forced Preemption (Server)”产生的配置项是：CONFIG_PREEMPT_NONE=y # CONFIG_PREEMPT_VOLUNTARY is not set # CONFIG_PREEMPT is not set选择”Voluntary Kernel Preemption (Desktop)”产生的配置项是：# CONFIG_PREEMPT_NONE is not set CONFIG_PREEMPT_VOLUNTARY=y # CONFIG_PREEMPT is not set选择”Preemptible Kernel (Low-Latency Desktop)”产生的配置项是：CONFIG_PREEMPT_RCU=y # CONFIG_PREEMPT_NONE is not set # CONFIG_PREEMPT_VOLUNTARY is not set CONFIG_PREEMPT=y CONFIG_PREEMPT_COUNT=y CONFIG_DEBUG_PREEMPT=y # CONFIG_PREEMPT_TRACER is not set我们将在这样一个场景中对比三种模式：两个线程T1和T2，T1优先级高（50），T2优先级低（30）；T1睡眠3秒；T2调用一个内核函数，这个内核函数是12秒的纯计算（没有IO）；之后返回用户态；No Forced Preemption (Server) (2)这是引入”Kernel Preemption”之前的状态（linux 2.6之前），就是没有内核抢占。它提升throughput但交互性差，所以适合server系统。这种模式下kernel不允许在内核模式下进行线程切换；线程切换只能发生在以下两种情形之一：a. 从内核态返回用户态的时候；b. 在内核模式下主动放弃CPU（等待资源）；所以，在前面假定的场景中：T1在内核函中不会被抢占（纯计算12秒），T2在12秒之后被唤醒（它只睡眠3秒，但12秒后才被唤醒，晚了9秒）。实验验证一下！实验的代码包括两部分：一个内核模块，其中一个函数持续使用CPU12秒（使用mdelay来模拟）；一个用户态程序，调用这个内核函数。结构如下：# tree preemption-demo/ preemption-demo/ ├── demo-module │ ├── Makefile │ └── demo.c └── userspace ├── Makefile ├── a.out └── read.cpreemption-demo/demo-module/demo.c:#include <linux/mempool.h> #include <linux/init.h> #include <linux/kernel.h> #include <linux/fs.h> #include <linux/kernel.h> #include <asm/uaccess.h> #include <linux/delay.h> static int major_num; static int device_open_count = 0; static char* msg_buffer = "this is a demo"; static char* msg_ptr; static ssize_t device_read(struct file *flip, char __user *buffer, size_t len, loff_t *offset) { int by…