Linux:简单聊聊线程调度

目录

  • 一、什么是线程调度?
  • 二、什么时候会进行线程调度?
  • 三、什么原因会导致某个线程调度延迟?
  • 四、内核抢占和抢占式调度
  • 五、应用层线程优先级设置过高会导致什么问题?

   在工作中,线程调度这个词我们经常提及,但是关于其稍微详细一点的知识很多人并不知道。我也一样,所以简单学习整理一下,在此分享!

一、什么是线程调度?

  在 Linux 中,线程调度是由内核管理的过程。内核负责根据预定义的调度策略和优先级来决定哪个线程在给定的时间点运行。它的目标是提高系统的并发能力、公平性和响应性。下面是一些关于 Linux 线程调度的详细信息:

  调度策略:Linux 提供了几种线程调度策略,包括:

  • SCHED_OTHER(默认):基于时间片轮转的策略,允许多个线程共享 CPU 时间。
  • SCHED_FIFO:先进先出策略,优先级高的线程会一直运行,直到它主动放弃 CPU。
  • SCHED_RR:基于时间片轮转的策略,优先级高的线程运行一段时间后被抢占。
Linux的调度策略主要有以下几种:2. O(1)调度策略:在Linux内核版本2.6之前,使用了O(1)调度策略。该策略利用了一个运行队列
数组,按照优先级将任务分配到不同的队列中,并使用时间片轮转的方式进行调度。然而,O(1)调
度策略在多核系统中存在性能问题,因此在2.6版本之后被淘汰。3. CFS调度策略:从Linux内核版本2.6.23开始,引入了CFS(Completely Fair Scheduler,完
全公平调度器)调度策略。CFS通过红黑树的数据结构来维护任务队列,每个任务都被赋予一个虚拟
运行时间。CFS追求公平性,即使在多核系统中也可以保证任务的公平调度。4. BFS调度策略:BFS(Brain Fuck Scheduler,天才调度器)是一种第三方的调度策略,主要
针对桌面应用和交互式应用的响应性能做了优化。BFS调度策略采用一种简单的先进先出(FIFO)
调度算法,并通过调整任务的优先级来提高交互式应用的响应速度。5. 实时调度策略:Linux内核还支持实时调度策略,包括FIFO(先进先出)和RR(时间片轮转)。
实时调度策略主要用于对时间敏感的应用,如音频和视频处理。关于Linux调度策略的历史演变,最早的Linux内核并没有较为复杂的调度策略,主要使用了简单
的时间片轮转调度算法。随着Linux的发展和应用场景的变化,人们对调度策略的需求也越来越高。
因此,Linux内核在不同的版本中不断优化和改进调度策略,引入了O(1)调度策略和CFS调度策略,
以满足不同应用的需求。此外,第三方的调度策略如BFS也为Linux用户提供了更多的选择。

  优先级:每个线程都被分配了一个优先级,较高优先级的线程将优先获得CPU的使用权。优先级的范围通常是0-139,其中0是最高优先级,139是最低优先级。0-99是实时线程。100-139为普通线程。该优先级数值是从内核角度来看的。应用层优先级是数值越大优先级越高。

  调度相关系统调用:在编写多线程程序时,可以使用以下系统调用来控制线程的行为:

在 Linux 系统中,线程调度相关的 API 函数主要包括以下几个:1. sched_yield():该函数允许一个线程主动放弃 CPU 时间片,让系统调度器重新选择一
个新的可运行线程来执行。调用该函数后,当前线程会进入可运行状态并让出 CPU。2. sched_setscheduler():该函数用于设置指定线程的调度策略。它接受三个参数:线程
标识符,调度策略和相关参数。常见的调度策略包括 SCHED_FIFO、SCHED_RR 和 SCHED_OTHER。3. sched_getscheduler():该函数用于获取指定线程的当前调度策略。它接受一个参数:线程
标识符。4. sched_get_priority_max()sched_get_priority_min():这两个函数分别
用于获取指定调度策略下的最大和最小优先级值。它们接受一个参数:调度策略。5. pthread_setschedparam():该函数用于设置指定线程的调度参数,包括调度策略和优先级。
它接受三个参数:线程标识符,调度策略和优先级。6. pthread_getschedparam():该函数用于获取指定线程的当前调度参数,包括调度策略和优先
级。它接受两个参数:线程标识符和一个用于存储调度参数的结构体。这些 API 函数可以通过编程语言提供的相应接口来调用,如 C 语言中的 `<sched.h>` 头文件和 
POSIX 线程 (`pthread`) 相关的函数。它们允许开发者根据需求设置线程的调度策略和优先级,
以及与线程调度相关的其他操作。请注意,在使用这些函数时,需要具备相应的权限和了解相关的
调度策略和参数设置。
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <sched.h>void *thread_func(void *arg)
{int thread_id = *(int *)arg;printf("Thread %d is running\n", thread_id);// 线程执行任务return NULL;
}int main()
{pthread_t thread1, thread2;int thread1_id = 1, thread2_id = 2;int sched_policy;struct sched_param sched_param;// 创建线程1pthread_create(&thread1, NULL, thread_func, (void *)&thread1_id);// 创建线程2pthread_create(&thread2, NULL, thread_func, (void *)&thread2_id);// 设置线程1的调度策略为 SCHED_FIFO,优先级为 80sched_policy = SCHED_FIFO;sched_param.sched_priority = 80;pthread_setschedparam(thread1, sched_policy, &sched_param);// 获取线程1的当前调度策略和优先级pthread_getschedparam(thread1, &sched_policy, &sched_param);printf("Thread 1 - Policy: %d, Priority: %d\n", sched_policy, sched_param.sched_priority);// 设置线程2的调度策略为 SCHED_RR,优先级为 50sched_policy = SCHED_RR;sched_param.sched_priority = 50;pthread_setschedparam(thread2, sched_policy, &sched_param);// 获取线程2的当前调度策略和优先级pthread_getschedparam(thread2, &sched_policy, &sched_param);printf("Thread 2 - Policy: %d, Priority: %d\n", sched_policy, sched_param.sched_priority);// 等待线程1和线程2结束pthread_join(thread1, NULL);pthread_join(thread2, NULL);return 0;
}

  线程亲和性:Linux 还支持线程亲和性,允许将线程绑定到特定的 CPU 核心,以提高缓存利用和性能。

二、什么时候会进行线程调度?

  在多线程编程中,线程可能会因为以下几种情况而被切换调度:

  1. 时间片用完:操作系统为每个线程分配一个时间片,当线程的时间片用完时,操作系统会将调度权交给其他线程。
  2. 阻塞操作:当线程执行了一个阻塞操作,例如等待输入、等待磁盘读写等,操作系统会将线程切换到阻塞状态,同时调度其他可运行线程。
  3. 优先级调度:线程的优先级可能会影响调度。当高优先级的线程就绪时,操作系统可能会将其切换到运行状态,同时调度低优先级的线程。
  4. 睡眠和唤醒:线程可以通过调用 sleep() 或 wait() 等方法主动让出CPU,进入睡眠状态。当休眠时间到期或条件满足时,线程会被唤醒并重新调度。
  5. 中断处理:当线程遇到硬件中断时,操作系统可能会中断当前线程的执行,切换到中断处理程序的上下文。

  需要注意的是,线程切换调度具体的实现方式和调度算法取决于操作系统。不同的操作系统可能有不同的调度机制和策略。以上列出的情况只是一些常见的导致线程切换调度的操作,具体行为可能会因操作系统的不同而有所差异。

三、什么原因会导致某个线程调度延迟?

  1. 调度器繁忙: 其他正在运行的线程过多,导致线程b等待的时间增加。在高负载系统中,调度器可能需要更多的时间来决定要切换到哪个线程。

  2. 锁竞争: 线程b所需要的资源(如共享的锁)可能正在被其他线程使用,导致线程b需要等待。这可能会导致线程切换的延迟增加。

  3. I/O操作阻塞: 线程b可能因为等待某个I/O操作(如磁盘读取、网络请求等)而被阻塞。这可能导致线程b等待的时间增加,从而延长了整个线程切换的时间。

  4. 优先级调度: 线程b的优先级可能较低,导致它在其他线程之后才能得到执行,这可能会延长线程切换的时间。

  5. 系统负载: 系统的整体负载可能较重,包括CPU、内存、I/O等资源都处于高负载状态,这可能导致线程切换的延迟增加。

  6. 频繁中断: 如果在某个线程调度前,频繁发生了多次中断,系统会优先处理中断,此时并不会去调度线程。

  综上所述,线程调度延迟的增加可能与系统的负载、资源竞争、优先级、I/O阻塞等因素有关。针对具体场景,可以通过性能分析工具、系统监控工具等来深入分析和定位问题所在。

四、内核抢占和抢占式调度

  内核抢占和抢占式调度是两个概念,在 Linux 中它们有不同的含义和作用:

  1. 内核抢占(Kernel Preemption):指的是在内核代码执行期间,允许更高优先级的内核代码打断当前正在执行的内核代码。这种抢占可以确保即使在内核空间执行期间,也能及时响应高优先级的内核任务。内核抢占主要涉及操作系统内核本身的代码执行,确保了内核的实时响应性和稳定性。

  2. 抢占式调度(Preemptive Scheduling):指的是操作系统内核允许更高优先级的进程或线程抢占当前正在执行的进程或线程。这种抢占保证了即使用户空间代码正在执行,也能及时响应更高优先级的任务请求。抢占式调度主要是面向用户空间的任务调度,提高了系统的实时性和并发处理能力。

  因此,内核抢占和抢占式调度是针对不同层面的任务调度和响应机制,各自有着独特的作用和意义。内核抢占确保了操作系统内核对内核级任务的即时响应,而抢占式调度则提供了用户空间任务对 CPU 的抢占能力,保证了系统的并发处理能力和实时性能。

小知识学习:什么是高负载
  高负载是指系统或设备承担的工作量或负荷过大,超出了其正常或设计能力范围的情况。在计算机领域,高负载通常指的是系统所承受的并发请求或任务量非常大,导致系统性能下降,响应时间延长,甚至出现系统崩溃或死机等问题。高负载可能由于资源不足、处理能力不足、网络拥堵、数据库负载过大等原因引起。

五、应用层线程优先级设置过高会导致什么问题?

  在 Linux 应用层将线程优先级设置得过高(比如设为 80-99 之间的数值)可能会导致以下问题:

  1. 系统整体性能下降: 设置线程的优先级过高会导致该线程相对于其他线程更容易获得 CPU 时间片,这可能会导致其他任务得到的 CPU 时间变少,从而影响系统整体的响应能力和性能。

  2. 饥饿问题: 高优先级线程有可能将其他低优先级线程“饿死”,即低优先级线程无法获得足够的CPU时间来执行,这可能导致系统中的某些任务无法得到有效执行,导致系统性能不均衡。

  3. 系统不稳定: 如果大量线程都将优先级设置得过高,导致 CPU 时间持续被高优先级线程占用,可能导致系统变得不稳定,甚至无法响应其他任务(包括系统关键任务)的请求。

  4. 实时性能下降: 如果系统中存在实时任务,将非实时任务的优先级设置得过高可能会导致实时任务无法按时得到执行,从而影响实时性能。

  因此,建议在设置线程优先级时,要谨慎考虑,并确保综合考虑系统的整体负载情况和各个线程的重要性,避免将线程优先级设置得过高,特别是在应用层。优先保证系统的整体稳定性和公平性,避免出现过度竞争和系统性能下降的问题。

  在 Linux 中,将应用层线程的优先级设置为最高值 99 通常不会直接影响系统内核线程的运行。内核线程通常具有更高的特权级别,并受到操作系统内核的管理和保护,因此不太容易受到应用层线程优先级设置的直接影响。

  欢迎大家指导和交流!如果我有任何错误或遗漏,请立即指正,我愿意学习改进。期待与大家一起进步!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/653792.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MyBatis详解(6)-- 分页及缓存

MyBatis详解&#xff08;6&#xff09; 分页缓存特点&#xff1a;限制&#xff1a;术语&#xff1a;加载分类立即加载&#xff1a;延迟加载&#xff1a;mybatis缓存&#xff1a; 缓存的适用性MyBatis 缓存分类一级缓存注意&#xff1a;一级缓存未命中二级缓存二级缓存的优劣自定…

java - 多态

多态&#xff1a;它允许不同类型的对象对同一方法进行不同的实现&#xff0c; 具体来说&#xff0c;多态性指的是&#xff1a;通过父类的引用变量&#xff0c;来引用子类的对象&#xff0c;从而实现对不同对象的统一操作。 多态&#xff0c;只适用于方法&#xff0c;不适用于属…

php小数四舍五入、向上取整、向下取整

简单示例 1、round 对浮点数进行四舍五入 $result round(3.456, 2); var_dump($result);// 输出结果为&#xff1a;double(3.46)$result round(3.451, 2); var_dump($result);// 输出结果为&#xff1a;double(3.45) 2、ceil 进一取整:向上取整返回下一个最高的整数 $r…

qq通讯录怎么关闭?QQ好友删除了怎么恢复?

在QQ中&#xff0c;通讯录是我们管理好友和进行聊天的重要工具&#xff0c;但有时候我们可能需要一些隐私保护&#xff0c;不让一些用户通过手机通讯录添加自己。如果您正在思考qq通讯录怎么关闭以及恢复意外删除的好友&#xff0c;本文将为您详细介绍如何关闭QQ通讯录和恢复被…

php实现多进程的几种方式

目录 一&#xff1a;使用pcntl扩展库 二&#xff1a;使用Swoole扩展 三&#xff1a;使用多进程模式PHP-FPM 在PHP中实现多进程主要有以下几种方式&#xff1a; 一&#xff1a;使用pcntl扩展库 pcntl扩展库提供了多线程相关的函数&#xff0c;如pcntl_fork()用于创建子进程…

详解操作系统各章大题汇总(死锁资源分配+银行家+进程的PV操作+实时调度+逻辑地址->物理地址+页面置换算法+磁盘调度算法)

文章目录 第三章&#xff1a;死锁资源分配图例一例二 第三章&#xff1a;银行家算法第四章&#xff1a;进程的同步与互斥做题步骤PV操作的代码小心容易和读者写者混 1.交通问题&#xff08;类似读者写者&#xff09;分析代码 2.缓冲区问题&#xff08;第二个缓冲区是复制缓冲区…

RK3568驱动指南|驱动基础进阶篇-进阶6 内核运行ko文件实验——系统调用

瑞芯微RK3568芯片是一款定位中高端的通用型SOC&#xff0c;采用22nm制程工艺&#xff0c;搭载一颗四核Cortex-A55处理器和Mali G52 2EE 图形处理器。RK3568 支持4K 解码和 1080P 编码&#xff0c;支持SATA/PCIE/USB3.0 外围接口。RK3568内置独立NPU&#xff0c;可用于轻量级人工…

Idea编写mapper.xml文件提示表名和字段

一、连接database 二、setting- > language -> sql Dialects中 的选项设为 mysql就可以了 三、测试

从零开始做题:逆向 ret2shellcode orw

1.题目信息 BUUCTF在线评测 下载orw时防病毒要关闭 2.题目分析 orw是open、read、write的简写。有时候binary会通过prctl、seccomp进行沙箱保护&#xff0c;并不能getshell。只能通过orw的方式拿到flag。 fdopen&#xff08;‘./flag’); # 打开flag文件&#xff0c;得到fd…

解决Qt连接不上mysql数据库

问题: QSqlDatabase: QMYSQL driver not loaded QSqlDatabase: available drivers: QSQLITE QODBC QODBC3 QPSQL QPSQL7 下载网盘中的三个文件&#xff08;网盘链接在文章结尾&#xff09;&#xff1a;qsqlmysql.dll、qsqlmysqld.qll、libmysql.dll找到你安装Qt的目录&#xff…

Android MTE技术详解

1.MTE概念 MTE&#xff08;内存标记扩展&#xff09;是ARM v8.5-A新增的一项缓解内存安全的机制。在Android Linux现有的安全机制中&#xff0c;类似的机制有ASAN、HWSAN。但两者因为性能开销代价高昂&#xff0c;不适用于广泛部署&#xff08;仅调试使用&#xff09;。MTE当前…

语义分割 | 基于 VGG16 预训练网络和 Segnet 架构实现迁移学习

Hi&#xff0c;大家好&#xff0c;我是源于花海。本文主要使用数据标注工具 Labelme 对猫&#xff08;cat&#xff09;和狗&#xff08;dog&#xff09;这两种训练样本进行标注&#xff0c;使用预训练模型 VGG16 作为卷积基&#xff0c;并在其之上添加了全连接层。基于标注样本…

【算法专题】动态规划综合篇

动态规划7.0 1. 最长公共子序列2. 不相交的线3. 不同的子序列4. 通配符匹配5. 正则表达式匹配6. 交错字符串7. 两个字符串的最小ASCII删除和8. 最长重复子数组 1. 最长公共子序列 题目链接 -> Leetcode -1143.最长公共子序列 Leetcode -1143.最长公共子序列 题目&#xf…

单张图像三维重建RealFusion 360◦ Reconstruction of Any Object from a Single Image

Luke Melas-Kyriazi&#xff0c; Iro Laina&#xff0c; Christian Rupprecht&#xff0c; Andrea Vedaldi.RealFusion 360◦ Reconstruction of Any Object from a Single Image。RealFusion: 360 Reconstruction of Any Object from a Single Image Abstract We consider th…

升降机SEW MOVIDRIVE变频器设置

SEW变频器Startup 安装好MOVITOOL软件 打开项目开始配置通信连接 以太网连接为例 在此栏输入需要连接的SEW变频器的IP地址,例如:172.25.20.120 设置完成单击“OK” 当设置完成后,请一路OK回到软件管理画面 通信配置正确的软件界面状态 单击红色刷新图标 一切准备就绪后…

DjangoURL调度器(一)

一、介绍 当一个用户请求 Django 站点的一个页面&#xff0c;下面是 Django 系统决定执行哪个 Python 代码使用的算法&#xff1a; Django确定要使用的根URLconf模块&#xff0c;一般是在settings中的ROOT_URLCONF设置的值&#xff0c;但是如果传入 HttpRequest 对象具有一个ur…

Pytest 识别case规则

一、Python测试框架&#xff0c;主要特点有以下几点&#xff1a; 简单灵活&#xff0c;容易上手&#xff1b;支持参数化&#xff1b;能够支持简单的单元测试和复杂的功能测试&#xff0c;还可以用来做selenium/appnium等自动化测试、接口自动化测试&#xff08;pytestrequests…

C#简单使用Yolov5的Onnx格式模型进行目标检测

背景 最近要离职了&#xff0c;同事需要了解一下C#如何使用yolov5系列onnx格式模型进行目标检测&#xff0c;由于其对C#不熟练&#xff0c;可能会影响公司后续的开发进度&#xff0c;所以趁着还在&#xff0c;赶紧把手尾搞好。 方案 1、创建一个C# DotNet 8 控制台项目[可千…

LabVIEW动态数据交换实现数据通信

LabVIEW动态数据交换实现数据通信 介绍了LabVIEW软件在驱动一般多功能接口卡中的应用。LabVIEW作为一种图形化编程平台&#xff0c;被广泛应用于自动测量系统、工业过程自动化等领域。利用LabVIEW驱动实验室中常用的多功能接口卡&#xff0c;以实现数据采集和分析。 系统主要…

EtherCAT主站SOEM -- 18 --Qt-Soem通过CSV模式(周期同步速度模式)控制一个电机转圈圈

EtherCAT主站SOEM -- 18 --Qt-Soem通过CSV模式(周期同步速度模式)控制一个电机转圈圈 0 QT-SOEM视频预览及源代码下载:0.1 QT-SOEM视频预览0.2 QT-SOEM源代码下载1 程序文件修改替换1.1 allvalue.h1.2 motrorcontrol.h1.3 mainwindow.cpp1.4 motrorcontrol.cpp2 ui界面显示该…