从内核调度算法的层面进行线程设计

线程是如何运行的

线程是内核调度的最小单元,每一个线程创建都会在内核实例出一个thread_info结构体,结构体中有一个task_struct结构体。这两个结构体描述着线程的信息。当中断触发调度时,schedule函数开始在几种不同类型的线程之间进行调度选择,在相同类型的线程之间进行调度算法选择。task_struct中描述着线程的类型,和调度方法,等各种信息。
详细可参考:
https://blog.csdn.net/weixin_42427338/article/details/89323122
https://blog.csdn.net/yangming2466/article/details/83280643

什么决定线程的运行的顺序

线程的优先级决定着线程运行的顺序,不同的线程优先级与调度算法不同。

实时进程

实时调度算法SCHED_FIFO和SCHED_RR。使用top命令,如果PR列的值为RT,则说明该进程采用的是实时策略,即调度策略是SCHED_FIFO或者为SCHED_RR,而对于非实时调度策略(比如SCHED_OTHER)的进程,该列的值是NI+20,以供Linux内核使用。我们可以通过命令:

ps -eo state,uid,pid,ppid,rtprio,time,comm

来查看进程对应的实时优先级(位于RTPRIO列下),如果有进程对应的列显示“-”,则说明它不是实时进程。注意任何实时策略进程的优先级都高于普通的进程,也就说实时优先级和nice优先级处于互不相交的两个范畴。

SCHED_FIFO:

先入先出调度策略(First in-first out scheduling)。该策略简单的说就是一旦线程占用cpu则一直运行,一直运行直到有更高优先级任务到达或自己放弃。

SCHED_RR:

时间片轮转调度(Round-robin scheduling)。该策略是SCHED_FIFO基础上改进来的,他给每个线程增加了一个时间片限制,当时间片用完后,系统将把该线程置于队列末尾。放在队列尾保证了所有具有相同优先级的RR任务的调度公平。此调度会比普通进程的时间片要长。

普通进程

大多数的普通进程,用的就是CFS算法

CFS:

自行百度

小结

实时线程的优先级肯定高于普通线程。调度算法会在运行队列中先调取实时进程来执行,而后才轮到普通进程。我们可根据任务的类型选择索要开创的线程是SCHED_FIFO还是SCHED_RR或是一个普通线程。

代码展示线程

在线程中更改调度算法

C语言代码。c++的话可以自己去封装一个线程类

#include <sched.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <pthread.h>
#include <unistd.h>

void * threadFun(void *)
{
    printf("the thread init scheduler = %d \n",sched_getscheduler(0));
    // 尽可能高的实时优先级
    struct sched_param my_params;
    my_params.sched_priority = sched_get_priority_max(SCHED_FIFO);
    if (sched_setscheduler(0,SCHED_FIFO,&my_params) < 0) {
        printf("threadFun sched_setscheduler fail\n");
    }

    while (1) {
        printf("the thread scheduler = %d \n",sched_getscheduler(0));
        sleep(1);
    }
    return nullptr;
}

int main()
{
    // 获取当前线程的调度算法
    printf("the main init scheduler = %d \n",sched_getscheduler(0));
    pthread_t id;
    pthread_attr_t attr; // 定义属性变量
    pthread_attr_init(&attr); // 初始化
    // 设置线程为分离属性,不用join回收资源
    pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);
    // 设置线程继承属性为手动设置调度策略,否则调度策略继承父亲的
    pthread_attr_setinheritsched(&attr,PTHREAD_EXPLICIT_SCHED);
    /* *** 设置调度策略,必须得先设置继承属性 *** */
    struct sched_param my_params;
    my_params.sched_priority = sched_get_priority_max(SCHED_RR);
    // 设置调度策略
    pthread_attr_setschedpolicy(&attr, SCHED_RR);
    // 设置策略优先级参数
    pthread_attr_setschedparam(&attr, &my_params);
    printf("get sched_priority is %d\n", my_params.sched_priority);
    if (0 != pthread_create(&id, &attr, threadFun, nullptr)) {
        printf("set policy fail\n");
    }

    while (1) {
        printf("the main scheduler = %d \n",sched_getscheduler(0));
        sleep(1);
    }
}

程序输出(程序执行时要加 sudo)

the main init scheduler = 0 // 主线程初始为一个普通线程
get sched_priority is 99 
the main scheduler = 0 
the thread init scheduler = 2 // 线程被我们以SCHED_RR策略创建
the thread scheduler = 1 // 线程自己动态修改调度策略SCHED_FIFO
the thread scheduler = 1 
the main scheduler = 0 
the thread scheduler = 1 
the main scheduler = 0 
the thread scheduler = 1 

普通进程通过nice值来设置优先级

ps 命令简介

输入ps -l 如下
在这里插入图片描述
ps命令各个参数的含有详情可自己搜索这里只简单介绍。此实验使用
ps -l -p (查看指定线程的信息)

UID:启动这些进程的用户。
PID:进程的进程ID。
PPID:父进程的进程号(图中的终端就是bash,在此终端运行的程序的ppid都是他的pid:5644)。
C:进程生命周期中的CPU利用率。
STIME:进程启动时的系统时间。
TTY:进程启动时的终端设备。
TIME:运行进程需要的累计CPU时间。
CMD:启动的程序名称。
F:内核分配给进程的系统标记。
S:进程的状态(O代表正在运行;S代表在休眠;R代表可运行,正等待运行;Z代表僵化,进程已结束但父进程已不存在;T代表停止)。
PRI:进程的优先级(越大的数字代表越低的优先级)。
NI:谦让度值用来参与决定优先级。
ADDR:进程的内存地址。
SZ:假如进程被换出,所需交换空间的大致大小。
WCHAN:进程休眠的内核函数的地址。

实时进程和普通进程设置nice值后得变化

在LINUX系统中,Nice值的范围从-20到+19(不同系统的值范围是不一样的),正值表示低优先级,负值表示高优先级,值为零则表示不会调整该进程的优先级。具有最高优先级的程序,其nice值最低,所以在LINUX系统中,值-20使得一项任务变得非常重要;与之相反,如果任务的nice为+19,则表示它是一个高尚的、无私的任务,允许所有其他任务比自己享有宝贵的CPU时间的更大使用份额,这也就是nice的名称的来意。

int main()
{
    int a = getpid();
    printf("pid is %d \n", getpid());
    std::string comd1 = "renice -n 2 -p " + std::to_string(a);
    std::string comd2 = "renice -n -6 -p " + std::to_string(a);

    // 暂停住程序
    printf("初始默认进程\n");
    getchar();
    // 修改 nice 值 2
    system(comd1.c_str());

    // 暂停住程序
    printf("修改nice值为2\n");
    getchar();
    // 设置线程为实时线程后修改nice值
    struct sched_param my_params;
    my_params.sched_priority = sched_get_priority_max(SCHED_FIFO);
    if (sched_setscheduler(0,SCHED_FIFO,&my_params) < 0) {
        printf("sched_setscheduler fail\n");
    }

    // 暂停住程序
    printf("修改线程为实时线程\n");
    getchar();
    // 修改 nice 值 -6
    system(comd2.c_str());
    printf("修改实时线程nice值为-6\n");
    while (true);
}

程序运行(必须有系统权限):
在这里插入图片描述

程序输出:
在这里插入图片描述

小结

由此可看出,普通进程的nice值,过代码中系统调用修改后可改变优先级PRI,但是实时进程修改nice值不改变。原因是因为进程不同优先级算法不同

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值